`
diff --git a/docs/images/box1-multi.png b/docs/images/box1-multi.png
new file mode 100644
index 0000000000..2da7320667
Binary files /dev/null and b/docs/images/box1-multi.png differ
diff --git a/docs/images/box1-single.png b/docs/images/box1-single.png
new file mode 100644
index 0000000000..c5c802d486
Binary files /dev/null and b/docs/images/box1-single.png differ
diff --git a/docs/installation.md b/docs/installation.md
index b36174e665..333a1e72bc 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -1,88 +1,191 @@
+---
+deeplabcut:
+ last_content_updated: '2026-02-23'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
(how-to-install)=
# How To Install DeepLabCut
-DeepLabCut can be run on Windows, Linux, or MacOS (see also [technical considerations](tech-considerations-during-install) and if you run into issues also check out the [Installation Tips](https://deeplabcut.github.io/DeepLabCut/docs/recipes/installTips.html) page).
+- **DeepLabCut can be run on Windows, Linux, or MacOS as long as you have Python 3.10 installed**
+ - (see also [technical considerations](tech-considerations-during-install) and if you run into issues also check out the [Installation Tips](https://deeplabcut.github.io/DeepLabCut/docs/recipes/installTips.html) page).
+- 🚧 Please note, there are several modes of installation:
+ - please decide to either use a [**conda environment**](https://deeplabcut.github.io/DeepLabCut/docs/installation.html#conda-the-installation-process-is-as-easy-as-this-figure) based installation (**recommended**),
+ - or the supplied [**Docker container**](docker-containers) (recommended for Ubuntu advanced users).
+- 🚀 Please note, you will get the best performance with using a **GPU**!
+ - Please see the section on [GPU support](https://deeplabcut.github.io/DeepLabCut/docs/installation.html#gpu-support) to install your GPU driver and CUDA.
-We recommend using our supplied CONDA environment.
+```{Hint} Familiar with python packages and conda? Quick Install Guide:
-## PIP:
+This assumes you have `conda`/`mamba` installed and this will install DeepLabCut in a fresh
+environment. If you have an NVIDIA GPU, install PyTorch according to [their instructions
+](https://pytorch.org/get-started/locally/) (with your desired CUDA version) - you just
+need your GPU drivers installed.
-- Everything you need to build custom models within DeepLabCut (i.e., use our source code and our dependencies) can be installed with `pip install 'deeplabcut[gui,tf]'` (for GUI support w/tensorflow) or without the gui: `pip install 'deeplabcut[tf]'`.
-- If you want to use the SuperAnimal models, then please use `pip install 'deeplabcut[gui,tf,modelzoo]'`.
+```bash
+conda create -n DEEPLABCUT python=3.12
+conda activate DEEPLABCUT
+
+# install PyTorch with your desired CUDA version (or for CPU only) - check [their
+](https://pytorch.org/get-started/locally/) website:
+# GPU version of pytorch for CUDA 11.3
+conda install pytorch cudatoolkit=11.3 -c pytorch
+
+
+# install the latest version of DeepLabCut
+pip install --pre deeplabcut
+# or if you want to use the GUI
+pip install --pre deeplabcut[gui]
+
+# ONLY IF YOU HAVE A CUDA GPU - check that PyTorch can access your GPU; this
+# should print `True`
+python -c "import torch; print(torch.cuda.is_available())"
+```
+
+- If you're familiar with the command line and want TensorFlow support, look [below](
+deeplabcut-with-tf-install) for a fresh installation that has worked for us (on Linux)
+and makes it possible to use the GPU with both PyTorch and TensorFlow.
-- Please note, there are several modes of installation, and the user should decide to either use a **system-wide** (see [note below](system-wide-considerations-during-install)), **conda environment** based installation (**recommended**), or the supplied [**Docker container**](docker-containers) (recommended for Ubuntu advanced users). One can of course also use other Python distributions than Anaconda, but **Anaconda is the easiest route.**
## CONDA: The installation process is as easy as this figure! -->
-### Step 1: You need to have Python installed
+### 🚨 Before you start with our conda file, do you have a GPU?
+````{admonition} 🚨 Click here for more information!
+:class: dropdown
+- We recommend having a GPU if possible!
+- You **need to decide if you want to use a CPU or GPU for your models**: (Note, you can also use the CPU-only for project management and labeling the data! Then, for example, use Google Colaboratory GPUs for free (read more [here](https://github.com/DeepLabCut/DeepLabCut/tree/master/examples#demo-4-deeplabcut-training-and-analysis-on-google-colaboratory-with-googles-gpus) and there are a lot of helper videos on [our YouTube channel!](https://www.youtube.com/playlist?list=PLjpMSEOb9vRFwwgIkLLN1NmJxFprkO_zi)).
+
+ - **CPU?** Great, jump to the next section below!
+
+ - **NVIDIA GPU?** If you want to use your own GPU (i.e., a GPU is in your workstation), then you need to be sure you have a CUDA compatible GPU, CUDA, and cuDNN installed. Please note, which CUDA you install depends on what version of PyTorch you want to use. So, please check "GPU Support" below carefully. **Note, DeepLabCut is up to date with the latest CUDA and PyTorch!**
+
+ - **Apple M-chip GPU?** Be sure to install miniconda3, and your GPU will be used by default.
+````
+
+### Step 1: Install Python via Anaconda
-#### Install [anaconda](https://www.anaconda.com/distribution/) or use miniconda3 (ideal for MacOS users)!
+### Install [anaconda](https://docs.conda.io/projects/conda/en/latest/user-guide/install/index.html#), or use miniconda3 for MacOS users (see below)
- Anaconda is an easy way to install Python and additional packages across various operating systems. With Anaconda you create all the dependencies in an [environment](https://conda.io/docs/user-guide/tasks/manage-environments.html) on your machine.
```{Hint}
-Download anaconda for your operating system: https://www.anaconda.com/distribution/.
+Download anaconda for your operating system: [anaconda.com/download/
+](https://www.anaconda.com/download/)
```
+- IF you use a M1 or M2 chip in your MacBook with v12.5+ (typically 2020 or newer machines), we recommend **miniconda3,** which operates with the same principles as anaconda. This is straight forward and explained in detail here: https://docs.conda.io/projects/conda/en/latest/user-guide/install/macos.html. But in short, open the program "terminal" and copy/paste and run the code that is supplied below.
-- IF you use a M1 or M2 chip in your MacBook with v12.5+ (typically 2020 or newer machines), you should use **miniconda3,** which operates with the same principles as anaconda. This is straight forward and explained in detail here: https://docs.conda.io/projects/conda/en/latest/user-guide/install/macos.html. But in short, open the program "terminal" and copy/paste and run:
-
-```
-wget https://repo.anaconda.com/miniconda/Miniconda3-py39_4.12.0-MacOSX-arm64.sh -O ~/miniconda.sh
+### 💡 miniconda for Mac
+````{admonition} Click the button to see code for miniconda for Mac
+:class: dropdown
+wget https://repo.anaconda.com/miniconda/Miniconda3-py310_4.12.0-MacOSX-arm64.sh -O ~/miniconda.sh
bash ~/miniconda.sh -b -p $HOME/miniconda
source ~/miniconda/bin/activate
conda init zsh
-```
+````
-#### We recommend having a GPU.
+### Step 2: Build an Env using our Conda file!
-- You **need to decide if you want to use a CPU or GPU for your models**: (Note, you can also use the CPU-only for project management and labeling the data! Then, for example, use Google Colaboratory GPUs for free (read more [here](https://github.com/DeepLabCut/DeepLabCut/tree/master/examples#demo-4-deeplabcut-training-and-analysis-on-google-colaboratory-with-googles-gpus) and there are a lot of helper videos on [our YouTube channel!](https://www.youtube.com/playlist?list=PLjpMSEOb9vRFwwgIkLLN1NmJxFprkO_zi)).
-
- - **CPU?** Great, jump to the next section below!
-
- - **NVIDIA GPU?** If you want to use your own GPU (i.e., a GPU is in your workstation), then you need to be sure you have a CUDA compatible GPU, CUDA, and cuDNN installed. Please note, which CUDA you install depends on what version of tensorflow you want to use. So, please check "GPU Support" below carefully. **Note, DeepLabCut is up to date with the latest CUDA and tensorflow versions!**
-
- - **Apple M1/M2 GPU?** Be sure to install miniconda3, and your GPU will be used by default.
-
-### Step 2: please use our supplied conda environment
+You simply need to have this `.yaml` file anywhere locally on your computer. So, let's download it!
```{Hint}
Windows users: Be sure you have `git` installed along with anaconda: https://gitforwindows.org/
```
-- **Windows/Linux/MacBooks:** git clone this repo (in the terminal/cmd program, while **in a folder** you wish to place DeepLabCut
-To git clone type: ``git clone https://github.com/DeepLabCut/DeepLabCut.git``). Note, this can be anywhere, even downloads is fine.)
+- TO DIRECTLY DOWNLOAD THE CONDA FILE conda:
+
+ - click ➡️ for [CONDA FILE](https://github.com/DeepLabCut/DeepLabCut/blob/main/conda-environments/DEEPLABCUT.yaml#:~:text=Raw%20file%20content-,Download,-%E2%8C%98) and then click the "..." and select Download
+
+
+- **Now, in Terminal (or Anaconda Command Prompt for Windows users)**, if you clicked to download, go to your downloads folder.
```{Hint}
Windows users: Be sure to open the program terminal/cmd/anaconda prompt with a RIGHT-click, "open as admin"
```
-- Now, in Terminal (or Anaconda Command Prompt for Windows users), go to the DeepLabCut folder. If you cloned the repo onto your Desktop, the command may look like:
-
+```{Hint}
+:class: dropdown
+If you cloned the repo onto your Desktop, the command may look like:
``cd C:\Users\YourUserName\Desktop\DeepLabCut\conda-environments``
-
-To get the location right, a cool trick is to drag the folder and drop it into Terminal. Alternatively, you can (on Windows) hold SHIFT and right-click > Copy as path, or (on Mac) right-click and while in the menu press the OPTION key to reveal Copy as Pathname.
-
-- Now, in the terminal run (Windows/Linux/MacBook Intel chip):
+You can (on Windows) hold SHIFT and right-click > Copy as path, or (on Mac) right-click and while in the menu press the OPTION key to reveal Copy as Pathname.
+```
+Be sure you are in the folder that has the `.yaml` file, then run:
``conda env create -f DEEPLABCUT.yaml``
-- or for Apple M1 / M2 chips:
-
-``conda env create -f DEEPLABCUT_M1.yaml``
-- You can now use this environment from anywhere on your comptuer (i.e., no need to go back into the conda- folder). Just enter your environment by running:
- - Ubuntu/MacOS: ``source/conda activate nameoftheenv`` (i.e. on your Mac: ``conda activate DEEPLABCUT`` or ``conda activate DEEPLABCUT_M1``)
+- You can now use this environment from anywhere on your computer (i.e., no need to go back into the conda- folder). Just enter your environment by running:
+ - Ubuntu/MacOS: ``source/conda activate nameoftheenv`` (i.e. on your Mac: ``conda activate DEEPLABCUT``)
- Windows: ``activate nameoftheenv`` (i.e. ``activate DEEPLABCUT``)
-Now you should see (`nameofenv`) on the left of your terminal screen, i.e. ``(DEEPLABCUT_M1) YourName-MacBook...``
+Now you should see (`nameofenv`) on the left of your terminal screen, i.e. ``(DEEPLABCUT) YourName-MacBook...``
NOTE: no need to run pip install deeplabcut, as it is already installed!!! :)
-**Great, that's it! DeepLabCut is installed!**
+(deeplabcut-with-tf-install)=
+### 💡 Notice: PyTorch and TensorFlow Support within DeepLabCut
+
+````{admonition} DeepLabCut TensorFlow Support
+:class: dropdown
+As of June 2024 we have a PyTorch Engine backend and we will be depreciating the
+TensorFlow backend by the end of 2024. Currently, if you want to use TensorFlow, you
+need to run `pip install deeplabcut[tf]` in order to install the correct version of
+TensorFlow in your conda env. Please note, we will be providing bug fixes, but we will
+not be supporting new TensorFlow versions beyond 2.10 (Windows), and 2.12 for other OS.
-Next, [head over to the Docs to decide which mode to use DeepLabCut in. You have both standard and multi-animal installed.](https://deeplabcut.github.io/DeepLabCut/docs/README.html)
+Installing TensorFlow and getting it to have access to the GPU can be a bit tricky.
+Check TensorFlow's [compatibility matrix](https://www.tensorflow.org/install/source#gpu)
+to know which version of CUDA and cuDNN you should install.
+
+We have found that installing DeepLabCut with the following commands works well for
+Linux users to install PyTorch 2.3.1, TensorFlow 2.12, CUDA 11.8 and cuDNN 8 in a Conda
+environment:
+
+```bash
+conda create -n deeplabcut-with-tf "python=3.10"
+conda activate deeplabcut-with-tf
+
+# Install the desired TensorFlow version, built for CUDA 11.8 and cuDNN 8
+pip install "tensorflow==2.12" "tensorpack>=0.11" "tf_slim>=1.1.0"
+
+# Install PyTorch with a version using CUDA 11.8 and cuDNN 8
+pip install "torch==2.3.1" torchvision --index-url https://download.pytorch.org/whl/cu118
+
+# Create symbolic links to NVIDIA shared libraries for TensorFlow
+# -> as described in their installation docs:
+# https://www.tensorflow.org/install/pip#step-by-step_instructions
+
+pushd $(dirname $(python -c 'print(__import__("tensorflow").__file__)'))
+ln -svf ../nvidia/*/lib/*.so* .
+popd
+
+pip install --pre deeplabcut
+```
+````
+
+**Great, that's it! DeepLabCut is installed!** 🎉💜
+
+
+### Step 3: Really, that's it! Let's run DeepLabCut
+
+Head over to the [User Guide Overview](https://deeplabcut.github.io/DeepLabCut/docs/UseOverviewGuide.html) for information.
+
+🎉 Launch DeepLabCut in your new env by running `python -m deeplabcut`
+
+## Other ways to install DeepLabCut and additional tips
+
+### Alternatively, you can git clone this repo and install from source!
+i.e., if the download did not work or you just want to have the source code handy!
+
+- **Windows/Linux/MacBooks:** git clone this repo (in the terminal/cmd program, while **in a folder** you wish to place DeepLabCut
+To git clone type: ``git clone https://github.com/DeepLabCut/DeepLabCut.git``). Note, this can be anywhere, even downloads is fine.)
+- Then follow the same steps as in Step 2 above, adjusting for the file now being in the downloaded folder.
+
+### PIP:
+
+- Everything you need to build custom models within DeepLabCut (i.e., use our source code and our dependencies) can be installed with `pip install 'deeplabcut[gui]'` (for GUI support w/PyTorch) or without the gui: `pip install 'deeplabcut'`.
+- If you want to use the SuperAnimal models, then please use `pip install 'deeplabcut[gui,modelzoo]'`.
## DOCKER:
@@ -90,16 +193,31 @@ Next, [head over to the Docs to decide which mode to use DeepLabCut in. You have
## Pro Tips:
-More [installation ProTips](installTips) are also available.
+More [installation ProTips](installation-tips) are also available.
-If you ever want to update your DLC, just run `pip install --upgrade deeplabcut` once you are inside your env. If you want to use a specific release, then you need to specify the version you want, such as `pip install deeplabcut==2.2`. Once installed, you can check the version by running `import deeplabcut` `deeplabcut.__version__`. Don't be afraid to update, DLC is backwards compatible with your 2.0+ projects and performance continues to get better and new features are added nearly monthly.
+If you ever want to update your DLC, just run `pip install --upgrade deeplabcut` once
+you are inside your env. If you want to use a specific release, then you need to specify
+the version you want, such as `pip install deeplabcut==3.0`. Once installed, you can
+check the version by running `import deeplabcut` `deeplabcut.__version__`. Don't be
+afraid to update, DLC is backwards compatible with your 2.0+ projects and performance
+continues to get better and new features are added nearly monthly.
-Here are some conda environment management tips: https://kapeli.com/cheat_sheets/Conda.docset/Contents/Resources/Documents/index
+**All of the data you labelled in version 2.X is also compatible with version 3+ and the
+PyTorch engine**! There is no change in the workflow or the way labels are handled: the
+big changes happen under-the-hood! If you've been working with DeepLabCut 2.X and want
+to learn more about moving to the PyTorch engine, checkout our docs on [moving from
+TensorFlow to PyTorch](dlc3-user-guide)
-**Pro Tip:** If you want to modify code and then test it, you can use our provided testscripts. This would mean you need to be up-to-date with the latest GitHub-based code though! Please see [here](installTips) on how to get the latest GitHub code, and how to test your installation by following this video: https://www.youtube.com/watch?v=IOWtKn3l33s.
+Here are some conda environment management tips: [kapeli.com: Conda Cheat Sheet](
+https://kapeli.com/cheat_sheets/Conda.docset/Contents/Resources/Documents/index)
+**Pro Tip:** If you want to modify code and then test it, you can use our provided
+testscripts. This would mean you need to be up-to-date with the latest GitHub-based code
+though! Please see [here](installation-tips) on how to get the latest GitHub code, and
+how to test your installation by following this video:
+https://www.youtube.com/watch?v=IOWtKn3l33s.
-### Creating your own customized conda env (recommended route for Linux: Ubuntu, CentOS, Mint, etc.)
+## Creating your own customized conda env (recommended route for Linux: Ubuntu, CentOS, Mint, etc.)
*Note in a fresh ubuntu install, you will often have to run: ``sudo apt-get install gcc python3-dev`` to install the GNU Compiler Collection and the python developing environment.
@@ -107,13 +225,12 @@ Some users might want to create their own customize env. - Here is an example.
In the terminal type:
-`conda create -n DLC python=3.8`
+`conda create -n DLC python=3.10`
-**Current version:** The only thing you then need to add to the env is deeplabcut (`pip install deeplabcut[tf]`) or `pip install 'deeplabcut[gui,tf]'` which has a pyside/napari based GUI.
+**Current version:** The only thing you then need to add to the env is deeplabcut (
+`pip install deeplabcut`) or `pip install 'deeplabcut[gui]'` which has a napari based
+GUI.
-**Pre-version2.3 (Dec 2022):** The only thing you then need to add to the env is deeplabcut (`pip install deeplabcut`) or `pip install 'deeplabcut[gui]'` which has wxPython for GUI support. For Windows and MacOS, you just run `pip install -U wxPython<4.1.0` but for linux you might need the specific wheel (https://wxpython.org/pages/downloads/index.html).
-
-We have some tips for linux users here, as the latest Ubuntu doesn't easily support a 1-click install: https://deeplabcut.github.io/DeepLabCut/docs/recipes/installTips.html
## **GPU Support:**
@@ -121,35 +238,51 @@ The ONLY thing you need to do **first** if you have an NVIDIA GPU and the matchi
- CUDA: https://developer.nvidia.com/cuda-downloads (just follow the prompts here!)
- DRIVERS: https://www.nvidia.com/Download/index.aspx
-#### The most common "new user" hurdle is installing and using your GPU, so don't get discouraged!
+### The most common "new user" hurdle is installing and using your GPU, so don't get discouraged!
-**CRITICAL:** If you have a GPU, you should FIRST **install the NVIDIA CUDA package and an appropriate driver for your specific GPU**, then you can use the supplied conda file. Please follow the instructions found here https://www.tensorflow.org/install/gpu, and more tips below, to install the correct version of CUDA and your graphic card driver. The order of operations matters.
+**CRITICAL:** If you have a GPU, you should FIRST **install an appropriate driver for
+your specific GPU**, then you can use the supplied conda file. You'll need an NVIDIA GPU
+which is compatible with CUDA. To see a list of CUDA-enabled NVIDIA GPUs, please [see
+their website](https://developer.nvidia.com/cuda-gpus).
-- Here we provide notes on how to install and check your GPU use with TensorFlow (which is used by DeepLabCut and already installed with the Anaconda files above). Thus, you do not need to independently install tensorflow.
+- Here we provide notes on how to install and check your GPU use with TensorFlow (which
+is used by DeepLabCut and already installed with the Anaconda files above). Thus, you do
+not need to independently install tensorflow.
+**FIRST**, install a driver for your GPU. Find DRIVER HERE:
+https://www.nvidia.com/download/index.aspx
-**FIRST**, install a driver for your GPU. Find DRIVER HERE: https://www.nvidia.com/download/index.aspx
-- check which driver is installed by typing this into the terminal: ``nvidia-smi``.
+- Check which driver is installed by typing this into the terminal: ``nvidia-smi``.
**SECOND**, install CUDA: https://developer.nvidia.com/ (Note that cuDNN, https://developer.nvidia.com/cudnn, is supplied inside the anaconda environment files, so you don't need to install it again).
**THIRD:** Follow the steps above to get the `DEEPLABCUT` conda file and install it!
-##### Notes:
-
- - **All of the TensorFlow versions work with DeepLabCut**. But, please be mindful different versions of TensorFlow require different CUDA versions.
- - As the combination of TensorFlow and CUDA matters, we strongly encourage you to **check your driver/cuDNN/CUDA/TensorFlow versions** [on this StackOverflow post](https://stackoverflow.com/questions/30820513/what-is-version-of-cuda-for-nvidia-304-125/30820690#30820690).
- - To check your GPU is working, in the terminal, run:
-
- `nvcc -V` to check your installed version(s).
-
-- The best practice is to then run the supplied `testscript.py` (this is inside the examples folder you acquired when you git cloned the repo). Here is more information/a short [video on running the testscript](https://www.youtube.com/watch?v=IOWtKn3l33s).
-
-- Additionally, if you want to use the bleeding edge, with yout git clone you also get the latest code. While inside the main DeepLabCut folder, you can run `./reinstall.sh` to be sure it's installed (more here: https://github.com/DeepLabCut/DeepLabCut/wiki/How-to-use-the-latest-GitHub-code)
-
-- You can test that your GPU is being properly engaged with these additional [tips](https://www.tensorflow.org/programmers_guide/using_gpu).
-
-- Ubuntu users might find this [installation guide](https://deeplabcut.github.io/DeepLabCut/docs/recipes/installTips.html#installation-on-ubuntu-20-04-lts) for a fresh ubuntu install useful as well.
+### Notes:
+
+- **As of version 3.0+ we moved to PyTorch. The Last supported version of TensorFlow is
+2.10 (window users) and 2.12 for others (we have not tested beyond this).**
+- Please be mindful different versions of TensorFlow require different CUDA versions.
+- As the combination of TensorFlow and CUDA matters, we strongly encourage you to
+**check your driver/cuDNN/CUDA/TensorFlow versions** [on this StackOverflow post](
+https://stackoverflow.com/questions/30820513/what-is-version-of-cuda-for-nvidia-304-125/30820690#30820690
+).
+- To check your GPU is working, in the terminal, run:
+
+`nvcc -V` to check your installed version(s).
+
+- The best practice is to then run the supplied `testscript_pytorch_single_animal.py`
+(or `testscript_tensorflow_single_animal.py` for the TensorFlow engine); this is inside the examples folder you
+acquired when you git cloned the repo. Here is more information/a short
+[video on running the testscript](https://www.youtube.com/watch?v=IOWtKn3l33s).
+- Additionally, if you want to use the bleeding edge, with your git clone you also get
+the latest code. While inside the main DeepLabCut folder, you can run `./reinstall.sh`
+to be sure it's installed (more [here](installation-tips))
+- You can test that your GPU is being properly engaged with these additional [tips](
+https://www.tensorflow.org/programmers_guide/using_gpu).
+- Ubuntu users might find this [installation guide](
+https://deeplabcut.github.io/DeepLabCut/docs/recipes/installTips.html#installation-on-ubuntu-20-04-lts
+) for a fresh ubuntu install useful as well.
## Troubleshooting:
@@ -168,8 +301,6 @@ Here are some additional resources users have found helpful (posted without endo
- https://developer.nvidia.com/cuda-toolkit-archive
-- http://www.python36.com/install-tensorflow-gpu-windows/
-
FFMPEG:
@@ -202,10 +333,6 @@ If you perform the system-wide installation, and the computer has other Python p
- Anaconda/Python3: Anaconda: a free and open source distribution of the Python programming language (download from https://www.anaconda.com/). DeepLabCut is written in Python 3 (https://www.python.org/) and not compatible with Python 2.
- `pip install deeplabcut`
- TensorFlow
- - You will need [TensorFlow](https://www.tensorflow.org/) (we used version 1.0 in the Nature Neuroscience paper, later versions also work with the provided code (we tested **TensorFlow versions 1.0 to 1.15, and 2.0 to 2.10**; we recommend TF2.10 now) for Python 3.8, 3.9, 3.10 with GPU support.
+ - If you want to use a pre3.0 version, you will need [TensorFlow](https://www.tensorflow.org/) (we used version 1.0 in the Nature Neuroscience paper, later versions also work with the provided code (we tested **TensorFlow versions 1.0 to 1.15, and 2.0 to 2.10**; we recommend TF2.10 now) for Python 3.8, 3.9, 3.10 with GPU support.
- To note, is it possible to run DeepLabCut on your CPU, but it will be VERY slow (see: [Mathis & Warren](https://www.biorxiv.org/content/early/2018/10/30/457242)). However, this is the preferred path if you want to test DeepLabCut on your own computer/data before purchasing a GPU, with the added benefit of a straightforward installation! Otherwise, use our COLAB notebooks for GPU access for testing.
- Docker: We highly recommend advanced users use the supplied [Docker container](docker-containers)
-
-
-
-Return to [readme](readme).
diff --git a/docs/intro.md b/docs/intro.md
index 104e5cebda..cab6f4b183 100644
--- a/docs/intro.md
+++ b/docs/intro.md
@@ -1,202 +1,7 @@
-
-
-[](https://badge.fury.io/py/deeplabcut)
-[](https://pepy.tech/project/deeplabcut)
-[](https://pepy.tech/project/deeplabcut)
-[](https://github.com/DeepLabCut/DeepLabCut)
-
-[](CONTRIBUTING.md)
-[](https://www.gnu.org/licenses/lgpl-3.0)
-[](https://forum.image.sc/tag/deeplabcut)
-[](https://gitter.im/DeepLabCut/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
-[](https://twitter.com/DeepLabCut)
-[](https://github.com/DeepLabCut/DeepLabCut)
-
-
-
-
-
-# DeepLabCut Documentation
-
-DeepLabCut is a toolbox for markerless pose estimation of animals performing various tasks. Read a short development and application summary below. As long as you can see (label) what you want to track, you can use this toolbox, as it is animal and object agnostic.
-
-This new JupyterBook Docs Hub serves as a landing page for both new and advanced users. Check out the left sidebar for new the main docs, but also tutorials and "recipes" for interesting ways to use DLC and functionality that is not documented elsewhere. Have a new recipe? Please contribute!
-
-**Latest updates:**
-
-- **DeepLabCut supports multi-animal pose estimation!** maDLC is out of beta/rc mode and beta is depreciated, thanks to the testers out there! Your labeled data will be backwards compatible, but not all other steps. Please see the [new `2.2+` releases](https://github.com/DeepLabCut/DeepLabCut/releases) for what's new & how to install it, please see our new paper in [Nature Methods (2022)](https://www.nature.com/articles/s41592-022-01443-0), and the docs on how to use it!
-
-- We have a **real-time DeepLabCut-live!** package available! http://DLClive.deeplabcut.org
-
-- Check out the docs in JupyterBook! https://deeplabcut.github.io/DeepLabCut (or in the [README](readme)).
-
-- For a step-by-step user guide, please also see the [Nature Protocols paper](https://doi.org/10.1038/s41596-019-0176-0)!
-
-- For a deeper understanding and more resources for you to get started with Python and DeepLabCut, please check out our free online course! http://DLCcourse.deeplabcut.org
-
-
-
-
-
-## Why use DeepLabCut?
-
-In 2018, we demonstrated the capabilities for [trail tracking](https://vnmurthylab.org/), [reaching in mice](http://www.mousemotorlab.org/) and various Drosophila behaviors during egg-laying (see [Mathis et al.](https://www.nature.com/articles/s41593-018-0209-y) for details). There is, however, nothing specific that makes the toolbox only applicable to these tasks and/or species. The toolbox has already been successfully applied (by us and others) to [rats](http://www.mousemotorlab.org/deeplabcut), humans, various fish species, bacteria, leeches, various robots, cheetahs, [mouse whiskers](http://www.mousemotorlab.org/deeplabcut) and [race horses](http://www.mousemotorlab.org/deeplabcut). DeepLabCut utilized the feature detectors (ResNets + readout layers) of one of the state-of-the-art algorithms for human pose estimation by Insafutdinov et al., called DeeperCut, which inspired the name for our toolbox (see references below). Since this time, the package has changed substantially. The code has been re-tooled and re-factored since 2.1+: We have added faster and higher performance variants with MobileNetV2s, EfficientNets, and our own DLCRNet backbones (see [Pretraining boosts out-of-domain robustness for pose estimation](https://arxiv.org/abs/1909.11229) and [Lauer et al 2021](https://www.biorxiv.org/content/10.1101/2021.04.30.442096v1)). Additionally, we have improved the inference speed and provided both additional and novel augmentation methods, added real-time, and multi-animal support. We currently provide state-of-the-art performance for animal pose estimation.
-
-
-
-
-
-
-
-
-**Left:** Due to transfer learning it requires **little training data** for multiple, challenging behaviors (see [Mathis et al. 2018](https://www.nature.com/articles/s41593-018-0209-y) for details). **Mid Left:** The feature detectors are robust to video compression (see [Mathis/Warren](https://www.biorxiv.org/content/early/2018/10/30/457242) for details). **Mid Right:** It allows 3D pose estimation with a single network and camera (see [Mathis/Warren](https://www.biorxiv.org/content/early/2018/10/30/457242)). **Right:** It allows 3D pose estimation with a single network trained on data from multiple cameras together with standard triangulation methods (see [Nath* and Mathis* et al. 2019](https://doi.org/10.1038/s41596-019-0176-0)).
-
-**DeepLabCut** is embedding in a larger open-source eco-system, providing behavioral tracking for neuroscience, ecology, medical, and technical applications. Moreover, many new tools are being actively developed. See [DLC-Utils](https://github.com/DeepLabCut/DLCutils) for some helper code.
-
-
-
-
-
-## Code contributors:
-
-DLC code was originally developed by [Alexander Mathis](https://github.com/AlexEMG) & [Mackenzie Mathis](https://github.com/MMathisLab), and was extended in 2.0 with [Tanmay Nath](http://www.mousemotorlab.org/team), and currently (2.1+) actively developed with our CZI DLC Fellow, [Jessy Lauer](https://github.com/jeylau). DeepLabCut is an open-source tool and has benefited from suggestions and edits by many individuals including Mert Yuksekgonul, Tom Biasi, Richard Warren, Ronny Eichler, Hao Wu, Federico Claudi, Gary Kane and Jonny Saunders as well as the [contributors](https://github.com/DeepLabCut/DeepLabCut/graphs/contributors). Please see [AUTHORS](https://github.com/DeepLabCut/DeepLabCut/blob/master/AUTHORS) for more details!
-
-This is an actively developed package and we welcome community development and involvement.
-
-## Community Support, Developers, & Help:
-
-- We are a community partner on the [](https://forum.image.sc/tag/deeplabcut). Please post help and support questions on the forum with the tag DeepLabCut. Check out their mission statement [Scientific Community Image Forum: A discussion forum for scientific image software](https://journals.plos.org/plosbiology/article?id=10.1371/journal.pbio.3000340).
-
-- If you encounter a previously unreported bug/code issue, please post here (we encourage you to search issues first): https://github.com/DeepLabCut/DeepLabCut/issues
-
-- For quick discussions amongst users, please see here: [](https://gitter.im/DeepLabCut/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
-
-- If you want to contribute to the code, please read our guide [here!](https://github.com/DeepLabCut/DeepLabCut/blob/master/CONTRIBUTING.md)
-
-- The project [road map](dev-roadmap). Get in touch with us if you want to help!
-
-## References:
-
-If you use this code or data we kindly as that you please [cite Mathis et al, 2018](https://www.nature.com/articles/s41593-018-0209-y) and, if you use the Python package (DeepLabCut2.x) please also cite [Nath, Mathis et al, 2019](https://doi.org/10.1038/s41596-019-0176-0). If you utilize the MobileNetV2s or EfficientNets please cite [Mathis, Biasi et al. 2021](https://openaccess.thecvf.com/content/WACV2021/papers/Mathis_Pretraining_Boosts_Out-of-Domain_Robustness_for_Pose_Estimation_WACV_2021_paper.pdf). If you use multi-animal code please cite [Lauer et al. 2022](https://www.nature.com/articles/s41592-022-01443-0).
-
-DOIs (#ProTip, for helping you find citations for software, check out [CiteAs.org](http://citeas.org/)!):
-
-- Mathis et al 2018: [10.1038/s41593-018-0209-y](https://doi.org/10.1038/s41593-018-0209-y)
-- Nath, Mathis et al 2019: [10.1038/s41596-019-0176-0](https://doi.org/10.1038/s41596-019-0176-0)
-
-
-Please check out the following references for more details:
-
- @article{Mathisetal2018,
- title = {DeepLabCut: markerless pose estimation of user-defined body parts with deep learning},
- author = {Alexander Mathis and Pranav Mamidanna and Kevin M. Cury and Taiga Abe and Venkatesh N. Murthy and Mackenzie W. Mathis and Matthias Bethge},
- journal = {Nature Neuroscience},
- year = {2018},
- url = {https://www.nature.com/articles/s41593-018-0209-y}}
-
- @article{NathMathisetal2019,
- title = {Using DeepLabCut for 3D markerless pose estimation across species and behaviors},
- author = {Nath*, Tanmay and Mathis*, Alexander and Chen, An Chi and Patel, Amir and Bethge, Matthias and Mathis, Mackenzie W},
- journal = {Nature Protocols},
- year = {2019},
- url = {https://doi.org/10.1038/s41596-019-0176-0}}
-
- @InProceedings{Mathis_2021_WACV,
- author = {Mathis, Alexander and Biasi, Thomas and Schneider, Steffen and Yuksekgonul, Mert and Rogers, Byron and Bethge, Matthias and Mathis, Mackenzie W.},
- title = {Pretraining Boosts Out-of-Domain Robustness for Pose Estimation},
- booktitle = {Proceedings of the IEEE/CVF Winter Conference on Applications of Computer Vision (WACV)},
- month = {January},
- year = {2021},
- pages = {1859-1868}}
-
- @article{Lauer2022MultianimalPE,
- title={Multi-animal pose estimation, identification and tracking with DeepLabCut},
- author={Jessy Lauer and Mu Zhou and Shaokai Ye and William Menegas and Steffen Schneider and Tanmay Nath and Mohammed Mostafizur Rahman and Valentina Di Santo and Daniel Soberanes and Guoping Feng and Venkatesh N. Murthy and George Lauder and Catherine Dulac and M. Mathis and Alexander Mathis},
- journal={Nature Methods},
- year={2022},
- volume={19},
- pages={496 - 504}}
-
- @article{insafutdinov2016eccv,
- title = {DeeperCut: A Deeper, Stronger, and Faster Multi-Person Pose Estimation Model},
- author = {Eldar Insafutdinov and Leonid Pishchulin and Bjoern Andres and Mykhaylo Andriluka and Bernt Schiele},
- booktitle = {ECCV'16},
- url = {http://arxiv.org/abs/1605.03170}}
-
-Review articles:
-
- @article{Mathis2020DeepLT,
- title={Deep learning tools for the measurement of animal behavior in neuroscience},
- author={Mackenzie W. Mathis and Alexander Mathis},
- journal={Current Opinion in Neurobiology},
- year={2020},
- volume={60},
- pages={1-11}}
-
- @article{Mathis2020Primer,
- title={A Primer on Motion Capture with Deep Learning: Principles, Pitfalls, and Perspectives},
- author={Alexander Mathis and Steffen Schneider and Jessy Lauer and Mackenzie W. Mathis},
- journal={Neuron},
- year={2020},
- volume={108},
- pages={44-65}}
-
-Other open-access pre-prints related to our work on DeepLabCut:
-
- @article{MathisWarren2018speed,
- author = {Mathis, Alexander and Warren, Richard A.},
- title = {On the inference speed and video-compression robustness of DeepLabCut},
- year = {2018},
- doi = {10.1101/457242},
- publisher = {Cold Spring Harbor Laboratory},
- URL = {https://www.biorxiv.org/content/early/2018/10/30/457242},
- eprint = {https://www.biorxiv.org/content/early/2018/10/30/457242.full.pdf},
- journal = {bioRxiv}
- }
-
-## License:
-
-This project is licensed under the GNU Lesser General Public License v3.0. Note that the software is provided "as is", without warranty of any kind, express or implied. If you use the code or data, please cite us! Note, artwork (DeepLabCut logo) and images are copyrighted; please do not take or use these images without written permission.
-
-## Versions:
-
-VERSION 2.2: Multi-animal pose estimation and tracking with DeepLabCut.
-
-VERSION 2.0-2.1: This is the **Python package** of [DeepLabCut](https://www.nature.com/articles/s41593-018-0209-y) that was originally released with our [Nature Protocols](https://doi.org/10.1038/s41596-019-0176-0) paper (preprint [here](https://www.biorxiv.org/content/10.1101/476531v1)).
-This package includes graphical user interfaces to label your data, and take you from data set creation to automatic behavioral analysis. It also introduces an active learning framework to efficiently use DeepLabCut on large experimental projects, and data augmentation tools that improve network performance, especially in challenging cases (see [panel b](https://camo.githubusercontent.com/77c92f6b89d44ca758d815bdd7e801247437060b/68747470733a2f2f737461746963312e73717561726573706163652e636f6d2f7374617469632f3537663664353163396637343536366635356563663237312f742f3563336663316336373538643436393530636537656563372f313534373638323338333539352f636865657461682e706e673f666f726d61743d37353077)).
-
-VERSION 1.0: The initial, Nature Neuroscience version of [DeepLabCut](https://www.nature.com/articles/s41593-018-0209-y) can be found in the history of git, or here: https://github.com/DeepLabCut/DeepLabCut/releases/tag/1.11
-
-## News (and in the news):
-
-- April 2022: multi-animal identification and tracking with DeepLabCut is published in Nature Methods!
-
-- August 2021: 2.2 becomes the new stable release for DeepLabCut.
-
-- July 2021: Docs are now at https://deeplabcut.github.io/DeepLabCut and we now include TensorFlow 2 support!
-
-- May 2021: DeepLabCut hit 200,000 downloads! Also, Our preprint on 2.2, multi-animal DeepLabCut is released!
-
-- Jan 2021: [Pretraining boosts out-of-domain robustness for pose estimation](https://openaccess.thecvf.com/content/WACV2021/html/Mathis_Pretraining_Boosts_Out-of-Domain_Robustness_for_Pose_Estimation_WACV_2021_paper.html) published in the IEEE Winter Conference on Applications of Computer Vision. We also added EfficientNet backbones to DeepLabCut, those are best trained with cosine decay (see paper). To use them, just pass "`efficientnet-b0`" to "`efficientnet-b6`" when creating the trainingset!
-- Dec 2020: We released a real-time package that allows for online pose estimation and real-time feedback. See [DLClive.deeplabcut.org](http://DLClive.deeplabcut.org).
-- 5/22 2020: We released 2.2beta5. This beta release has some of the features of DeepLabCut 2.2, whose major goal is to integrate multi-animal pose estimation to DeepLabCut.
-- Mar 2020: Inspired by suggestions we heard at this weeks CZI's Essential Open Source Software meeting in Berkeley, CA we updated our [docs](overview). Let us know what you think!
-- Feb 2020: Our [review on animal pose estimation is published!](https://www.sciencedirect.com/science/article/pii/S0959438819301151)
-- Nov 2019: DeepLabCut was recognized by the Chan Zuckerberg Initiative (CZI) with funding to support this project. Read more in the [Harvard Gazette](https://news.harvard.edu/gazette/story/newsplus/harvard-researchers-awarded-czi-open-source-award/), on [CZI's Essential Open Source Software for Science site](https://chanzuckerberg.com/eoss/proposals/) and in their [Medium post](https://medium.com/@cziscience/how-open-source-software-contributors-are-accelerating-biomedicine-1a5f50f6846a)
-- Oct 2019: DLC 2.1 released with lots of updates. In particular, a Project Manager GUI, MobileNetsV2, and augmentation packages (Imgaug and Tensorpack). For detailed updates see [releases](https://github.com/DeepLabCut/DeepLabCut/releases)
-- Sept 2019: We published two preprints. One showing that [ImageNet pretraining contributes to robustness](https://arxiv.org/abs/1909.11229) and a [review on animal pose estimation](https://arxiv.org/abs/1909.13868). Check them out!
-- Jun 2019: DLC 2.0.7 released with lots of updates. For updates see [releases](https://github.com/DeepLabCut/DeepLabCut/releases)
-- Feb 2019: DeepLabCut joined [twitter](https://twitter.com/deeplabcut) [](https://twitter.com/DeepLabCut)
-- Jan 2019: We hosted workshops for DLC in Warsaw, Munich and Cambridge. The materials are available [here](https://github.com/DeepLabCut/DeepLabCut-Workshop-Materials)
-- Jan 2019: We joined the Image Source Forum for user help: [](https://forum.image.sc/tag/deeplabcut)
-
-- Nov 2018: We posted a detailed guide for DeepLabCut 2.0 on [BioRxiv](https://www.biorxiv.org/content/early/2018/11/24/476531). It also contains a case study for 3D pose estimation in cheetahs.
-- Nov 2018: Various (post-hoc) analysis scripts contributed by users (and us) will be gathered at [DLCutils](https://github.com/DeepLabCut/DLCutils). Feel free to contribute! In particular, there is a script guiding you through
-importing a project into the new data format for DLC 2.0
-- Oct 2018: new pre-print on the speed video-compression and robustness of DeepLabCut on [BioRxiv](https://www.biorxiv.org/content/early/2018/10/30/457242)
-- Sept 2018: Nature Lab Animal covers DeepLabCut: [Behavior tracking cuts deep](https://www.nature.com/articles/s41684-018-0164-y)
-- Kunlin Wei & Konrad Kording write a very nice News & Views on our paper: [Behavioral Tracking Gets Real](https://www.nature.com/articles/s41593-018-0215-0)
-- August 2018: Our [preprint](https://arxiv.org/abs/1804.03142) appeared in [Nature Neuroscience](https://www.nature.com/articles/s41593-018-0209-y)
-- August 2018: NVIDIA AI Developer News: [AI Enables Markerless Animal Tracking](https://news.developer.nvidia.com/ai-enables-markerless-animal-tracking/)
-- July 2018: Ed Yong covered DeepLabCut and interviewed several users for the [Atlantic](https://www.theatlantic.com/science/archive/2018/07/deeplabcut-tracking-animal-movements/564338).
-- April 2018: first DeepLabCut preprint on [arXiv.org](https://arxiv.org/abs/1804.03142)
+---
+deeplabcut:
+ last_content_updated: '2024-06-14'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
+Please see the main [READ ME!](https://deeplabcut.github.io/DeepLabCut/README.html)
diff --git a/docs/maDLC_UserGuide.md b/docs/maDLC_UserGuide.md
index af53cade6d..1c4dcc5731 100644
--- a/docs/maDLC_UserGuide.md
+++ b/docs/maDLC_UserGuide.md
@@ -1,12 +1,25 @@
+---
+deeplabcut:
+ last_content_updated: '2026-02-10'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
(multi-animal-userguide)=
# DeepLabCut for Multi-Animal Projects
This document should serve as the user guide for maDLC,
and it is here to support the scientific advances presented in [Lauer et al. 2022](https://doi.org/10.1038/s41592-022-01443-0).
-
Note, we strongly encourage you to use the [Project Manager GUI](project-manager-gui) when you first start using multi-animal mode. Each tab is customized for multi-animal when you create or load a multi-animal project. As long as you follow the recommendations within the GUI, you should be good to go!
+````{versionadded} 3.0.0
+PyTorch is now available as a deep learning engine for pose estimation models, along
+with new model architectures! For more information about moving from TensorFlow to
+PyTorch (if you're already familiar with DeepLabCut & the TensorFlow engine),
+check out [the PyTorch user guide](dlc3-user-guide). If you're just starting
+out with DeepLabCut, we suggest you use the PyTorch backend.
+````
+
## How to think about using maDLC:
You should think of maDLC being **four** parts.
@@ -21,38 +34,47 @@ Thus, you should always label, train, and evaluate the pose estimation performan
## Install:
-**Quick start:** If you are using DeepLabCut on the cloud, or otherwise cannot use the GUIs and you should install with: `pip install deeplabcut`; if you need GUI support, please use: `pip install 'deeplabcut[gui]'`.
+**Quick start:** If you are using DeepLabCut on the cloud, or otherwise cannot use the GUIs and you should install with: `pip install 'deeplabcut'`; if you need GUI support, please use: `pip install 'deeplabcut[gui]'`. Check the [installation page](how-to-install) for more information, including GPU support.
-IF you want to use the bleeding edge version to make edits to the code, see here on how to install it and test it (https://deeplabcut.github.io/DeepLabCut/docs/recipes/installTips.html#how-to-use-the-latest-updates-directly-from-github).
+IF you want to use the bleeding edge version to make edits to the code, see [here on how to install it and test it](https://deeplabcut.github.io/DeepLabCut/docs/recipes/installTips.html#how-to-use-the-latest-updates-directly-from-github).
## Get started in the terminal or Project GUI:
-**GUI:** simply open your conda env, and windows/linux type `python -m deeplabcut`. MacOS users: `pythonw -m deeplabcut.`
+**GUI:** simply launch your conda env, and type `python -m deeplabcut` in the terminal.
Then follow the tabs! It might be useful to read the following, however, so you understand what each command does.
-**TERMINAL:** To begin, (windows) navigate to anaconda prompt and right-click to "open as admin ", or (unix/MacOS) simply launch "terminal" on your computer. We assume you have DeepLabCut installed (if not, [see installation instructions](how-to-install)). Next, launch your conda env (i.e., for example `conda activate DLC-CPU`).
+**TERMINAL:** To begin, 🚨 (windows) navigate to anaconda prompt and right-click to "open as admin", or (unix/MacOS) simply launch "terminal" on your computer. We assume you have DeepLabCut installed (if not, [see installation instructions](how-to-install)). Next, launch your conda env (i.e., for example `conda activate DEEPLABCUT`).
-Start iPython, or if you are using MacOS, you must use ``pythonw`` vs. typing ``ipython`` or ``python``, but otherwise it's the same.
-If you use Windows, please always open the terminal with administrator privileges. Please read more [here](https://github.com/DeepLabCut/Docker4DeepLabCut2.0), and in our Nature Protocols paper [here](https://www.nature.com/articles/s41596-019-0176-0). And, see our [troubleshooting wiki](https://github.com/DeepLabCut/DeepLabCut/wiki/Troubleshooting-Tips).
+```{Hint}
+🚨 If you use Windows, please always open the terminal with administrator privileges! Right click, and "run as administrator".
+```
+ Please read more [here](https://deeplabcut.github.io/DeepLabCut/docs/docker.html), and in our Nature Protocols paper [here](https://www.nature.com/articles/s41596-019-0176-0). And, see our [troubleshooting wiki](https://github.com/DeepLabCut/DeepLabCut/wiki/Troubleshooting-Tips).
Open an ``ipython`` session and import the package by typing in the terminal:
```python
ipython
import deeplabcut
```
-**TIP:** for every function there is a associated help document that can be viewed by adding a **?** after the function name; i.e. ``deeplabcut.create_new_project?``. To exit this help screen, type ``:q``.
+```{TIP}
+for every function there is a associated help document that can be viewed by adding a **?** after the function name; i.e. ``deeplabcut.create_new_project?``. To exit this help screen, type ``:q``.
+```
-### Create a New Project:
+### (A) Create a New Project
```python
-deeplabcut.create_new_project('ProjectName','YourName', ['/usr/FullPath/OfVideo1.avi', '/usr/FullPath/OfVideo2.avi', '/usr/FullPath/OfVideo1.avi'],
- copy_videos=True, multianimal=True)
+deeplabcut.create_new_project(
+ "ProjectName",
+ "YourName",
+ ["/usr/FullPath/OfVideo1.avi", "/usr/FullPath/OfVideo2.avi", "/usr/FullPath/OfVideo1.avi"],
+ copy_videos=True,
+ multianimal=True,
+)
```
-Tip: if you want to place the project folder somewhere pass : ``working_directory = 'FullPathOftheworkingDirectory'``
+Tip: if you want to place the project folder somewhere specific, please also pass : ``working_directory = "FullPathOftheworkingDirectory"``
-- Note, if you are a linux/macos user the path should look like: ``['/home/username/yourFolder/video1.mp4']``; if you are a Windows user, it should look like: ``[r'C:\username\yourFolder\video1.mp4']``
+- Note, if you are a linux/macOS user the path should look like: ``["/home/username/yourFolder/video1.mp4"]``; if you are a Windows user, it should look like: ``[r"C:\username\yourFolder\video1.mp4"]``
- Note, you can also put ``config_path = `` in front of the above line to create the path to the config.yaml that is used in the next step, i.e. ``config_path=deeplabcut.create_project(...)``)
- If you do not, we recommend setting a variable so this can be easily used! Once you run this step, the config_path is printed for you once you run this line, so set a variable for ease of use, i.e. something like:
```python
@@ -60,9 +82,20 @@ config_path = '/thefulloutputpath/config.yaml'
```
- just be mindful of the formatting for Windows vs. Unix, see above.
-This set of arguments will create a project directory with the name **Name of the project+name of the experimenter+date of creation of the project** in the **Working directory** and creates the symbolic links to videos in the **videos** directory. The project directory will have subdirectories: **dlc-models**, **labeled-data**, **training-datasets**, and **videos**. All the outputs generated during the course of a project will be stored in one of these subdirectories, thus allowing each project to be curated in separation from other projects. The purpose of the subdirectories is as follows:
-
-**dlc-models:** This directory contains the subdirectories *test* and *train*, each of which holds the meta information with regard to the parameters of the feature detectors in configuration files. The configuration files are YAML files, a common human-readable data serialization language. These files can be opened and edited with standard text editors. The subdirectory *train* will store checkpoints (called snapshots in TensorFlow) during training of the model. These snapshots allow the user to reload the trained model without re-training it, or to pick-up training from a particular saved checkpoint, in case the training was interrupted.
+This set of arguments will create a project directory with the name **Name of the project+name of the experimenter+date of creation of the project** in the **Working directory** and creates the symbolic links to videos in the **videos** directory. The project directory will have subdirectories: **dlc-models**, **dlc-models-pytorch**, **labeled-data**, **training-datasets**, and **videos**. All the outputs generated during the course of a project will be stored in one of these subdirectories, thus allowing each project to be curated in separation from other projects. The purpose of the subdirectories is as follows:
+
+**dlc-models** and **dlc-models-pytorch** have a similar structure: the first contains
+files for the TensorFlow engine while the second contains files for the PyTorch engine.
+At the top level in these directories, there are
+directories referring to different iterations of labels refinement (see below): **iteration-0**, **iteration-1**, etc.
+The refinement iterations directories store shuffle directories, each shuffle directory stores model data related to a
+particular experiment: trained and tested on a particular training and testing sets, and with a particular model
+architecture. Each shuffle directory contains the subdirectories *test* and *train*, each of which holds the meta
+information with regard to the parameters of the feature detectors in configuration files. The configuration files are
+YAML files, a common human-readable data serialization language. These files can be opened and edited with standard text
+editors. The subdirectory *train* will store checkpoints (called snapshots) during training of the model. These
+snapshots allow the user to reload the trained model without re-training it, or to pick-up training from a particular
+saved checkpoint, in case the training was interrupted.
**labeled-data:** This directory will store the frames used to create the training dataset. Frames from different videos are stored in separate subdirectories. Each frame has a filename related to the temporal index within the corresponding video, which allows the user to trace every frame back to its origin.
@@ -71,33 +104,55 @@ This set of arguments will create a project directory with the name **Name of th
**videos:** Directory of video links or videos. When **copy\_videos** is set to ``False``, this directory contains symbolic links to the videos. If it is set to ``True`` then the videos will be copied to this directory. The default is ``False``. Additionally, if the user wants to add new videos to the project at any stage, the function **add\_new\_videos** can be used. This will update the list of videos in the project's configuration file. Note: you neither need to use this folder for videos, nor is it required for analyzing videos (they can be anywhere).
```python
-deeplabcut.add_new_videos('Full path of the project configuration file*', ['full path of video 4', 'full path of video 5'], copy_videos=True/False)
+deeplabcut.add_new_videos(
+ "Full path of the project configuration file*",
+ ["full path of video 4", "full path of video 5"],
+ copy_videos=True/False,
+)
```
*Please note, *Full path of the project configuration file* will be referenced as ``config_path`` throughout this protocol.
-You can also use annotated data from singe-animal projects, by converting those files. There are docs for this: [convert single to multianimal annotation data](convert-maDLC)
+You can also use annotated data from single-animal projects, by converting those files.
+There are docs for this: [convert single to multianimal annotation data](convert-maDLC)
-### Configure the Project:
+
-- open the **config.yaml** file (in a text editor (like atom, gedit, vim etc.)), which can be found in the subfolder created when you set your project name, to change parameters and identify label names! This is a crucial step.
+### API Docs
+````{admonition} Click the button to see API Docs
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.create_new_project.rst
+```
+````
+
+### (B) Configure the Project
+
+Next, open the **config.yaml** file, which was created during **create\_new\_project**.
+You can edit this file in any text editor. Familiarize yourself with the meaning of the
+parameters (Box 1). You can edit various parameters, in particular you **must add the list of *individuals* and *bodyparts* (or points of interest)**.
-Next, open the **config.yaml** file, which was created during **create\_new\_project**. You can edit this file in any text editor. Familiarize yourself with the meaning of the parameters (Box 1). You can edit various parameters, in particular you **must add the list of *bodyparts* (or points of interest)** that you want to track. You can also set the *colormap* here that is used for all downstream steps (can also be edited at anytime), like labeling GUIs, videos, etc. Here any [matplotlib colormaps](https://matplotlib.org/tutorials/colors/colormaps.html) will do!
+You can also set the *colormap* here that is used for all downstream steps (can also be edited at anytime), like labeling GUIs, videos, etc. Here any [matplotlib colormaps](https://matplotlib.org/tutorials/colors/colormaps.html) will do!
An easy way to programmatically edit the config file at any time is to use the function **edit\_config**, which takes the full path of the config file to edit and a dictionary of key–value pairs to overwrite.
-````python
-edits = {'colormap': 'summer',
- 'individuals': ['mickey', 'minnie', 'bianca'],
- 'skeleton': [['snout', 'tailbase'], ['snout', 'rightear']]}
+```python
+import deeplabcut
+
+config_path = "/path/to/project-dlc-2025-01-01/config.yaml"
+edits = {
+ "colormap": "summer",
+ "individuals": ["mickey", "minnie", "bianca"],
+ "skeleton": [["snout", "tailbase"], ["snout", "rightear"]]
+}
deeplabcut.auxiliaryfunctions.edit_config(config_path, edits)
-````
+```
Please DO NOT have spaces in the names of bodyparts, uniquebodyparts, individuals, etc.
-**ATTENTIONt:** You need to edit the config.yaml file to **modify the following items** which specify the animal ID, body parts, and any unique labels. Note, we also highly recommend that you use **more bodypoints** that you might be interested in for your experiment, i.e., labeling along the spine/tail for 8 bodypoints would be better than four. This will help the performance.
+**ATTENTION:** You need to edit the config.yaml file to **modify the following items** which specify the animal ID, bodyparts, and any unique labels. Note, we also highly recommend that you use **more bodyparts** that you might be interested in for your experiment, i.e., labeling along the spine/tail for 8 bodyparts would be better than four. This will help the performance.
Modifying the `config.yaml` is crucial:
@@ -119,6 +174,7 @@ multianimalbodyparts:
identity: True/False
```
+
**Individuals:** are names of "individuals" in the annotation dataset. These should/can be generic (e.g. mouse1, mouse2, etc.). These individuals are comprised of the same bodyparts defined by `multianimalbodyparts`. For annotation in the GUI and training, it is important that all individuals in each frame are labeled. Thus, keep in mind that you need to set individuals to the maximum number in your labeled-data set, .i.e., if there is (even just one frame) with 17 animals then the list should be `- indv1` to `- indv17`. Note, once trained if you have a video with more or less animals, that is fine - you can have more or less animals during video analysis!
**Identity:** If you can tell the animals apart, i.e., one might have a collar, or a black marker on the tail of a mouse, then you should label these individuals consistently (i.e., always label the mouse with the black marker as "indv1", etc). If you have this scenario, please set `identity: True` in your `config.yaml` file. If you have 4 black mice, and you truly cannot tell them apart, then leave this as `false`.
@@ -127,14 +183,22 @@ identity: True/False
**Uniquebodyparts:** are points that you want to track, but that appear only once within each frame, i.e. they are "unique". Typically these are things like unique objects, landmarks, tools, etc. They can also be animals, e.g. in the case where one German shepherd is attending to many sheep the sheep bodyparts would be multianimalbodyparts, the shepherd parts would be uniquebodyparts and the individuals would be the list of sheep (e.g. Polly, Molly, Dolly, ...).
-### Select Frames to Label:
+### (C) Select Frames to Label
**CRITICAL:** A good training dataset should consist of a sufficient number of frames that capture the breadth of the behavior. This ideally implies to select the frames from different (behavioral) sessions, different lighting and different animals, if those vary substantially (to train an invariant, robust feature detector). Thus for creating a robust network that you can reuse in the laboratory, a good training dataset should reflect the diversity of the behavior with respect to postures, luminance conditions, background conditions, animal identities, etc. of the data that will be analyzed. For the simple lab behaviors comprising mouse reaching, open-field behavior and fly behavior, 100−200 frames gave good results [Mathis et al, 2018](https://www.nature.com/articles/s41593-018-0209-y). However, depending on the required accuracy, the nature of behavior, the video quality (e.g. motion blur, bad lighting) and the context, more or less frames might be necessary to create a good network. Ultimately, in order to scale up the analysis to large collections of videos with perhaps unexpected conditions, one can also refine the data set in an adaptive way (see refinement below). **For maDLC, be sure you have labeled frames with closely interacting animals!**
The function `extract_frames` extracts frames from all the videos in the project configuration file in order to create a training dataset. The extracted frames from all the videos are stored in a separate subdirectory named after the video file’s name under the ‘labeled-data’. This function also has various parameters that might be useful based on the user’s need.
+
```python
-deeplabcut.extract_frames(config_path, mode='automatic/manual', algo='uniform/kmeans', userfeedback=False, crop=True/False)
+deeplabcut.extract_frames(
+ config_path,
+ mode='automatic/manual',
+ algo='uniform/kmeans',
+ userfeedback=False,
+ crop=True/False,
+)
```
+
**CRITICAL POINT:** It is advisable to keep the frame size small, as large frames increase the training and
inference time, or you might not have a large enough GPU for this.
When running the function `extract_frames`, if the parameter crop=True, then you will be asked to draw a box within the GUI (and this is written to the config.yaml file).
@@ -156,63 +220,99 @@ behaviors, and not extract the frames across the whole video. This can be achiev
parameters in the config.yaml file. Also, the user can change the number of frames to extract from each video using
the numframes2extract in the config.yaml file.
- **For maDLC, be sure you have labeled frames with closely interacting animals!** Therefore, manually selecting some frames is a good idea if interactions are not highly frequent in the video.
+```{TIP}
+For maDLC, **be sure you have labeled frames with closely interacting animals**!
+Therefore, manually selecting some frames is a good idea if interactions are not highly
+frequent in the video.
+```
-However, picking frames is highly dependent on the data and the behavior being studied. Therefore, it is hard to
-provide all purpose code that extracts frames to create a good training dataset for every behavior and animal. If the user feels specific frames are lacking, they can extract hand selected frames of interest using the interactive GUI
+However, picking frames is highly dependent on the data and the behavior being studied.
+Therefore, it is hard to provide all purpose code that extracts frames to create a good
+training dataset for every behavior and animal. If the user feels specific frames are
+lacking, they can extract hand selected frames of interest using the interactive GUI
provided along with the toolbox. This can be launched by using:
+
```python
deeplabcut.extract_frames(config_path, 'manual')
```
-The user can use the *Load Video* button to load one of the videos in the project configuration file, use the scroll
-bar to navigate across the video and *Grab a Frame* (or a range of frames, as of version 2.0.5) to extract the frame(s). The user can also look at the extracted frames and e.g. delete frames (from the directory) that are too similar before reloading the set and then manually annotating them.
+// FIXME(niels) - add a napari frame extractor description.
+The user can use the *Load Video* button to load one of the videos in the project
+configuration file, use the scroll bar to navigate across the video and *Grab a Frame*.
+The user can also look at the extracted frames and e.g. delete frames (from the
+directory) that are too similar before reloading the set and then manually annotating
+them.
+
+````{admonition} Click the button to see API Docs
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.extract_frames.rst
+```
+````
-### Label Frames:
+### (D) Label Frames
```python
deeplabcut.label_frames(config_path)
```
-As of 2.2 there is a new multi-animal labeling GUI (as long as in your `config.yaml` says `multianimalproject: true` at the top, this will automatically launch).
+The toolbox provides a function **label_frames** which helps the user to easily label
+all the extracted frames using an interactive graphical user interface (GUI). The user
+should have already named the bodyparts to label (points of interest) in the
+project’s configuration file by providing a list. The following command invokes the
+napari-deeplabcut labelling GUI.
-The toolbox provides a function **label_frames** which helps the user to easily label all the extracted frames using
-an interactive graphical user interface (GUI). The user should have already named the body parts to label (points of
-interest) in the project’s configuration file by providing a list. The following command invokes the labeling toolbox.
+[🎥 DEMO](https://youtu.be/hsA9IB5r73E)
-The user needs to use the *Load Frames* button to select the directory which stores the extracted frames from one of
-the videos. Subsequently, the user can use one of the radio buttons (top right) to select a body part to label. **RIGHT** click to add the label. Left click to drag the label, if needed. If you label a part accidentally, you can use the middle button on your mouse to delete (or hit the delete key while you hover over the point)! If you cannot see a body part in the frame, skip over the label! Please see the ``HELP`` button for more user instructions. This auto-advances once you labeled the first body part. You can also advance to the next frame by clicking on the RIGHT arrow on your keyboard (and go to a previous frame with LEFT arrow).
-Each label will be plotted as a dot in a unique color.
+HOT KEYS IN THE Labeling GUI (also see "help" in GUI):
-The user is free to move around the body part and once satisfied with its position, can select another radio button
-(in the top right) to switch to the respective body part (it otherwise auto-advances). The user can skip a body part if it is not visible. Once all the visible body parts are labeled, then the user can use ‘Next Frame’ to load the following frame. The user needs to save the labels after all the frames from one of the videos are labeled by clicking the save button at the bottom right. Saving the labels will create a labeled dataset for each video in a hierarchical data file format (HDF) in the
-subdirectory corresponding to the particular video in **labeled-data**. You can save at any intermediate step (even without closing the GUI, just hit save) and you return to labeling a dataset by reloading it!
+```
+Ctrl + C: Copy labels from previous frame.
+Keyboard arrows: advance frames.
+Delete key: delete label.
+```
-**CRITICAL POINT:** It is advisable to **consistently label similar spots** (e.g., on a wrist that is very large, try
-to label the same location). In general, invisible or occluded points should not be labeled by the user, unless you want to teach the network to "guess" - this is possible, but could affect accuracy. If you don't want/or don't see a bodypart, they can simply be skipped by not applying the label anywhere on the frame.
+
-OPTIONAL: In the event of adding more labels to the existing labeled dataset, the user need to append the new
-labels to the bodyparts in the config.yaml file. Thereafter, the user can call the function **label_frames**. A box will pop up and ask the user if they wish to display all parts, or only add in the new labels. Saving the labels after all the images are labelled will append the new labels to the existing labeled dataset.
+**CRITICAL POINT:** It is advisable to **consistently label similar spots** (e.g., on a
+wrist that is very large, try to label the same location). In general, invisible or
+occluded points should not be labeled by the user, unless you want to teach the network
+to "guess" - this is possible, but could affect accuracy. If you don't want/or don't see
+a bodypart, they can simply be skipped by not applying the label anywhere on the frame.
-**maDeepLabCut CRITICAL POINT:** For multi-animal labeling, unless you can tell apart the animals, you do not need to worry about the "ID" of each animal. For example: if you have a white and black mouse label the white mouse as animal 1, and black as animal 2 across all frames. If two black mice, then the ID label 1 or 2 can switch between frames - no need for you to try to identify them (but always label consistently within a frame). If you have 2 black mice but one always has an optical fiber (for example), then DO label them consistently as animal1 and animal_fiber (for example). The point of multi-animal DLC is to train models that can first group the correct bodyparts to individuals, then associate those points in a given video to a specific individual, which then also uses temporal information to link across the video frames.
+OPTIONAL: In the event of adding more labels to the existing labeled dataset, the user
+needs to append the new labels to the bodyparts in the config.yaml file. Thereafter, the
+user can call the function **label_frames**. A box will pop up and ask the user if they
+wish to display all parts, or only add in the new labels. Saving the labels after all
+the images are labelled will append the new labels to the existing labeled dataset.
-Note, we also highly recommend that you use more bodypoints that you might otherwise have (see the example below).
+**maDeepLabCut CRITICAL POINT:** For multi-animal labeling, unless you can tell apart
+the animals, you do not need to worry about the "ID" of each animal. For example: if you
+have a white and black mouse label the white mouse as animal 1, and black as animal 2
+across all frames. If two black mice, then the ID label 1 or 2 can switch between
+frames - no need for you to try to identify them (but always label consistently within a
+frame). If you have 2 black mice but one always has an optical fiber (for example), then
+DO label them consistently as animal1 and animal_fiber (for example). The point of
+multi-animal DLC is to train models that can first group the correct bodyparts to
+individuals, then associate those points in a given video to a specific individual,
+which then also uses temporal information to link across the video frames.
-**Example Labeling with maDeepLabCut:**
-- note you should within an animal be consistent, i.e., all bodyparts on mouse1 should be on mouse1, but across frames "mouse1" can be any of the black mice (as here it is nearly impossible to tell them apart visually). IF you can tell them apart, do label consistently!
+Note, we also highly recommend that you use more bodyparts that you might otherwise have
+(see the example below).
-
-
-
+For more information, checkout the [napari-deeplabcut docs](file:napari-gui-landing) for
+more information about the labelling workflow.
-### Check Annotated Frames:
+### (E) Check Annotated Frames
Checking if the labels were created and stored correctly is beneficial for training, since labeling
is one of the most critical parts for creating the training dataset. The DeepLabCut toolbox provides a function
-‘check_labels’ to do so. It is used as follows:
+`check_labels` to do so. It is used as follows:
+
```python
deeplabcut.check_labels(config_path, visualizeindividuals=True/False)
```
+
**maDeepLabCut:** you can check and plot colors per individual or per body part, just set the flag `visualizeindividuals=True/False`. Note, you can run this twice in both states to see both images.
@@ -221,13 +321,33 @@ deeplabcut.check_labels(config_path, visualizeindividuals=True/False)
For each video directory in labeled-data this function creates a subdirectory with **labeled** as a suffix. Those directories contain the frames plotted with the annotated body parts. The user can double check if the body parts are labeled correctly. If they are not correct, the user can reload the frames (i.e. `deeplabcut.label_frames`), move them around, and click save again.
+````{admonition} Click the button to see API Docs
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.check_labels.rst
+```
+````
+
+### (F) Create Training Dataset
+
+At this point, you'll need to select your neural network type.
-### Create Training Dataset:
+For the **PyTorch engine**, please see [the PyTorch Model Architectures](
+dlc3-architectures) for options.
-At this point you also select your neural network type. Please see Lauer et al. 2021 for options. For **create_multianimaltraining_dataset** we already changed this such that by default you will use imgaug, ADAM optimization, our new DLCRNet, and batch training. We suggest these defaults at this time. Then run:
+For the **TensorFlow engine**, please see Lauer et al. 2021 for options. Multi-animal
+models will use `imgaug`, ADAM optimization, our new DLCRNet, and batch training. We
+suggest keeping these defaults at this time. At this step, the ImageNet pre-trained
+networks (i.e. ResNet-50) weights will be downloaded. If they do not download (you will
+see this downloading in the terminal, then you may not have permission to do so (
+something we have seen with some Windows users - see the **[
+WIKI troubleshooting for more help!](
+https://github.com/DeepLabCut/DeepLabCut/wiki/Troubleshooting-Tips)**).
+
+Then run:
```python
-deeplabcut.create_multianimaltraining_dataset(path_config_file)
+deeplabcut.create_training_dataset(config_path)
```
- The set of arguments in the function will shuffle the combined labeled dataset and split it to create train and test
@@ -238,47 +358,33 @@ keeps track of how often the dataset was refined).
- OPTIONAL: If the user wishes to benchmark the performance of the DeepLabCut, they can create multiple
training datasets by specifying an integer value to the `num_shuffles`; see the docstring for more details.
-- Each iteration of the creation of a training dataset will create several files, which is used by the feature detectors,
-and a ``.pickle`` file that contains the meta information about the training dataset. This also creates two subdirectories
-within **dlc-models** called ``test`` and ``train``, and these each have a configuration file called pose_cfg.yaml.
-Specifically, the user can edit the **pose_cfg.yaml** within the **train** subdirectory before starting the training. These
-configuration files contain meta information with regard to the parameters of the feature detectors. Key parameters
-are listed in Box 2.
-
-- At this step, the ImageNet pre-trained networks (i.e. ResNet-50) weights will be downloaded. If they do not download (you will see this downloading in the terminal, then you may not have permission to do so (something we have seen with some Windows users - see the **[WIKI troubleshooting for more help!](https://github.com/DeepLabCut/DeepLabCut/wiki/Troubleshooting-Tips)**).
-
-**OPTIONAL POINTS:**
-
-With the data-driven skeleton selection introduced in 2.2rc1+, DLC networks are trained by default
-on complete skeletons (i.e., they learn all possible redundant connections), before being optimally pruned
-at model evaluation. Although this procedure is by far superior to manually defining a graph,
-we leave manually-defining a skeleton as an option for the advanced user:
-
-```python
-my_better_graph = [[0, 1], [1, 2], [2, 3]] # These are indices in the list of multianimalbodyparts
-deeplabcut.create_multianimaltraining_dataset(path_config_file, paf_graph=my_better_graph)
-```
-
-Alternatively, the `skeleton` defined in the `config.yaml` file can also be used:
-
-```python
-deeplabcut.create_multianimaltraining_dataset(path_config_file, paf_graph='config')
-```
-
-Importantly, if a user-defined graph is used it still is required to cover all multianimalbodyparts at least once.
-
-**DATA AUGMENTATION:** At this stage you can also decide what type of augmentation to use. The default loaders work well for most all tasks (as shown on www.deeplabcut.org), but there are many options, more data augmentation, intermediate supervision, etc. Please look at the [**pose_cfg.yaml**](https://github.com/DeepLabCut/DeepLabCut/blob/master/deeplabcut/pose_cfg.yaml) file for a full list of parameters **you might want to change before running this step.** There are several data loaders that can be used. For example, you can use the default loader (introduced and described in the Nature Protocols paper), [TensorPack](https://github.com/tensorpack/tensorpack) for data augmentation (currently this is easiest on Linux only), or [imgaug](https://imgaug.readthedocs.io/en/latest/). We recommend `imgaug` (which is default now!). You can set this by passing:``` deeplabcut.create_training_dataset(config_path, augmenter_type='imgaug') ```
-
-The differences of the loaders are as follows:
-- `default`: our standard DLC 2.0 introduced in Nature Protocols variant (scaling, auto-crop augmentation) *will be renamed to `crop_scale` in a future release!*
-- `imgaug`: a lot of augmentation possibilities, efficient code for target map creation & batch sizes >1 supported. You can set the parameters such as the `batch_size` in the `pose_cfg.yaml` file for the model you are training.
-- `tensorpack`: a lot of augmentation possibilities, multi CPU support for fast processing, target maps are created less efficiently than in imgaug, does not allow batch size>1
-- `deterministic`: only useful for testing, freezes numpy seed; otherwise like default.
-
-Our recent [A Primer on Motion Capture with Deep Learning: Principles, Pitfalls, and Perspectives](https://www.cell.com/neuron/pdf/S0896-6273(20)30717-0.pdf), details the advantage of augmentation for a worked example (see Fig 7). TL;DR: use imgaug and use the symmetries of your data!
-
-
-Alternatively, you can set the loader (as well as other training parameters) in the **pose_cfg.yaml** file of the model that you want to train. Note, to get details on the options, look at the default file: [**pose_cfg.yaml**](https://github.com/DeepLabCut/DeepLabCut/blob/master/deeplabcut/pose_cfg.yaml).
+- Each iteration of the creation of a training dataset will create several files, which
+is used by the feature detectors, and a ``.pickle`` file that contains the meta
+information about the training dataset. This also creates two subdirectories within
+**dlc-models-pytorch** (**dlc-models** for the TensorFlow engine) called ``test`` and
+``train``, and these each have a configuration file called pose_cfg.yaml. Specifically,
+the user can edit the **pytorch_config.yaml** (**pose_cfg.yaml** for TensorFlow engine)
+within the **train** subdirectory before starting the training. These configuration
+files contain meta information with regard to the parameters of the feature detectors.
+Key parameters are listed in Box 2.
+
+**DATA AUGMENTATION:** At this stage you can also decide what type of augmentation to
+use. Once you've called `create_training_dataset`, you can edit the
+[**pytorch_config.yaml**](dlc3-pytorch-config) file that was created (or for the
+TensorFlow engine, the [**pose_cfg.yaml**](
+https://github.com/DeepLabCut/DeepLabCut/blob/master/deeplabcut/pose_cfg.yaml) file).
+
+- PyTorch Engine: [Albumentations](https://albumentations.ai/docs/) is used for data
+augmentation. Look at the [**pytorch_config.yaml**](dlc3-pytorch-config) for more
+information about image augmentation options.
+- TensorFlow Engine: The default augmentation works well for most tasks (as shown on
+www.deeplabcut.org), but there are many options, more data augmentation, intermediate
+supervision, etc. Only `imgaug` augmentation is available for multi-animal projects.
+
+[A Primer on Motion Capture with Deep Learning: Principles, Pitfalls, and Perspectives](
+https://www.cell.com/neuron/pdf/S0896-6273(20)30717-0.pdf), details the advantage of
+augmentation for a worked example (see Fig 8). TL;DR: use imgaug and use the symmetries
+of your data!
Importantly, image cropping as previously done with `deeplabcut.cropimagesandlabels` in multi-animal projects
is now part of the augmentation pipeline. In other words, image crops are no longer stored in labeled-data/..._cropped
@@ -288,80 +394,220 @@ In addition, one can specify a crop sampling strategy: crop centers can either b
As a reminder, cropping images into smaller patches is a form of data augmentation that simultaneously
allows the use of batch processing even on small GPUs that could not otherwise accommodate larger images + larger batchsizes (this usually increases performance and decreasing training time).
+**MODEL COMPARISON**: You can also test several models by creating the same train/test
+split for different networks.
+You can easily do this in the Project Manager GUI (by selecting the "Use an existing
+data split" option), which also lets you compare PyTorch and TensorFlow models.
-### Train The Network:
+````{versionadded} 3.0.0
+You can now create new shuffles using the same train/test split as
+existing shuffles with `create_training_dataset_from_existing_split`. This allows you to
+compare model performance (between different architectures or when using different
+training hyper-parameters) as the shuffles were trained on the same data, and evaluated
+on the same test data!
-```python
-deeplabcut.train_network(config_path, allow_growth=True)
-```
+Example usage - creating 3 new shuffles (with indices 10, 11 and 12) for a ResNet 50
+pose estimation model, using the same data split as was used for shuffle 0:
-The set of arguments in the function starts training the network for the dataset created for one specific shuffle. Note that you can change the loader (imgaug/default/etc) as well as other training parameters in the **pose_cfg.yaml** file of the model that you want to train (before you start training).
-
-Example parameters that one can call:
```python
-deeplabcut.train_network(config_path, shuffle=1, trainingsetindex=0, gputouse=None, max_snapshots_to_keep=5, autotune=False, displayiters=100, saveiters=15000, maxiters=30000, allow_growth=True)
+deeplabcut.create_training_dataset_from_existing_split(
+ config_path,
+ from_shuffle=0,
+ shuffles=[10, 11, 12],
+ net_type="resnet_50",
+)
```
+````
-By default, the pretrained networks are not in the DeepLabCut toolbox (as they are around 100MB each), but they get downloaded before you train. However, if not previously downloaded from the TensorFlow model weights, it will be downloaded and stored in a subdirectory *pre-trained* under the subdirectory *models* in *Pose_Estimation_Tensorflow*.
-At user specified iterations during training checkpoints are stored in the subdirectory *train* under the respective iteration directory.
-
-If the user wishes to restart the training at a specific checkpoint they can specify the full path of the checkpoint to
-the variable ``init_weights`` in the **pose_cfg.yaml** file under the *train* subdirectory (see Box 2).
-
-**CRITICAL POINT:** It is recommended to train the networks for thousands of iterations until the loss plateaus (typically around **500,000**) if you use batch size 1, and **50-100K** if you use batchsize 8 (the default).
+````{admonition} Click the button to see API Docs for deeplabcut.create_training_dataset
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.create_training_dataset.rst
+```
+````
-If you use **maDeepLabCut** the recommended training iterations is **20K-100K** (it automatically stops at 200K!), as we use Adam and batchsize 8; if you have to reduce the batchsize for memory reasons then the number of iterations needs to be increased.
+````{admonition} Click the button to see API Docs for deeplabcut.create_training_model_comparison
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.create_training_model_comparison.rst
+```
+````
-The variables ``display_iters`` and ``save_iters`` in the **pose_cfg.yaml** file allows the user to alter how often the loss is displayed and how often the weights are stored.
+````{admonition} Click the button to see API Docs for deeplabcut.create_training_dataset_from_existing_split
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.create_training_dataset_from_existing_split.rst
+```
+````
-**maDeepLabCut CRITICAL POINT:** For multi-animal projects we are using not only different and new output layers, but also new data augmentation, optimization, learning rates, and batch training defaults. Thus, please use a lower ``save_iters`` and ``maxiters``. I.e. we suggest saving every 10K-15K iterations, and only training until 50K-100K iterations. We recommend you look closely at the loss to not overfit on your data. The bonus, training time is much less!!!
+### (G) Train The Network
-**Parameters:**
+```python
+deeplabcut.train_network(config_path, shuffle=1)
```
-config : string
- Full path of the config.yaml file as a string.
-shuffle: int, optional
- Integer value specifying the shuffle index to select for training. Default is set to 1
+The set of arguments in the function starts training the network for the dataset created
+for one specific shuffle. Note that you can change training parameters in the
+[**pytorch_config.yaml**](dlc3-pytorch-config) file (or **pose_cfg.yaml** for TensorFlow
+models) of the model that you want to train (before you start training).
-trainingsetindex: int, optional
- Integer specifying which TrainingsetFraction to use. By default the first (note that TrainingFraction is a list in config.yaml).
+At user specified iterations during training checkpoints are stored in the subdirectory
+*train* under the respective iteration & shuffle directory.
-gputouse: int, optional. Natural number indicating the number of your GPU (see number in nvidia-smi). If you do not have a GPU, put None.
-See: https://nvidia.custhelp.com/app/answers/detail/a_id/3751/~/useful-nvidia-smi-queries
+````{admonition} Tips on training models with the PyTorch Engine
+:class: dropdown
-max_snapshots_to_keep: int, or None. Sets how many snapshots are kept, i.e. states of the trained network. For every saving iteration a snapshot is stored, however, only the last max_snapshots_to_keep many are kept! If you change this to None, then all are kept.
-See: https://github.com/DeepLabCut/DeepLabCut/issues/8#issuecomment-387404835
+Example parameters that one can call:
-autotune: property of TensorFlow, somehow faster if 'false' (as Eldar found out, see https://github.com/tensorflow/tensorflow/issues/13317). Default: False
+```python
+deeplabcut.train_network(
+ config_path,
+ shuffle=1,
+ trainingsetindex=0,
+ device="cuda:0",
+ max_snapshots_to_keep=5,
+ displayiters=100,
+ save_epochs=5,
+ epochs=200,
+)
+```
+
+Pytorch models in DeepLabCut 3.0 are trained for a set number of epochs, instead of a
+maximum number of iterations (which is what was used for TensorFlow models). An epoch
+is a single pass through the training dataset, which means your model has seen each
+training image exactly once. So if you have 64 training images for your network, an
+epoch is 64 iterations with batch size 1 (or 32 iterations with batch size 2, 16 with
+batch size 4, etc.).
+
+By default, the pretrained networks are not in the DeepLabCut toolbox (as they can be
+more than 100MB), but they get downloaded automatically before you train.
+
+If the user wishes to restart the training at a specific checkpoint they can specify the
+full path of the checkpoint to the variable ``resume_training_from`` in the [
+**pytorch_config.yaml**](
+dlc3-pytorch-config) file (checkout the "Restarting Training at a Specific Checkpoint"
+section of the docs) under the *train* subdirectory.
+
+**CRITICAL POINT:** It is recommended to train the networks **until the loss plateaus**
+(depending on the dataset, model architecture and training hyper-parameters this happens
+after 100 to 250 epochs of training).
+
+The variables ``display_iters`` and ``save_epochs`` in the [**pytorch_config.yaml**](
+dlc3-pytorch-config) file allows the user to alter how often the loss is displayed
+and how often the weights are stored. We suggest saving every 5 to 25 epochs.
+````
-displayiters: this variable is actually set in pose_config.yaml. However, you can overwrite it with this hack. Don't use this regularly, just if you are too lazy to dig out
-the pose_config.yaml file for the corresponding project. If None, the value from there is used, otherwise it is overwritten! Default: None
+````{admonition} Tips on training models with the TensorFlow Engine
+:class: dropdown
-saveiters: this variable is actually set in pose_config.yaml. However, you can overwrite it with this hack. Don't use this regularly, just if you are too lazy to dig out
-the pose_config.yaml file for the corresponding project. If None, the value from there is used, otherwise it is overwritten! Default: None
+Example parameters that one can call:
+
+```python
+deeplabcut.train_network(
+ config_path,
+ shuffle=1,
+ trainingsetindex=0,
+ gputouse=None,
+ max_snapshots_to_keep=5,
+ autotune=False,
+ displayiters=100,
+ saveiters=15000,
+ maxiters=30000,
+ allow_growth=True,
+)
+```
+
+By default, the pretrained networks are not in the DeepLabCut toolbox (as they are
+around 100MB each), but they get downloaded before you train. However, if not previously
+downloaded from the TensorFlow model weights, it will be downloaded and stored in a
+subdirectory *pre-trained* under the subdirectory *models* in
+*Pose_Estimation_Tensorflow*. At user specified iterations during training checkpoints
+are stored in the subdirectory *train* under the respective iteration directory.
+
+If the user wishes to restart the training at a specific checkpoint they can specify the
+full path of the checkpoint to the variable ``init_weights`` in the **pose_cfg.yaml**
+file under the *train* subdirectory (see Box 2).
+
+**CRITICAL POINT:** It is recommended to train the networks for thousands of iterations
+until the loss plateaus (typically around **500,000**) if you use batch size 1, and
+**50-100K** if you use batchsize 8 (the default).
+
+If you use **maDeepLabCut** the recommended training iterations is **20K-100K**
+(it automatically stops at 200K!), as we use Adam and batchsize 8; if you have to reduce
+ the batchsize for memory reasons then the number of iterations needs to be increased.
+
+The variables ``display_iters`` and ``save_iters`` in the **pose_cfg.yaml** file allows
+the user to alter how often the loss is displayed and how often the weights are stored.
+
+**maDeepLabCut CRITICAL POINT:** For multi-animal projects we are using not only
+different and new output layers, but also new data augmentation, optimization, learning
+rates, and batch training defaults. Thus, please use a lower ``save_iters`` and
+``maxiters``. I.e. we suggest saving every 10K-15K iterations, and only training until
+50K-100K iterations. We recommend you look closely at the loss to not overfit on your
+data. The bonus, training time is much less!!!
+````
-maxiters: This sets how many iterations to train. This variable is set in pose_config.yaml. However, you can overwrite it with this. If None, the value from there is used, otherwise it is overwritten! Default: None
+````{admonition} Click the button to see API Docs for train_network
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.train_network.rst
```
+````
-### Evaluate the Trained Network:
+### (H) Evaluate the Trained Network
+
+It is important to evaluate the performance of the trained network. This performance is
+measured by computing two metrics:
+
+- **Average root mean square error** (RMSE) between the manual labels and the ones
+predicted by your trained DeepLabCut model. The RMSE is proportional to the mean average
+Euclidean error (MAE) between the manual labels and the ones predicted by DeepLabCut.
+The MAE is displayed for all pairs and only likely pairs (>p-cutoff). This helps to
+exclude, for example, occluded body parts. One of the strengths of DeepLabCut is that
+due to the probabilistic output of the scoremap, it can, if sufficiently trained, also
+reliably report if a body part is visible in a given frame. (see discussions of finger
+tips in reaching and the Drosophila legs during 3D behavior in [Mathis et al, 2018]).
+- **Mean Average Precision** (mAP) and **Mean Average Recall** (mAR) for the individuals
+predicted by your trained DeepLabCut model. This metric describes the precision of your
+model, based on a considered definition of what a correct detection of an individual is.
+It isn't as useful for single-animal models, as RMSE does a great job of evaluating your
+model in that case.
+
+```{admonition} A more detailed description of mAP and mAR
+:class: dropdown
+
+For multi-animal pose estimation, multiple predictions can be made for each image.
+We want to get some idea of the proportion of correct predictions among all predictions
+that are made.
+However, the notion of "correct prediction" for pose estimation is not straightforward:
+is a prediction correct if all predicted keypoints are within 5 pixels of the ground
+truth? Within 2 pixels of the ground truth? What if all pixels but one match the ground
+truth perfectly, but the wrong prediction is 50 pixels away? Mean average precision (
+and mean average recall) estimate the precision/recall of your models by setting
+different "thresholds of correctness" and averaging results. How "correct" a
+prediction is can be evaluated through [object-keypoint similarity](
+https://cocodataset.org/#keypoints-eval).
+
+A good resource to get a deeper understanding of mAP is the [Stanford CS230 course](
+https://cs230.stanford.edu/section/8/#object-detection-iou-ap-and-map). While it
+describes mAP for object detection (where bounding boxes are predicted instead of
+keypoints), the same metric can be computed for pose estimation, where similarity
+between predictions and ground truth is computed through [object-keypoint similarity](
+https://cocodataset.org/#keypoints-eval) instead of intersection-over-union (IoU).
+```
+
+It's also important to visually inspect predictions on individual frames to assess the
+performance of your model. You can do this by setting `plotting=True` when you call
+`evaluate_network`. The evaluation results are computed by typing:
-Here, for traditional projects you will get a pixel distance metric and you should inspect the individual frames:
```python
-deeplabcut.evaluate_network(config_path, plotting=True)
+deeplabcut.evaluate_network(config_path, Shuffles=[1], plotting=True)
```
-:movie_camera:[VIDEO TUTORIAL AVAILABLE!](https://www.youtube.com/watch?v=bgfnz1wtlpo)
-It is important to evaluate the performance of the trained network. This performance is measured by computing
-the mean average Euclidean error (MAE; which is proportional to the average root mean square error) between the
-manual labels and the ones predicted by DeepLabCut. The MAE is saved as a comma separated file and displayed
-for all pairs and only likely pairs (>p-cutoff). This helps to exclude, for example, occluded body parts. One of the
-strengths of DeepLabCut is that due to the probabilistic output of the scoremap, it can, if sufficiently trained, also
-reliably report if a body part is visible in a given frame. (see discussions of finger tips in reaching and the Drosophila
-legs during 3D behavior in [Mathis et al, 2018]). The evaluation results are computed by typing:
+🎥 [VIDEO TUTORIAL AVAILABLE!](https://www.youtube.com/watch?v=bgfnz1wtlpo)
Setting ``plotting`` to True plots all the testing and training frames with the manual and predicted labels; these will
-be colored by body part type by default. They can alternatively be colored by individual by passing `plotting`=`individual`.
+be colored by body part type by default. They can alternatively be colored by individual by passing `plotting="individual"`.
The user should visually check the labeled test (and training) images that are created in the ‘evaluation-results’ directory.
Ideally, DeepLabCut labeled unseen (test images) according to the user’s required accuracy, and the average train
and test errors are comparable (good generalization). What (numerically) comprises an acceptable MAE depends on
@@ -369,37 +615,59 @@ many factors (including the size of the tracked body parts, the labeling variabi
also be larger than the training error due to human variability (in labeling, see Figure 2 in Mathis et al, Nature Neuroscience 2018).
**Optional parameters:**
-```
- Shuffles: list, optional -List of integers specifying the shuffle indices of the training dataset. The default is [1]
- plotting: bool, optional -Plots the predictions on the train and test images. The default is `False`; if provided it must be either `True` or `False`
+- `Shuffles: list, optional` - List of integers specifying the shuffle indices of the training dataset.
+The default is [1]
- show_errors: bool, optional -Display train and test errors. The default is `True`
+- `plotting: bool | str, optional` - Plots the predictions on the train and test images. The default is `False`;
+if provided it must be either `True`, `False`, `"bodypart"`, or `"individual"`.
- comparisonbodyparts: list of bodyparts, Default is all -The average error will be computed for those body parts only (Has to be a subset of the body parts).
+- `show_errors: bool, optional` - Display train and test errors. The default is `True`
- gputouse: int, optional -Natural number indicating the number of your GPU (see number in nvidia-smi). If you do not have a GPU, put None. See: https://nvidia.custhelp.com/app/answers/detail/a_id/3751/~/useful-nvidia-smi-queries
-```
+- `comparisonbodyparts: list of bodyparts, Default is all` - The average error will be computed for those body parts
+only (Has to be a subset of the body parts).
+
+- `gputouse: int, optional` - Natural number indicating the number of your GPU (see number in nvidia-smi). If you do not
+have a GPU, put None. See: https://nvidia.custhelp.com/app/answers/detail/a_id/3751/~/useful-nvidia-smi-queries
+
+- `pcutoff: float | list[float] | dict[str, float], optional`
+(Only applicable when using the PyTorch engine. For TensorFlow, set `pcutoff` in the `config.yaml` file.)
+Specifies the cutoff value(s) used to compute evaluation metrics.
+ - If `None` (default), the cutoff will be loaded from the project configuration.
+ - To apply a single cutoff value to all bodyparts, provide a `float`.
+ - To specify different cutoffs per bodypart, provide either:
+ - A `list[float]`: one value per bodypart, with an additional value for each unique bodypart if applicable.
+ - A `dict[str, float]`: where keys are bodypart names and values are the corresponding cutoff values.
+If a bodypart is not included in the provided dictionary, a default `pcutoff` of `0.6` will be used for that bodypart.
The plots can be customized by editing the **config.yaml** file (i.e., the colormap, scale, marker size (dotsize), and
-transparency of labels (alphavalue) can be modified). By default each body part is plotted in a different color
+transparency of labels (alpha-value) can be modified). By default each body part is plotted in a different color
(governed by the colormap) and the plot labels indicate their source. Note that by default the human labels are
-plotted as plus (‘+’), DeepLabCut’s predictions either as ‘.’ (for confident predictions with likelihood > p-cutoff) and
+plotted as plus (‘+’), DeepLabCut’s predictions either as ‘.’ (for confident predictions with likelihood > `pcutoff`) and
’x’ for (likelihood <= `pcutoff`).
-The evaluation results for each shuffle of the training dataset are stored in a unique subdirectory in a newly created
-directory ‘evaluation-results’ in the project directory. The user can visually inspect if the distance between the labeled
-and the predicted body parts are acceptable. In the event of benchmarking with different shuffles of same training
-dataset, the user can provide multiple shuffle indices to evaluate the corresponding network. If the generalization is
-not sufficient, the user might want to:
+The evaluation results for each shuffle of the training dataset are stored in a unique
+subdirectory in a newly created directory ‘evaluation-results-pytorch’ (or
+‘evaluation-results’ for TensorFlow models) in the project directory.
+The user can visually inspect if the distance between the labeled and the predicted body
+parts are acceptable. In the event of benchmarking with different shuffles of same training
+dataset, the user can provide multiple shuffle indices to evaluate the corresponding
+network. If the generalization is not sufficient, the user might want to:
-• check if the labels were imported correctly; i.e., invisible points are not labeled and the points of interest are
-labeled accurately
+• check if the labels were imported correctly; i.e., invisible points are not labeled
+and the points of interest are labeled accurately
• make sure that the loss has already converged
• consider labeling additional images and make another iteration of the training data set
+````{admonition} Click the button to see API Docs for evaluate_network
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.evaluate_network.rst
+```
+````
+
**maDeepLabCut: (or on normal projects!)**
In multi-animal projects, model evaluation is crucial as this is when
@@ -412,22 +680,55 @@ You should also plot the scoremaps, locref layers, and PAFs to assess performanc
```python
deeplabcut.extract_save_all_maps(config_path, shuffle=shuffle, Indices=[0, 5])
```
-you can drop "Indices" to run this on all training/testing images (this is very slow!)
-### -------------------- DECISION POINT -------------------
+You can drop "Indices" to run this on all training/testing images (this is very slow!)
+
+### (I) Analyze new Videos
+
+````{versionadded} 3.0.0
+With the addition of conditional top-down models in DeepLabCut 3.0, it's now possible to
+track individuals directly **during video analysis**. If you choose to train any model
+with a name that starts with `ctd_`, you'll be able to call `deeplabcut.analyze_videos`
+with `ctd_tracking=True`. To learn more about tracking with CTD, see the [
+`COLAB_BUCTD_and_CTD_tracking`](
+https://github.com/DeepLabCut/DeepLabCut/blob/main/examples/COLAB/COLAB_BUCTD_and_CTD_tracking.ipynb)
+COLAB notebook.
+````
-#### ATTENTION!
-**Pose estimation and tracking should be thought of as separate steps.** If you do not have good pose estimation evaluation metrics at this point, stop, check original labels, add more data, etc --> don't move forward with this model. If you think you have a good model, please test the "raw" pose estimation performance on a video to validate performance:
+**-------------------- DECISION POINT -------------------**
+
+**ATTENTION!**
+**Pose estimation and tracking should be thought of as separate steps.** If you do not
+have good pose estimation evaluation metrics at this point, stop, check original labels,
+add more data, etc --> don't move forward with this model. If you think you have a good
+model, please test the "raw" pose estimation performance on a video to validate
+performance:
Please run:
```python
-scorername = deeplabcut.analyze_videos(config_path,['/fullpath/project/videos/testVideo.mp4'], videotype='.mp4')
-deeplabcut.create_video_with_all_detections(config_path, ['/fullpath/project/videos/testVideo.mp4'], videotype='.mp4')
+videos_to_analyze = ['/fullpath/project/videos/testVideo.mp4']
+scorername = deeplabcut.analyze_videos(config_path, videos_to_analyze, videotype='.mp4')
+deeplabcut.create_video_with_all_detections(config_path, videos_to_analyze, videotype='.mp4')
```
-Please note that you do **not** get the .h5/csv file you might be used to getting (this comes after tracking). You will get a `pickle` file that is used in `create_video_with_all_detections`.
-Another sanity check may be to examine the distributions of edge affinity costs using `deeplabcut.utils.plot_edge_affinity_distributions`. Easily separable distributions indicate that the model has learned strong links to group keypoints into distinct individuals — likely a necessary feature for the assembly stage (note that the amount of overlap will also depend on the amount of interactions between your animals in the daset).
-IF you have good clean out video, ending in `....full.mp4` (and the evaluation metrics look good, scoremaps look good, plotted evaluation images, and affinity distributions are far apart for most edges), then go forward!!!
+
+Please note that you do **not** get the .h5/csv file you might be used to getting (this
+comes after tracking). You will get a `pickle` file that is used in
+`create_video_with_all_detections`.
+
+For models predicting part-affinity fields, another sanity check may be to
+examine the distributions of edge affinity costs using `deeplabcut.utils.plot_edge_affinity_distributions`. Easily separable distributions
+indicate that the model has learned strong links to group keypoints into distinct
+individuals — likely a necessary feature for the assembly stage (note that the amount of
+overlap will also depend on the amount of interactions between your animals in the
+dataset). All TensorFlow multi-animal models use part-affinity fields and PyTorch models
+consisting of just a backbone name (e.g. `resnet_50`, `resnet_101`) use part-affinity
+fields. If you're unsure whether your PyTorch model has a one, check
+the **pytorch_config.yaml** for a `DLCRNetHead`.
+
+IF you have good clean out video, ending in `....full.mp4` (and the evaluation metrics
+look good, scoremaps look good, plotted evaluation images, and affinity distributions
+are far apart for most edges), then go forward!!!
If this does not look good, we recommend extracting and labeling more frames (even from more videos). Try to label close interactions of animals for best performance. Once you label more, you can create a new training set and train.
@@ -435,30 +736,53 @@ You can either:
1. extract more frames manually from existing or new videos and label as when initially building the training data set, or
2. let DeepLabCut find frames where keypoints were poorly detected and automatically extract those for you. All you need is
to run:
+
```python
deeplabcut.find_outliers_in_raw_data(config_path, pickle_file, video_file)
```
+
where pickle_file is the `_full.pickle` one obtains after video analysis.
Flagged frames will be added to your collection of images in the corresponding labeled-data folders for you to label.
-### ------------------- ANIMAL ASSEMBLY & TRACKING ACROSS FRAMES -------------------
+### Animal Assembly and Tracking across frames
-After pose estimation, now you perform assembly and tracking. *NEW* in 2.2 is a novel data-driven way to set the optimal skeleton and assembly metrics, so this no longer requires user input. The metrics, in case you do want to edit them, can be found in the `inference_cfg.yaml` file.
+After pose estimation, now you perform assembly and tracking.
+
+````{versionadded} v2.2.0
+*NEW* in 2.2 is a novel data-driven way to set the optimal skeleton and assembly
+metrics, so this no longer requires user input. The metrics, in case you do want to edit
+them, can be found in the `inference_cfg.yaml` file.
+````
### Optimized Animal Assembly + Video Analysis:
-- Please note that **novel videos DO NOT need to be added to the config.yaml file**. You can simply have a folder elsewhere on your computer and pass the video folder (then it will analyze all videos of the specified type (i.e. ``videotype='.mp4'``), or pass the path to the **folder** or exact video(s) you wish to analyze:
+Please note that **novel videos DO NOT need to be added to the config.yaml file**. You
+can simply have a folder elsewhere on your computer and pass the video folder (then it
+will analyze all videos of the specified type (i.e. ``videotype='.mp4'``), or pass the
+path to the **folder** or exact video(s) you wish to analyze:
```python
deeplabcut.analyze_videos(config_path, ['/fullpath/project/videos/'], videotype='.mp4', auto_track=True)
```
-## IF auto_track = True:
-- *NEW* in 2.2.0.3+: `deeplabcut.analyze_videos` has a new argument `auto_track=True`, chaining pose estimation, tracking, and stitching in a single function call with defaults we found to work well. Thus, you'll now get the `.h5` file you might be used to getting in standard DLC. If `auto_track=False`, one must run `convert_detections2tracklets` and `stitch_tracklets` manually (see below), granting more control over the last steps of the workflow (ideal for advanced users).
+### IF auto_track = True:
+
+```{versionadded} v2.2.0.3
+A new argument `auto_track=True`, was added to `deeplabcut.analyze_videos` chaining pose
+estimation, tracking, and stitching in a single function call with defaults we found to
+work well. Thus, you'll now get the `.h5` file you might be used to getting in standard
+DLC. If `auto_track=False`, one must run `convert_detections2tracklets` and
+`stitch_tracklets` manually (see below), granting more control over the last steps of
+the workflow (ideal for advanced users).
+```
-## IF auto_track = False:
+### IF auto_track = False:
- - You can validate the tracking parameters. Namely, you can iteratively change the parameters, run `convert_detections2tracklets` then load them in the GUI (`refine_tracklets`) if you want to look at the performance. If you want to edit these, you will need to open the `inference_cfg.yaml` file (or click button in GUI). The options are:
+You can validate the tracking parameters. Namely, you can iteratively change the
+parameters, run `convert_detections2tracklets` then load them in the GUI
+(`refine_tracklets`) if you want to look at the performance. If you want to edit these,
+you will need to open the `inference_cfg.yaml` file (or click button in GUI). The
+options are:
```python
# Tracking:
@@ -497,8 +821,13 @@ from the final h5 file as was customary in single animal projects.
**Next, tracklets are stitched to form complete tracks with:
```python
-deeplabcut.stitch_tracklets(config_path, ['videofile_path'], videotype='mp4',
- shuffle=1, trainingsetindex=0)
+deeplabcut.stitch_tracklets(
+ config_path,
+ ['videofile_path'],
+ videotype='mp4',
+ shuffle=1,
+ trainingsetindex=0,
+)
```
Note that the base signature of the function is identical to `analyze_videos` and `convert_detections2tracklets`.
@@ -509,15 +838,42 @@ can be directly specified as follows:
```python
deeplabcut.stitch_tracklets(..., n_tracks=n)
```
+
In such cases, file columns will default to dummy animal names (ind1, ind2, ..., up to indn).
-## Using Unsupervised Identity Tracking:
+### API Docs
+
+````{admonition} Click the button to see API Docs for analyze_videos
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.analyze_videos.rst
+```
+````
+
+````{admonition} Click the button to see API Docs for convert_detections2tracklets
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.convert_detections2tracklets.rst
+```
+````
+
+````{admonition} Click the button to see API Docs for stitch_tracklets
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.stitch_tracklets.rst
+```
+````
+
+### Using Unsupervised Identity Tracking:
-In Lauer et al. 2022 we introduced a new method to do unsupervised reID of animals. Here, you can use the tracklets to learn the identity of animals to enhance your tracking performance. To use the code:
+In Lauer et al. 2022 we introduced a new method to do unsupervised reID of animals.
+Here, you can use the tracklets to learn the identity of animals to enhance your
+tracking performance. To use the code:
```python
deeplabcut.transformer_reID(config, videos_to_analyze, n_tracks=None, videotype="mp4")
```
+
Note you should pass the n_tracks (number of animals) you expect to see in the video.
### Refine Tracklets:
@@ -525,7 +881,7 @@ Note you should pass the n_tracks (number of animals) you expect to see in the v
You can also optionally **refine the tracklets**. You can fix both "major" ID swaps, i.e. perhaps when animals cross, and you can micro-refine the individual body points. You will load the `...trackertype.pickle` or `.h5'` file that was created above, and then you can launch a GUI to interactively refine the data. This also has several options, so please check out the docstring. Upon saving the refined tracks you get an `.h5` file (akin to what you might be used to from standard DLC. You can also load (1) filter this to take care of small jitters, and (2) load this `.h5` this to refine (again) in case you find another issue, etc!
```python
-deeplabcut.refine_tracklets(path_config_file, pickle_or_h5_file, videofile_path, max_gap=0, min_swap_len=2, min_tracklet_len=2, trail_len=50)
+deeplabcut.refine_tracklets(config_path, pickle_or_h5_file, videofile_path, max_gap=0, min_swap_len=2, min_tracklet_len=2, trail_len=50)
```
If you use the GUI (or otherwise), here are some settings to consider:
@@ -543,7 +899,7 @@ Short demo:
-### Once you have analyzed video data (and refined your maDeepLabCut tracklets):
+### (J) Filter Pose Data
Firstly, Here are some tips for scaling up your video analysis, including looping over many folders for batch processing: https://github.com/DeepLabCut/DeepLabCut/wiki/Batch-Processing-your-Analysis
@@ -553,7 +909,14 @@ deeplabcut.filterpredictions(config_path,['/fullpath/project/videos/reachingvide
```
Note, this creates a file with the ending filtered.h5 that you can use for further analysis. This filtering step has many parameters, so please see the full docstring by typing: ``deeplabcut.filterpredictions?``
-### Plotting Results:
+````{admonition} Click the button to see API Docs
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.filterpredictions.rst
+```
+````
+
+### (K) Plot Trajectories , (L) Create Labeled Videos
- **NOTE :bulb::mega::** Before you create a video, you should set what threshold to use for plotting. This is set in the `config.yaml` file as `pcutoff` - if you have a well trained network, this should be high, i.e. set it to `0.8` or higher! IF YOU FILLED IN GAPS, you need to set this to `0` to "see" the filled in parts.
@@ -573,6 +936,20 @@ Create videos:
(more details [here](functionDetails.md#i-video-analysis-and-plotting-results))
+````{admonition} Click the button to see API Docs for plot_trajectories
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.plot_trajectories.rst
+```
+````
+
+````{admonition} Click the button to see API Docs for create_labeled_video
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.create_labeled_video.rst
+```
+````
+
### HELP:
In ipython/Jupyter notebook:
@@ -587,7 +964,7 @@ In python or pythonw:
help(deeplabcut.nameofthefunction)
```
-### Tips for "daily" use:
+## Tips for "daily" use:
@@ -620,10 +997,10 @@ Now, you can run any of the functions described in this documentation.
[](https://gitter.im/DeepLabCut/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
- If you want to share some results, or see others:
-[](https://twitter.com/DeepLabCut)
+[](https://x.com/DeepLabCut)
- If you have a code bug report, please create an issue and show the minimal code to reproduce the error: https://github.com/DeepLabCut/DeepLabCut/issues
-- if you are looking for resources to increase your understanding of the software and general guidelines, we have an open source, free course: http://DLCcourse.deeplabcut.org.
+- if you are looking for resources to increase your understanding of the software and general guidelines, we have an open source, free course: https://deeplabcut.github.io/DeepLabCut/docs/course.html.
**Please note:** what we cannot do is provided support or help designing your experiments and data analysis. The number of requests for this is too great to sustain in our inbox. We are happy to answer such questions in the forum as a community, in a scalable way. We hope and believe we have given enough tools and resources to get started and to accelerate your research program, and this is backed by the >700 citations using DLC, 2 clinical trials by others, and countless applications. Thus, we believe this code works, is accessible, and with limited programming knowledge can be used. Please read our [Missions & Values statement](mission-and-values) to learn more about what we DO hope to provide you.
diff --git a/docs/napariGUI.md b/docs/napariGUI.md
deleted file mode 100644
index 80897a671e..0000000000
--- a/docs/napariGUI.md
+++ /dev/null
@@ -1,177 +0,0 @@
-# napari labeling GUI
-
-We replaced wxPython with PySide6 + as of version 2.3. Here is how to use the napari-aspects of the new GUI. It is available in napari-hub as a stand alone GUI as well as integrated into our main GUI, [please see docs here](https://deeplabcut.github.io/DeepLabCut/docs/PROJECT_GUI.html).
-
-[](https://www.gnu.org/licenses/bsd3)
-[](https://pypi.org/project/napari-deeplabcut)
-[](https://python.org)
-[](https://github.com/DeepLabCut/napari-deeplabcut/actions)
-[](https://codecov.io/gh/DeepLabCut/napari-deeplabcut)
-[](https://napari-hub.org/plugins/napari-deeplabcut)
-
-A napari plugin for keypoint annotation with DeepLabCut.
-
-
-## Installation
-
-You can install the full DeepLabCut napari-based GUI via [pip] by running this in your conda env:
-
-` pip install 'deeplabcut[gui]'`
-
-*please note this is available since v2.3
-
-You can install the stand-alone `napari-deeplabcut` via [pip]:
-
-` pip install napari-deeplabcut `
-
-
-
-To install latest development version:
-
- ` pip install git+https://github.com/DeepLabCut/napari-deeplabcut.git `
-
-
-## Usage
-
-To use the full GUI, please run:
-
-`python -m deeplabcut`
-
-To use the stand-alone napari plugin, please launch napari:
-
-` napari `
-
-Then, activate the plugin in Plugins > napari-deeplabcut: Keypoint controls.
-
-All accepted files (config.yaml, images, h5 data files) can be loaded
-either by dropping them directly onto the canvas or via the File menu.
-
-The easiest way to get started is to drop a folder (typically a folder from within a DeepLabCut's `labeled-data` directory), and, if labeling from scratch, drop the corresponding `config.yaml` to automatically add a `Points layer` and populate the dropdown menus.
-
-**Tools & shortcuts are:**
-
-- `2` and `3`, to easily switch between labeling and selection mode
-- `4`, to enable pan & zoom (which is achieved using the mouse wheel or finger scrolling on the Trackpad)
-- `M`, to cycle through regular (sequential), quick, and cycle annotation mode (see the description [here](https://github.com/DeepLabCut/DeepLabCut-label/blob/ee71b0e15018228c98db3b88769e8a8f4e2c0454/dlclabel/layers.py#L9-L19))
-- `E`, to enable edge coloring (by default, if using this in refinement GUI mode, points with a confidence lower than 0.6 are marked
-in red)
-- `F`, to toggle between animal and body part color scheme.
-- `backspace` to delete a point.
-- Check the box "display text" to show the label names on the canvas.
-- To move to another folder, be sure to save (Ctrl+S), then delete the layers, and re-drag/drop the next folder.
-
-
-### Save Layers
-
-Annotations and segmentations are saved with `File > Save Selected Layer(s)...` (or its shortcut `Ctrl+S`).
-Only when saving segmentation masks does a save file dialog pop up to name the destination folder;
-keypoint annotations are otherwise automatically saved in the corresponding folder as `CollectedData_.h5`.
-- As a reminder, DLC will only use the H5 file; so be sure if you open already labeled images you save/overwrite the H5.
-- Note, before saving a layer, make sure the points layer is selected. If the user clicked on the image(s) layer first, does `Save As`, then closes the window, any labeling work during that session will be lost!
-
-
-### Video frame extraction and prediction refinement
-
-Since v0.0.4, videos can be viewed in the GUI.
-
-Since v0.0.5, trailing points can be visualized; e.g., helping in the identification
-of swaps or outlier, jittery predictions.
-
-Loading a video (and its corresponding output h5 file) will enable the video actions
-at the top of the dock widget: they offer the option to manually extract video
-frames from the GUI, or to define cropping coordinates.
-Note that keypoints can be displaced and saved, as when annotating individual frames.
-
-
-## Workflow
-
-Suggested workflows, depending on the image folder contents:
-
-1. **Labeling from scratch** – the image folder does not contain `CollectedData_.h5` file.
-
- Open *napari* as described in [Usage](#usage) and open an image folder together with the DeepLabCut project's `config.yaml`.
- The image folder creates an *image layer* with the images to label.
- Supported image formats are: `jpg`, `jpeg`, `png`.
- The `config.yaml` file creates a *Points layer*, which holds metadata (such as keypoints read from the config file) necessary for labeling.
- Select the *Points layer* in the layer list (lower left pane on the GUI) and click on the *+*-symbol in the layer controls menu (upper left pane) to start labeling.
- The current keypoint can be viewed/selected in the keypoints dropdown menu (right pane).
- The slider below the displayed image (or the left/right arrow keys) allows selecting the image to label.
-
- To save the labeling progress refer to [Save Layers](#save-layers).
- `Data successfully saved` should be shown in the status bar, and the image folder should now contain a `CollectedData_.h5` file.
- (Note: For convenience, a CSV file with the same name is also saved.)
-
-2. **Resuming labeling** – the image folder contains a `CollectedData_.h5` file.
-
- Open *napari* and open an image folder (which needs to contain a `CollectedData_.h5` file).
- In this case, it is not necessary to open the DLC project's `config.yaml` file, as all necessary metadata is read from the `h5` data file.
-
- Saving works as described in *1*.
-
-3. **Refining labels** – the image folder contains a `machinelabels-iter<#>.h5` file.
-
- The process is analog to *2*.
-
-4. **Drawing segmentation masks**
-
- Drop an image folder as in *1*, manually add a *shapes layer*. Then select the *rectangle* in the layer controls (top left pane),
- and start drawing rectangles over the images. Masks and rectangle vertices are saved as described in [Save Layers](#save-layers).
- Note that masks can be reloaded and edited at a later stage by dropping the `vertices.csv` file onto the canvas.
-
-
-### Labeling multiple image folders
-
-Labeling multiple image folders has to be done in sequence; i.e., only one image folder can be opened at a time.
-After labeling the images of a particular folder is done and the associated *Points layer* has been saved, *all* layers should be removed from the layers list (lower left pane on the GUI) by selecting them and clicking on the trashcan icon.
-Now, another image folder can be labeled, following the process described in *1*, *2*, or *3*, depending on the particular image folder.
-
-
-### Defining cropping coordinates
-
-Prior to defining cropping coordinates, two elements should be loaded in the GUI:
-a video and the DLC project's `config.yaml` file (into which the crop dimensions will be stored).
-Then it suffices to add a `Shapes layer`, draw a `rectangle` in it with the desired area,
-and hit the button `Store crop coordinates`; coordinates are automatically written to the configuration file.
-
-
-## Contributing
-
-Contributions are very welcome. Tests can be run with [tox], please ensure
-the coverage at least stays the same before you submit a pull request.
-
-To locally install the code, please git clone the repo and then run `pip install -e .`
-
-## License
-
-Distributed under the terms of the [BSD-3] license,
-"napari-deeplabcut" is free and open source software.
-
-## Issues
-
-If you encounter any problems, please [file an issue] along with a detailed description.
-
-[file an issue]: https://github.com/DeepLabCut/napari-deeplabcut/issues
-
-
-## Acknowledgements
-
-
-This [napari] plugin was generated with [Cookiecutter] using [@napari]'s [cookiecutter-napari-plugin] template. We thank the Chan Zuckerberg Initiative (CZI) for funding this work!
-
-
-
-
-[napari]: https://github.com/napari/napari
-[Cookiecutter]: https://github.com/audreyr/cookiecutter
-[@napari]: https://github.com/napari
-[cookiecutter-napari-plugin]: https://github.com/napari/cookiecutter-napari-plugin
-[BSD-3]: http://opensource.org/licenses/BSD-3-Clause
-[tox]: https://tox.readthedocs.io/en/latest/
-[pip]: https://pypi.org/project/pip/
-[PyPI]: https://pypi.org/
diff --git a/docs/pytorch/Benchmarking_shuffle_guide.md b/docs/pytorch/Benchmarking_shuffle_guide.md
new file mode 100644
index 0000000000..ba54ceca9f
--- /dev/null
+++ b/docs/pytorch/Benchmarking_shuffle_guide.md
@@ -0,0 +1,141 @@
+---
+deeplabcut:
+ last_content_updated: '2025-06-30'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
+# DeepLabCut Benchmarking - User Guide
+
+## Reasoning for benchmarking models in DLC (across DLC versions and architectures)
+
+DeepLabCut 3.0+ introduced using PyTorch 🔥 as a deep learning engine (and TensorFlow will be depreciated).
+It is of importance for replicability of data analysis to benchmark existing models created using DeepLabCut versions
+prior to 3.0 against new models created in DeepLabCut 3.0+ and later versions.
+
+When comparing different models, it's important to use the same train-test data
+split to ensure fair comparisons. If the models are trained on different datasets,
+their performance metrics can't be accurately compared. This is crucial when
+comparing the performance of models with different architectures or different
+sets of hyperparameters. For example, if we compare the RMSE of a model on an
+"easy" test image with the RMSE of another model on a "hard" test image, it
+doesn't determine whether a model is better than the other because the
+architecture performs better or because the training images were "better" to
+learn from. Thus, we not only need to compare the models based on metrics
+computed on the same test images, but also train them on an identical fixed
+training set in order to "decouple" the dataset from the model architecture.
+
+Creating a model using the same data split can be carried out using a GUI or
+using code, and this guide outlines the steps for both.
+
+## Important files & folders
+
+```
+dlc-project
+|
+|___dlc-models-pytorch
+| |__ iterationX
+| |__ shuffleX
+| |__ pytorch_config.yaml
+|
+|___training-datasets
+| |__ metadata.yaml
+|
+|___config.yaml
+```
+
+## Benchmarking a TensorFlow model against a PyTorch model
+
+### Creating a shuffle
+
+Creating a new shuffle with the same train/test split as an existing one:
+### In the DeepLabCut GUI
+1. Front page > Load project > Open project folder > choose *config.yaml*
+2. Select *'Create training dataset'* tab
+3. Tick *Use an existing data split* option
+
+ ![create_from_existing]()
+4. Click 'View existing shuffles':
+ - This is used to view the indices of shuffles created for a project to determine which index is available to assign to a new shuffle.
+ - The elements described in this window are:
+ - train_fraction: The fraction of the dataset used for training.
+ - index: The index of the shuffle.
+ - split: The data split for the shuffle. The integer value on its own does not
+hold any meaning, but this "split" value indicates which shuffles have the same split
+(as their results can then be compared)
+ - engine: Whether it is a PyTorch or TensorFlow shuffle
+
+ ![view_existing_sh]()
+5. Choose the index of the training shuffle to replicate. Let us assume we want
+to replicate the train-test split from OpenfieldOct30-trainset95shuffle3, in which
+`split: 3`. In this case, we insert in the *'From shuffle'* menu
+
+ ![choose_existing_index]()
+6. To create this new dataset, set the shuffle option to an un-used shuffle
+(here 4)
+
+ ![choose_new_index]()
+7. Click *'Create training dataset'* and move on to *'train network'*. Shuffle should be
+set to the new shuffle entered at the previous step (in this case, 4)
+
+ ![create_from_existing]()
+8. To view/edit the specifications of the model you created, you can go to `pytoch_config.yaml` file at:
+ ```
+ dlc-project
+ |
+ |___ dlc-models-pytorch
+ |__ iterationX
+ |__ shuffleX
+ |__ pytorch_config.yaml
+ ```
+
+### In Code
+
+With the `deeplabcut` module in Python, use the
+`create_training_dataset_from_existing_split()` method to create new shuffles from
+existing ones (e.g. TensorFlow shuffles).
+
+Similarly, here, we create a new shuffle '4' from the existing shuffle '3'.
+
+```python
+import deeplabcut
+from deeplabcut.core.engine import Engine
+
+config = "path/to/project/config.yaml"
+
+training_dataset = deeplabcut.create_training_dataset_from_existing_split(
+ config=config,
+ from_shuffle=3,
+ from_trainsetindex=0,
+ shuffles=[4],
+ net_type="resnet_50",
+)
+```
+
+We can then train our new PyTorch model with the same data split as the
+TensorFlow model.
+
+```python
+deeplabcut.train_network(config, shuffle=4, engine=Engine.PYTORCH, batch_size=8)
+```
+
+Once trained we can evaluate our model using
+
+```python
+deeplabcut.evaluate_network(config, Shuffles=[4], snapshotindex="all")
+```
+Now, we can compare performances with peace of mind!
+
+### Good practices: naming shuffles created from existing ones
+
+In a setting where one has multiple TensorFlow models and intends to benchmark
+their performances against new PyTorch models, it is good practice to follow
+a naming pattern for the shuffles we create.
+
+Say we have TensorFlow shuffles 0, 1, and 2. We can create new PyTorch shuffles
+from them by naming them 1000, 1001, and 1002. This allows us to quickly
+recognize that the shuffles belonging to the 100x range are PyTorch shuffles
+and that shuffle 1001, for example, has the same data split as TensorFlow
+shuffle 1. This way, the comparison can be more straightforward and guaranteed
+to be correct!
+
+This was contributed by the [2024 DLC AI Residents](https://www.deeplabcutairesidency.org/our-team)!
diff --git a/docs/pytorch/architectures.md b/docs/pytorch/architectures.md
new file mode 100644
index 0000000000..755a967a77
--- /dev/null
+++ b/docs/pytorch/architectures.md
@@ -0,0 +1,153 @@
+---
+deeplabcut:
+ last_content_updated: '2025-06-30'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
+(dlc3-architectures)=
+# DeepLabCut 3.0 - PyTorch Model Architectures
+
+## Introduction
+
+You can see a list of supported architectures/variants by using:
+
+```python
+from deeplabcut.pose_estimation_pytorch import available_models
+print(available_models())
+```
+
+You can see a list of supported object detection architectures/variants by using:
+
+```python
+from deeplabcut.pose_estimation_pytorch import available_detectors
+print(available_detectors())
+```
+
+## Neural Networks Architectures
+
+Several architectures are currently implemented in DeepLabCut PyTorch (more will come,
+and you can add more easily in our new model registry). Also check out the explanations of bottom-up/top-down below.
+
+**ResNets**
+- Adapted from [He, Kaiming, et al. "Deep residual learning for image recognition." Proceedings of the IEEE conference on Computer Vision and Pattern Recognition. 2016.](https://openaccess.thecvf.com/content_cvpr_2016/html/He_Deep_Residual_Learning_CVPR_2016_paper.html) and [Insafutdinov, Eldar et al. "DeeperCut: A Deeper, Stronger, and Faster Multi-Person Pose Estimation Model". European Conference on Computer Vision (ECCV) 2016.]
+- Current bottom-up variants are `resnet_50`, `resnet_101`
+- Current top-down variants are `top_down_resnet_101`, `top_down_resnet_50`
+
+**HRNet**
+- Adapted from [Wang, Jingdong, et al. "Deep high-resolution representation learning for visual recognition." IEEE transactions on pattern analysis and machine intelligence 43.10 (2020): 3349-3364.](https://arxiv.org/abs/1908.07919)
+- Current variants are `hrnet_w18`, `hrnet_w32`, `hrnet_w48`,
+- Current top-down variants are `top_down_hrnet_w18`, `top_down_hrnet_w32`, `top_down_hrnet_w48`
+- Slower but typically more powerful than ResNets
+
+**DEKR**
+- Adapted from [Geng, Zigang et al. "Bottom-Up Human Pose Estimation Via Disentangled Keypoint Regression." Proceedings of the IEEE conference on Computer Vision and Pattern Recognition. 2021.](https://openaccess.thecvf.com/content/CVPR2021/papers/Geng_Bottom-Up_Human_Pose_Estimation_via_Disentangled_Keypoint_Regression_CVPR_2021_paper.pdf)
+- This model is a bottom-up model using HRNet as a backbone. It learns to predict the center of each animal, and predicts the offset between each animal center and their keypoints
+- Current variants that are implemented (from smallest to largest): `dekr_w18`, `dekr_w32`, `dekr_w48`
+- Note, this is a powerful multi-animal model but very heavy (slow)
+
+**BUCTD**
+- Adapted from [Zhou\*, Stoffl\*, Mathis, Mathis. "Rethinking Pose Estimation in Crowds: Overcoming the Detection Information Bottleneck and Ambiguity." Proceedings of the IEEE/CVF International Conference on Computer Vision (ICCV). 2023](https://openaccess.thecvf.com/content/ICCV2023/papers/Zhou_Rethinking_Pose_Estimation_in_Crowds_Overcoming_the_Detection_Information_Bottleneck_ICCV_2023_paper.pdf)
+- [](https://paperswithcode.com/sota/pose-estimation-on-crowdpose?p=rethinking-pose-estimation-in-crowds)
+- This is a top-performing multi-animal method that combines the strengths of bottom-up and top-down approaches, and delivers exceptional performance on humans too (which are also animals)
+- It can be used with a diverse set of architectures. Current variants are: `ctd_coam_w32`, `ctd_coam_w48`/`ctd_coam_w48_human`, `ctd_prenet_hrnet_w32`, `ctd_prenet_hrnet_w48`, `ctd_prenet_rtmpose_s`, `ctd_prenet_rtmpose_m`, `ctd_prenet_rtmpose_x`/`ctd_prenet_rtmpose_x_human`
+
+**DLCRNet**
+- From [Lauer, Zhou, et al. "Multi-animal pose estimation, identification and tracking with DeepLabCut." Nature Methods 19.4 (2022): 496-504.](https://www.nature.com/articles/s41592-022-01443-0)
+- This model uses a multi-scale variant of a ResNet as a backbone, and part-affinity fields to assemble individuals
+- Variants: `dlcrnet_stride16_ms5`, `dlcrnet_stride32_ms5`
+
+**RTMPose**
+- From [Jiang, Tao et al. "RTMPose: Real-Time Multi-Person Pose Estimation based on MMPose"](https://arxiv.org/abs/2303.07399)
+- Top-down pose estimation model using a fast CSPNeXt backbone with a SimCC-style head
+- Variants: `rtmpose_s`, `rtmpose_m`, `rtmpose_x`
+
+**AnimalTokenPose**
+- Adapted from [Li, Yanjie, et al. "Tokenpose: Learning keypoint tokens for human pose estimation." Proceedings of the IEEE/CVF International conference on computer vision. 2021.](https://arxiv.org/abs/2104.03516) as in Ye et al. "SuperAnimal pretrained pose estimation models for behavioral analysis." Nature Communications. 2024](https://arxiv.org/abs/2203.07436)
+ - One variant is implemented as: `animal_tokenpose_base` for video inference only (we don't support directly training this within deeplabcut)
+
+
+## Information on Single Animal Models
+
+Single-animal models are composed of a backbone (encoder) and a head (decoder)
+predicting the position of keypoints. The default head contains a single deconvolutional
+layer. To create the single animal model composed of a backbone and head, you can call
+`deeplabcut.create_training_dataset` with `net_type` set to the backbone name (e.g.
+`resnet_50` or `hrnet_w32`).
+
+If you want to add a second deconvolutional layer (which will make your model slower,
+but it might improve performance), you can simply edit your `pytorch_config.yaml` file.
+
+Of course, any multi-animal model can also be used for single-animal projects!
+
+## Approaches to Multi-Animal pose estimation
+
+Single-animal pose estimation is quite straightforward: the model takes an image as
+input, and it outputs the predicted coordinate of each bodypart.
+
+Multi-animal pose estimation is more complex. Not only do you need to localize bodyparts
+in the image, but you also need to group bodyparts per individual. There are two main
+approaches to multi-animal pose estimation.
+
+### Bottom-up estimation
+
+The first approach, **bottom-up** pose estimation, starts by detecting bodyparts in the
+image before figuring out how they belong together (i.e., which keypoints belong to the
+same animal).
+
+
+
+### Backbones with Part-Affinity Fields
+
+As in DeepLabCut 2.X, the base multi-animal model is composed of a backbone (encoder)
+and a head predicting keypoints and part-affinity fields (PAFs). These PAFs are used to
+assemble keypoints for individuals.
+
+Passing a backbone as a net type (e.g., `resnet_50`, `hrnet_w32`) for a multi-animal
+project will create a model consisting of a backbone and a heatmap + PAF head.
+
+### Top-down estimation
+
+The second approach, **top-down** pose estimation, uses a two-step approach. A first
+model (an object detector) is used to localize every animal present in the image through
+its bounding box. Then, the pose for each animal is determined by predicting bodyparts
+in each bounding box. The pose estimation
+
+
+
+The top-down approach tends to be more accurate in less crowded scenes, as the pose
+model only needs to process the pixels related to a single animal. However, in more
+crowded scenes, the pose estimation task becomes ambiguous. Multiple overlapping
+individuals will have very similar bounding boxes, and the pose model has no way of
+knowing which animal it is supposed to predict keypoints for.
+
+The bottom-up approach does not have this ambiguïty, and also has the advantage of
+only needing to run a pose estimation model, instead of needing to run an object
+detector first. However, grouping keypoints is a difficult problem.
+
+
+Hence any single-animal model can be transformed into a top-down, multi-animal model. To
+do so, simply prefix `top_down` to your single-animal model name. Currently, the
+following detectors are available: `ssdlite`, `fasterrcnn_mobilenet_v3_large_fpn`,
+`fasterrcnn_resnet50_fpn_v2`.
+
+
+### Hybrid, Bottom-up (BU) plus a ``conditioned" Top-down (CTD)
+
+A new approach to pose estimation, named bottom-up conditioned top-down (or **BUCTD**), was
+introduced in [Zhou, Stoffl, Mathis, Mathis. "Rethinking Pose Estimation in Crowds:
+Overcoming the Detection Information Bottleneck and Ambiguity." Proceedings of the
+IEEE/CVF International Conference on Computer Vision (ICCV). 2023](
+https://openaccess.thecvf.com/content/ICCV2023/papers/Zhou_Rethinking_Pose_Estimation_in_Crowds_Overcoming_the_Detection_Information_Bottleneck_ICCV_2023_paper.pdf)
+. It's a hybrid two-stage approach leveraging the strengths of the bottom-up and
+top-down approaches to overcome the ambiguïty introduced through bounding boxes. Instead
+of using an object detection model to localize individuals, it uses a bottom-up pose
+estimation model. The predictions made by the bottom-up model are given as proposals (or
+_conditions_) to the pose estimation model. This is illustrated in the figure below. In modern language, one could state that CTD models are "pose-promptable".
+
+
+
+Zhou, Mu, et al. *"Rethinking pose estimation in crowds: overcoming the
+detection information bottleneck and ambiguity."* Proceedings of the IEEE/CVF
+International Conference on Computer Vision. 2023.
diff --git a/docs/pytorch/assets/bboxes_from_kpts.png b/docs/pytorch/assets/bboxes_from_kpts.png
new file mode 100644
index 0000000000..f5be1eb3a0
Binary files /dev/null and b/docs/pytorch/assets/bboxes_from_kpts.png differ
diff --git a/docs/pytorch/assets/bottom-up-approach.png b/docs/pytorch/assets/bottom-up-approach.png
new file mode 100644
index 0000000000..025c292c8d
Binary files /dev/null and b/docs/pytorch/assets/bottom-up-approach.png differ
diff --git a/docs/pytorch/assets/img1.png b/docs/pytorch/assets/img1.png
new file mode 100644
index 0000000000..dde3d96115
Binary files /dev/null and b/docs/pytorch/assets/img1.png differ
diff --git a/docs/pytorch/assets/img2.png b/docs/pytorch/assets/img2.png
new file mode 100644
index 0000000000..6e86649dd8
Binary files /dev/null and b/docs/pytorch/assets/img2.png differ
diff --git a/docs/pytorch/assets/img3.png b/docs/pytorch/assets/img3.png
new file mode 100644
index 0000000000..39c516bfb7
Binary files /dev/null and b/docs/pytorch/assets/img3.png differ
diff --git a/docs/pytorch/assets/img4.png b/docs/pytorch/assets/img4.png
new file mode 100644
index 0000000000..a231130b5f
Binary files /dev/null and b/docs/pytorch/assets/img4.png differ
diff --git a/docs/pytorch/assets/img5.png b/docs/pytorch/assets/img5.png
new file mode 100644
index 0000000000..4e7a44ca0d
Binary files /dev/null and b/docs/pytorch/assets/img5.png differ
diff --git a/docs/pytorch/assets/top-down-approach.png b/docs/pytorch/assets/top-down-approach.png
new file mode 100644
index 0000000000..c28265bcb1
Binary files /dev/null and b/docs/pytorch/assets/top-down-approach.png differ
diff --git a/docs/pytorch/pytorch_config.md b/docs/pytorch/pytorch_config.md
new file mode 100644
index 0000000000..cbd435c46a
--- /dev/null
+++ b/docs/pytorch/pytorch_config.md
@@ -0,0 +1,641 @@
+---
+deeplabcut:
+ last_content_updated: '2025-10-02'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
+(dlc3-pytorch-config)=
+# The PyTorch Configuration file
+
+The `pytorch_config.yaml` file specifies the configuration for your PyTorch pose models,
+from the model architecture to which optimizer will be used for training, how training
+runs will be logged, the data augmentation that will be applied and which metric should
+be used to save the "best" model snapshot.
+
+You can create default configurations for a shuffle using
+`deeplabcut.create_training_set` or `deeplabcut.create_training_model_comparison`. This
+will create a `pytorch_config.yaml` file for your selected net type. The basic structure
+of the file is as follows:
+
+```yaml
+data: # which data augmentations will be used
+ ...
+device: auto # the default device to use for training and evaluation
+inference: # configures inference-related parameters (multithreading, different torch options)
+metadata: # metadata regarding the project (bodyparts, individuals, paths, ...) - filled automatically
+ ...
+method: bu # indicates how pose predictions are made (bottom-up (`bu`) or top-down (`td`))
+model: # configures the model architecture (which backbone, heads, ...)
+ ...
+net_type: resnet_50 # the type of neural net configured in the file
+runner: # configuring the runner used for training
+ ...
+train_settings: # generic training settings, such as batch size and maximum number of epochs
+ ...
+logger: # optional: the configuration for a logger if you want one
+resume_training_from: # optional: restart the training at the specific checkpoint
+```
+
+## Sections
+
+### Singleton Parameters
+
+There are a few singleton parameters defined in the PyTorch configuration file:
+
+- `device`: The device to use for training/inference. The default is `auto`, which sets
+the device to `cuda` if an NVIDIA GPU is available, and `cpu` otherwise. For users
+running models on macOS with an M1/M2/M3 chip, this is set to `mps` for certain models
+(not all operations are currently supported on Apple GPUs - so some models like HRNets
+need to be trained on CPU, while others like ResNets can take advantage of the GPU).
+- `method`: Either `bu` for bottom-up models, or `td` for top-down models.
+- `net_type`: The type of pose model configured by the file (e.g. `resnet_50`).
+
+### Data
+
+The data section configures:
+
+- `bbox_margin`: The margin (in pixels) to add around ground truth pose when generating
+bounding boxes. For more information, see [generating bounding boxes from pose](
+#bbox-from-pose).
+- `colormode`: in which format images are given to the model (e.g., `RGB`, `BGR`)
+- `inference`: which transformations should be applied to images when running evaluation
+or inference
+- `train`: which transformations should be applied to images when training
+
+The default configuration for a pose model is:
+
+```yaml
+data:
+ bbox_margin: 20
+ colormode: RGB # should never be changed
+ inference: # the augmentations to apply to images during inference
+ normalize_images: true # this should always be set to true
+ train:
+ affine:
+ p: 0.5
+ rotation: 30
+ scaling: [0.5, 1.25]
+ translation: 0
+ covering: true
+ crop_sampling:
+ width: 448 # if your images are very small or very large, you may need to edit!
+ height: 448 # see below for more information about crop_sampling!
+ max_shift: 0.1
+ method: hybrid
+ gaussian_noise: 12.75
+ motion_blur: true
+ normalize_images: true # this should always be set to true
+```
+
+The following transformations are available for the `train` and `inference` keys.
+
+**Affine**: Applies an affine (rotation, translation, scaling) transformation to the
+images.
+
+```yaml
+affine:
+ p: 0.9 # float: the probability that an affine transform is applied
+ rotation: 30 # int: the maximum angle of rotation applied to the image (in degrees)
+ scaling: [ 0.5, 1.25 ] # [float, float]: the (min, max) scale to use to resize images
+ translation: 40 # int: the maximum translation to apply to images (in pixels)
+```
+
+**Auto-Padding**: Pads the image to some desired shape (e.g., a minimum height/width or
+such that the height/width are divisible by a given number). Some backbones (such as
+HRNets) require the height and width of images to be multiples of 32. Setting up
+auto-padding with `pad_height_divisor: 32` and `pad_width_divisor: 32` ensures that is
+the case. Note that **not all keys need to be set**! The values shown are the default
+values. Only one of 'min_height' and 'pad_height_divisor' parameters must be set, and
+only one of 'min_width' and 'pad_width_divisor' parameters must be set.
+
+```yaml
+auto_padding:
+ min_height: null # int: if not None, the minimum height of the image
+ min_width: null # int: if not None, the minimum width of the image
+ pad_height_divisor: null # int: if not None, ensures image height is dividable by value of this argument.
+ pad_width_divisor: null # int: if not None, ensures image width is dividable by value of this argument.
+ position: random # str: position of the image, one of 'A.PadIfNeeded.Position'
+ border_mode: reflect_101 # str: 'constant' or 'reflect_101' (see cv2.BORDER modes)
+ border_value: null # str: padding value if border_mode is 'constant'
+ border_mask_value: null # str: padding value for mask if border_mode is 'constant'
+```
+
+**Covering**: Based on Albumentations's [CoarseDropout](
+https://albumentations.ai/docs/api_reference/augmentations/dropout/coarse_dropout/#albumentations.augmentations.dropout.coarse_dropout)
+augmentation, this "cuts" holes out of the image. As defined in
+[Improved Regularization of Convolutional Neural Networks with Cutout](
+https://arxiv.org/abs/1708.04552).
+
+```yaml
+covering: true # bool: if true, applies a coarse dropout with probability 50%
+```
+
+**Gaussian Noise**: Applies gaussian noise to the input image. Can either be a float
+(the standard deviation of the noise) or simply a boolean (the standard deviation of
+the noise will be set as 12.75).
+
+```yaml
+gaussian_noise: 12.75 # bool, float: add gaussian noise
+```
+
+**Horizontal Flips**: This flips the image horizontally around the y-axis. As the
+resulting image is mirrored, it does not preserve labels (the left hand would become the
+right hand, and vice versa). This augmentation should not be used for pose models if you
+have symmetric keypoints! However, it is safe to use it to train detectors. If you want
+to use horizontal flips with symmetric keypoints, you need to specify them through the
+`symmetries` parameter!
+
+```yaml
+# augmentation for object detectors or when no symmetric (left/right) keypoints exist:
+hflip: true
+
+# augmentation if your bodyparts are [snout, eye_L, eye_R, ear_L, ear_R]
+hflip:
+ p: 0.5 # apply a horizontal flip with 50% probability
+ symmetries: [[1, 2], [3, 4]] # the indices of symmetric keypoints
+```
+
+**Histogram Equalization**: Applies histogram equalization with probability 50%.
+
+```yaml
+hist_eq: true # bool: whether to apply histogram equalization
+```
+
+**Motion Blur**: Applies motion blur to the image with probability 50%.
+
+```yaml
+motion_blur: true # bool: whether to apply motion blur
+```
+
+**Normalization**: This should always be set to `true`.
+
+```yaml
+normalize_images: true # normalizes images
+```
+
+### Dealing with Variable Image Sizes
+
+```{NOTE}
+When training with batch size 1 (or if all images in your dataset have the same size),
+you don't need to worry about any of this! However, you can still use `crop_sampling`
+which may help your model generalize.
+```
+
+When training with a batch size greater than 1, all images in a batch **must** have the
+same size. PyTorch **collates** all images into one tensor of shape `[b, c, h, w]`,
+where `b` is the batch size, `c` the number of channels in the image, `h` and `w` the
+height and width of images in the batches. There are a few different ways to ensure that
+all images in a batch have the same size:
+
+1. **Crop sampling**. This is the default behavior for the PyTorch engine in DeepLabCut.
+A part of each image (of a fixed size) is cropped and given to the model to train. See
+below for more information.
+2. **A custom collate function**. Collate functions define a way that images of different
+sizes can be combined into one tensor. This involves resizing and padding images to the
+same size and aspect ratio. Available collate functions are defined in
+`deeplabcut/pose_estimation_pytorch/data/collate.py`.
+3. **Resizing all images**. All images can simply be resized to the same size. This
+usually doesn't lead to the best performance.
+
+**Resizing - Crop Sampling**: An alternative way to ensure all images have the same size
+is through cropping. The `crop_sampling` crops images down to a maximum width and
+height, with options to sample the center of the crop according to the positions of the
+keypoints. The methods to sample the center of the crop are as follows:
+
+- `uniform`: randomly over the image
+- `keypoints`: randomly over the annotated keypoints
+- `density`: weighing preferentially dense regions of keypoints
+- `hybrid`: alternating randomly between `uniform` and `density`
+
+```yaml
+crop_sampling:
+ height: 400 # int: the height of the crop
+ width: 400 # int: the height of the crop
+ max_shift: 0.4 # float: maximum allowed shift of the cropping center position as a fraction of the crop size.
+ method: hybrid # str: the center sampling method (one of 'uniform', 'keypoints', 'density', 'hybrid')
+```
+
+**Collate**: Defines how images are collated into batches. The default way collate
+function to use is `ResizeFromDataSizeCollate` (other collate functions are defined in
+`deeplabcut/pose_estimation_pytorch/data/collate.py`). For each batch to collate, this
+implementation:
+1. Selects the target width & height all images will be resized to by getting the size
+of the first image in the batch, and multiplying it by a scale sampled uniformly at
+random from `(min_scale, max_scale)`.
+2. Resizes all images in the batch (while preserving their aspect ratio) such that they
+are the smallest size such that the target size fits entirely in the image.
+3. Crops each resulting image into the target size with a random crop.
+
+```yaml
+collate: # rescales the images when putting them in a batch
+ type: ResizeFromDataSizeCollate # You can also use `ResizeFromListCollate`
+ max_shift: 10 # the maximum shift, in pixels, to add to the random crop (this means
+ # there can be a slight border around the image)
+ max_size: 1024 # the maximum size of the long edge of the image when resized. If the
+ # longest side will be greater than this value, resizes such that the longest side
+ # is this size, and the shortest side is smaller than the desired size. This is
+ # useful to keep some information from images with extreme aspect ratios.
+ min_scale: 0.4 # the minimum scale to resize the image with
+ max_scale: 1.0 # the maximum scale to resize the image with
+ min_short_side: 128 # the minimum size of the target short side
+ max_short_side: 1152 # the maximum size of the target short side
+ multiple_of: 32 # pads the target height, width such that they are multiples of 32
+ to_square: false # instead of using the aspect ratio of the first image, only the
+ # short side of the first image will be used to sample a "side", and the images will
+ # be cropped in squares
+```
+
+**Resizing**: Resizes the images while preserving the aspect ratio (first resizes to the
+maximum possible size, then adds padding for the missing pixels).
+
+```yaml
+resize:
+ height: 640 # int: the height to which all images will be resized
+ width: 480 # int: the width to which all images will be resized
+ keep_ratio: true # bool: whether the aspect ratio should be preserved when resizing
+```
+
+### Model
+
+The model configuration is further split into a `backbone`, optionally a `neck` and a
+number of heads.
+
+Changing the `model` configuration should only be done by expert users, and in rare
+occasions. When updating a model configuration (e.g. adding more deconvolution layers
+to a `HeatmapHead`) must be done in a way where the model configuration still makes
+sense for the project (e.g. the number of heatmaps output needs to match the number of
+bodyparts in the project).
+
+An example model configuration for a single-animal HRNet would look something like:
+
+```yaml
+model:
+ backbone: # the BaseBackbone used by the pose model
+ type: HRNet
+ model_name: hrnet_w18 # creates an HRNet W18 backbone
+ backbone_output_channels: 18
+ heads: # configures how the different heads will make predictions
+ bodypart: # configures how pose will be predicted for bodyparts
+ type: HeatmapHead
+ predictor: # the BasePredictor used to make predictions from the head's outputs
+ type: HeatmapPredictor
+ ...
+ target_generator: # the BaseTargetGenerator used to create targets for the head
+ type: HeatmapPlateauGenerator
+ ...
+ criterion: # the loss criterion used for the head
+ ...
+ ... # head-specific options, such as `heatmap_config` or `locref_config` for a "HeatmapHead"
+```
+
+The `backbone`, `neck` and `head` configurations are loaded using the
+`deeplabcut.pose_estimation_pytorch.models.backbones.base.BACKBONES`,
+`deeplabcut.pose_estimation_pytorch.models.necks.base.NECKS` and
+`deeplabcut.pose_estimation_pytorch.models.heads.base.HEADS` registries. You specify
+which type to load with the `type` parameter. Any argument for the head can then be used
+in the configuration.
+
+So to use an `HRNet` backbone for your model (as defined in
+`deeplabcut.pose_estimation_pytorch.models.backbones.hrnet.HRNet`), you could set:
+
+```yaml
+model:
+ backbone:
+ type: HRNet
+ model_name: hrnet_w32 # creates an HRNet W32
+ pretrained: true # the backbone weights for training will be loaded from TIMM (pre-trained on ImageNet)
+ interpolate_branches: false # don't interpolate & concatenate channels from all branches
+ increased_channel_count: true # use the incre_modules defined in the TIMM HRNet
+ backbone_output_channels: 128 # number of channels output by the backbone
+```
+
+### Runner
+
+The runner contains elements relating to the training runner to use (including the optimizer and
+learning rate schedulers). Unless you're experienced with machine learning and training
+models **it is not recommended to change the optimizer or scheduler**.
+
+```yaml
+runner:
+ type: PoseTrainingRunner # should not need to modify this
+ key_metric: "test.mAP" # the metric to use to select the "best snapshot"
+ key_metric_asc: true # whether "larger=better" for the key_metric
+ eval_interval: 1 # the interval between each passes through the evaluation dataset
+ optimizer: # the optimizer to use to train the model
+ ...
+ scheduler: # optional: a learning rate scheduler
+ ...
+ load_scheduler_state_dict: true/false # whether to load scheduler state when resuming training from a snapshot,
+ snapshots: # parameters for the TorchSnapshotManager
+ max_snapshots: 5 # the maximum number of snapshots to save (the "best" model does not count as one of them)
+ save_epochs: 25 # the interval between each snapshot save
+ save_optimizer_state: false # whether the optimizer state should be saved with the model snapshots (very little reason to set to true)
+ gpus: # GPUs to use to train the network
+ - 0
+ - 1
+```
+
+**Key metric**: Every time the model is evaluated on the test set, metrics are computed
+to see how the model is performing. The key metric is used to determine whether the
+current model is the "best" so far. If it is, the snapshot is saved as `...-best.pt`.
+For pose models, metrics to choose from would be `test.mAP` (with `key_metric_asc: true`
+) or `test.rmse` (with `key_metric_asc: false`).
+
+**Evaluation interval**: Evaluation slows down training (it takes time to go through all
+the evaluation images, make predictions and log results!). So instead of evaluating
+after every epoch, you could decide to evaluate every 5 epochs (by setting
+`eval_interval: 5`). While this means you get coarser information about how your model
+is training, it can speed up training on large datasets.
+
+**Optimizer**: Any optimizer inheriting `torch.optim.Optimizer`. More information about
+optimizers can be found in [PyTorch's documentation](
+https://pytorch.org/docs/stable/optim.html). Examples:
+
+```yaml
+ # SGD with initial learning rate 1e-3 and momentum 0.9
+ # see https://pytorch.org/docs/stable/generated/torch.optim.SGD.html
+ optimizer:
+ type: SGD
+ params:
+ lr: 1e-3
+ momentum: 0.9
+
+ # AdamW optimizer with initial learning rate 1e-4
+ # see https://pytorch.org/docs/stable/generated/torch.optim.AdamW.html
+ optimizer:
+ type: AdamW
+ params:
+ lr: 1e-4
+```
+
+**Scheduler**: You can use [any scheduler](
+https://pytorch.org/docs/stable/optim.html#how-to-adjust-learning-rate) defined in
+`torch.optim.lr_scheduler`, where the arguments given are arguments of the scheduler.
+The default scheduler is an LRListScheduler, which changes the learning rates at each
+milestone to the corresponding values in `lr_list`. Examples:
+
+```yaml
+ # reduce to 1e-5 at epoch 160 and 1e-6 at epoch 190
+ scheduler:
+ type: LRListScheduler
+ params:
+ lr_list: [ [ 1e-5 ], [ 1e-6 ] ]
+ milestones: [ 160, 190 ]
+
+ # Decays the learning rate of each parameter group by gamma every step_size epochs
+ # see https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.StepLR.html
+ scheduler:
+ type: StepLR
+ params:
+ step_size: 100
+ gamma: 0.1
+```
+
+You can also use schedulers that use other schedulers as parameters, such as a
+[`ChainedScheduler`](
+https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ChainedScheduler.html)
+or a [`SequentialLR`](
+https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.SequentialLR.html).
+
+The `SequentialLR` can be particularly useful, such as to use a first scheduler for some
+warmup epochs, and a second scheduler later. An example usage would be:
+
+```yaml
+ # Multiply the learning rate by `factor` for the first `total_iters` epochs
+ # After 5 epochs, start decaying the learning rate by `gamma` every `step_size` epochs
+ # If the initial learning rate is set to 1, the learning rates will be:
+ # epoch 0: 0.01 - using ConstantLR
+ # epoch 1: 0.01 - using ConstantLR
+ # epoch 2: 1.0 - using ConstantLR
+ # epoch 3: 1.0 - using ConstantLR
+ # epoch 4: 1.0 - using ConstantLR
+ # epoch 5: 1.0 - using StepLR
+ # epoch 6: 1.0 - using StepLR
+ # epoch 7: 0.1 - using StepLR
+ # epoch 8: 0.1 - using StepLR
+ scheduler:
+ type: SequentialLR
+ params:
+ schedulers:
+ - type: ConstantLR
+ params:
+ factor: 0.01
+ total_iters: 2
+ - type: StepLR
+ params:
+ step_size: 2
+ gamma: 0.1
+ milestones:
+ - 5
+```
+
+### Train Settings
+
+The `train_settings` key contains parameters that are specific to training. For more
+information about the `dataloader_workers` and `dataloader_pin_memory` settings, see
+[Single- and Multi-process Data Loading](
+https://pytorch.org/docs/stable/data.html#single-and-multi-process-data-loading)
+and [memory pinning](https://pytorch.org/docs/stable/data.html#memory-pinning). Setting
+`dataloader_workers: 0` uses single-process data loading, while setting it to 1 or more
+will use multi-process data loading. You should always keep
+`dataloader_pin_memory: true` when training on an NVIDIA GPU.
+
+```yaml
+train_settings:
+ batch_size: 1 # the batch size used for training
+ dataloader_workers: 0 # the number of workers for the PyTorch Dataloader
+ dataloader_pin_memory: true # pin DataLoader memory
+ display_iters: 500 # the number of iterations (steps) between each log print
+ epochs: 200 # the maximum number of epochs for which to train the model
+ seed: 42 # the random seed to set for reproducibility
+```
+
+### Logger
+
+Training runs are logged to the model folder (where the snapshots are stored) by
+default.
+
+Additionally, you can log results to [Weights and Biases](https://wandb.ai/site), by adding a
+`WandbLogger`. Just make sure you're logged in to your `wandb` account before starting
+your training run (with `wandb login` from your shell). For more information, see their
+[tutorials](https://docs.wandb.ai/tutorials) and their documentation for [`wandb.init`](https://docs.wandb.ai/ref/python/init).
+
+Logging to `wandb` is a good way to keep track of what you've run, including performance
+and metrics.
+
+```yaml
+logger:
+ type: WandbLogger
+ project_name: my-dlc3-project # the name of the project where the run should be logged
+ run_name: dekr-w32-shuffle0 # the name of the run to log
+ ... # any other argument you can pass to `wandb.init`, such as `tags: ["dekr", "split=0"]`
+```
+
+If you set up a `WandbLogger`, the corresponding run info (`entity`, `project`, `run_id`)
+will be saved in a `wandb_info.yaml` file in the model train directory, so that the WandB run
+can be easily be recovered at a later stage.
+
+You can also log images as they are seen by the model to `wandb`
+with the `image_log_interval`. This logs a random train and test image, as well as the
+targets and heatmaps for that image.
+
+### Restarting Training at a Specific Checkpoint
+
+If you wish to restart the training at a specific checkpoint, you can specify the
+full path of the checkpoint to the `resume_training_from` variable, as shown below. In this
+example, `snapshot-010.pt` will be loaded before training starts, and the model will
+continue to train from the 10th epoch on.
+
+```yaml
+# model configuration
+...
+# weights from which to resume training
+resume_training_from: /Users/john/dlc-project-2021-06-22/dlc-models-pytorch/iteration-0/dlcJun22-trainset95shuffle0/train/snapshot-010.pt
+```
+
+When continuing to train a model, you may want to modify the learning rate scheduling
+that was being used (by editing the configuration under the `scheduler` key). When doing
+so, you *must set `load_scheduler_state_dict: false`* in your `runner` config!
+Otherwise, the parameters for the scheduler your started training with will be loaded
+from the state dictionary, and your edits might not be kept!
+
+### Inference
+The `inference:` block in `pytorch_config.yaml` allows configuring **inference-specific
+behavior** for your model. It is independent of training settings and can include multiple
+sub-configs, currently supporting **multithreading**, **compile**, **autocast**, and **conditions**.
+
+**Example**
+```yaml
+inference:
+ multithreading:
+ enabled: true
+ queue_length: 4
+ timeout: 30.0
+ compile:
+ enabled: false
+ backend: "inductor"
+ autocast:
+ enabled: false
+ conditions:
+ config_path: /path/to/model-dir/pytorch_config.yaml
+ snapshot_path: /path/to/model-dir/snapshot-best-150.pth
+```
+
+**Sub-configs**
+- `multithreading`
+ Controls producer-consumer threading during inference for preprocessing and batching.
+ - `enabled` (`bool`): Enable/disable multithreading.
+ - `queue_length` (`int`): Maximum number of batches to queue between preprocessing and model prediction.
+ - `timeout` (`float`): Timeout in seconds for the preprocessing queue.
+- `compile`
+ Controls optional `torch.compile` usage during inference.
+ **Note:** Using `torch.compile` may speed up inference but introduces some initialization overhead.
+ It is also known to fail in certain setups, environments, or architectures (e.g., `ctd_coam_*` models).
+ Use at your own risk.
+ - `enabled` (`bool`): Enable/disable compilation. Default: `false`.
+ - `backend` (`str`): Backend to use when compiling (`"inductor"`, `"aot_eager"`, etc.).
+- `autocast`
+ Controls optional mixed precision during inference.
+ - `enabled` (`bool`): Enable/disable `torch.autocast`. Default: `false`.
+ Note: Enabling autocast may reduce inference accuracy. It is disabled by default.
+- `conditions`
+ Only used for **Conditional Top-Down (CTD)** models to specify which conditions should be used during inference.
+
+## Training Top-Down Models
+
+Top-down models are split into two main elements: a detector (localizing individuals in
+the images) and a pose model predicting each individual's pose (once localization is
+done, obtaining pose is just like getting pose in a single-animal model!).
+
+The "pose" part of the model configuration is exactly the same as for single-animal or
+bottom-up models (configured through the `data`, `model`, `runner` and `train_settings`
+). The detector is configured through a detector key, at the top-level of the
+configuration.
+
+### Detector Configuration
+
+When training top-down models, you also need to configure how the detector will be
+trained. All information relating to the detector is placed under the `detector` key.
+
+```yaml
+detector:
+ data: # which data augmentations will be used, same options as for the pose model
+ colormode: RGB
+ inference: # default inference configuration for detectors
+ normalize_images: true
+ train: # default train configuration for detectors
+ affine:
+ p: 0.9
+ rotation: 30
+ scaling: [ 0.5, 1.25 ]
+ translation: 40
+ hflip: true
+ normalize_images: true
+ model: # the detector to train
+ type: FasterRCNN
+ variant: fasterrcnn_mobilenet_v3_large_fpn
+ pretrained: true
+ runner: # detector train runner configuration (same keys as for the pose model)
+ type: DetectorTrainingRunner
+ ...
+ train_settings: # detector train settings (same keys as for the pose model)
+ ...
+ resume_training_from: # optional: restart the training at the specific checkpoint
+```
+
+Currently, the only detectors available are `FasterRCNN` and `SSDLite`. However, multiple variants of
+`FasterRCNN` are available (you can view the different variants on
+[torchvision's object detection page](https://pytorch.org/vision/stable/models.html#object-detection)). It's recommended to use the fastest
+detector that brings enough performance. The recommended variants are the following
+(from fastest to most powerful, taken from torchvision's documentation):
+
+| name | Box MAP (larger = more powerful) | Params (larger = more powerful) | GFLOPS (larger = slower) |
+|-----------------------------------|---------------------------------:|--------------------------------:|-------------------------:|
+| SSDLite | 21.3 | 3.4M | 0.58 |
+| fasterrcnn_mobilenet_v3_large_fpn | 32.8 | 19.4M | 4.49 |
+| fasterrcnn_resnet50_fpn | 37 | 41.8M | 134.38 |
+| fasterrcnn_resnet50_fpn_v2 | 46.7 | 43.7M | 280.37 |
+
+
+### Restarting Training of an Object Detector at a Specific Checkpoint
+
+If you wish to restart the training of a detector at a specific checkpoint, you can
+specify the full path of the checkpoint to the detector's `resume_training_from` variable, as
+shown below. In this example, `snapshot-detector-020.pt` will be loaded before training
+starts, and the model will continue to train from the 20th epoch on.
+
+```yaml
+detector:
+ # detector configuration
+ ...
+ # weights from which to resume training
+ resume_training_from: /Users/john/dlc-project-2021-06-22/dlc-models-pytorch/iteration-0/dlcJun22-trainset95shuffle0/train/snapshot-detector-020.pt
+```
+
+When continuing to train a detector, you may want to modify the learning rate scheduling
+that was being used (by editing the configuration under the `scheduler` key). When doing
+so, you *must set `load_scheduler_state_dict: false`* in your `detector`: `runner`
+config! Otherwise, the parameters for the scheduler your started training with will be
+loaded from the state dictionary, and your edits might not be kept!
+
+(bbox-from-pose)=
+### Generating Bounding Boxes from Pose
+
+To train object detection models (for top-down pose estimation), ground truth bounding
+boxes are needed. As they are not annotated in DeepLabCut, they are generated from the
+ground truth pose: simply take the minimum and maximum for the x and y axes, add a small
+margin and you have your bounding box! The default setting adds a margin of 20 pixels
+around the pose. This works well in most cases, but in some cases you should update this
+value (e.g. when you have very small or large images).
+
+You can edit that value in the `pytorch_config.yaml` for your model through the
+`data: bbox_margin` parameter for the detector:
+
+```yaml
+detector:
+ data:
+ bbox_margin: 20
+ ...
+```
+
+
diff --git a/docs/pytorch/user_guide.md b/docs/pytorch/user_guide.md
new file mode 100644
index 0000000000..d4e5e6f2e9
--- /dev/null
+++ b/docs/pytorch/user_guide.md
@@ -0,0 +1,100 @@
+---
+deeplabcut:
+ last_content_updated: '2025-07-01'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
+(dlc3-user-guide)=
+# DeepLabCut 3.0 - PyTorch User Guide
+
+## Using DeepLabCut 3.0
+
+**DeepLabCut 3.0 keeps the same high-level API that you know, but has a full new PyTorch backend.
+Moreover, it is a rewrite that is more developer friendly, more powerful, and built for modern deep
+learning-based computer vision applications.**
+
+**NOTE**🔥: We suggest that if you're just starting with DeepLabCut you start with the
+PyTorch backend. You will easily know which "engine" you are using by looking at the
+main `config.yaml` file, or top right corner in the GUI. If you have DeepLabCut projects
+in TensorFlow, we've got you covered too: you can seamlessly switch to train your
+already labeled data by simply switching the engine (and thereby also compare
+performance). In short, expect a boost 🔥.
+
+In short, PyTorch models can be trained in any DeepLabCut project. If you have a project
+already made, simply add a new key to your project `config.yaml` file specifying
+`engine: pytorch`. Then any new training dataset that will be created will be a PyTorch
+model (see [Creating Shuffles and Model Configuration](
+#Creating-Shuffles-and-Model-Configuration)) to learn more about training PyTorch
+models. To train Tensorflow models again, you can set `engine: tensorflow`.
+
+### Installation
+
+To see the DeepLabCut 3.0 installation guide, check the [installation docs](how-to-install).
+
+### Using the GUI
+
+You can use the GUI to train DeepLabCut projects. You can switch between the PyTorch
+and TensorFlow engine through the drop-down menu in the top right corner.
+
+### Quick guide (standard API)
+
+The standard use of DLC does not change (via the high-level API), as you can see in the standard guide: for [single](https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide) and [multiple individuals](https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide).
+
+Also check out several COLAB notebooks on how you can use the code.
+
+For the
+
+## Major changes
+
+### From iterations to epochs
+
+Pytorch models in DeepLabCut 3.0 are trained for a set number of `epochs`, instead of a
+maximum number of `iterations`. An epoch is a single pass through the training dataset,
+which means your model has seen each training image exactly once.
+
+- So if you have 64 training images for your network, an epoch is 64 iterations with batch
+size 1 (or 32 iterations with batch size 2, 16 with batch size 4, etc.).
+
+## API
+
+### Creating Shuffles and Model Configuration
+
+You can configure models using the `pytorch_config.yaml` file, as described
+[here](dlc3-pytorch-config). You can use the same methods to create new shuffles in
+DeepLabCut 3.0 as you did for Tensorflow models (`deeplabcut.create_training_dataset`
+and `deeplabcut.create_training_model_comparison`).
+
+More information about the different PyTorch model architectures available in DeepLabCut
+is available [here](architectures). You can see a list of supported
+architectures/variants by using:
+
+```python
+from deeplabcut.pose_estimation_pytorch import available_models
+print(available_models())
+```
+
+
+
+### Development State and Road Map 🚧
+
+The table below describes the DeepLabCut API methods that have been implemented for the
+PyTorch engine, as well as indications which options are not yet implemented, and which
+parameters are not valid for the DLC 3.0 PyTorch API.
+
+
+| API Method | Implemented | Parameters not yet implemented | Parameters invalid for pytorch |
+|--------------------------------|:-----------:|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------|
+| `train_network` | 🟢 | | `maxiters`, `saveiters`, `allow_growth`, `autotune` |
+| `return_train_network_path` | 🟢 | | |
+| `evaluate_network` | 🟢 | | |
+| `return_evaluate_network_data` | 🔴 | | `TFGPUinference`, `allow_growth` |
+| `analyze_videos` | 🟠 | `greedy`, `calibrate`, `window_size` | |
+| `create_tracking_dataset` | 🟢 | | |
+| `analyze_time_lapse_frames` | 🟢 | the name has changed to `analyze_images` to better reflect what it actually does (no video needed) | |
+| `convert_detections2tracklets` | 🟠 | `greedy`, `calibrate`, `window_size` | |
+| `extract_maps` | 🟢 | | |
+| `visualize_scoremaps` | 🟢 | | |
+| `visualize_locrefs` | 🟢 | | |
+| `visualize_paf` | 🟢 | | |
+| `extract_save_all_maps` | 🟢 | | |
+| `export_model` | 🟢 | | |
diff --git a/docs/pytorch_dlc.md b/docs/pytorch_dlc.md
new file mode 100644
index 0000000000..6d2ddca45b
--- /dev/null
+++ b/docs/pytorch_dlc.md
@@ -0,0 +1,173 @@
+---
+deeplabcut:
+ last_content_updated: '2024-01-17'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
+# DeepLabCut: PyTorch API
+
+## Modules
+
+- [data](https://github.com/nastya236/DLCdev/blob/69005057eeac3c1492712863303f8268cee776e6/deeplabcut/pose_estimation_pytorch/data/project.py#L7):
+The `deeplabcut.pose_estimations_pytorch.data` package contains all code for pytorch
+dataset creation and test/train splitting.
+ - `Project` class provides train and test splitting and converts dataset to required
+ format. For instance, to [COCO]() format.
+ - `PoseTrainDataset` class is a [torch.utils.Dataset](https://pytorch.org/docs/stable/data.html) class, which converts raw
+ images and keypoints to a tensor dataset for training and evaluation.
+- [models](https://github.com/nastya236/DLCdev/blob/69005057eeac3c1492712863303f8268cee776e6/deeplabcut/pose_estimation_pytorch/data/models):
+The `deeplabcut.pose_estimations_pytorch.models` package contains all related to
+building a model with `backbone`, `neck` (optional) and `head`.
+- [train_module](https://github.com/nastya236/DLCdev/blob/69005057eeac3c1492712863303f8268cee776e6/deeplabcut/pose_estimation_pytorch/data/models):
+The `deeplabcut.pose_estimations_pytorch.train_module` contains all classes for model
+training and validation.
+
+## API
+
+The PyTorch implementation of DeepLabCut is very similar to the Tensorflow multi-animal
+implementation: the same steps need to be followed, just with slightly different API
+calls (and different model names).
+
+Up until it's time to create the training dataset, there are no changes to the way a
+PyTorch or Tensorflow project should be created.
+
+### Creating a Training Dataset
+
+To create a training dataset for a DeepLabCut PyTorch model, simply call:
+```python
+import deeplabcut
+deeplabcut.create_training_dataset(
+ path_config_file,
+ net_type="dekr_32",
+)
+```
+
+This will create folders for the training dataset in the same way as the Tensorflow
+version, with an addition configuration file in the `train` folder:
+`pytorch_config.yaml`. This is the file that can be edited to modify the model
+architecture or training parameters.
+
+There are currently two "families" of models implemented in PyTorch: DEKR (Geng, Zigang,
+et al. "Bottom-up human pose estimation via disentangled keypoint regression."
+Proceedings of the IEEE/CVF conference on computer vision and pattern recognition.
+2021.) and Tokenpose (Li, Yanjie, et al. "Tokenpose: Learning keypoint tokens for human
+pose estimation." Proceedings of the IEEE/CVF International conference on computer
+vision. 2021.). The choices of `net_type` that will create PyTorch training sets are:
+- `"dekr_16"`
+- `"dekr_32"`
+- `"dekr_48"`
+- `"token_pose_w16"`
+- `"token_pose_w32"`
+- `"token_pose_w48"`
+
+Note that Tokenpose models cannot currently be used with projects that contain unique
+keypoints.
+
+### Training the network
+Training a PyTorch model is done in a very similar manner as a tensorflow model, though
+currently the PyTorch API needs to be called directly:
+```python
+import deeplabcut.pose_estimation_pytorch.apis as api
+api.train_network(config_path, shuffle=1, trainingsetindex=0)
+```
+
+**Parameters**
+```
+config : path to the yaml config file of the project
+shuffle : index of the shuffle we want to train on
+trainingsetindex : training set index
+transform: Augmentation pipeline for the images
+ if None, the augmentation pipeline is built from config files
+ Advice if you want to use custom transformations:
+ Keep in mind that in order for transfer learning to be efficient, your
+ data statistical distribution should resemble the one used to pretrain your backbone
+ In most cases (e.g backbone was pretrained on ImageNet), that means it should be Normalized with
+ A.Normalize(mean = [0.485, 0.456, 0.406], std = [0.229, 0.224, 0.225])
+transform_cropped: Augmentation pipeline for the cropped images around animals
+ if None, the augmentation pipeline is built from config files
+ Advice if you want to use custom transformations:
+ Keep in mind that in order for transfer learning to be efficient, your
+ data statistical distribution should resemble the one used to pretrain your backbone
+ In most cases (e.g backbone was pretrained on ImageNet), that means it should be Normalized with
+ A.Normalize(mean = [0.485, 0.456, 0.406], std = [0.229, 0.224, 0.225])
+modelprefix: directory containing the deeplabcut configuration files to use
+ to train the network (and where snapshots will be saved). By default, they
+ are assumed to exist in the project folder.
+snapshot_path: if resuming training, used to specify the snapshot from which to resume
+detector_path: if resuming training of a top down model, used to specify the detector snapshot from
+ which to resume
+**kwargs : could be any entry of the pytorch_config dictionary. Examples are
+ to see the full list see the pytorch_cfg.yaml file in your project folder
+```
+
+### Evaluating the network
+As for training, the main difference is the need to call the API directly.
+```python
+import deeplabcut.pose_estimation_pytorch.apis as api
+api.evaluate_network(config_path, shuffle=1, trainingsetindex="all")
+```
+
+**Parameters**
+```
+config: path to the project's config file
+shuffles: Iterable of integers specifying the shuffle indices to evaluate.
+trainingsetindex: Integer specifying which training set fraction to use.
+ Evaluates all fractions if set to "all"
+snapshotindex: index (starting at 0) of the snapshot we want to load. To
+ evaluate the last one, use -1. To evaluate all snapshots, use "all". For
+ example if we have 3 models saved
+ - snapshot-0.pt
+ - snapshot-50.pt
+ - snapshot-100.pt
+ and we want to evaluate snapshot-50.pt, snapshotindex should be 1. If None,
+ the snapshotindex is loaded from the project configuration.
+plotting: Plots the predictions on the train and test images. If provided it must
+ be either ``True``, ``False``, ``"bodypart"``, or ``"individual"``. Setting
+ to ``True`` defaults as ``"bodypart"`` for multi-animal projects.
+show_errors: display train and test errors.
+transform: transformation pipeline for evaluation
+ ** Should normalise the data the same way it was normalised during training **
+modelprefix: directory containing the deeplabcut models to use when evaluating
+ the network. By default, they are assumed to exist in the project folder.
+batch_size: the batch size to use for evaluation
+```
+
+### Analyzing novel videos
+One big difference between the PyTorch and Tensorflow implementations comes in the way
+animal assembly happens (for multi-animal models). While in Tensorflow, assembly was a
+separate step that needed to be done from the keypoints, in the PyTorch version it's
+integrated directly into the models. From an API standpoint, that does not change much.
+
+Again, the PyTorch API needs to be invoked directly (it also has the `auto_track`
+option).
+```python
+import deeplabcut.pose_estimation_pytorch.apis as api
+api.analyze_videos(config_path, ["/fullpath/project/videos/test.mp4"], videotype=".mp4")
+```
+
+The PyTorch detections need to be converted to tracklets using the PyTorch API, but then
+the original tracklet stitching can be used.
+```python
+import deeplabcut
+import deeplabcut.pose_estimation_pytorch.apis as api
+api.convert_detections2tracklets(
+ config_path,
+ videos=['/fullpath/project/videos/test.mp4'],
+ videotype=".mp4",
+)
+deeplabcut.stitch_tracklets(
+ config_path,
+ videos=['/fullpath/project/videos/test.mp4'],
+ videotype=".mp4",
+)
+```
+
+Creating labeled videos can then be called in exactly the same way as before.
+```python
+import deeplabcut
+deeplabcut.create_labeled_video(
+ config_path,
+ videos=['/fullpath/project/videos/test.mp4'],
+ videotype=".mp4",
+)
+```
diff --git a/docs/quick-start/single_animal_quick_guide.md b/docs/quick-start/single_animal_quick_guide.md
new file mode 100644
index 0000000000..99362ea9d0
--- /dev/null
+++ b/docs/quick-start/single_animal_quick_guide.md
@@ -0,0 +1,78 @@
+---
+deeplabcut:
+ last_content_updated: '2025-06-30'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
+# QUICK GUIDE to single Animal Training:
+**The main steps to take you from project creation to analyzed videos:**
+
+Open ipython in the terminal:
+```
+ipython
+```
+
+Import DeepLabCut:
+```
+import deeplabcut
+```
+
+Create a new project:
+```
+deeplabcut.create_new_project("project_name", "experimenter", ["path of video 1", "path of video2", ..])
+```
+
+Set a config_path variable for ease of use + go edit this file!:
+```
+config_path = "yourdirectory/project_name/config.yaml"
+```
+
+Extract frames:
+```
+deeplabcut.extract_frames(config_path)
+```
+
+Label frames:
+```
+deeplabcut.label_frames(config_path)
+```
+
+Check labels [OPTIONAL]:
+```
+deeplabcut.check_labels(config_path)
+```
+
+Create training dataset:
+```
+deeplabcut.create_training_dataset(config_path)
+```
+
+Train the network:
+```
+deeplabcut.train_network(config_path)
+```
+
+Evaluate the trained network:
+```
+deeplabcut.evaluate_network(config_path)
+```
+
+ Video analysis:
+```
+deeplabcut.analyze_videos(config_path, ["path of video 1", "path of video2", ..])
+```
+
+Filter predictions [OPTIONAL]:
+```
+deeplabcut.filterpredictions(config_path, ["path of video 1", "path of video2", ..])
+```
+
+Plot results (trajectories):
+```
+deeplabcut.plot_trajectories(config_path, ["path of video 1", "path of video2", ..], filtered=True)
+```
+
+Create a video:
+```
+deeplabcut.create_labeled_video(config_path, ["path of video 1", "path of video2", ..], filtered=True)
+```
diff --git a/docs/tutorial.md b/docs/quick-start/tutorial_maDLC.md
similarity index 92%
rename from docs/tutorial.md
rename to docs/quick-start/tutorial_maDLC.md
index 947e036513..111ed996f5 100644
--- a/docs/tutorial.md
+++ b/docs/quick-start/tutorial_maDLC.md
@@ -1,3 +1,9 @@
+---
+deeplabcut:
+ last_content_updated: '2025-02-28'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
# Multi-animal pose estimation with DeepLabCut: A 5-minute tutorial
## GUI:
@@ -69,7 +75,17 @@ deeplabcut.create_multianimaltraining_dataset(
```
**(7) Train the network**
+
```python
+# PyTorch Engine
+deeplabcut.train_network(
+ config_path,
+ device="cuda",
+ save_epochs=5,
+ epochs=200,
+)
+
+# TensorFlow Engine
deeplabcut.train_network(
config_path,
saveiters=10000,
diff --git a/docs/recipes/BatchProcessing.md b/docs/recipes/BatchProcessing.md
index abac0bf27d..279433eceb 100644
--- a/docs/recipes/BatchProcessing.md
+++ b/docs/recipes/BatchProcessing.md
@@ -1,12 +1,19 @@
-
+---
+deeplabcut:
+ last_content_updated: '2025-09-16'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
# Automate training and video analysis: Batch Processing
## Tips for working with DLC networks:
-- Now you have a DLC network and are happy with the performance on selected videos, you may want to run it on all your videos without hassle. If all your videos are in one folder this is easy, simply pass the foldername to `deeplabcut.analyze_videos(config,[folder])` and you are fine. What if the videos are scattered?
-
+Now you have a DLC network and are happy with the performance on selected videos, you may want to run it on all your
+videos without hassle. If all your videos are in one folder this is easy, simply pass the foldername to
+`deeplabcut.analyze_videos(config,[folder])` and you are fine. What if the videos are scattered?
-You could create a simply script that runs over all your video folders with the network of choice. Your "key" to this network is your config.yaml file.
+You can create a simple script that runs over all your video folders with the network of choice. Your "key" to this
+network is your config.yaml file.

@@ -33,18 +40,18 @@ import deeplabcut
def getsubfolders(folder):
''' returns list of subfolders '''
- return [os.path.join(folder,p) for p in os.listdir(folder) if os.path.isdir(os.path.join(folder,p))]
+ return [os.path.join(folder, p) for p in os.listdir(folder) if os.path.isdir(os.path.join(folder, p))]
-project='ComplexWheelD3-12-Fumi-2019-01-28'
+project = "ComplexWheelD3-12-Fumi-2019-01-28"
-shuffle=1
+shuffle = 1
-prefix='/home/alex/DLC-workshopRowland'
+prefix = "/home/alex/DLC-workshopRowland"
-projectpath=os.path.join(prefix,project)
-config=os.path.join(projectpath,'config.yaml')
+projectpath = os.path.join(prefix, project)
+config = os.path.join(projectpath, "config.yaml")
-basepath='/home/alex/BenchmarkingExperimentsJan2019' #data'
+basepath = "/home/alex/BenchmarkingExperimentsJan2019"
'''
@@ -57,13 +64,13 @@ Imagine that the data (here: videos of 3 different types) are in subfolders:
'''
-subfolders=getsubfolders(basepath)
+subfolders = getsubfolders(basepath)
for subfolder in subfolders: #this would be January, February etc. in the upper example
- print("Starting analyze data in:", subfolder)
- subsubfolders=getsubfolders(subfolder)
- for subsubfolder in subsubfolders: #this would be Febuary1, etc. in the upper example...
- print("Starting analyze data in:", subsubfolder)
- for vtype in ['.mp4','.m4v','.mpg']:
+ print("Starting analyze data in: ", subfolder)
+ subsubfolders = getsubfolders(subfolder)
+ for subsubfolder in subsubfolders: #this would be February1, etc. in the upper example...
+ print("Starting analyze data in: ", subsubfolder)
+ for vtype in [".mp4", ".m4v", ".mpg"]:
deeplabcut.analyze_videos(config,[subsubfolder],shuffle=shuffle,videotype=vtype,save_as_csv=True)
```
@@ -90,25 +97,25 @@ import os
import deeplabcut
-Maxiter=int(1.5*10**5)
+epochs = 200
model=int(sys.argv[1])
-Projects=[['project1-phoenix-2019-01-28'],['ComplexWheelD3-12-Fumi-2019-01-28', 'maze-ariel-2019-01-28'], ['TBI-BvA-2019-01-28','group-eli-2019-01-28']]
+Projects=[["project1-phoenix-2019-01-28"], ["ComplexWheelD3-12-Fumi-2019-01-28", "maze-ariel-2019-01-28"], ["TBI-BvA-2019-01-28", "group-eli-2019-01-28"]]
shuffle=1
-prefix='/home/alex/DLC-workshopRowland'
+prefix = "/home/alex/DLC-workshopRowland"
for project in Projects[model]:
- projectpath=os.path.join(prefix,project)
- config=os.path.join(projectpath,'config.yaml')
+ projectpath = os.path.join(prefix, project)
+ config = os.path.join(projectpath, "config.yaml")
- cfg=deeplabcut.auxiliaryfunctions.read_config(config)
- previous_path=cfg['project_path']
+ cfg = deeplabcut.auxiliaryfunctions.read_config(config)
+ previous_path = cfg["project_path"]
- cfg['project_path']=projectpath
- deeplabcut.auxiliaryfunctions.write_config(config,cfg)
+ cfg["project_path"]=projectpath
+ deeplabcut.auxiliaryfunctions.write_config(config, cfg)
print("This is the name of the script: ", sys.argv[0])
print("Shuffle: ", shuffle)
@@ -116,22 +123,18 @@ for project in Projects[model]:
deeplabcut.create_training_dataset(config, Shuffles=[shuffle])
- deeplabcut.train_network(config, shuffle=shuffle, max_snapshots_to_keep=5, maxiters=Maxiter)
+ deeplabcut.train_network(config, shuffle=shuffle, max_snapshots_to_keep=5, epochs=epochs)
print("Evaluating...")
- deeplabcut.evaluate_network(config, Shuffles=[shuffle],plotting=True)
+ deeplabcut.evaluate_network(config, Shuffles=[shuffle], plotting=True)
print("Analyzing videos..., switching to last snapshot...")
- #cfg=deeplabcut.auxiliaryfunctions.read_config(config)
- #cfg['snapshotindex']=-1
- #deeplabcut.auxiliaryfunctions.write_config(config,cfg)
-
for vtype in ['.mp4','.m4v','.mpg']:
try:
- deeplabcut.analyze_videos(config,[str(os.path.join(projectpath,'videos'))],shuffle=shuffle,videotype=vtype,save_as_csv=True)
- except:
+ deeplabcut.analyze_videos(config, [str(os.path.join(projectpath, "videos"))], shuffle=shuffle, videotype=vtype, save_as_csv=True)
+ except Exception:
pass
print("DONE WITH ", project," resetting to original path")
- cfg['project_path']=previous_path
- deeplabcut.auxiliaryfunctions.write_config(config,cfg)
+ cfg["project_path"] = previous_path
+ deeplabcut.auxiliaryfunctions.write_config(config, cfg)
```
diff --git a/docs/recipes/ClusteringNapari.md b/docs/recipes/ClusteringNapari.md
index 4d309d74e5..bb7faf41c9 100644
--- a/docs/recipes/ClusteringNapari.md
+++ b/docs/recipes/ClusteringNapari.md
@@ -1,62 +1,93 @@
-
+---
+deeplabcut:
+ last_content_updated: '2026-02-10'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
# Clustering in the napari-DeepLabCut GUI
-To increase model performance, one can find the errors in the user-defined label (or in output H5 files after video inference). You can correct the errors and add them back into the training dataset, a process called active learning.
+To increase model performance, one can find the errors in the user-defined label (or in output H5 files after video
+inference). You can correct the errors and add them back into the training dataset, a process called active learning.
-User errors can be detrimental to model performance, so beyond just `check_labels`, this tool allows you to find your mistakes. If you are curious about how errors affect performance, read the paper: [A Primer on Motion Capture with Deep Learning: Principles, Pitfalls, and Perspectives](https://www.sciencedirect.com/science/article/pii/S0896627320307170).
+User errors can be detrimental to model performance, so beyond just `check_labels`, this tool allows you to find your
+mistakes. If you are curious about how errors affect performance, read the paper:
+[A Primer on Motion Capture with Deep Learning: Principles, Pitfalls, and Perspectives](https://www.sciencedirect.com/science/article/pii/S0896627320307170).
**TL;DR: your data quality matters!**
-
+
```{Hint}
**Labeling Pitfalls: How Corruptions Affect Performance**
-(A) Illustration of two types of labeling errors. Top is ground truth, middle is missing a label at the tailbase, and bottom is if the labeler swapped the ear identity (left to right, etc.). (B) Using a small training dataset of 106 frames, how do the corruptions in (A) affect the percent of correct keypoints (PCK) on the test set as the distance to ground truth increases from 0 pixels (perfect prediction) to 20 pixels (larger error)? The x axis denotes the difference in the ground truth to the predicted location (RMSE in pixels), whereas the y axis is the fraction of frames considered accurate (e.g., z80% of frames fall within 9 pixels, even on this small training dataset, for points that are not corrupted, whereas for swapped points this falls to z65%). The fraction of the dataset that is corrupted affects this value. Shown is when missing the tailbase label (top) or swapping the ears in 1%, 5%, 10%, and 20% of frames (of 106 labeled training images). Swapping versus missing labels has a more notable adverse effect on network performance.
+(A) Illustration of two types of labeling errors. Top is ground truth, middle is missing a label at the tailbase, and
+bottom is if the labeler swapped the ear identity (left to right, etc.). (B) Using a small training dataset of 106
+frames, how do the corruptions in (A) affect the percent of correct keypoints (PCK) on the test set as the distance
+to ground truth increases from 0 pixels (perfect prediction) to 20 pixels (larger error)? The x axis denotes the
+difference in the ground truth to the predicted location (RMSE in pixels), whereas the y axis is the fraction of
+frames considered accurate (e.g., z80% of frames fall within 9 pixels, even on this small training dataset, for
+points that are not corrupted, whereas for swapped points this falls to z65%). The fraction of the dataset that is
+corrupted affects this value. Shown is when missing the tailbase label (top) or swapping the ears in 1%, 5%, 10%,
+and 20% of frames (of 106 labeled training images). Swapping versus missing labels has a more notable adverse effect
+on network performance.
```
-The DeepLabCut toolbox supports **active learning** by extracting outlier frames be several methods and allowing the user to correct the frames, then retrain the model. See the [Nature Protocols paper](https://www.nature.com/articles/s41596-019-0176-0) for the detailed steps, or in the docs, [here](https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide.html#m-optional-active-learning-network-refinement-extract-outlier-frames).
+The DeepLabCut toolbox supports **active learning** by extracting outlier frames be several methods and allowing the
+user to correct the frames, then retrain the model. See the
+[Nature Protocols paper](https://www.nature.com/articles/s41596-019-0176-0) for the detailed steps, or in the docs,
+[here](active-learning).
-To facilitate this process, here we propose a new way to detect 'outlier frames', which is planned to be released in ~Sept 2022. Your contributions and suggestions are welcomed, so test the [PR](https://github.com/DeepLabCut/napari-deeplabcut/pull/38) and give us feedback!
+To facilitate this process, here we propose a new way to detect 'outlier frames'.
+Your contributions and suggestions are welcomed, so test the
+[PR](https://github.com/DeepLabCut/napari-deeplabcut/pull/38) and give us feedback!
-This #cookbook recipe aims to show a usecase of **clustering in napari** and is contributed by 2022 DLC AI Resident [Sabrina Benas](https://twitter.com/Sabrineiitor) 💜.
+This #cookbook recipe aims to show a usecase of **clustering in napari** and is contributed by 2022 DLC AI Resident
+[Sabrina Benas](https://x.com/Sabrineiitor) 💜.
## Detect Outliers to Refine Labels
### Open `napari` and the `DeepLabCut plugin`
- - Then open your `CollectedData_.h5` file. We used the Horse-30 dataset, presented in [Mathis, Biasi et al. WACV 2022](http://horse10.deeplabcut.org/), as our demo and development set. Here is an example of what it should look like:
+Then open your `CollectedData_.h5` file. We used the Horse-30 dataset, presented in
+[Mathis, Biasi et al. WACV 2022](http://horse10.deeplabcut.org/), as our demo and development set. Here is an example of what it should look like:
-
+
### Clustering
-- Click on the button `cluster` and wait a few seconds until it displays a new layer with the cluster:
+Click on the button `cluster` and wait a few seconds until it displays a new layer with the cluster:
-
+
You can click on a point and see the image on the right with the keypoints:
-
+
### Visualize & refine
-If you decided to refine that frame (we moved the points to make outliers obvious), click `show img` and refine them using the plugin features and instructions:
+If you decided to refine that frame (we moved the points to make outliers obvious), click `show img` and refine them
+using the plugin features and instructions:
-
+
- ```{Attention}
- When you're done, you need to click `ctl-s` to save it.
+```{Attention}
+When you're done, you need to click `ctl-s` to save it.
```
-- You can go back to the cluster layer by clicking on `close img` and refine another image. Reminder, when you're done editing you need to click `ctl-s` to save your work. And now you can take the updated `CollectedData` file, create and **new training shuffle**, and train the network! Read more about how to [create a training dataset](https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide.html#f-create-training-dataset-s).
+You can go back to the cluster layer by clicking on `close img` and refine another image. Reminder, when you're done
+editing you need to click `ctl-s` to save your work. And now you can take the updated `CollectedData` file, create
+and **new training shuffle**, and train the network! Read more about how to
+[create a training dataset](create-training-dataset).
```{hint}
-If you want to change the clustering method, you can modify the file [kmeans.py](https://github.com/DeepLabCutAIResidency/napari-deeplabcut/blob/cluster1/src/napari_deeplabcut/kmeans.py)
+If you want to change the clustering method, you can modify the file
+[kmeans.py](https://github.com/DeepLabCutAIResidency/napari-deeplabcut/blob/cluster1/src/napari_deeplabcut/kmeans.py)
+```
::::{important}
-You have to keep the way the file is opened (pandas dataframe) and the output has to be the cluster points, the points colors in the cluster colors and the frame names (in this order).
+You have to keep the way the file is opened (pandas dataframe) and the output has to be the cluster points, the points
+colors in the cluster colors and the frame names (in this order).
::::
```
diff --git a/docs/recipes/DLCMethods.md b/docs/recipes/DLCMethods.md
index b2f7a5a7af..b3710b7d55 100644
--- a/docs/recipes/DLCMethods.md
+++ b/docs/recipes/DLCMethods.md
@@ -1,8 +1,20 @@
+---
+deeplabcut:
+ last_content_updated: '2025-02-28'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
# How to write a DLC Methods Section
**Pose estimation using DeepLabCut**
-For body part tracking we used DeepLabCut (version 2.X.X) [Mathis et al, 2018, Nath et al, 2019]. Specifically, we labeled X number of frames taken from X videos/animals (then X% was used for training (default is 95%). We used a X-based neural network (i.e., X = ResNet-50, ResNet-101, MobileNetV2-0.35, MobileNetV2-0.5, MobileNetV2-0.75, MobileNetV2-1, EfficientNet ..X, dlcrnet_ms5, etc.)*** with default parameters* for X number of training iterations. We validated with X number of shuffles, and found the test error was: X pixels, train: X pixels (image size was X by X). We then used a p-cutoff of X (i.e. 0.9) to condition the X,Y coordinates for future analysis. This network was then used to analyze videos from similar experimental settings.
+For body part tracking we used DeepLabCut (version 3.X.X) [Mathis et al, 2018, Nath et al, 2019]. Specifically, we
+labeled X number of frames taken from X videos/animals (then X% was used for training (default is 95%). We used a
+X-based neural network (i.e., X = ResNet-50, ResNet-101, MobileNetV2-0.35, MobileNetV2-0.5, MobileNetV2-0.75,
+MobileNetV2-1, EfficientNet ..X, dlcrnet_ms5, cspnext_s, dekr_w32, rtmpose_s, etc.)*** with default parameters* for X
+number of training iterations. We validated with X number of shuffles, and found the test error was: X pixels, train:
+X pixels (image size was X by X). We then used a p-cutoff of X (i.e. 0.9) to condition the X,Y coordinates for future
+analysis. This network was then used to analyze videos from similar experimental settings.
*If any defaults were changed in *`pose_config.yaml`*, mention them.
@@ -43,4 +55,5 @@ If you use ResNets, consider citing Insafutdinov et al 2016 & He et al 2016. If
> 770–778 (2016). URL https://arxiv.org/abs/
> 1512.03385.
-We also have the network graphic freely available on SciDraw.io if you'd like to use it! https://scidraw.io/drawing/290. If you use our DLC logo, please include the TM symbol, thank you!
+We also have the network graphic freely available on SciDraw.io if you'd like to use it! https://scidraw.io/drawing/290.
+If you use our DLC logo, please include the TM symbol, thank you!
diff --git a/docs/recipes/MegaDetectorDLCLive.md b/docs/recipes/MegaDetectorDLCLive.md
index 20e35a38fc..416a23125c 100644
--- a/docs/recipes/MegaDetectorDLCLive.md
+++ b/docs/recipes/MegaDetectorDLCLive.md
@@ -1,3 +1,9 @@
+---
+deeplabcut:
+ last_content_updated: '2026-02-10'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
# 💚 MegaDetector+DeepLabCut 💜
[DeepLabCut-Live](https://github.com/DeepLabCut/DeepLabCut-live) is an open source and free real-time package from DeepLabCut that allows for real-time, low-latency pose estimation. [The DeepLabCut-ModelZoo](http://modelzoo.deeplabcut.org/) is our growing collection of pretrained animal models for rapid deployment; no training is typically required to use these models. MegaDetector is a free open software trained to detect animals, people, and vehicles from camera trap images. Check [here](https://github.com/microsoft/CameraTraps/blob/main/megadetector.md) for further information.
@@ -14,7 +20,9 @@ MegaDetector detects an animal and generates a bounding box around the animal. T
## DeepLabCut-Live
-DeepLabCut-Live! is a real-time package for running DeepLabCut. However, you can also use it as a lighter-weight package for running DeeplabCut even if you don't need real-time. It's very useful to use in HPC or servers, or in Apps, as we do here. To read more, check out the [docs](https://deeplabcut.github.io/DeepLabCut/docs/deeplabcutlive.html).
+DeepLabCut-Live! is a real-time package for running DeepLabCut. However, you can also use it as a lighter-weight
+package for running DeeplabCut even if you don't need real-time. It's very useful to use in HPC or servers, or in Apps,
+as we do here. To read more, check out the [docs](deeplabcut-live).
### MegaDetector meets DeepLabCut
@@ -66,9 +74,9 @@ All information seen on the output image is recorded on the **Download JSON file
"file": "image0.jpg", //image filename uploaded
"number_of_bb": 1, //number of bounding boxes detected on the image
"dlc_model": "full_dog", //model used
- "bb_0": {
+ "bb_0": {
"corner_1": [ //top left corner
- 76.08082580566406, //x
+ 76.08082580566406, //x
91.02932739257812 //y
],
"corner_2": [ //bottom right corner
@@ -100,7 +108,7 @@ We encourage you to try out and experiment on your camera trap or other animal i
-Or these lil' cuties 🐶🐶🙀🐶 outside a restaurant, from the [Twitter meme](https://twitter.com/standardpuppies/status/1563188163962515457?s=21&t=f2kM2HoUygyLmmAH7Ho-HQ).
+Or these lil' cuties 🐶🐶🙀🐶 outside a restaurant.
diff --git a/docs/recipes/OpenVINO.md b/docs/recipes/OpenVINO.md
index 2033045ce8..06bcbba3b5 100644
--- a/docs/recipes/OpenVINO.md
+++ b/docs/recipes/OpenVINO.md
@@ -1,10 +1,21 @@
+---
+deeplabcut:
+ last_content_updated: '2025-02-28'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
# Intel OpenVINO backend
+::::{warning}
+This feature is currently implemented for TensorFlow-based models only.
+::::
+
DeepLabCut provides an option to run deep learning model with [OpenVINO](https://github.com/openvinotoolkit/openvino) backend.
-To enable OpenVINO in your pipeline, use `use_openvino` flag of `analyze_videos` method with one of string values indicating device:
-* "CPU" - Use CPU. This is a default value.
-* "GPU" - Use iGPU (requires OpenCL to be installed). First launch might take some time for kernels initialization.
-* "MULTI:CPU,GPU" - Use CPU and GPU simultaneously. In most cases this option provides the best efficiency.
+To enable OpenVINO in your pipeline, use `use_openvino` flag of `analyze_videos` method with one of string values
+indicating device:
+* ```"CPU"``` - Use CPU. This is a default value.
+* ```"GPU"``` - Use GPU (requires OpenCL to be installed). First launch might take some time for kernels initialization.
+* ```"MULTI:CPU,GPU"``` - Use CPU and GPU simultaneously. In most cases this option provides the best efficiency.
```python
def analyze_videos(
diff --git a/docs/recipes/OtherData.md b/docs/recipes/OtherData.md
new file mode 100644
index 0000000000..21efb74f49
--- /dev/null
+++ b/docs/recipes/OtherData.md
@@ -0,0 +1,39 @@
+---
+deeplabcut:
+ last_content_updated: '2025-02-28'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
+# How to use data labeled outside of DeepLabCut
+- and/or if you merge projects across scorers (see below):
+
+
+
+## Using data labeled elsewhere:
+
+Some users may have annotation data in different formats, yet want to use the DLC pipeline. In this case, you need to convert the data to our format. Simply, you can format your data in an excel sheet (.csv file) or pandas array (.h5 file).
+
+Here is a guide to do this via the ".csv" route: (the pandas array route is identical, just format the pandas array in the same way).
+
+**Step 1**: create a project as describe in the user guide: https://github.com/DeepLabCut/DeepLabCut/blob/main/docs/UseOverviewGuide.md#create-a-new-project
+
+**Step 2**: edit the ``config.yaml`` file to include the body part names, please take care that spelling, spacing, and capitalization are IDENTICAL to the "labeled data body part names".
+
+**Step 3**: Please inspect the excel formatted sheet (.csv) from our [demo project](https://github.com/DeepLabCut/DeepLabCut/tree/main/examples/Reaching-Mackenzie-2018-08-30/labeled-data/reachingvideo1)
+- i.e. this file: https://github.com/DeepLabCut/DeepLabCut/blob/main/examples/Reaching-Mackenzie-2018-08-30/labeled-data/reachingvideo1/CollectedData_Mackenzie.csv
+
+**Step 4**: Edit the .csv file such that it contains the X, Y pixel coordinates, the body part names, the scorer name as well as the relative path to the image: e.g. /labeled-data/somefolder/img017.jpg
+Then make sure the scorer name, and body parts are the same in the config.yaml file.
+
+Also add for each folder a video to the `video_set` in the config.yaml file. This can also be a dummy variable, but should be e.g.
+C://somefolder.avi if the folder is called somefolder. See demo config.yaml file for proper formatting.
+
+**Step 5**: When you are done, run ``deeplabcut.convertcsv2h5('path_to_config.yaml', scorer= 'experimenter')``
+
+ - The scorer name must be identical to the input name for experimenter that you used when you created the project. This will automatically update "Mackenzie" to your name in the example demo notebook.
+
+## If you merge projects:
+
+**Step 1**: rename the CSV files to be the target name.
+
+**Step 2**: run and pass the target name ``deeplabcut.convertcsv2h5('path_to_config.yaml', scorer= 'experimenter')``. This will overwrite the H5 file so the data is all merged under the target name.
diff --git a/docs/recipes/TechHardware.md b/docs/recipes/TechHardware.md
index f4610f5442..9ab75ade22 100644
--- a/docs/recipes/TechHardware.md
+++ b/docs/recipes/TechHardware.md
@@ -1,3 +1,9 @@
+---
+deeplabcut:
+ last_content_updated: '2026-02-10'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
# Technical (Hardware) Considerations
## Quick summary:
@@ -11,7 +17,14 @@ For reference, we use e.g. Dell workstations (79xx series) with **Ubuntu 16.04 L
### Computer Hardware:
-Ideally, you will use a strong GPU with *at least* 8GB memory such as the [NVIDIA GeForce 1080 Ti, 2080 Ti, or 3090](https://www.nvidia.com/en-us/shop/geforce/?page=1&limit=9&locale=en-us). A GPU is not strictly necessary, but on a CPU the (training and evaluation) code is considerably slower (10x) for ResNets, but MobileNets and EfficientNets are slightly faster. Still, a GPU will give you a massive speed boost. You might also consider using cloud computing services like [Google cloud/amazon web services](https://github.com/DeepLabCut/DeepLabCut/issues/47) or Google Colaboratory.
+Ideally, you will use a strong GPU with *at least* 8GB memory such as the [NVIDIA GeForce 1080 Ti, 2080 Ti, or 3090](https://marketplace.nvidia.com/en-us/consumer/graphics-cards/). A GPU is not strictly necessary, but on a CPU the (training and evaluation) code is considerably slower (10x) for ResNets, but MobileNets and EfficientNets are slightly faster. Still, a GPU will give you a massive speed boost. You might also consider using cloud computing services like [Google cloud/amazon web services](https://github.com/DeepLabCut/DeepLabCut/issues/47) or Google Colaboratory.
+
+```{note}
+If you encounter errors during inference related to
+`torch.inference_mode` and DirectML, set the environment variable
+`DLC_DIRECTML_NO_GRAD=true` before starting Python. This switches the inference
+context to `torch.no_grad`, which is compatible with the DirectML execution path.
+```
### Camera Hardware:
@@ -23,10 +36,20 @@ The software is very robust to track data from any camera (cell phone cameras, g
**Anaconda/Python3:** Anaconda: a free and open source distribution of the Python programming language (download from https://www.anaconda.com/). DeepLabCut is written in Python 3 (https://www.python.org/) and not compatible with Python 2.
+**For the TensorFlow Engine:** You will need [TensorFlow](https://www.tensorflow.org/).
+We used version 1.0 in the paper, later versions also work with the provided code (we
+tested **TensorFlow versions 1.0 to 1.15, and 2.0 to 2.12 (2.10 for Windows)**; we
+recommend TF2.12 for MacOS/Ubuntu and 2.10 for Windows) for Python 3.10 with GPU
+support.
-**TensorFlow** You will need [TensorFlow](https://www.tensorflow.org/) (we used version 1.0 in the paper, later versions also work with the provided code (we tested **TensorFlow versions 1.0 to 1.15, and 2.0 to 2.5**; we recommend TF2.5 now) for Python 3.7, 3.8, or 3.9 with GPU support.
+To note, is it possible to run DeepLabCut on your CPU, but it will be VERY slow (see:
+[Mathis & Warren](https://www.biorxiv.org/content/early/2018/10/30/457242)). However, this is the preferred path if you want to test
+DeepLabCut on your own computer/data before purchasing a GPU, with the added benefit of
+a straightforward installation! Otherwise, use our COLAB notebooks for GPU access for
+testing.
-To note, is it possible to run DeepLabCut on your CPU, but it will be VERY slow (see: [Mathis & Warren](https://www.biorxiv.org/content/early/2018/10/30/457242)). However, this is the preferred path if you want to test DeepLabCut on your own computer/data before purchasing a GPU, with the added benefit of a straightforward installation! Otherwise, use our COLAB notebooks for GPU access for testing.
+Docker: We highly recommend advanced users use the supplied [Docker container](
+docker-containers).
-Docker: We highly recommend advaced users use the supplied [Docker container](https://github.com/MMathisLab/Docker4DeepLabCut2.0).
-NOTE: [this container does not work on windows hosts!](https://github.com/NVIDIA/nvidia-docker/issues/43)
+NOTE: [Currently GPU support in Docker Desktop is only available on Windows with the
+WSL2 backend.](https://docs.docker.com/desktop/features/gpu/)
diff --git a/docs/recipes/UsingModelZooPupil.md b/docs/recipes/UsingModelZooPupil.md
index 2aac4a4d00..2b906ad755 100644
--- a/docs/recipes/UsingModelZooPupil.md
+++ b/docs/recipes/UsingModelZooPupil.md
@@ -1,17 +1,31 @@
+---
+deeplabcut:
+ last_content_updated: '2025-02-28'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
# Using ModelZoo models on your own datasets
-Animal behavior has to be analyzed with painstaking accuracy. Therefore, animal pose estimation has been an important tool to study animal behavior precisely.
+
Animal behavior has to be analyzed with painstaking accuracy. Therefore, animal pose estimation has been
+an important tool to study animal behavior precisely.
-Beside providing an open source toolbox for researchers to develop customized deep neural networks for markerless pose estimation, we at DeepLabCut also aim to build robust, generalizable models. Part of this effort is via the [DeeplabCut ModelZoo](http://www.mackenziemathislab.org/dlc-modelzoo).
+Beside providing an open source toolbox for researchers to develop customized deep neural networks for markerless pose
+estimation, we at DeepLabCut also aim to build robust, generalizable models. Part of this effort is via the
+[DeeplabCut ModelZoo](http://modelzoo.deeplabcut.org/).
-The Zoo hosts user-contributed and #teamDLC developed models that are trained on specific animals and scenarios. You can analyze your videos directly with these models without training. The models have strong zero-shot performance on unseen out-of-domain data which can be further improved via pseudo-labeling. Please check the first [ModelZoo manuscript](https://arxiv.org/abs/2203.07436v1) for further details.
+The Zoo hosts user-contributed and DLC-team developed models that are trained on specific animals and scenarios. You can
+analyze your videos directly with these models without training. The models have strong zero-shot performance on unseen
+out-of-domain data which can be further improved via pseudo-labeling. Please check the first
+[ModelZoo manuscript](https://arxiv.org/abs/2203.07436v1) for further details.
-This recipe aims to show a usecase of the **mouse_pupil_vclose** and is contributed by 2022 DLC AI Resident [Neslihan Wittek](https://github.com/neslihanedes) 💜.
+This recipe aims to show a usecase of the **mouse_pupil_vclose** and is contributed by 2022 DLC AI Resident
+[Neslihan Wittek](https://github.com/neslihanedes) 💜.
## `mouse_pupil_vclose` model
This model was contributed by Jim McBurney-Lin at University of California Riverside, USA.
-The model was trained on images of C57/B6J mice eyes, and also then augmented with mouse eye data from the Mathis Lab at EPFL.
+The model was trained on images of C57/B6J mice eyes, and also then augmented with mouse eye data from the Mathis Lab at
+EPFL.
@@ -30,23 +44,37 @@ The model was trained on images of C57/B6J mice eyes, and also then augmented wi
| 8 | VLpupil | Ventral/left aspect of pupil |
-Since we would like to evaluate the models performance on out-of-domain data, we will analyze pigeon pupils. For more discussions and work on so-called out-of-domain data, see [Mathis, Biasi 2020](http://www.mackenziemathislab.org/horse10).
+Since we would like to evaluate the models performance on out-of-domain data, we will analyze pigeon pupils. For more
+discussions and work on so-called out-of-domain data, see
+[Mathis, Biasi 2020](https://paperswithcode.com/dataset/horse-10).
## Pigeon Pupil
-The eye pupil admits and regulates the amount of light entering the retina in order to enable image perception. Beside this curicial role, the pupil also reflects the state of the brain. The systemic behavior of the pupil has not been vastly studied in birds, although researchers from Max Planck Institute for Ornithology in Seewiesen have shed light on pupil behaviors in pigeons.
+The eye pupil admits and regulates the amount of light entering the retina in order to enable image perception. Beside
+this curicial role, the pupil also reflects the state of the brain. The systemic behavior of the pupil has not been
+vastly studied in birds, although researchers from
+Max Planck Institute for Ornithology in Seewiesen
+have shed light on pupil behaviors in pigeons.
-The pupils of male pigeons get smaller during courtship behavior. This is in contrast to mammals, for which the pupil size dilates in response to an increase in arousal. In addition, the pupil size of pigeons dilates during non-REM sleep, while they rapidly constrict during REM sleep. Examining these differences and the reason behind them, might be helpful to understand the pupillary behavior in general.
+The pupils of male pigeons get smaller during courtship behavior. This is in contrast to mammals, for which the pupil
+size dilates in response to an increase in arousal. In addition, the pupil size of pigeons dilates during non-REM sleep,
+while they rapidly constrict during REM sleep. Examining these differences and the reason behind them, might be helpful
+to understand the pupillary behavior in general.
-In light of these findings, we wanted to show whether the **mouse_pupil_vclose** model give us an accurate tracking performance for the pigeon pupil as well.
+In light of these findings, we wanted to show whether the **mouse_pupil_vclose** model give us an accurate tracking
+performance for the pigeon pupil as well.
### Jupyter & Google Colab Notebook
-DeepLabCut provides a Google Colab Notebook to analyze your video with a pretrained networks from the ModelZoo. No need for local installation of DeepLabCut!
+DeepLabCut provides a Google Colab Notebook to analyze your video with a pretrained networks from the ModelZoo. No need
+for local installation of DeepLabCut!
-Since we are interested in the accuracy of the **mouse_pupil_vclose** on pigeon pupil data, we will use a video which consists of 7 recordings of pigeon pupils.
+Since we are interested in the accuracy of the **mouse_pupil_vclose** on pigeon pupil data, we will use a video which
+consists of 7 recordings of pigeon pupils.
-Check ModelZoo Colab page and a video tutorial on how to use the ModelZoo on Google Colab.
+Check the
+[ModelZoo Colab page](https://github.com/DeepLabCut/DeepLabCut/blob/main/examples/COLAB/COLAB_DLC_ModelZoo.ipynb)
+and a video tutorial on how to use the ModelZoo on Google Colab.
@@ -63,35 +91,39 @@ files.download("/content/file.zip")
### Analyze Videos at Your Local Machine
-DeepLabCut host models from the
DeepLabCut ModelZoo Project .
+DeepLabCut host models from the [DeepLabCut ModelZoo Project](http://modelzoo.deeplabcut.org/).
The `create_pretrained_project` function will create a new project directory with the necessary sub-directories and a basic configuration file.
It will also initialize your project with a pre-trained model from the DeepLabCut ModelZoo.
The rest of the code should be run within your DeepLabCut environment.
-Check
here for the instructions for the DeepLabCut installation.
+Check [here](how-to-install) for the instructions for the DeepLabCut installation.
+To initialize a new project directory with a pre-trained model from the DeepLabCut ModelZoo, run the code below.
+
+::::{warning}
+This method is currently implemented for Tensorflow only, Pytorch compatibility is coming soon.
+::::
```python
import deeplabcut
-```
-To initialize a new project directory with a pre-trained model from the DeepLabCut ModelZoo, run the code below.
-```python
deeplabcut.create_pretrained_project(
"projectname",
"experimenter",
[r"path_for_the_videos"],
- model= "mouse_pupil_vclose",
- working_directory= r"project_directory",
- copy_videos= True,
- videotype= ".mp4 or .avi?",
- analyzevideo= True,
- filtered= True,
- createlabeledvideo= True,
- trainFraction= None
+ model="mouse_pupil_vclose",
+ working_directory=r"project_directory",
+ copy_videos=True,
+ videotype=".mp4 or .avi?",
+ analyzevideo=True,
+ filtered=True,
+ createlabeledvideo=True,
+ trainFraction=None,
+ engine=deeplabcut.Engine.TF,
)
```
+
::::{important}
Your videos should be cropped around the eye for better model accuracy! 👁🐭
::::
@@ -100,13 +132,12 @@ Excitingly, 6 out of the 7 pigeon pupils were tracked nicely:
-
-When we further evaluate the model accuracy by checking the likelihood of tracked points, we see that the tracking is low confidience when the pigeons close their eyelid (which is of course expected, and can be leveraged to measure blinking 👁).
-
+When we further evaluate the model accuracy by checking the likelihood of tracked points, we see that the tracking is
+low confidience when the pigeons close their eyelid (which is of course expected, and can be leveraged to measure
+blinking 👁).
-
But you also might encounter larger problems than small tracking glitches:
@@ -117,12 +148,26 @@ The more problems you encounter, the higher the number of frames you might want
You should also add the path of the video(s) into the `config.yaml` file, or run the following command to add the videos to your project:
```python
-deeplabcut.add_new_videos('/pathofproject/config.yaml', ['/pathofvideos/pigeon.mp4'], copy_videos=False, coords=None, extract_frames=False)
+deeplabcut.add_new_videos(
+ "/pathofproject/config.yaml",
+ ["/pathofvideos/pigeon.mp4"],
+ copy_videos=False,
+ coords=None,
+ extract_frames=False
+)
```
The `deeplabcut.extract_outlier_frames` function will check for outliers and ask your feedback on whether to extract these outliers frames.
```python
-deeplabcut.extract_outlier_frames('/pathofproject/config.yaml', ['/pathofvideos/pigeon.mp4'], automatic=True)
+deeplabcut.analyze_videos(
+ "/pathofproject/config.yaml",
+ ["/pathofvideos/pigeon.mp4"]
+)
+deeplabcut.extract_outlier_frames(
+ "/pathofproject/config.yaml",
+ ["/pathofvideos/pigeon.mp4"],
+ automatic=True
+)
```
The `deeplabcut.refine_labels` function starts the GUI which allows you to refine the outlier frames manually.
You should load the outlier frames directory and corresponding `.h5` file from the previous model.
@@ -130,9 +175,9 @@ It will ask you to define the `likelihood` threshold: labels under the threshold
After refining, you should combine these data with your previous model's data set and create a new training data set.
```python
-deeplabcut.refine_labels('/pathofproject/config.yaml')
-deeplabcut.merge_datasets('/pathofproject/config.yaml')
-deeplabcut.create_training_dataset('/pathofproject/config.yaml')
+deeplabcut.refine_labels("/pathofproject/config.yaml")
+deeplabcut.merge_datasets("/pathofproject/config.yaml")
+deeplabcut.create_training_dataset("/pathofproject/config.yaml")
```
Before starting the training of your model, there is one last step left: editing the `init_weights` parameter in your `pose_cfg.yaml` file.
Go to your project and check the latest snapshot (e.g., `snapshot-610000`) of your model in `dlc-models/train` directory.
@@ -142,7 +187,7 @@ Edit the value of the `init_weights` key in the `pose_cfg.yaml` file and start t
`init_weights: pathofyourproject\dlc-models\iteration-0\DLCFeb31-trainset95shuffle1\train\snapshot-610000`
```python
-deeplabcut.train_network('/pathofproject/config.yaml', shuffle=1, saveiters=25000)
+deeplabcut.train_network("/pathofproject/config.yaml", shuffle=1, saveiters=25000)
```
```{hint}
Check this video for model refining!
diff --git a/docs/recipes/flip_and_rotate.ipynb b/docs/recipes/flip_and_rotate.ipynb
index 9cce5b39c8..bea9391cb8 100644
--- a/docs/recipes/flip_and_rotate.ipynb
+++ b/docs/recipes/flip_and_rotate.ipynb
@@ -47,8 +47,13 @@
"source": [
"import deeplabcut\n",
"\n",
- "project_folder = \"/home/user/projects/\" #the folder in which the DLC project will be created\n",
- "deeplabcut.create_new_project(project='bat_augmentation_austin_2020_bat_data',experimenter='DLC',videos=['/home/user/dummyVideos/'],working_directory=project_folder)"
+ "project_folder = \"/home/user/projects/\" # the folder in which the DLC project will be created\n",
+ "deeplabcut.create_new_project(\n",
+ " project=\"bat_augmentation_austin_2020_bat_data\",\n",
+ " experimenter=\"DLC\",\n",
+ " videos=[\"/home/user/dummyVideos/\"],\n",
+ " working_directory=project_folder,\n",
+ ")"
]
},
{
@@ -76,11 +81,11 @@
},
"outputs": [],
"source": [
- "#define config file\n",
+ "# define config file\n",
"config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
"\n",
- "#import tools for modifying our config file\n",
- "from deeplabcut.utils.auxiliaryfunctions import read_config, edit_config\n"
+ "# import tools for modifying our config file\n",
+ "from deeplabcut.utils.auxiliaryfunctions import edit_config, read_config"
]
},
{
@@ -102,7 +107,29 @@
"outputs": [],
"source": [
"# replace the default list of bodyparts with a list of the parts that we have actually digitized\n",
- "edit_config(config_path,{\"bodyparts\":['t3L', 'wstL', 't5L', 'elbL', 'shdL', 'ankL', 'nl', 'str', 'lmb', 'shdR', 'ankR', 'elbR', 'wstR', 't5R', 't3R', 'tail']})"
+ "edit_config(\n",
+ " config_path,\n",
+ " {\n",
+ " \"bodyparts\": [\n",
+ " \"t3L\",\n",
+ " \"wstL\",\n",
+ " \"t5L\",\n",
+ " \"elbL\",\n",
+ " \"shdL\",\n",
+ " \"ankL\",\n",
+ " \"nl\",\n",
+ " \"str\",\n",
+ " \"lmb\",\n",
+ " \"shdR\",\n",
+ " \"ankR\",\n",
+ " \"elbR\",\n",
+ " \"wstR\",\n",
+ " \"t5R\",\n",
+ " \"t3R\",\n",
+ " \"tail\",\n",
+ " ]\n",
+ " },\n",
+ ")"
]
},
{
@@ -115,9 +142,9 @@
},
"outputs": [],
"source": [
- "#fetch the list of videos from an older project using the same videos\n",
+ "# fetch the list of videos from an older project using the same videos\n",
"videolist = read_config(\"/home/user/projects/old_project-DLC-2022-08-03/config.yaml\")[\"video_sets\"]\n",
- "edit_config(config_path,{'video_sets':videolist})"
+ "edit_config(config_path, {\"video_sets\": videolist})"
]
},
{
@@ -142,6 +169,7 @@
"source": [
"# Convert training data into the DeepLabCut format\n",
"import deeplabcut\n",
+ "\n",
"deeplabcut.convertcsv2h5(config_path, userfeedback=False)\n",
"\n",
"# Check labels (sanity check)\n",
@@ -163,7 +191,7 @@
"source": [
"## Structuring the project for testing\n",
"\n",
- "Since bats are such a challenging animal to automatcially digitize, in my lab, we've been relying on what I call \"refining\" to improve DLC accuracy. This just means that for each video that we want to analyze, we first digitize a few frames from it and include those frames as training data for the DeepLabCut network.\n",
+ "Since bats are such a challenging animal to automatically digitize, in my lab, we've been relying on what I call \"refining\" to improve DLC accuracy. This just means that for each video that we want to analyze, we first digitize a few frames from it and include those frames as training data for the DeepLabCut network.\n",
"\n",
"We typically digitize approximately one frame per wingbeat. For a wingbeat frequency of 15 and a framerate of 800, this means approximately every 50th frame. For a lower framerate, say 400, we instead need to digitize every 50th frame. This is obviously less labour than digitizing every frame, but the workflow still scales poorly with increased acquisition. Therefore, I want to reduce the required amount of manual digitization. The challenge I've set myself is therefore this - **to use augmentation to match or beat the accuracy of refining**.\n",
"\n",
@@ -252,10 +280,13 @@
"# We need pandas for creatig a nice list to parse\n",
"import pandas as pd\n",
"\n",
- "# Read the h5 file containing all the frames, (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
- "df = pd.read_hdf('/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5')\n",
+ "# Read the h5 file containing all the frames:\n",
+ "# (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
+ "df = pd.read_hdf(\n",
+ " \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5\"\n",
+ ")\n",
"\n",
- "image_paths = df.index.to_list() # turn dataframe into list\n",
+ "image_paths = df.index.to_list() # turn dataframe into list\n",
"\n",
"# create empty lists for putting testing and training indices in\n",
"test_inds = []\n",
@@ -276,7 +307,7 @@
" trainIndices=[train_inds],\n",
" testIndices=[test_inds],\n",
" net_type=\"resnet_50\",\n",
- " augmenter_type=\"../imagesaug\"\n",
+ " augmenter_type=\"../imagesaug\",\n",
")\n",
"\n",
"# train on half+ref, shuffle 2\n",
@@ -296,7 +327,7 @@
" trainIndices=[train_inds],\n",
" testIndices=[test_inds],\n",
" net_type=\"resnet_50\",\n",
- " augmenter_type=\"../imagesaug\"\n",
+ " augmenter_type=\"../imagesaug\",\n",
")\n",
"\n",
"# train on full, test data is OOD, shuffle 3\n",
@@ -316,7 +347,7 @@
" trainIndices=[train_inds],\n",
" testIndices=[test_inds],\n",
" net_type=\"resnet_50\",\n",
- " augmenter_type=\"../imagesaug\"\n",
+ " augmenter_type=\"../imagesaug\",\n",
")\n",
"\n",
"# train on full+ref, shuffle 4\n",
@@ -338,7 +369,7 @@
" trainIndices=[train_inds],\n",
" testIndices=[test_inds],\n",
" net_type=\"resnet_50\",\n",
- " augmenter_type=\"../imagesaug\"\n",
+ " augmenter_type=\"../imagesaug\",\n",
")"
]
},
@@ -383,8 +414,11 @@
"outputs": [],
"source": [
"import os\n",
- "files = os.listdir(\"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18\")\n",
- "print(*files,sep='\\n')"
+ "\n",
+ "files = os.listdir(\n",
+ " \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18\"\n",
+ ")\n",
+ "print(*files, sep=\"\\n\")"
]
},
{
@@ -426,46 +460,50 @@
"# sure deeplabcut is imported and the config_path defined\n",
"import deeplabcut\n",
"\n",
- "config_path = '/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml'\n",
+ "config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
"\n",
"# we also need the package os for folder manipulation\n",
"import os\n",
+ "\n",
"# and shutil for copying files\n",
"import shutil\n",
"\n",
- "#import tools for reading our config file\n",
+ "# import tools for reading our config file\n",
"from deeplabcut.utils.auxiliaryfunctions import read_config\n",
"\n",
"# Number and name for our model folder\n",
"model_number = 0\n",
- "modelprefix_pre = 'data_augm'\n",
- "daug_str = 'base'\n",
+ "modelprefix_pre = \"data_augm\"\n",
+ "daug_str = \"base\"\n",
"\n",
"# Get config as dict and associated paths\n",
"cfg = read_config(config_path)\n",
- "project_path = cfg[\"project_path\"] # or: os.path.dirname(config_path) #dlc_models_path = os.path.join(project_path, \"dlc-models\")\n",
+ "project_path = cfg[\n",
+ " \"project_path\"\n",
+ "] # or: os.path.dirname(config_path) #dlc_models_path = os.path.join(project_path, \"dlc-models\")\n",
"training_datasets_path = os.path.join(project_path, \"training-datasets\")\n",
"\n",
"# Define shuffles\n",
- "shuffles = [1,2,3,4]\n",
+ "shuffles = [1, 2, 3, 4]\n",
"trainingsetindices = [0, 1, 2, 3]\n",
"\n",
"# Get train and test pose config file paths from base project, for each shuffle\n",
"list_base_train_pose_config_file_paths = []\n",
"list_base_test_pose_config_file_paths = []\n",
- "for shuffle_number, trainingsetindex in zip(shuffles, trainingsetindices):\n",
- " base_train_pose_config_file_path_TEMP,\\\n",
- " base_test_pose_config_file_path_TEMP,\\\n",
- " _ = deeplabcut.return_train_network_path(config_path,\n",
- " shuffle=shuffle_number,\n",
- " trainingsetindex=trainingsetindex) # base_train_pose_config_file\n",
+ "for shuffle_number, trainingsetindex in zip(shuffles, trainingsetindices, strict=False):\n",
+ " base_train_pose_config_file_path_TEMP, base_test_pose_config_file_path_TEMP, _ = (\n",
+ " deeplabcut.return_train_network_path(config_path, shuffle=shuffle_number, trainingsetindex=trainingsetindex)\n",
+ " ) # base_train_pose_config_file\n",
" list_base_train_pose_config_file_paths.append(base_train_pose_config_file_path_TEMP)\n",
" list_base_test_pose_config_file_paths.append(base_test_pose_config_file_path_TEMP)\n",
"\n",
"# Create subdirs for this augmentation method\n",
- "model_prefix = '_'.join([modelprefix_pre, \"{0:0=2d}\".format(model_number), daug_str]) # modelprefix_pre = aug_\n",
+ "model_prefix = \"_\".join([modelprefix_pre, f\"{model_number:0=2d}\", daug_str]) # modelprefix_pre = aug_\n",
"aug_project_path = os.path.join(project_path, model_prefix)\n",
- "aug_dlc_models = os.path.join(aug_project_path, \"dlc-models\", )\n",
+ "aug_dlc_models = os.path.join(\n",
+ " aug_project_path,\n",
+ " \"dlc-models\",\n",
+ ")\n",
"\n",
"# make the folder for this modelprefix\n",
"try:\n",
@@ -475,25 +513,20 @@
" print(\"Skipping this one as it already exists\")\n",
"\n",
"# Copy base train pose config file to the directory of this augmentation method\n",
- "for j, (shuffle, trainingsetindex) in enumerate(zip(shuffles,trainingsetindices)):\n",
- " one_train_pose_config_file_path,\\\n",
- " one_test_pose_config_file_path,\\\n",
- " _ = deeplabcut.return_train_network_path(config_path,\n",
- " shuffle=shuffle,\n",
- " trainingsetindex=trainingsetindex,\n",
- " modelprefix=model_prefix)\n",
- " \n",
+ "for j, (shuffle, trainingsetindex) in enumerate(zip(shuffles, trainingsetindices, strict=False)):\n",
+ " one_train_pose_config_file_path, one_test_pose_config_file_path, _ = deeplabcut.return_train_network_path(\n",
+ " config_path, shuffle=shuffle, trainingsetindex=trainingsetindex, modelprefix=model_prefix\n",
+ " )\n",
+ "\n",
" # make train and test directories for this subdir\n",
- " os.makedirs(str(os.path.dirname(one_train_pose_config_file_path))) # create parentdir 'train'\n",
- " os.makedirs(str(os.path.dirname(one_test_pose_config_file_path))) # create parentdir 'test\n",
- " \n",
+ " os.makedirs(str(os.path.dirname(one_train_pose_config_file_path))) # create parentdir 'train'\n",
+ " os.makedirs(str(os.path.dirname(one_test_pose_config_file_path))) # create parentdir 'test\n",
+ "\n",
" # copy test and train config from base project to this subdir\n",
" # copy base train config file\n",
- " shutil.copyfile(list_base_train_pose_config_file_paths[j],\n",
- " one_train_pose_config_file_path) \n",
+ " shutil.copyfile(list_base_train_pose_config_file_paths[j], one_train_pose_config_file_path)\n",
" # copy base test config file\n",
- " shutil.copyfile(list_base_test_pose_config_file_paths[j],\n",
- " one_test_pose_config_file_path)\n"
+ " shutil.copyfile(list_base_test_pose_config_file_paths[j], one_test_pose_config_file_path)"
]
},
{
@@ -514,25 +547,27 @@
},
"outputs": [],
"source": [
- "\n",
- "model_prefix = 'data_augm_00_base'\n",
+ "model_prefix = \"data_augm_00_base\"\n",
"\n",
"## Initialise dict with additional edits to train config: optimizer\n",
"train_edits_dict = {}\n",
- "dict_optimizer = {'optimizer':'adam',\n",
- " 'batch_size': 8, # the gpu I'm using has plenty of memory so batch size 8 makes sense\n",
- " 'multi_step': [[1e-4, 7500], [5 * 1e-5, 12000], [1e-5, 150000]]} # if no yaml file passed, initialise as an empty dict\n",
- "train_edits_dict.update({'optimizer': dict_optimizer['optimizer'], #'adam',\n",
- " 'batch_size': dict_optimizer['batch_size'],\n",
- " 'multi_step': dict_optimizer['multi_step']})\n",
- "\n",
- "for shuffle, trainingsetindex in zip(shuffles,trainingsetindices):\n",
- " one_train_pose_config_file_path,\\\n",
- " _,\\\n",
- " _ = deeplabcut.return_train_network_path(config_path,\n",
- " shuffle=shuffle,\n",
- " trainingsetindex=trainingsetindex,\n",
- " modelprefix=model_prefix)\n",
+ "dict_optimizer = {\n",
+ " \"optimizer\": \"adam\",\n",
+ " \"batch_size\": 8, # the gpu I'm using has plenty of memory so batch size 8 makes sense\n",
+ " \"multi_step\": [[1e-4, 7500], [5 * 1e-5, 12000], [1e-5, 150000]],\n",
+ "} # if no yaml file passed, initialise as an empty dict\n",
+ "train_edits_dict.update(\n",
+ " {\n",
+ " \"optimizer\": dict_optimizer[\"optimizer\"], #'adam',\n",
+ " \"batch_size\": dict_optimizer[\"batch_size\"],\n",
+ " \"multi_step\": dict_optimizer[\"multi_step\"],\n",
+ " }\n",
+ ")\n",
+ "\n",
+ "for shuffle, trainingsetindex in zip(shuffles, trainingsetindices, strict=False):\n",
+ " one_train_pose_config_file_path, _, _ = deeplabcut.return_train_network_path(\n",
+ " config_path, shuffle=shuffle, trainingsetindex=trainingsetindex, modelprefix=model_prefix\n",
+ " )\n",
"\n",
" edit_config(str(one_train_pose_config_file_path), train_edits_dict)"
]
@@ -556,27 +591,28 @@
"outputs": [],
"source": [
"import deeplabcut\n",
+ "\n",
"# define config path and model prefix\n",
- "config_path='/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml'\n",
- "model_prefix = 'data_augm_00_base'\n",
+ "config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
+ "model_prefix = \"data_augm_00_base\"\n",
"\n",
"# the computer I'm working on has several gpus, here I used the third one.\n",
- "gputouse=3\n",
+ "gputouse = 3\n",
"\n",
"# define shuffles and trainingsetindices\n",
- "shuffles = [1,2,3,4]\n",
- "trainingsetindices = [0,1,2,3]\n",
+ "shuffles = [1, 2, 3, 4]\n",
+ "trainingsetindices = [0, 1, 2, 3]\n",
"\n",
"# loop over shuffles and train each\n",
- "for shuffle, trainingsetindex in zip(shuffles, trainingsetindices):\n",
+ "for shuffle, trainingsetindex in zip(shuffles, trainingsetindices, strict=False):\n",
" deeplabcut.train_network(\n",
" config_path,\n",
" shuffle=shuffle,\n",
" modelprefix=model_prefix,\n",
" gputouse=gputouse,\n",
" trainingsetindex=trainingsetindex,\n",
- " max_snapshots_to_keep=3, # training for 150000 iterations so let's save 50, 100, and 150.\n",
- " saveiters=50000\n",
+ " max_snapshots_to_keep=3, # training for 150000 iterations so let's save 50, 100, and 150.\n",
+ " saveiters=50000,\n",
" )"
]
},
@@ -585,7 +621,7 @@
"metadata": {},
"source": [
"# Perform evaluation\n",
- "After training the networks, we need to test how they perform. To do that, we first need to evaluate them using the buil-in DeepLabCut method ```evaluate_network```. By default this will evaluate the last snapshot, in our case, this means that the network will be evaluated after 150k iterations, but we want to know how well it does at 50k and 100k too, so let's edit our config file to test all saved snapshot.\n",
+ "After training the networks, we need to test how they perform. To do that, we first need to evaluate them using the built-in DeepLabCut method ```evaluate_network```. By default this will evaluate the last snapshot, in our case, this means that the network will be evaluated after 150k iterations, but we want to know how well it does at 50k and 100k too, so let's edit our config file to test all saved snapshot.\n",
"\n",
"\n"
]
@@ -604,11 +640,11 @@
"# sure deeplabcut is imported and the config_path defined\n",
"import deeplabcut\n",
"\n",
- "config_path = '/home/juser/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml'\n",
+ "config_path = \"/home/juser/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
"\n",
- "from deeplabcut.utils.auxiliaryfunctions import read_config, edit_config\n",
+ "from deeplabcut.utils.auxiliaryfunctions import edit_config, read_config\n",
"\n",
- "edit_config(config_path,{'snapshotindex':'all'})"
+ "edit_config(config_path, {\"snapshotindex\": \"all\"})"
]
},
{
@@ -629,13 +665,16 @@
"outputs": [],
"source": [
"import deeplabcut\n",
+ "\n",
"config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
- "model_prefix = 'data_augm_00_base'\n",
- "Shuffles = [1,2,3,4]\n",
- "trainingsetindices = [0,1,2,3]\n",
+ "model_prefix = \"data_augm_00_base\"\n",
+ "Shuffles = [1, 2, 3, 4]\n",
+ "trainingsetindices = [0, 1, 2, 3]\n",
"\n",
- "for shuffle, trainingsetindex in zip(Shuffles,trainingsetindices):\n",
- " deeplabcut.evaluate_network(config_path, modelprefix = model_prefix, Shuffles = [shuffle], trainingsetindex=trainingsetindex)"
+ "for shuffle, trainingsetindex in zip(Shuffles, trainingsetindices, strict=False):\n",
+ " deeplabcut.evaluate_network(\n",
+ " config_path, modelprefix=model_prefix, Shuffles=[shuffle], trainingsetindex=trainingsetindex\n",
+ " )"
]
},
{
@@ -663,22 +702,26 @@
"import deeplabcut\n",
"\n",
"config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
- "model_prefix = 'data_augm_00_base'\n",
- "Shuffles = [1,2,3,4]\n",
- "trainingsetindices = [0,1,2,3]\n",
+ "model_prefix = \"data_augm_00_base\"\n",
+ "Shuffles = [1, 2, 3, 4]\n",
+ "trainingsetindices = [0, 1, 2, 3]\n",
"\n",
"# We need pandas for creatig a nice list to parse\n",
+ "import sys\n",
+ "\n",
"import pandas as pd\n",
"\n",
- "import sys\n",
- "sys.path.append('..') #my python file for this function is stored in the parent folder as I'm running this\n",
- "from getErrorDistribution import getErrorDistribution #import the getErrorDistribution function\n",
+ "sys.path.append(\"..\") # my python file for this function is stored in the parent folder as I'm running this\n",
"import numpy as np\n",
+ "from getErrorDistribution import getErrorDistribution # import the getErrorDistribution function\n",
"\n",
- "# Read the h5 file containing all the frames, (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
- "df = pd.read_hdf('/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5')\n",
+ "# Read the h5 file containing all the frames:\n",
+ "# (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
+ "df = pd.read_hdf(\n",
+ " \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5\"\n",
+ ")\n",
"\n",
- "image_paths = df.index.to_list() # turn dataframe into list\n",
+ "image_paths = df.index.to_list() # turn dataframe into list\n",
"\n",
"# get test indices\n",
"test_inds = []\n",
@@ -689,30 +732,25 @@
"error_distributions = []\n",
"error_distributions_pcut = []\n",
"\n",
- "for shuffle, trainFractionIndex in zip(Shuffles,trainingsetindices):\n",
- " error_distributions_temp = []\n",
- " error_distributions_pcut_temp = []\n",
- " for snapshot in [0,1,2]: #we saved three snapshots, one at 50k iteratinos, one at 100k, and one at 150k\n",
- " (\n",
- " ErrorDistribution_all,\n",
- " ErrorDistribution_test,\n",
- " ErrorDistribution_train,\n",
- " ErrorDistributionPCutOff_all,\n",
- " _,\n",
- " _\n",
- " ) = getErrorDistribution(\n",
- " config_path,\n",
- " shuffle=shuffle,\n",
- " snapindex=snapshot,\n",
- " trainFractionIndex = trainFractionIndex,\n",
- " modelprefix = model_prefix\n",
- " )\n",
- " error_distributions_temp.append(ErrorDistribution_all.iloc[test_inds].values.flatten())\n",
- " error_distributions_pcut_temp.append(ErrorDistributionPCutOff_all.iloc[test_inds].values.flatten())\n",
- " error_distributions.append(error_distributions_temp)\n",
- " error_distributions_pcut.append(error_distributions_pcut_temp)\n",
- "error_distributionsb = np.array(error_distributions) # array with dimensions [shuffle, snapshot, frames]\n",
- "error_distributions_pcut = np.array(error_distributions_pcut) # array with dimensions [shuffle, snapshot, frames]"
+ "for shuffle, trainFractionIndex in zip(Shuffles, trainingsetindices, strict=False):\n",
+ " error_distributions_temp = []\n",
+ " error_distributions_pcut_temp = []\n",
+ " for snapshot in [0, 1, 2]: # we saved three snapshots, one at 50k iteratinos, one at 100k, and one at 150k\n",
+ " (ErrorDistribution_all, ErrorDistribution_test, ErrorDistribution_train, ErrorDistributionPCutOff_all, _, _) = (\n",
+ " getErrorDistribution(\n",
+ " config_path,\n",
+ " shuffle=shuffle,\n",
+ " snapindex=snapshot,\n",
+ " trainFractionIndex=trainFractionIndex,\n",
+ " modelprefix=model_prefix,\n",
+ " )\n",
+ " )\n",
+ " error_distributions_temp.append(ErrorDistribution_all.iloc[test_inds].values.flatten())\n",
+ " error_distributions_pcut_temp.append(ErrorDistributionPCutOff_all.iloc[test_inds].values.flatten())\n",
+ " error_distributions.append(error_distributions_temp)\n",
+ " error_distributions_pcut.append(error_distributions_pcut_temp)\n",
+ "error_distributionsb = np.array(error_distributions) # array with dimensions [shuffle, snapshot, frames]\n",
+ "error_distributions_pcut = np.array(error_distributions_pcut) # array with dimensions [shuffle, snapshot, frames]"
]
},
{
@@ -723,32 +761,39 @@
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
- "fig, (ax1,ax2) = plt.subplots(1,2)\n",
+ "fig, (ax1, ax2) = plt.subplots(1, 2)\n",
"fig.set_figheight(8)\n",
"fig.set_figwidth(10)\n",
"\n",
- "for shuffle in [0,1,2,3]: #we start counting at 0, so for now, let's consider each index one less\n",
- " ax1.errorbar(np.array([50,100,150])-1.5+shuffle,np.nanmean(error_distributions[shuffle,:],axis=1), np.nanstd(error_distributions[shuffle,:],axis=1)/len(test_inds)**.5)\n",
- " ax2.errorbar(np.array([50,100,150])-1.5+shuffle,np.nanmean(error_distributions_pcut[shuffle,:],axis=1), np.nanstd(error_distributions_pcut[shuffle,:],axis=1)/len(test_inds)**.5)\n",
+ "for shuffle in [0, 1, 2, 3]: # we start counting at 0, so for now, let's consider each index one less\n",
+ " ax1.errorbar(\n",
+ " np.array([50, 100, 150]) - 1.5 + shuffle,\n",
+ " np.nanmean(error_distributions[shuffle, :], axis=1),\n",
+ " np.nanstd(error_distributions[shuffle, :], axis=1) / len(test_inds) ** 0.5,\n",
+ " )\n",
+ " ax2.errorbar(\n",
+ " np.array([50, 100, 150]) - 1.5 + shuffle,\n",
+ " np.nanmean(error_distributions_pcut[shuffle, :], axis=1),\n",
+ " np.nanstd(error_distributions_pcut[shuffle, :], axis=1) / len(test_inds) ** 0.5,\n",
+ " )\n",
"\n",
"ax1.set_xticks([50, 100, 150])\n",
"ax2.set_xticks([50, 100, 150])\n",
- "ax1.set_xlim([25,175])\n",
- "ax2.set_xlim([25,175])\n",
- "ax1.set_ylim([0,23])\n",
- "ax2.set_ylim([0,23])\n",
+ "ax1.set_xlim([25, 175])\n",
+ "ax2.set_xlim([25, 175])\n",
+ "ax1.set_ylim([0, 23])\n",
+ "ax2.set_ylim([0, 23])\n",
"ax1.set_title(\"Without P-cut\")\n",
"ax2.set_title(\"With P-cut 0.6\")\n",
"ax2.set_yticklabels([])\n",
- "ax2.legend([\"half, OOD\",\"half, Ref\",\"full, OOD\", \"full, Ref\"])\n",
+ "ax2.legend([\"half, OOD\", \"half, Ref\", \"full, OOD\", \"full, Ref\"])\n",
"\n",
"# add a big axis, hide frame\n",
"fig.add_subplot(111, frameon=False)\n",
"## hide tick and tick label of the big axis\n",
- "plt.tick_params(labelcolor='none', which='both', top=False, bottom=False, left=False, right=False)\n",
+ "plt.tick_params(labelcolor=\"none\", which=\"both\", top=False, bottom=False, left=False, right=False)\n",
"plt.xlabel(\"Iterations (thousands)\")\n",
- "plt.ylabel(\"Error (px)\")\n",
- "\n"
+ "plt.ylabel(\"Error (px)\")"
]
},
{
@@ -780,13 +825,16 @@
"import numpy as np\n",
"from scipy.stats import wilcoxon\n",
"\n",
- "p_value = np.empty((4,4))\n",
- "p_value[:]=np.NaN\n",
+ "p_value = np.empty((4, 4))\n",
+ "p_value[:] = np.NaN\n",
"\n",
- "for i in [0,1,2,3]:\n",
- " for j in [0,1,2,3]:\n",
- " if j<=i: continue\n",
- " _, p_value[i,j] = wilcoxon(x = error_distributions_pcut[i,-1,:], y = error_distributions_pcut[j,-1,:],nan_policy='omit')\n",
+ "for i in [0, 1, 2, 3]:\n",
+ " for j in [0, 1, 2, 3]:\n",
+ " if j <= i:\n",
+ " continue\n",
+ " _, p_value[i, j] = wilcoxon(\n",
+ " x=error_distributions_pcut[i, -1, :], y=error_distributions_pcut[j, -1, :], nan_policy=\"omit\"\n",
+ " )\n",
"\n",
"print(p_value)"
]
@@ -812,22 +860,26 @@
"import deeplabcut\n",
"\n",
"config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
- "model_prefix = 'data_augm_00_base'\n",
- "Shuffles = [1,2,3,4]\n",
- "trainingsetindices = [0,1,2,3]\n",
+ "model_prefix = \"data_augm_00_base\"\n",
+ "Shuffles = [1, 2, 3, 4]\n",
+ "trainingsetindices = [0, 1, 2, 3]\n",
"\n",
"# We need pandas for creatig a nice list to parse\n",
+ "import sys\n",
+ "\n",
"import pandas as pd\n",
"\n",
- "import sys\n",
- "sys.path.append('..') #my python file for this function is stored in the parent folder as I'm running this\n",
- "from getErrorDistribution import getErrorDistribution #import the getErrorDistribution function\n",
+ "sys.path.append(\"..\") # my python file for this function is stored in the parent folder as I'm running this\n",
"import numpy as np\n",
+ "from getErrorDistribution import getErrorDistribution # import the getErrorDistribution function\n",
"\n",
- "# Read the h5 file containing all the frames, (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
- "df = pd.read_hdf('/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5')\n",
+ "# Read the h5 file containing all the frames:\n",
+ "# (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
+ "df = pd.read_hdf(\n",
+ " \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5\"\n",
+ ")\n",
"\n",
- "image_paths = df.index.to_list() # turn dataframe into list\n",
+ "image_paths = df.index.to_list() # turn dataframe into list\n",
"\n",
"# get test indices\n",
"test_inds = []\n",
@@ -838,38 +890,41 @@
"# this gives us the paths of our 27 test videos\n",
"test_paths = list(set([image_paths[i][1] for i in test_inds]))\n",
"\n",
- "#%% sorted so that the corresponding videos have the same index in three lists (one per camera)\n",
- "test_paths_cam1 = ['TS5-544-Cam1_2020-06-25_000099Track8_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000103Track3_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000104Track3_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000108Track6_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000123Track6_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000128Track2_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000134Track5_50_test'\n",
- " ]\n",
- "test_paths_cam2 = ['IL5-519-Cam2_2020-06-25_000099Track6_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000103Track3_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000104Track2_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000109Track1_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000124Track9_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000130Track2_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000136Track10_50_test'\n",
- " ]\n",
- "test_paths_cam3 = ['IL5-534-Cam3_2020-06-25_000095Track14_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000100Track4_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000101Track4_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000106Track3_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000122Track7_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000127Track4_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000133Track9_50_test'\n",
- " ]\n",
- "\n",
- "nvideos = 7 # number of videos\n",
+ "# %% sorted so that the corresponding videos have the same index in three lists (one per camera)\n",
+ "test_paths_cam1 = [\n",
+ " \"TS5-544-Cam1_2020-06-25_000099Track8_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000103Track3_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000104Track3_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000108Track6_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000123Track6_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000128Track2_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000134Track5_50_test\",\n",
+ "]\n",
+ "test_paths_cam2 = [\n",
+ " \"IL5-519-Cam2_2020-06-25_000099Track6_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000103Track3_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000104Track2_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000109Track1_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000124Track9_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000130Track2_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000136Track10_50_test\",\n",
+ "]\n",
+ "test_paths_cam3 = [\n",
+ " \"IL5-534-Cam3_2020-06-25_000095Track14_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000100Track4_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000101Track4_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000106Track3_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000122Track7_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000127Track4_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000133Track9_50_test\",\n",
+ "]\n",
+ "\n",
+ "nvideos = 7 # number of videos\n",
"\n",
"# get test frame indexes per camera\n",
- "test_inds_cam1 = [[],[],[],[],[],[],[]]\n",
- "test_inds_cam2 = [[],[],[],[],[],[],[]]\n",
- "test_inds_cam3 = [[],[],[],[],[],[],[]]\n",
+ "test_inds_cam1 = [[], [], [], [], [], [], []]\n",
+ "test_inds_cam2 = [[], [], [], [], [], [], []]\n",
+ "test_inds_cam3 = [[], [], [], [], [], [], []]\n",
"\n",
"for i, path in enumerate(image_paths):\n",
" for j in range(nvideos):\n",
@@ -882,67 +937,91 @@
"\n",
"nshuffles = len(Shuffles)\n",
"\n",
- "#pre-allocate matrixes for mean values and standard errors\n",
- "mean_cam1 = np.zeros([nshuffles,nvideos]) # shuffle x movie\n",
- "mean_cam2 = np.zeros([nshuffles,nvideos])\n",
- "mean_cam3 = np.zeros([nshuffles,nvideos])\n",
+ "# pre-allocate matrixes for mean values and standard errors\n",
+ "mean_cam1 = np.zeros([nshuffles, nvideos]) # shuffle x movie\n",
+ "mean_cam2 = np.zeros([nshuffles, nvideos])\n",
+ "mean_cam3 = np.zeros([nshuffles, nvideos])\n",
"\n",
- "ste_cam1 = np.zeros([nshuffles,nvideos])\n",
- "ste_cam2 = np.zeros([nshuffles,nvideos])\n",
- "ste_cam3 = np.zeros([nshuffles,nvideos])\n",
+ "ste_cam1 = np.zeros([nshuffles, nvideos])\n",
+ "ste_cam2 = np.zeros([nshuffles, nvideos])\n",
+ "ste_cam3 = np.zeros([nshuffles, nvideos])\n",
"\n",
- "meanPcut_cam1 = np.zeros([nshuffles,nvideos]) # shuffle x movie\n",
- "meanPcut_cam2 = np.zeros([nshuffles,nvideos])\n",
- "meanPcut_cam3 = np.zeros([nshuffles,nvideos])\n",
+ "meanPcut_cam1 = np.zeros([nshuffles, nvideos]) # shuffle x movie\n",
+ "meanPcut_cam2 = np.zeros([nshuffles, nvideos])\n",
+ "meanPcut_cam3 = np.zeros([nshuffles, nvideos])\n",
"\n",
- "stePcut_cam1 = np.zeros([nshuffles,nvideos])\n",
- "stePcut_cam2 = np.zeros([nshuffles,nvideos])\n",
- "stePcut_cam3 = np.zeros([nshuffles,nvideos])\n",
+ "stePcut_cam1 = np.zeros([nshuffles, nvideos])\n",
+ "stePcut_cam2 = np.zeros([nshuffles, nvideos])\n",
+ "stePcut_cam3 = np.zeros([nshuffles, nvideos])\n",
"\n",
"# %%\n",
"\n",
- "for i, shuffle in enumerate(Shuffles): \n",
+ "for i, shuffle in enumerate(Shuffles):\n",
" trainFractionIndex = i\n",
- " snapshot=-1\n",
- " (\n",
- " ErrorDistribution_all,\n",
- " _,\n",
- " _,\n",
- " ErrorDistributionPCutOff_all,\n",
- " _,\n",
- " _\n",
- " ) = getErrorDistribution(\n",
+ " snapshot = -1\n",
+ " (ErrorDistribution_all, _, _, ErrorDistributionPCutOff_all, _, _) = getErrorDistribution(\n",
" config_path,\n",
" shuffle=shuffle,\n",
" snapindex=snapshot,\n",
- " trainFractionIndex = trainFractionIndex,\n",
- " modelprefix = model_prefix\n",
+ " trainFractionIndex=trainFractionIndex,\n",
+ " modelprefix=model_prefix,\n",
" )\n",
" for movie_number in range(7):\n",
- "\n",
- " meanPcut_cam1[i,movie_number] = np.nanmean(ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:])\n",
- " stePcut_cam1[i,movie_number] = np.nanstd(ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:])/(ErrorDistribution_all.values[test_inds_cam1[movie_number]][:].size**.5)\n",
- "\n",
- " meanPcut_cam2[i,movie_number] = np.nanmean(ErrorDistributionPCutOff_all.values[test_inds_cam2[movie_number]][:])\n",
- " stePcut_cam2[i,movie_number] = np.nanstd(ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:])/(ErrorDistribution_all.values[test_inds_cam2[movie_number]][:].size**.5)\n",
- "\n",
- " meanPcut_cam3[i,movie_number] = np.nanmean(ErrorDistributionPCutOff_all.values[test_inds_cam3[movie_number]][:])\n",
- " stePcut_cam3[i,movie_number] = np.nanstd(ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:])/(ErrorDistribution_all.values[test_inds_cam3[movie_number]][:].size**.5)\n",
- "\n",
- "fig, (ax1,ax2,ax3) = plt.subplots(3,1)\n",
+ " meanPcut_cam1[i, movie_number] = np.nanmean(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:]\n",
+ " )\n",
+ " stePcut_cam1[i, movie_number] = np.nanstd(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:]\n",
+ " ) / (ErrorDistribution_all.values[test_inds_cam1[movie_number]][:].size ** 0.5)\n",
+ "\n",
+ " meanPcut_cam2[i, movie_number] = np.nanmean(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam2[movie_number]][:]\n",
+ " )\n",
+ " stePcut_cam2[i, movie_number] = np.nanstd(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:]\n",
+ " ) / (ErrorDistribution_all.values[test_inds_cam2[movie_number]][:].size ** 0.5)\n",
+ "\n",
+ " meanPcut_cam3[i, movie_number] = np.nanmean(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam3[movie_number]][:]\n",
+ " )\n",
+ " stePcut_cam3[i, movie_number] = np.nanstd(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:]\n",
+ " ) / (ErrorDistribution_all.values[test_inds_cam3[movie_number]][:].size ** 0.5)\n",
+ "\n",
+ "fig, (ax1, ax2, ax3) = plt.subplots(3, 1)\n",
"fig.set_figheight(15)\n",
"fig.set_figwidth(10)\n",
"for i, shuffle in enumerate(Shuffles):\n",
- " \n",
" # to jitter the error bars to keep them from overlapping\n",
- " movie_number = list(range(1,8))\n",
- " movie_number = [x - 2/50 + shuffle/50 for x in movie_number]\n",
- " \n",
- " ax1.errorbar(movie_number,meanPcut_cam1[i,:], stePcut_cam1[i,:,])\n",
+ " movie_number = list(range(1, 8))\n",
+ " movie_number = [x - 2 / 50 + shuffle / 50 for x in movie_number]\n",
+ "\n",
+ " ax1.errorbar(\n",
+ " movie_number,\n",
+ " meanPcut_cam1[i, :],\n",
+ " stePcut_cam1[\n",
+ " i,\n",
+ " :,\n",
+ " ],\n",
+ " )\n",
"\n",
- " ax2.errorbar(movie_number,meanPcut_cam2[i,:], stePcut_cam2[i,:,])\n",
+ " ax2.errorbar(\n",
+ " movie_number,\n",
+ " meanPcut_cam2[i, :],\n",
+ " stePcut_cam2[\n",
+ " i,\n",
+ " :,\n",
+ " ],\n",
+ " )\n",
"\n",
- " ax3.errorbar(movie_number,meanPcut_cam3[i,:], stePcut_cam3[i,:,])\n",
+ " ax3.errorbar(\n",
+ " movie_number,\n",
+ " meanPcut_cam3[i, :],\n",
+ " stePcut_cam3[\n",
+ " i,\n",
+ " :,\n",
+ " ],\n",
+ " )\n",
"\n",
"ax1.set_ylim([0, 50])\n",
"ax2.set_ylim([0, 50])\n",
@@ -953,7 +1032,7 @@
"ax1.set_ylabel(\"Error (px\")\n",
"ax2.set_ylabel(\"Error (px\")\n",
"ax3.set_ylabel(\"Error (px\")\n",
- "ax1.legend([\"half, OOD\",\"half, Ref\",\"full, OOD\", \"full, Ref\"])"
+ "ax1.legend([\"half, OOD\", \"half, Ref\", \"full, OOD\", \"full, Ref\"])"
]
},
{
@@ -989,18 +1068,20 @@
"# sure deeplabcut is imported and the config_path defined\n",
"import deeplabcut\n",
"\n",
- "config_path = '/home/jusers/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml'\n",
- "model_prefix = 'data_augm_00_base'\n",
+ "config_path = \"/home/jusers/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
+ "model_prefix = \"data_augm_00_base\"\n",
"\n",
"# we only want to plot the last snapshot (150k iterations)\n",
- "from deeplabcut.utils.auxiliaryfunctions import read_config, edit_config\n",
+ "from deeplabcut.utils.auxiliaryfunctions import edit_config, read_config\n",
"\n",
- "edit_config(config_path,{'snapshotindex':-1})\n",
+ "edit_config(config_path, {\"snapshotindex\": -1})\n",
"\n",
"shuffle = 3\n",
"trainingsetindex = 2\n",
"\n",
- "deeplabcut.evaluate_network(config_path, modelprefix = model_prefix, Shuffles = [shuffle], trainingsetindex=trainingsetindex, plotting=True)"
+ "deeplabcut.evaluate_network(\n",
+ " config_path, modelprefix=model_prefix, Shuffles=[shuffle], trainingsetindex=trainingsetindex, plotting=True\n",
+ ")"
]
},
{
@@ -1048,46 +1129,50 @@
"# sure deeplabcut is imported and the config_path defined\n",
"import deeplabcut\n",
"\n",
- "config_path = '/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml'\n",
+ "config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
"\n",
"# we also need the package os for folder manipulation\n",
"import os\n",
+ "\n",
"# and shutil for copying files\n",
"import shutil\n",
"\n",
- "#import tools for reading our config file\n",
+ "# import tools for reading our config file\n",
"from deeplabcut.utils.auxiliaryfunctions import read_config\n",
"\n",
"# Number and name for our model folder\n",
- "model_number = 1 # CHANGE\n",
- "modelprefix_pre = 'data_augm'\n",
- "daug_str = 'fliplr' # CHANGE\n",
+ "model_number = 1 # CHANGE\n",
+ "modelprefix_pre = \"data_augm\"\n",
+ "daug_str = \"fliplr\" # CHANGE\n",
"\n",
"# Get config as dict and associated paths\n",
"cfg = read_config(config_path)\n",
- "project_path = cfg[\"project_path\"] # or: os.path.dirname(config_path) #dlc_models_path = os.path.join(project_path, \"dlc-models\")\n",
+ "project_path = cfg[\n",
+ " \"project_path\"\n",
+ "] # or: os.path.dirname(config_path) #dlc_models_path = os.path.join(project_path, \"dlc-models\")\n",
"training_datasets_path = os.path.join(project_path, \"training-datasets\")\n",
"\n",
"# Define shuffles\n",
- "shuffles = [1,2,3,4]\n",
+ "shuffles = [1, 2, 3, 4]\n",
"trainingsetindices = [0, 1, 2, 3]\n",
"\n",
"# Get train and test pose config file paths from base project, for each shuffle\n",
"list_base_train_pose_config_file_paths = []\n",
"list_base_test_pose_config_file_paths = []\n",
- "for shuffle_number, trainingsetindex in zip(shuffles, trainingsetindices):\n",
- " base_train_pose_config_file_path_TEMP,\\\n",
- " base_test_pose_config_file_path_TEMP,\\\n",
- " _ = deeplabcut.return_train_network_path(config_path,\n",
- " shuffle=shuffle_number,\n",
- " trainingsetindex=trainingsetindex) # base_train_pose_config_file\n",
+ "for shuffle_number, trainingsetindex in zip(shuffles, trainingsetindices, strict=False):\n",
+ " base_train_pose_config_file_path_TEMP, base_test_pose_config_file_path_TEMP, _ = (\n",
+ " deeplabcut.return_train_network_path(config_path, shuffle=shuffle_number, trainingsetindex=trainingsetindex)\n",
+ " ) # base_train_pose_config_file\n",
" list_base_train_pose_config_file_paths.append(base_train_pose_config_file_path_TEMP)\n",
" list_base_test_pose_config_file_paths.append(base_test_pose_config_file_path_TEMP)\n",
"\n",
"# Create subdirs for this augmentation method\n",
- "model_prefix = '_'.join([modelprefix_pre, \"{0:0=2d}\".format(model_number), daug_str]) # modelprefix_pre = aug_\n",
+ "model_prefix = \"_\".join([modelprefix_pre, f\"{model_number:0=2d}\", daug_str]) # modelprefix_pre = aug_\n",
"aug_project_path = os.path.join(project_path, model_prefix)\n",
- "aug_dlc_models = os.path.join(aug_project_path, \"dlc-models\", )\n",
+ "aug_dlc_models = os.path.join(\n",
+ " aug_project_path,\n",
+ " \"dlc-models\",\n",
+ ")\n",
"\n",
"# make the folder for this modelprefix\n",
"try:\n",
@@ -1097,25 +1182,20 @@
" print(\"Skipping this one as it already exists\")\n",
"\n",
"# Copy base train pose config file to the directory of this augmentation method\n",
- "for j, (shuffle, trainingsetindex) in enumerate(zip(shuffles,trainingsetindices)):\n",
- " one_train_pose_config_file_path,\\\n",
- " one_test_pose_config_file_path,\\\n",
- " _ = deeplabcut.return_train_network_path(config_path,\n",
- " shuffle=shuffle,\n",
- " trainingsetindex=trainingsetindex,\n",
- " modelprefix=model_prefix)\n",
- " \n",
+ "for j, (shuffle, trainingsetindex) in enumerate(zip(shuffles, trainingsetindices, strict=False)):\n",
+ " one_train_pose_config_file_path, one_test_pose_config_file_path, _ = deeplabcut.return_train_network_path(\n",
+ " config_path, shuffle=shuffle, trainingsetindex=trainingsetindex, modelprefix=model_prefix\n",
+ " )\n",
+ "\n",
" # make train and test directories for this subdir\n",
- " os.makedirs(str(os.path.dirname(one_train_pose_config_file_path))) # create parentdir 'train'\n",
- " os.makedirs(str(os.path.dirname(one_test_pose_config_file_path))) # create parentdir 'test\n",
- " \n",
+ " os.makedirs(str(os.path.dirname(one_train_pose_config_file_path))) # create parentdir 'train'\n",
+ " os.makedirs(str(os.path.dirname(one_test_pose_config_file_path))) # create parentdir 'test\n",
+ "\n",
" # copy test and train config from base project to this subdir\n",
" # copy base train config file\n",
- " shutil.copyfile(list_base_train_pose_config_file_paths[j],\n",
- " one_train_pose_config_file_path) \n",
+ " shutil.copyfile(list_base_train_pose_config_file_paths[j], one_train_pose_config_file_path)\n",
" # copy base test config file\n",
- " shutil.copyfile(list_base_test_pose_config_file_paths[j],\n",
- " one_test_pose_config_file_path)"
+ " shutil.copyfile(list_base_test_pose_config_file_paths[j], one_test_pose_config_file_path)"
]
},
{
@@ -1168,32 +1248,35 @@
},
"outputs": [],
"source": [
- "#import tools for changing our config file\n",
+ "# import tools for changing our config file\n",
"from deeplabcut.utils.auxiliaryfunctions import edit_config\n",
"\n",
- "model_prefix = 'data_augm_01_fliplr'\n",
+ "model_prefix = \"data_augm_01_fliplr\"\n",
"\n",
"## Initialise dict with additional edits to train config: optimizer\n",
"train_edits_dict = {}\n",
- "dict_optimizer = {'optimizer':'adam',\n",
- " 'batch_size': 8, # the gpu I'm using has plenty of memory so batch size 8 makes sense\n",
- " 'multi_step': [[1e-4, 7500], [5 * 1e-5, 12000], [1e-5, 150000]]} # if no yaml file passed, initialise as an empty dict\n",
- "train_edits_dict.update({'optimizer': dict_optimizer['optimizer'], #'adam',\n",
- " 'batch_size': dict_optimizer['batch_size'],\n",
- " 'multi_step': dict_optimizer['multi_step']})\n",
+ "dict_optimizer = {\n",
+ " \"optimizer\": \"adam\",\n",
+ " \"batch_size\": 8, # the gpu I'm using has plenty of memory so batch size 8 makes sense\n",
+ " \"multi_step\": [[1e-4, 7500], [5 * 1e-5, 12000], [1e-5, 150000]],\n",
+ "} # if no yaml file passed, initialise as an empty dict\n",
+ "train_edits_dict.update(\n",
+ " {\n",
+ " \"optimizer\": dict_optimizer[\"optimizer\"], #'adam',\n",
+ " \"batch_size\": dict_optimizer[\"batch_size\"],\n",
+ " \"multi_step\": dict_optimizer[\"multi_step\"],\n",
+ " }\n",
+ ")\n",
"\n",
"# Augmentation edits\n",
"edits_dict = dict()\n",
"edits_dict[\"symmetric_pairs\"] = (0, 14), (1, 12), (2, 13), (3, 11), (4, 9), (5, 10)\n",
"edits_dict[\"fliplr\"] = True\n",
"\n",
- "for shuffle, trainingsetindex in zip(shuffles,trainingsetindices):\n",
- " one_train_pose_config_file_path,\\\n",
- " _,\\\n",
- " _ = deeplabcut.return_train_network_path(config_path,\n",
- " shuffle=shuffle,\n",
- " trainingsetindex=trainingsetindex,\n",
- " modelprefix=model_prefix)\n",
+ "for shuffle, trainingsetindex in zip(shuffles, trainingsetindices, strict=False):\n",
+ " one_train_pose_config_file_path, _, _ = deeplabcut.return_train_network_path(\n",
+ " config_path, shuffle=shuffle, trainingsetindex=trainingsetindex, modelprefix=model_prefix\n",
+ " )\n",
"\n",
" edit_config(str(one_train_pose_config_file_path), edits_dict)\n",
" edit_config(str(one_train_pose_config_file_path), train_edits_dict)"
@@ -1218,27 +1301,28 @@
"outputs": [],
"source": [
"import deeplabcut\n",
+ "\n",
"# define config path and model prefix\n",
- "config_path='/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml'\n",
- "model_prefix = 'data_augm_01_flipr'\n",
+ "config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
+ "model_prefix = \"data_augm_01_flipr\"\n",
"\n",
"# the computer I'm working on has several gpus, here I used the third one.\n",
- "gputouse=3\n",
+ "gputouse = 3\n",
"\n",
"# define shuffles and trainingsetindices\n",
- "shuffles = [1,2,3,4]\n",
- "trainingsetindices = [0,1,2,3]\n",
+ "shuffles = [1, 2, 3, 4]\n",
+ "trainingsetindices = [0, 1, 2, 3]\n",
"\n",
"# loop over shuffles and train each\n",
- "for shuffle, trainingsetindex in zip(shuffles, trainingsetindices):\n",
+ "for shuffle, trainingsetindex in zip(shuffles, trainingsetindices, strict=False):\n",
" deeplabcut.train_network(\n",
" config_path,\n",
" shuffle=shuffle,\n",
" modelprefix=model_prefix,\n",
" gputouse=gputouse,\n",
" trainingsetindex=trainingsetindex,\n",
- " max_snapshots_to_keep=3, # training for 150000 iterations so let's save 50, 100, and 150.\n",
- " saveiters=50000\n",
+ " max_snapshots_to_keep=3, # training for 150000 iterations so let's save 50, 100, and 150.\n",
+ " saveiters=50000,\n",
" )"
]
},
@@ -1268,18 +1352,20 @@
"import deeplabcut\n",
"\n",
"config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
- "model_prefix = 'data_augm_01_fliplr'\n",
- "Shuffles = [1,2,3,4]\n",
- "trainingsetindices = [0,1,2,3]\n",
+ "model_prefix = \"data_augm_01_fliplr\"\n",
+ "Shuffles = [1, 2, 3, 4]\n",
+ "trainingsetindices = [0, 1, 2, 3]\n",
"\n",
- "#import tools for modifying our config file\n",
+ "# import tools for modifying our config file\n",
"from deeplabcut.utils.auxiliaryfunctions import edit_config\n",
"\n",
"# make sure we are testing all snapshots\n",
- "edit_config(config_path,{'snapshotindex':'all'})\n",
+ "edit_config(config_path, {\"snapshotindex\": \"all\"})\n",
"\n",
- "for shuffle, trainingsetindex in zip(Shuffles,trainingsetindices):\n",
- " deeplabcut.evaluate_network(config_path, modelprefix = model_prefix, Shuffles = [shuffle], trainingsetindex=trainingsetindex)"
+ "for shuffle, trainingsetindex in zip(Shuffles, trainingsetindices, strict=False):\n",
+ " deeplabcut.evaluate_network(\n",
+ " config_path, modelprefix=model_prefix, Shuffles=[shuffle], trainingsetindex=trainingsetindex\n",
+ " )"
]
},
{
@@ -1293,23 +1379,27 @@
"import deeplabcut\n",
"\n",
"config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
- "model_prefix_base = 'data_augm_00_base'\n",
- "model_prefix_augm = 'data_augm_01_fliplr'\n",
- "Shuffles = [4,3] # let's start with the refined un-augmented, i.e. shuffle 4\n",
- "trainingsetindices = [3,2]\n",
+ "model_prefix_base = \"data_augm_00_base\"\n",
+ "model_prefix_augm = \"data_augm_01_fliplr\"\n",
+ "Shuffles = [4, 3] # let's start with the refined un-augmented, i.e. shuffle 4\n",
+ "trainingsetindices = [3, 2]\n",
"\n",
"# We need pandas for creatig a nice list to parse\n",
+ "import sys\n",
+ "\n",
"import pandas as pd\n",
"\n",
- "import sys\n",
- "sys.path.append('..') #my python file for this function is stored in the parent folder as I'm running this\n",
- "from getErrorDistribution import getErrorDistribution #import the getErrorDistribution function\n",
+ "sys.path.append(\"..\") # my python file for this function is stored in the parent folder as I'm running this\n",
"import numpy as np\n",
+ "from getErrorDistribution import getErrorDistribution # import the getErrorDistribution function\n",
"\n",
- "# Read the h5 file containing all the frames, (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
- "df = pd.read_hdf('/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5')\n",
+ "# Read the h5 file containing all the frames:\n",
+ "# (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
+ "df = pd.read_hdf(\n",
+ " \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5\"\n",
+ ")\n",
"\n",
- "image_paths = df.index.to_list() # turn dataframe into list\n",
+ "image_paths = df.index.to_list() # turn dataframe into list\n",
"\n",
"# get test indices\n",
"test_inds = []\n",
@@ -1319,39 +1409,39 @@
"\n",
"error_distributions_pcut = []\n",
"\n",
- "for shuffle, trainFractionIndex in zip(Shuffles,trainingsetindices):\n",
- " error_distributions_pcut_temp = []\n",
- " if shuffle == 4: model_prefix = model_prefix_base\n",
- " elif shuffle == 3: model_prefix = model_prefix_augm\n",
- " \n",
- " for snapshot in [0,1,2]: #we saved three snapshots, one at 50k iteratinos, one at 100k, and one at 150k\n",
- " (\n",
- " _,\n",
- " _,\n",
- " _,\n",
- " ErrorDistributionPCutOff_all,\n",
- " _,\n",
- " _\n",
- " ) = getErrorDistribution(\n",
+ "for shuffle, trainFractionIndex in zip(Shuffles, trainingsetindices, strict=False):\n",
+ " error_distributions_pcut_temp = []\n",
+ " if shuffle == 4:\n",
+ " model_prefix = model_prefix_base\n",
+ " elif shuffle == 3:\n",
+ " model_prefix = model_prefix_augm\n",
+ "\n",
+ " for snapshot in [0, 1, 2]: # we saved three snapshots, one at 50k iteratinos, one at 100k, and one at 150k\n",
+ " (_, _, _, ErrorDistributionPCutOff_all, _, _) = getErrorDistribution(\n",
" config_path,\n",
" shuffle=shuffle,\n",
" snapindex=snapshot,\n",
- " trainFractionIndex = trainFractionIndex,\n",
- " modelprefix = model_prefix\n",
- " )\n",
- " error_distributions_pcut_temp.append(ErrorDistributionPCutOff_all.iloc[test_inds].values.flatten())\n",
- " error_distributions_pcut.append(error_distributions_pcut_temp)\n",
+ " trainFractionIndex=trainFractionIndex,\n",
+ " modelprefix=model_prefix,\n",
+ " )\n",
+ " error_distributions_pcut_temp.append(ErrorDistributionPCutOff_all.iloc[test_inds].values.flatten())\n",
+ " error_distributions_pcut.append(error_distributions_pcut_temp)\n",
"\n",
- "error_distributions_pcut = np.array(error_distributions_pcut) # array with dimensions [shuffle, snapshot, frames]\n",
+ "error_distributions_pcut = np.array(error_distributions_pcut) # array with dimensions [shuffle, snapshot, frames]\n",
"\n",
"import matplotlib.pyplot as plt\n",
+ "\n",
"plt.figure(figsize=(10, 5))\n",
- "for shuffle in [0,1]: #we start counting at 0, so for now, let's consider each index one less\n",
- " plt.errorbar(np.array([50,100,150])-1.5+shuffle,np.nanmean(error_distributions_pcut[shuffle,:],axis=1), np.nanstd(error_distributions_pcut[shuffle,:],axis=1)/len(test_inds)**.5)\n",
+ "for shuffle in [0, 1]: # we start counting at 0, so for now, let's consider each index one less\n",
+ " plt.errorbar(\n",
+ " np.array([50, 100, 150]) - 1.5 + shuffle,\n",
+ " np.nanmean(error_distributions_pcut[shuffle, :], axis=1),\n",
+ " np.nanstd(error_distributions_pcut[shuffle, :], axis=1) / len(test_inds) ** 0.5,\n",
+ " )\n",
"\n",
"plt.xticks([50, 100, 150])\n",
- "plt.xlim([25,175])\n",
- "plt.ylim([0,10])\n",
+ "plt.xlim([25, 175])\n",
+ "plt.ylim([0, 10])\n",
"plt.title(\"Error with P-cut 0.6, comparing baseline to fliplr and 180 degrees rotation augmented\")\n",
"\n",
"plt.legend([\"Full, ref, baseline\", \"Full, OOD, fliplr\"])\n",
@@ -1390,51 +1480,54 @@
},
"outputs": [],
"source": [
- "\n",
"# in case we restarted the kernel or something, let's make\n",
"# sure deeplabcut is imported and the config_path defined\n",
"import deeplabcut\n",
"\n",
- "config_path = '/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml'\n",
+ "config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
"\n",
"# we also need the package os for folder manipulation\n",
"import os\n",
+ "\n",
"# and shutil for copying files\n",
"import shutil\n",
"\n",
- "#import tools for reading our config file\n",
+ "# import tools for reading our config file\n",
"from deeplabcut.utils.auxiliaryfunctions import read_config\n",
"\n",
"# Number and name for our model folder\n",
- "model_number = 3 # CHANGE\n",
- "modelprefix_pre = 'data_augm'\n",
- "daug_str = 'max_rotate' # CHANGE\n",
+ "model_number = 3 # CHANGE\n",
+ "modelprefix_pre = \"data_augm\"\n",
+ "daug_str = \"max_rotate\" # CHANGE\n",
"\n",
"# Get config as dict and associated paths\n",
"cfg = read_config(config_path)\n",
- "project_path = cfg[\"project_path\"] # or: os.path.dirname(config_path) #dlc_models_path = os.path.join(project_path, \"dlc-models\")\n",
+ "project_path = cfg[\n",
+ " \"project_path\"\n",
+ "] # or: os.path.dirname(config_path) #dlc_models_path = os.path.join(project_path, \"dlc-models\")\n",
"training_datasets_path = os.path.join(project_path, \"training-datasets\")\n",
"\n",
"# Define shuffles\n",
- "shuffles = [1,2,3,4]\n",
+ "shuffles = [1, 2, 3, 4]\n",
"trainingsetindices = [0, 1, 2, 3]\n",
"\n",
"# Get train and test pose config file paths from base project, for each shuffle\n",
"list_base_train_pose_config_file_paths = []\n",
"list_base_test_pose_config_file_paths = []\n",
- "for shuffle_number, trainingsetindex in zip(shuffles, trainingsetindices):\n",
- " base_train_pose_config_file_path_TEMP,\\\n",
- " base_test_pose_config_file_path_TEMP,\\\n",
- " _ = deeplabcut.return_train_network_path(config_path,\n",
- " shuffle=shuffle_number,\n",
- " trainingsetindex=trainingsetindex) # base_train_pose_config_file\n",
+ "for shuffle_number, trainingsetindex in zip(shuffles, trainingsetindices, strict=False):\n",
+ " base_train_pose_config_file_path_TEMP, base_test_pose_config_file_path_TEMP, _ = (\n",
+ " deeplabcut.return_train_network_path(config_path, shuffle=shuffle_number, trainingsetindex=trainingsetindex)\n",
+ " ) # base_train_pose_config_file\n",
" list_base_train_pose_config_file_paths.append(base_train_pose_config_file_path_TEMP)\n",
" list_base_test_pose_config_file_paths.append(base_test_pose_config_file_path_TEMP)\n",
"\n",
"# Create subdirs for this augmentation method\n",
- "model_prefix = '_'.join([modelprefix_pre, \"{0:0=2d}\".format(model_number), daug_str]) # modelprefix_pre = aug_\n",
+ "model_prefix = \"_\".join([modelprefix_pre, f\"{model_number:0=2d}\", daug_str]) # modelprefix_pre = aug_\n",
"aug_project_path = os.path.join(project_path, model_prefix)\n",
- "aug_dlc_models = os.path.join(aug_project_path, \"dlc-models\", )\n",
+ "aug_dlc_models = os.path.join(\n",
+ " aug_project_path,\n",
+ " \"dlc-models\",\n",
+ ")\n",
"\n",
"# make the folder for this modelprefix\n",
"try:\n",
@@ -1444,25 +1537,20 @@
" print(\"Skipping this one as it already exists\")\n",
"\n",
"# Copy base train pose config file to the directory of this augmentation method\n",
- "for j, (shuffle, trainingsetindex) in enumerate(zip(shuffles,trainingsetindices)):\n",
- " one_train_pose_config_file_path,\\\n",
- " one_test_pose_config_file_path,\\\n",
- " _ = deeplabcut.return_train_network_path(config_path,\n",
- " shuffle=shuffle,\n",
- " trainingsetindex=trainingsetindex,\n",
- " modelprefix=model_prefix)\n",
- " \n",
+ "for j, (shuffle, trainingsetindex) in enumerate(zip(shuffles, trainingsetindices, strict=False)):\n",
+ " one_train_pose_config_file_path, one_test_pose_config_file_path, _ = deeplabcut.return_train_network_path(\n",
+ " config_path, shuffle=shuffle, trainingsetindex=trainingsetindex, modelprefix=model_prefix\n",
+ " )\n",
+ "\n",
" # make train and test directories for this subdir\n",
- " os.makedirs(str(os.path.dirname(one_train_pose_config_file_path))) # create parentdir 'train'\n",
- " os.makedirs(str(os.path.dirname(one_test_pose_config_file_path))) # create parentdir 'test\n",
- " \n",
+ " os.makedirs(str(os.path.dirname(one_train_pose_config_file_path))) # create parentdir 'train'\n",
+ " os.makedirs(str(os.path.dirname(one_test_pose_config_file_path))) # create parentdir 'test\n",
+ "\n",
" # copy test and train config from base project to this subdir\n",
" # copy base train config file\n",
- " shutil.copyfile(list_base_train_pose_config_file_paths[j],\n",
- " one_train_pose_config_file_path) \n",
+ " shutil.copyfile(list_base_train_pose_config_file_paths[j], one_train_pose_config_file_path)\n",
" # copy base test config file\n",
- " shutil.copyfile(list_base_test_pose_config_file_paths[j],\n",
- " one_test_pose_config_file_path)\n"
+ " shutil.copyfile(list_base_test_pose_config_file_paths[j], one_test_pose_config_file_path)"
]
},
{
@@ -1475,19 +1563,25 @@
},
"outputs": [],
"source": [
- "#import tools for changing our config file\n",
+ "# import tools for changing our config file\n",
"from deeplabcut.utils.auxiliaryfunctions import edit_config\n",
"\n",
- "model_prefix = 'data_augm_03_max_rotate'\n",
+ "model_prefix = \"data_augm_03_max_rotate\"\n",
"\n",
"## Initialise dict with additional edits to train config: optimizer\n",
"train_edits_dict = {}\n",
- "dict_optimizer = {'optimizer':'adam',\n",
- " 'batch_size': 8, # the gpu I'm using has plenty of memory so batch size 8 makes sense\n",
- " 'multi_step': [[1e-4, 7500], [5 * 1e-5, 12000], [1e-5, 150000]]} # if no yaml file passed, initialise as an empty dict\n",
- "train_edits_dict.update({'optimizer': dict_optimizer['optimizer'], #'adam',\n",
- " 'batch_size': dict_optimizer['batch_size'],\n",
- " 'multi_step': dict_optimizer['multi_step']})\n",
+ "dict_optimizer = {\n",
+ " \"optimizer\": \"adam\",\n",
+ " \"batch_size\": 8, # the gpu I'm using has plenty of memory so batch size 8 makes sense\n",
+ " \"multi_step\": [[1e-4, 7500], [5 * 1e-5, 12000], [1e-5, 150000]],\n",
+ "} # if no yaml file passed, initialise as an empty dict\n",
+ "train_edits_dict.update(\n",
+ " {\n",
+ " \"optimizer\": dict_optimizer[\"optimizer\"], #'adam',\n",
+ " \"batch_size\": dict_optimizer[\"batch_size\"],\n",
+ " \"multi_step\": dict_optimizer[\"multi_step\"],\n",
+ " }\n",
+ ")\n",
"\n",
"# Augmentation edits\n",
"edits_dict = dict()\n",
@@ -1495,16 +1589,13 @@
"edits_dict[\"fliplr\"] = True\n",
"edits_dict[\"rotation\"] = 180\n",
"\n",
- "for shuffle, trainingsetindex in zip(shuffles,trainingsetindices):\n",
- " one_train_pose_config_file_path,\\\n",
- " _,\\\n",
- " _ = deeplabcut.return_train_network_path(config_path,\n",
- " shuffle=shuffle,\n",
- " trainingsetindex=trainingsetindex,\n",
- " modelprefix=model_prefix)\n",
+ "for shuffle, trainingsetindex in zip(shuffles, trainingsetindices, strict=False):\n",
+ " one_train_pose_config_file_path, _, _ = deeplabcut.return_train_network_path(\n",
+ " config_path, shuffle=shuffle, trainingsetindex=trainingsetindex, modelprefix=model_prefix\n",
+ " )\n",
"\n",
" edit_config(str(one_train_pose_config_file_path), edits_dict)\n",
- " edit_config(str(one_train_pose_config_file_path), train_edits_dict)\n"
+ " edit_config(str(one_train_pose_config_file_path), train_edits_dict)"
]
},
{
@@ -1526,27 +1617,28 @@
"outputs": [],
"source": [
"import deeplabcut\n",
+ "\n",
"# define config path and model prefix\n",
- "config_path='/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml'\n",
- "model_prefix = 'data_augm_03_max_rotate'\n",
+ "config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
+ "model_prefix = \"data_augm_03_max_rotate\"\n",
"\n",
"# the computer I'm working on has several gpus, here I used the third one.\n",
- "gputouse=3\n",
+ "gputouse = 3\n",
"\n",
"# define shuffles and trainingsetindices\n",
- "shuffles = [1,2,3,4]\n",
- "trainingsetindices = [0,1,2,3]\n",
+ "shuffles = [1, 2, 3, 4]\n",
+ "trainingsetindices = [0, 1, 2, 3]\n",
"\n",
"# loop over shuffles and train each\n",
- "for shuffle, trainingsetindex in zip(shuffles, trainingsetindices):\n",
+ "for shuffle, trainingsetindex in zip(shuffles, trainingsetindices, strict=False):\n",
" deeplabcut.train_network(\n",
" config_path,\n",
" shuffle=shuffle,\n",
" modelprefix=model_prefix,\n",
" gputouse=gputouse,\n",
" trainingsetindex=trainingsetindex,\n",
- " max_snapshots_to_keep=3, # training for 150000 iterations so let's save 50, 100, and 150.\n",
- " saveiters=50000\n",
+ " max_snapshots_to_keep=3, # training for 150000 iterations so let's save 50, 100, and 150.\n",
+ " saveiters=50000,\n",
" )"
]
},
@@ -1564,18 +1656,20 @@
"import deeplabcut\n",
"\n",
"config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
- "model_prefix = 'data_augm_03_max_rotate'\n",
- "Shuffles = [1,2,3,4]\n",
- "trainingsetindices = [0,1,2,3]\n",
+ "model_prefix = \"data_augm_03_max_rotate\"\n",
+ "Shuffles = [1, 2, 3, 4]\n",
+ "trainingsetindices = [0, 1, 2, 3]\n",
"\n",
- "#import tools for modifying our config file\n",
+ "# import tools for modifying our config file\n",
"from deeplabcut.utils.auxiliaryfunctions import edit_config\n",
"\n",
"# make sure we are testing all snapshots\n",
- "edit_config(config_path,{'snapshotindex':'all'})\n",
+ "edit_config(config_path, {\"snapshotindex\": \"all\"})\n",
"\n",
- "for shuffle, trainingsetindex in zip(Shuffles,trainingsetindices):\n",
- " deeplabcut.evaluate_network(config_path, modelprefix = model_prefix, Shuffles = [shuffle], trainingsetindex=trainingsetindex, gputouse=3)"
+ "for shuffle, trainingsetindex in zip(Shuffles, trainingsetindices, strict=False):\n",
+ " deeplabcut.evaluate_network(\n",
+ " config_path, modelprefix=model_prefix, Shuffles=[shuffle], trainingsetindex=trainingsetindex, gputouse=3\n",
+ " )"
]
},
{
@@ -1595,23 +1689,27 @@
"import deeplabcut\n",
"\n",
"config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
- "model_prefix_base = 'data_augm_00_base'\n",
- "model_prefix_augm = 'data_augm_03_max_rotate'\n",
- "Shuffles = [4,3] # let's start with the refined un-augmented, i.e. shuffle 4\n",
- "trainingsetindices = [3,2]\n",
+ "model_prefix_base = \"data_augm_00_base\"\n",
+ "model_prefix_augm = \"data_augm_03_max_rotate\"\n",
+ "Shuffles = [4, 3] # let's start with the refined un-augmented, i.e. shuffle 4\n",
+ "trainingsetindices = [3, 2]\n",
"\n",
"# We need pandas for creatig a nice list to parse\n",
+ "import sys\n",
+ "\n",
"import pandas as pd\n",
"\n",
- "import sys\n",
- "sys.path.append('..') #my python file for this function is stored in the parent folder as I'm running this\n",
- "from getErrorDistribution import getErrorDistribution #import the getErrorDistribution function\n",
+ "sys.path.append(\"..\") # my python file for this function is stored in the parent folder as I'm running this\n",
"import numpy as np\n",
+ "from getErrorDistribution import getErrorDistribution # import the getErrorDistribution function\n",
"\n",
- "# Read the h5 file containing all the frames, (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
- "df = pd.read_hdf('/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5')\n",
+ "# Read the h5 file containing all the frames:\n",
+ "# (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
+ "df = pd.read_hdf(\n",
+ " \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5\"\n",
+ ")\n",
"\n",
- "image_paths = df.index.to_list() # turn dataframe into list\n",
+ "image_paths = df.index.to_list() # turn dataframe into list\n",
"\n",
"# get test indices\n",
"test_inds = []\n",
@@ -1621,30 +1719,25 @@
"\n",
"error_distributions_pcut = []\n",
"\n",
- "for shuffle, trainFractionIndex in zip(Shuffles,trainingsetindices):\n",
- " error_distributions_pcut_temp = []\n",
- " if shuffle == 4: model_prefix = model_prefix_base\n",
- " elif shuffle == 3: model_prefix = model_prefix_augm\n",
- " \n",
- " for snapshot in [0,1,2]: #we saved three snapshots, one at 50k iteratinos, one at 100k, and one at 150k\n",
- " (\n",
- " _,\n",
- " _,\n",
- " _,\n",
- " ErrorDistributionPCutOff_all,\n",
- " _,\n",
- " _\n",
- " ) = getErrorDistribution(\n",
+ "for shuffle, trainFractionIndex in zip(Shuffles, trainingsetindices, strict=False):\n",
+ " error_distributions_pcut_temp = []\n",
+ " if shuffle == 4:\n",
+ " model_prefix = model_prefix_base\n",
+ " elif shuffle == 3:\n",
+ " model_prefix = model_prefix_augm\n",
+ "\n",
+ " for snapshot in [0, 1, 2]: # we saved three snapshots, one at 50k iteratinos, one at 100k, and one at 150k\n",
+ " (_, _, _, ErrorDistributionPCutOff_all, _, _) = getErrorDistribution(\n",
" config_path,\n",
" shuffle=shuffle,\n",
" snapindex=snapshot,\n",
- " trainFractionIndex = trainFractionIndex,\n",
- " modelprefix = model_prefix\n",
- " )\n",
- " error_distributions_pcut_temp.append(ErrorDistributionPCutOff_all.iloc[test_inds].values.flatten())\n",
- " error_distributions_pcut.append(error_distributions_pcut_temp)\n",
+ " trainFractionIndex=trainFractionIndex,\n",
+ " modelprefix=model_prefix,\n",
+ " )\n",
+ " error_distributions_pcut_temp.append(ErrorDistributionPCutOff_all.iloc[test_inds].values.flatten())\n",
+ " error_distributions_pcut.append(error_distributions_pcut_temp)\n",
"\n",
- "error_distributions_pcut = np.array(error_distributions_pcut) # array with dimensions [shuffle, snapshot, frames]"
+ "error_distributions_pcut = np.array(error_distributions_pcut) # array with dimensions [shuffle, snapshot, frames]"
]
},
{
@@ -1657,12 +1750,16 @@
"\n",
"plt.figure(figsize=(10, 5))\n",
"\n",
- "for shuffle in [0,1]: #we start counting at 0, so for now, let's consider each index one less\n",
- " plt.errorbar(np.array([50,100,150])-1.5+shuffle,np.nanmean(error_distributions_pcut[shuffle,:],axis=1), np.nanstd(error_distributions_pcut[shuffle,:],axis=1)/len(test_inds)**.5)\n",
+ "for shuffle in [0, 1]: # we start counting at 0, so for now, let's consider each index one less\n",
+ " plt.errorbar(\n",
+ " np.array([50, 100, 150]) - 1.5 + shuffle,\n",
+ " np.nanmean(error_distributions_pcut[shuffle, :], axis=1),\n",
+ " np.nanstd(error_distributions_pcut[shuffle, :], axis=1) / len(test_inds) ** 0.5,\n",
+ " )\n",
"\n",
"plt.xticks([50, 100, 150])\n",
- "plt.xlim([25,175])\n",
- "plt.ylim([0,10])\n",
+ "plt.xlim([25, 175])\n",
+ "plt.ylim([0, 10])\n",
"plt.title(\"Error with P-cut 0.6, comparing baseline to fliplr and 180 degrees rotation augmented\")\n",
"\n",
"plt.legend([\"Full, ref, baseline\", \"Full, OOD, fliplr_180_rotate\"])\n",
@@ -1697,23 +1794,27 @@
"import deeplabcut\n",
"\n",
"config_path = \"/home/user/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/config.yaml\"\n",
- "model_prefix_base = 'data_augm_00_base'\n",
- "model_prefix_augm = 'data_augm_03_max_rotate'\n",
- "Shuffles = [4,3,3]\n",
- "trainingsetindices = [3,2,2]\n",
+ "model_prefix_base = \"data_augm_00_base\"\n",
+ "model_prefix_augm = \"data_augm_03_max_rotate\"\n",
+ "Shuffles = [4, 3, 3]\n",
+ "trainingsetindices = [3, 2, 2]\n",
"\n",
"# We need pandas for creatig a nice list to parse\n",
+ "import sys\n",
+ "\n",
"import pandas as pd\n",
"\n",
- "import sys\n",
- "sys.path.append('..') #my python file for this function is stored in the parent folder as I'm running this\n",
- "from getErrorDistribution import getErrorDistribution #import the getErrorDistribution function\n",
+ "sys.path.append(\"..\") # my python file for this function is stored in the parent folder as I'm running this\n",
"import numpy as np\n",
+ "from getErrorDistribution import getErrorDistribution # import the getErrorDistribution function\n",
"\n",
- "# Read the h5 file containing all the frames, (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
- "df = pd.read_hdf('/home/juser/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5')\n",
+ "# Read the h5 file containing all the frames:\n",
+ "# (project_folder/training-datasets/iteration-0/UnaufmentedDataSet_project_folder/CollectedData_LabelerName.h5)\n",
+ "df = pd.read_hdf(\n",
+ " \"/home/juser/projects/bat_augmentation_austin_2020_bat_data-DLC-2022-08-18/training-datasets/iteration-0/UnaugmentedDataSet_bat_augmentation_austin_2020_bat_dataAug18/CollectedData_DLC.h5\"\n",
+ ")\n",
"\n",
- "image_paths = df.index.to_list() # turn dataframe into list\n",
+ "image_paths = df.index.to_list() # turn dataframe into list\n",
"\n",
"# get test indices\n",
"test_inds = []\n",
@@ -1724,38 +1825,41 @@
"# this gives us the paths of our 27 test videos\n",
"test_paths = list(set([image_paths[i][1] for i in test_inds]))\n",
"\n",
- "#%% sorted so that the corresponding videos have the same index in three lists (one per camera)\n",
- "test_paths_cam1 = ['TS5-544-Cam1_2020-06-25_000099Track8_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000103Track3_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000104Track3_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000108Track6_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000123Track6_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000128Track2_50_test',\n",
- " 'TS5-544-Cam1_2020-06-25_000134Track5_50_test'\n",
- " ]\n",
- "test_paths_cam2 = ['IL5-519-Cam2_2020-06-25_000099Track6_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000103Track3_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000104Track2_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000109Track1_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000124Track9_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000130Track2_50_test',\n",
- " 'IL5-519-Cam2_2020-06-25_000136Track10_50_test'\n",
- " ]\n",
- "test_paths_cam3 = ['IL5-534-Cam3_2020-06-25_000095Track14_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000100Track4_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000101Track4_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000106Track3_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000122Track7_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000127Track4_50_test',\n",
- " 'IL5-534-Cam3_2020-06-25_000133Track9_50_test'\n",
- " ]\n",
- "\n",
- "nvideos = 7 # number of videos\n",
+ "# %% sorted so that the corresponding videos have the same index in three lists (one per camera)\n",
+ "test_paths_cam1 = [\n",
+ " \"TS5-544-Cam1_2020-06-25_000099Track8_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000103Track3_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000104Track3_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000108Track6_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000123Track6_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000128Track2_50_test\",\n",
+ " \"TS5-544-Cam1_2020-06-25_000134Track5_50_test\",\n",
+ "]\n",
+ "test_paths_cam2 = [\n",
+ " \"IL5-519-Cam2_2020-06-25_000099Track6_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000103Track3_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000104Track2_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000109Track1_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000124Track9_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000130Track2_50_test\",\n",
+ " \"IL5-519-Cam2_2020-06-25_000136Track10_50_test\",\n",
+ "]\n",
+ "test_paths_cam3 = [\n",
+ " \"IL5-534-Cam3_2020-06-25_000095Track14_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000100Track4_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000101Track4_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000106Track3_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000122Track7_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000127Track4_50_test\",\n",
+ " \"IL5-534-Cam3_2020-06-25_000133Track9_50_test\",\n",
+ "]\n",
+ "\n",
+ "nvideos = 7 # number of videos\n",
"\n",
"# get test frame indexes per camera\n",
- "test_inds_cam1 = [[],[],[],[],[],[],[]]\n",
- "test_inds_cam2 = [[],[],[],[],[],[],[]]\n",
- "test_inds_cam3 = [[],[],[],[],[],[],[]]\n",
+ "test_inds_cam1 = [[], [], [], [], [], [], []]\n",
+ "test_inds_cam2 = [[], [], [], [], [], [], []]\n",
+ "test_inds_cam3 = [[], [], [], [], [], [], []]\n",
"\n",
"for i, path in enumerate(image_paths):\n",
" for j in range(nvideos):\n",
@@ -1768,70 +1872,96 @@
"\n",
"nshuffles = len(Shuffles)\n",
"\n",
- "#pre-allocate matrixes for mean values and standard errors\n",
- "mean_cam1 = np.zeros([nshuffles,nvideos]) # shuffle x movie\n",
- "mean_cam2 = np.zeros([nshuffles,nvideos])\n",
- "mean_cam3 = np.zeros([nshuffles,nvideos])\n",
+ "# pre-allocate matrixes for mean values and standard errors\n",
+ "mean_cam1 = np.zeros([nshuffles, nvideos]) # shuffle x movie\n",
+ "mean_cam2 = np.zeros([nshuffles, nvideos])\n",
+ "mean_cam3 = np.zeros([nshuffles, nvideos])\n",
"\n",
- "ste_cam1 = np.zeros([nshuffles,nvideos])\n",
- "ste_cam2 = np.zeros([nshuffles,nvideos])\n",
- "ste_cam3 = np.zeros([nshuffles,nvideos])\n",
+ "ste_cam1 = np.zeros([nshuffles, nvideos])\n",
+ "ste_cam2 = np.zeros([nshuffles, nvideos])\n",
+ "ste_cam3 = np.zeros([nshuffles, nvideos])\n",
"\n",
- "meanPcut_cam1 = np.zeros([nshuffles,nvideos]) # shuffle x movie\n",
- "meanPcut_cam2 = np.zeros([nshuffles,nvideos])\n",
- "meanPcut_cam3 = np.zeros([nshuffles,nvideos])\n",
+ "meanPcut_cam1 = np.zeros([nshuffles, nvideos]) # shuffle x movie\n",
+ "meanPcut_cam2 = np.zeros([nshuffles, nvideos])\n",
+ "meanPcut_cam3 = np.zeros([nshuffles, nvideos])\n",
"\n",
- "stePcut_cam1 = np.zeros([nshuffles,nvideos])\n",
- "stePcut_cam2 = np.zeros([nshuffles,nvideos])\n",
- "stePcut_cam3 = np.zeros([nshuffles,nvideos])\n",
+ "stePcut_cam1 = np.zeros([nshuffles, nvideos])\n",
+ "stePcut_cam2 = np.zeros([nshuffles, nvideos])\n",
+ "stePcut_cam3 = np.zeros([nshuffles, nvideos])\n",
"\n",
"# %%\n",
"\n",
"for i, shuffle in enumerate(Shuffles):\n",
- " if shuffle == 4 or i == 2: model_prefix = model_prefix_base\n",
- " elif shuffle == 3: model_prefix = model_prefix_augm \n",
- "\n",
- " trainFractionIndex = shuffle-1\n",
- " snapshot=-1\n",
- " (\n",
- " ErrorDistribution_all,\n",
- " _,\n",
- " _,\n",
- " ErrorDistributionPCutOff_all,\n",
- " _,\n",
- " _\n",
- " ) = getErrorDistribution(\n",
+ " if shuffle == 4 or i == 2:\n",
+ " model_prefix = model_prefix_base\n",
+ " elif shuffle == 3:\n",
+ " model_prefix = model_prefix_augm\n",
+ "\n",
+ " trainFractionIndex = shuffle - 1\n",
+ " snapshot = -1\n",
+ " (ErrorDistribution_all, _, _, ErrorDistributionPCutOff_all, _, _) = getErrorDistribution(\n",
" config_path,\n",
" shuffle=shuffle,\n",
" snapindex=snapshot,\n",
- " trainFractionIndex = trainFractionIndex,\n",
- " modelprefix = model_prefix\n",
+ " trainFractionIndex=trainFractionIndex,\n",
+ " modelprefix=model_prefix,\n",
" )\n",
" for movie_number in range(7):\n",
- "\n",
- " meanPcut_cam1[i,movie_number] = np.nanmean(ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:])\n",
- " stePcut_cam1[i,movie_number] = np.nanstd(ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:])/(ErrorDistribution_all.values[test_inds_cam1[movie_number]][:].size**.5)\n",
- "\n",
- " meanPcut_cam2[i,movie_number] = np.nanmean(ErrorDistributionPCutOff_all.values[test_inds_cam2[movie_number]][:])\n",
- " stePcut_cam2[i,movie_number] = np.nanstd(ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:])/(ErrorDistribution_all.values[test_inds_cam2[movie_number]][:].size**.5)\n",
- "\n",
- " meanPcut_cam3[i,movie_number] = np.nanmean(ErrorDistributionPCutOff_all.values[test_inds_cam3[movie_number]][:])\n",
- " stePcut_cam3[i,movie_number] = np.nanstd(ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:])/(ErrorDistribution_all.values[test_inds_cam3[movie_number]][:].size**.5)\n",
- "\n",
- "fig, (ax1,ax2,ax3) = plt.subplots(3,1)\n",
+ " meanPcut_cam1[i, movie_number] = np.nanmean(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:]\n",
+ " )\n",
+ " stePcut_cam1[i, movie_number] = np.nanstd(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:]\n",
+ " ) / (ErrorDistribution_all.values[test_inds_cam1[movie_number]][:].size ** 0.5)\n",
+ "\n",
+ " meanPcut_cam2[i, movie_number] = np.nanmean(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam2[movie_number]][:]\n",
+ " )\n",
+ " stePcut_cam2[i, movie_number] = np.nanstd(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:]\n",
+ " ) / (ErrorDistribution_all.values[test_inds_cam2[movie_number]][:].size ** 0.5)\n",
+ "\n",
+ " meanPcut_cam3[i, movie_number] = np.nanmean(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam3[movie_number]][:]\n",
+ " )\n",
+ " stePcut_cam3[i, movie_number] = np.nanstd(\n",
+ " ErrorDistributionPCutOff_all.values[test_inds_cam1[movie_number]][:]\n",
+ " ) / (ErrorDistribution_all.values[test_inds_cam3[movie_number]][:].size ** 0.5)\n",
+ "\n",
+ "fig, (ax1, ax2, ax3) = plt.subplots(3, 1)\n",
"fig.set_figheight(15)\n",
"fig.set_figwidth(10)\n",
"for i, shuffle in enumerate(Shuffles):\n",
- " \n",
" # to jitter the error bars to keep them from overlapping\n",
- " movie_number = list(range(1,8))\n",
- " movie_number = [x - 2/50 + shuffle/50 for x in movie_number]\n",
- " \n",
- " ax1.errorbar(movie_number,meanPcut_cam1[i,:], stePcut_cam1[i,:,])\n",
+ " movie_number = list(range(1, 8))\n",
+ " movie_number = [x - 2 / 50 + shuffle / 50 for x in movie_number]\n",
+ "\n",
+ " ax1.errorbar(\n",
+ " movie_number,\n",
+ " meanPcut_cam1[i, :],\n",
+ " stePcut_cam1[\n",
+ " i,\n",
+ " :,\n",
+ " ],\n",
+ " )\n",
"\n",
- " ax2.errorbar(movie_number,meanPcut_cam2[i,:], stePcut_cam2[i,:,])\n",
+ " ax2.errorbar(\n",
+ " movie_number,\n",
+ " meanPcut_cam2[i, :],\n",
+ " stePcut_cam2[\n",
+ " i,\n",
+ " :,\n",
+ " ],\n",
+ " )\n",
"\n",
- " ax3.errorbar(movie_number,meanPcut_cam3[i,:], stePcut_cam3[i,:,])\n",
+ " ax3.errorbar(\n",
+ " movie_number,\n",
+ " meanPcut_cam3[i, :],\n",
+ " stePcut_cam3[\n",
+ " i,\n",
+ " :,\n",
+ " ],\n",
+ " )\n",
"\n",
"ax1.set_ylim([0, 50])\n",
"ax2.set_ylim([0, 50])\n",
@@ -1867,6 +1997,11 @@
],
"metadata": {
"celltoolbar": "Edit Metadata",
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-09-16",
+ "last_metadata_updated": "2026-03-06"
+ },
"kernelspec": {
"display_name": "Python [conda env:DEEPLABCUT_newGUI] *",
"language": "python",
diff --git a/docs/recipes/installTips.md b/docs/recipes/installTips.md
index cf923f2cf1..1ddefa0ca7 100644
--- a/docs/recipes/installTips.md
+++ b/docs/recipes/installTips.md
@@ -1,15 +1,36 @@
+---
+deeplabcut:
+ last_content_updated: '2025-02-28'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
(installation-tips)=
# Installation Tips
## How to use the latest updates directly from GitHub
-We often update the master deeplabcut code base on github, and then ~1 a month we push out a stable release on pypi. This is what most users turn to on a daily basis (i.e. pypi is where you get your `pip install deeplabcut` code from!
+We often update the master deeplabcut code base on GitHub, and then ~1 a month we push out a stable release on pypi. This is what most users turn to on a daily basis (i.e. pypi is where you get your `pip install deeplabcut` code from! But, sometimes we add things to the repo that are not yet integrated, or you might want to edit the code yourself. Here, we show you how to do this.
-But, sometimes we add things to the repo that are not yet integrated, or you might want to edit the code yourself. Here, we show you how to do this.
+### Method 1:
+
+If you want to *use* the latest, you can use pip and add the specific tags, such as `gui`, etc. by modifying and running:
+```
+pip install --upgrade 'git+https://github.com/deeplabcut/deeplabcut.git#egg=deeplabcut[gui]'
+```
+
+which will download and update deeplabcut, and any dependencies that don't match the new version. If you want to force upgrade all of the dependencies to the latest available versions, too, then use the additional `--upgrade-strategy eager`, i.e.:
+
+```
+pip install --upgrade --upgrade-strategy eager 'git+https://github.com/deeplabcut/deeplabcut.git#egg=deeplabcut[gui]'
+```
+
+### Method 2:
+
+If you want to be able to *edit* the source code of DeepLabCut, i.e., maybe add a feature or fix a 🐛, then you need to "clone" the source code:
**Step 1:**
-- git clone the repo into a folder on your computer:
+- git clone the repo into a folder on your computer:
- click on this green button and copy the link:
@@ -23,7 +44,7 @@ But, sometimes we add things to the repo that are not yet integrated, or you mig

-- Now, when you start `ipython` or `pythonw` (mac users), and `import deeplabcut` you are importing the folder "deeplabcut" - so any changes you make, or any changes we made before adding it to the pip package, are here.
+- Now, when you start `ipython` and `import deeplabcut` you are importing the folder "deeplabcut" - so any changes you make, or any changes we made before adding it to the pip package, are here.
- You can also check which deeplabcut you are importing by running: `deeplabcut.__file__`
@@ -39,21 +60,11 @@ If you make changes, you can also then utilize our test scripts. Run the desired
i.e., for example:
```
-python testscript_multianimal.py
-```
-
-### Quick pull and install from the github repository
-
-If you just want to install the latest pre-release without editing, you can activate your anaconda env, and run
-
-```
-pip install --upgrade git+https://github.com/deeplabcut/deeplabcut.git
-```
+# Testing with the PyTorch engine
+python testscript_pytorch_multi_animal.py
-which will download and update deeplabcut, and any dependencies that don't match the new version. If you want to force upgrade all of the dependencies to the latest available versions, too, then run
-
-```
-pip install --upgrade --upgrade-strategy eager git+https://github.com/deeplabcut/deeplabcut.git
+# Testing with the TensorFlow engine
+python testscript_tensorflow_multi_animal.py
```
@@ -242,6 +253,7 @@ Share images, automate workflows, and more with a free Docker ID:
For more examples and ideas, visit:
https://docs.docker.com/get-started/
```
+
### Next, Anaconda!
Click here to get the ubuntu/linux package: https://www.anaconda.com/products/individual#linux
@@ -282,6 +294,11 @@ Follow prompts!
## Troubleshooting: Note, if you get a failed build due to wxPython (note, this does not happen on Ubuntu 18, 16, etc), i.e.:
+```{warning}
+DeepLabCut no longer uses `wxpython` for its GUI - if you're getting such an error,
+you're likely installing an old version of DeepLabCut.
+```
+
```python
ERROR: Command errored out with exit status 1: /home/mackenzie/anaconda3/envs/DLC-GPU/bin/python -u -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-0jsmkrr1/wxpython_aeff462b2060421a9cf65df55f63a126/setup.py'"'"'; __file__='"'"'/tmp/pip-install-0jsmkrr1/wxpython_aeff462b2060421a9cf65df55f63a126/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-pzy9q5u2/install-record.txt --single-version-externally-managed --compile --install-headers /home/mackenzie/anaconda3/envs/DLC-GPU/include/python3.7m/wxpython Check the logs for full command output.
@@ -312,11 +329,11 @@ Activate! `conda activate DEEPLABCUT` and then run: `conda install -c conda-forg
Then run `python -m deeplabcut` which launches the DLC GUI.
-## DeepLabCut MacOS M1 and M2 chip installation environment instructions:
+## DeepLabCut MacOS M-chip installation environment instructions:
-This only assumes you have anaconda installed.
-
-Use the `DEEPLABCUT_M1.yaml` conda file if you have an Macbok with an M1 or M2 chip, and follow these steps:
+This only assumes you have anaconda installed. Use the `DEEPLABCUT_M1.yaml` conda file
+if you have a newer MacBook (with an M1, M2, M3, M4 chip or more later), and follow
+these steps:
(1) git clone the deeplabcut cut repo:
@@ -329,17 +346,24 @@ git clone https://github.com/DeepLabCut/DeepLabCut.git
(3) Then, run:
```bash
-conda env create -f DEEPLABCUT_M1.yaml
+conda env create -f DEEPLABCUT.yaml
```
(4) Finally, activate your environment and to launch DLC with the GUI
```bash
-conda activate DEEPLABCUT_M1
+conda activate DEEPLABCUT
python -m deeplabcut
```
-The GUI will open. Of course, you can also run DeepLabCut in headless mode.
+The GUI will open. Of course, you can also run DeepLabCut in headless mode.
+
+If **you want to use the TensorFlow engine**, you'll need to install the `apple_mchips`
+extra with DeepLabCut. You can do so by running:
+
+```bash
+pip install deeplabcut[apple_mchips]
+```
## How to confirm that your GPU is being used by DeepLabCut
@@ -347,7 +371,7 @@ During training and analysis steps, DeepLabCut does not use the GPU processor he
**On Windows**:
-(1) Open the task manager. If it looks like the image below, click on "More Details"
+(1) Open the task manager. If it looks like the image below, click on "More Details"

@@ -355,15 +379,16 @@ During training and analysis steps, DeepLabCut does not use the GPU processor he

-(3) Click on the **Performance** tab. On that page, click on the small arrow under GPU (it might start as **3D**, and change it to **CUDA**.
+(3) Click on the **Performance** tab. On that page, click on the small arrow under GPU (it might start as **3D**, and change it to **CUDA**.
-(4) During training, you should see the **Dedicated GPU memory usage** increase to near maximum, and you should see some activity in the **CUDA** graph. The graph below is the activity while running `testscript.py`.
+(4) During training, you should see the **Dedicated GPU memory usage** increase to near maximum, and you should see some activity in the **CUDA** graph. The graph below is the activity while running `testscript_tensorflow_single_animal.py`.

(5) If you don't see activity there during training, then your GPU is likely not installed correctly for DeepLabCut. Return to the installation instructions, and be sure you installed CUDA 11+, and ran `conda install cudnn -c conda-forge` after installing DeepLabCut.
-## How to install DeepLabCut for Intel and AMD GPUs on Windows
+## How to install DeepLabCut for Intel and AMD GPUs on Windows for the TensorFlow engine
+
If you are on Windows 10/11 and have a DirectX 12 compatible GPU from any vendor (AMD, Intel, or Nvidia), you utilise GPU acceleration for inference, with an installation that is consistent between devices. This method uses [Tensorflow-directml](https://github.com/microsoft/tensorflow-directml) which uses DirectML instead of Cuda for ML training and inference.
To check the DirectX version of your installed GPU, type in dxdiag into windows search and select the run command. In system information, the bottom item of the list shows your DirectX version. In addition to this ensure your standard GPU drivers are up-to-date. Updating drivers by any official means (Nvidia Geforce experience, AMD radeon software, direct from the vendor website) is fine.
diff --git a/docs/recipes/io.md b/docs/recipes/io.md
index e97238628b..66d101dce7 100644
--- a/docs/recipes/io.md
+++ b/docs/recipes/io.md
@@ -1,3 +1,9 @@
+---
+deeplabcut:
+ last_content_updated: '2022-04-11'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
# Input/output manipulations with DeepLabCut
## Analyzing very large videos in chunks
@@ -15,12 +21,12 @@ clips = vid.split(n_splits=10)
deeplabcut.analyze_videos(config_path, clips, ext)
```
-## Tips on video re-encoding and preprocessing
+## Tips on video re-encoding and preprocessing
-While moving videos between computers or from your computer to cloud storage you can encounter issues with `analyze_videos` or `create_labeled_video` due to video corruption.
-The issue can present itself during those steps and you have to carefully review the traceback. Sometimes it might look like the videos were analyzed but in fact analysis stopped right before the end of the video (corruption of the metadata when more indices are assigned than there are actual frames in a video).
+While moving videos between computers or from your computer to cloud storage you can encounter issues with `analyze_videos` or `create_labeled_video` due to video corruption.
+The issue can present itself during those steps and you have to carefully review the traceback. Sometimes it might look like the videos were analyzed but in fact analysis stopped right before the end of the video (corruption of the metadata when more indices are assigned than there are actual frames in a video).
To tackle this issue, the easiest solution might be to re-encode the video, this will not only help with corruption but can also – if you choose so – compress the video without perceivable loss of quality. Common package used for video processing is FFmpeg which you can use from the terminal inside your DEEPLABCUT environment (without going into iPython).
-There are number of video codecs that can be used to re-encode your video and if you want to keep the video in the same container (`.avi`, `.mp4`, `.ts` etc.) you should check which codec allows encoding to a certain container. For instance, for `.avi` it will be MJPEG and for `.mp4` H264 and H265.
+There are number of video codecs that can be used to re-encode your video and if you want to keep the video in the same container (`.avi`, `.mp4`, `.ts` etc.) you should check which codec allows encoding to a certain container. For instance, for `.avi` it will be MJPEG and for `.mp4` H264 and H265.
To re-encode your video, simply use:
```
ffmpeg -i "path_to_video" -c:v codec_name "output_path"
@@ -35,14 +41,14 @@ For `.avi` files you want to change the codec and the quality metric, since `crf
```
ffmpeg -i "path_to_video" -c:v mjpeg -q:v 10 "output_path"
```
-`-q:v` is a quality metric with values ranging from 1 to 31 with reasonable values being around 10.
+`-q:v` is a quality metric with values ranging from 1 to 31 with reasonable values being around 10.
If you want to compress all your recordings for easier storage or moving to cloud storage, you can use a for loop that will go through all videos in a directory that are in a certain container. Let’s say we want to transcode our `.avi` videos to `.mp4` and make them smaller without quality loss. Note, that the loop has be run from inside the folder the videos are in:
```
-for %i in (*.avi) do ffmpeg -i "%i" -c:v libx265 -preset fast -crf 18 "%~ni.mp4"
+for %i in (*.avi) do ffmpeg -i "%i" -c:v libx265 -preset fast -crf 18 "%~ni.mp4"
```
This command will re-encode all of your videos into an `.mp4` container and save them with the same name as the original (without overwriting them).
-Additionally, ffmpeg allows you to also crop or rescale the videos for possible improvement in inference speed further down the line in DLC workflow. To either crop or rescale you need to use
-`-filter:v` parameter after which you’d add either `"crop=Xsize:Ysize:Xstart:Ystart"` for cropping or
+Additionally, ffmpeg allows you to also crop or rescale the videos for possible improvement in inference speed further down the line in DLC workflow. To either crop or rescale you need to use
+`-filter:v` parameter after which you’d add either `"crop=Xsize:Ysize:Xstart:Ystart"` for cropping or
`"scale=Xsize:Ysize"` for rescale. Note that when using “scale” the values how be a result of integer division of the original video size. If you want to keep the aspect ratio, you can simply set either X or Y to `-1` and only give one of the or you can use `“scale=iw/2:ih/2”` which will simply make the video 2 times smaller in both dimensions. For instance, if you have a videos at 1920x1080 resolution and want to rescale it to 960x540 for faster inference while also reencoding from `.avi` and doing some compression in a loop, the command would be something like this:
```
for %i in (*.avi) do ffmpeg -i "%i" -c:v libx265 -preset fast -crf 18 -filter:v "scale= iw/2:ih/2" "%~ni.mp4"
diff --git a/docs/recipes/nn.md b/docs/recipes/nn.md
index ac01524560..b002dd6b14 100644
--- a/docs/recipes/nn.md
+++ b/docs/recipes/nn.md
@@ -1,15 +1,22 @@
+---
+deeplabcut:
+ last_content_updated: '2025-06-30'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
+(tf-training-tips-and-tricks)=
# Model training tips & tricks
-## Limiting a GPU's memory consumption
+## TensorFlow Engine: Limiting a GPU's memory consumption
-All GPU memory is allocated to training by default, preventing
+With TensorFlow, all GPU memory is allocated to training by default, preventing
other Tensorflow processes from being run on the same machine.
-A flexible solution to limiting memory usage is to call `deeplabcut.train(..., allow_growth=True)`,
-which dynamically grows the GPU memory region as it is needed.
-Another, stricter option is to explicitly cap GPU usage to only a fraction
-of the available memory. For example, allocating a maximum of 1/4 of the total
-memory could be done as follows:
+A flexible solution to limiting memory usage is to call
+`deeplabcut.train(..., allow_growth=True)`, which dynamically grows the GPU memory
+region as it is needed. Another, stricter option is to explicitly cap GPU usage to only
+a fraction of the available memory. For example, allocating a maximum of 1/4 of the
+total memory could be done as follows:
```python
import tensorflow as tf
@@ -18,6 +25,7 @@ gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.25)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))
```
+(tf-custom-image-augmentation)=
## Using custom image augmentation
Image augmentation is the process of artificially expanding the training set
@@ -25,89 +33,104 @@ by applying various transformations to images (e.g., rotation or rescaling)
in order to make models more robust and more accurate (read our
[primer](https://www.sciencedirect.com/science/article/pii/S0896627320307170) for
more information). Although data augmentation is automatically accomplished
-by DeepLabCut, default values (see the augmentation variables in the
-[default pose_cfg.yaml](https://github.com/DeepLabCut/DeepLabCut/blob/master/deeplabcut/pose_cfg.yaml#L23-L74) file)
-can be readily overwritten prior to training.
+by DeepLabCut, default values can be readily overwritten prior to training. See the
+augmentation variables defined in the:
-Another option we discuss is a different data-efficient approach based on a method called active learning. See this [this blog post](https://github.com/DeepLabCut/DeepLabCut/blob/master/docs/recipes/nn.md#using-custom-image-augmentation) for further details.
+- PyTorch Engine: [docs for the `pytorch_config.yaml` file](dlc3-pytorch-config)
+- TensorFlow Engine: [default pose_cfg.yaml file](
+https://github.com/DeepLabCut/DeepLabCut/blob/main/deeplabcut/pose_cfg.yaml#L23-L74)
-When you `create_training_dataset` [you have several options](https://github.com/DeepLabCut/DeepLabCut/wiki/DOCSTRINGS#create_training_dataset) on what types of augmentation to use.
-```python
-deeplabcut.create_training_dataset(configpath, augmenter_type='imgaug')
-```
-
-When you do this (i.e. pass `augmenter_type`) what underlying files you are calling are these:
-https://github.com/DeepLabCut/DeepLabCut/tree/master/deeplabcut/pose_estimation_tensorflow/datasets
-You can look at what types of augmentation are available to you (or edit those files to add more). Moreover, you can add more options to the pose_cfg.yaml file. Here is a simple script you can modify and run to automatically edit the correct pose_cfg.yaml to add more augmentation to the `imgaug` loader (or open it and edit yourself).
-
-But, you can add more:
-
-```python
-import deeplabcut
-
-train_pose_config, _ = deeplabcut.return_train_network_path(config_path)
-augs = {
- "gaussian_noise": True,
- "elastic_transform": True,
- "rotation": 180,
- "covering": True,
- "motion_blur": True,
-}
-deeplabcut.auxiliaryfunctions.edit_config(
- train_pose_config,
- augs,
-)
-```
+For the single-animal TensorFlow models, [you have several options](
+https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide.html#f-create-training-dataset-s-and-selection-of-your-neural-network)
+for image augmentation when calling `create_training_dataset`
+An in-depth tutorial on image augmentation and training hyperparameters can be found [
+here](
+https://deeplabcut.github.io/DeepLabCut/docs/recipes/pose_cfg_file_breakdown.html).
## Evaluating intermediate (and all) snapshots
-The latest snapshot stored during training may not necessarily be the one that yields the highest performance. Therefore, you should analyze ALL snapshots, and select the best. Put 'all' in the snapshots section of the config.yaml to do this.
+The latest snapshot stored during training may not necessarily be the one that yields
+the highest performance. Therefore, you should analyze ALL snapshots, and select the
+best. Put 'all' in the snapshots section of the `config.yaml` to do this.
+(what-neural-network-should-i-use)=
## What neural network should I use? (Trade offs, speed performance, and considerations)
-### With the release of even more network options, you now have to decide what to use! This additionally flexibility is hopefully helpful, but we want to give you some guidance on where to start.
+You always select the network type when you create a training data set: i.e., standard
+dlc: `deeplabcut.create_training_dataset(config, net_type=resnet_50)` , or maDLC:
+`deeplabcut.create_multianimaltraining_dataset(config, net_type=dlcrnet_ms5)`. There is
+nothing else you should change.
+### PyTorch Engine
-**TL;DR - your best performance for most everything is ResNet-50; MobileNetV2-1 is much faster, needs less memory on your GPU to train and nearly as accurate.**
+The different architectures available are described in the [PyTorch model architectures
+](dlc3-architectures) page.
-You always select the network type when you create a training data set: i.e., standard dlc: `deeplabcut.create_training_dataset(config, net_type=resnet_50)` , or maDLC: `deeplabcut.create_multianimaltraining_dataset(config, net_type=dlcrnet_ms5)`. There is nothing else you should change.
+### TensorFlow Engine
+With the release of even more network options, you now have to decide what to use! This
+additionally flexibility is hopefully helpful, but we want to give you some guidance on
+where to start.
+
+**TL;DR - your best performance for most everything is ResNet-50; MobileNetV2-1 is much
+faster, needs less memory on your GPU to train and nearly as accurate.**
***
-## ResNets:
+### ResNets:
-In Mathis et al. 2018 we benchmarked three networks: **ResNet-50, ResNet-101, and ResNet-101ws**. For ALL lab applications, ResNet-50 was enough. For all the demo videos on [www.deeplabcut.org](http://www.mousemotorlab.org/deeplabcut) the backbones are ResNet-50's. Thus, we recommend making this your go-to workhorse for data analysis. Here is a figure from the paper, see panel "B" (they are all within a few pixels of each other on the open-field dataset):
+In Mathis et al. 2018 we benchmarked three networks: **ResNet-50, ResNet-101, and
+ResNet-101ws**. For ALL lab applications, ResNet-50 was enough. For all the demo videos
+on [www.deeplabcut.org](http://www.mousemotorlab.org/deeplabcut) the backbones are
+ResNet-50's. Thus, we recommend making this your go-to workhorse for data analysis. Here
+is a figure from the paper, see panel "B" (they are all within a few pixels of each
+other on the open-field dataset):
-This is also one of the main result figures, generated with ResNet-50. BLUE is training - RED is testing - BLACK is our best human-level performance, and 10 pixels is the width of the mouse nose -so anything under that is good performance for us on this task!
+This is also one of the main result figures, generated with ResNet-50. BLUE is
+training - RED is testing - BLACK is our best human-level performance, and 10 pixels is
+the width - of the mouse nose -so anything under that is good performance for us on this
+task!
+
-Here are also some speed stats for analyzing videos with ResNet-50, see https://www.biorxiv.org/content/early/2018/10/30/457242 for more details:
+Here are also some speed stats for analyzing videos with ResNet-50, see
+https://www.biorxiv.org/content/early/2018/10/30/457242 for more details:
-**So, why use a ResNet-101 or even 152?** if you have a much more challenging problem, like multiple humans dancing, this is a good option. You should then also set `intermediate_supervision=True` for best performance in the pose_config.yaml of that shuffle folder ( before you train). Note, for ResNet-50 this does NOT help, and can hurt.
+**So, why use a ResNet-101 or even 152?** if you have a much more challenging problem,
+like multiple humans dancing, this is a good option. You should then also set
+`intermediate_supervision=True` for best performance in the `pose_config.yaml` of that
+shuffle folder (before you train). Note, for ResNet-50 this does NOT help, and can
+hurt.
-## When should I use a MobileNet?
+### When should I use a MobileNet?
-MobileNets are fast to run, fast to train, more memory efficient, and faster for analysis (inference) - e.g. on CPUs they are 4 times faster, on GPUs up to 2x! So, if you don't have a GPU (or a GPU with little memory), and don't want to use Google COLAB, etc, then these are a great starting point.
+MobileNets are fast to run, fast to train, more memory efficient, and faster for
+analysis (inference) - e.g. on CPUs they are 4 times faster, on GPUs up to 2x! So, if
+you don't have a GPU (or a GPU with little memory), and don't want to use Google COLAB,
+etc, then these are a great starting point.
-They are smaller/shallower networks though, so you don't want to be pushing in very large images. So, be sure to use `deeplabcut.DownSampleVideo` on your data (which is frankly never a bad idea).
+They are smaller/shallower networks though, so you don't want to be pushing in very
+large images. So, be sure to use `deeplabcut.DownSampleVideo` on your data (which is
+frankly never a bad idea).
-Additionally, these are good options for running on "live" videos, i.e. if you want to give real-time feedback in an experiment, you can run a video around a smaller cropped area, and run this rather fast!
+Additionally, these are good options for running on "live" videos, i.e. if you want to
+give real-time feedback in an experiment, you can run a video around a smaller cropped
+area, and run this rather fast!
**So, how fast are they?**
-Here are comparisons of 4 MobileNetV2 variants to ResNet-50 and ResNet-101 (darkest red):
-read more here: https://arxiv.org/abs/1909.11229
+Here are comparisons of 4 MobileNetV2 variants to ResNet-50 and ResNet-101 (darkest
+red - read more here: https://arxiv.org/abs/1909.11229)
@@ -117,14 +140,26 @@ read more here: https://arxiv.org/abs/1909.11229
-## When should I use an EfficientNet?
+### When should I use an EfficientNet?
-Built with inverse residual blocks like MobileNets, but more powerful than ResNets, due to optimal depth/width/resolution scaling, [EfficientNet](https://arxiv.org/abs/1905.11946) are an excellent choice if you want speed and performance. They do require more careful handling though! Especially for small datasets, you will need to tune the batch size and learning rates. So, we suggest these for more advanced users, or those willing to run experiments to find the best settings. Here is the speed comparison, and for performance see our latest work at: http://horse10.deeplabcut.org
+Built with inverse residual blocks like MobileNets, but more powerful than ResNets, due
+to optimal depth/width/resolution scaling, [EfficientNet](
+https://arxiv.org/abs/1905.11946) are an excellent choice if you want speed and
+performance. They do require more careful handling though! Especially for small
+datasets, you will need to tune the batch size and learning rates. So, we suggest these
+for more advanced users, or those willing to run experiments to find the best settings.
+Here is the speed comparison, and for performance see our latest work at:
+http://horse10.deeplabcut.org
-## How can I compare them?
+### How can I compare them?
-Great question! So, the best way to do this is to use the **same** test/train split (that is generated in create_training_dataset) with different models. Here, as of 2.1+, we have a **new** function that lets you do this easily. Instead of using `create_training_dataset` you will run `create_training_model_comparison` (see the docstrings by `deeplabcut.create_training_model_comparison?` or run the Project Manager GUI - `deeplabcut.launch_dlc()`- for assistance.
+Great question! So, the best way to do this is to use the **same** test/train split (
+that is generated in create_training_dataset) with different models. Here, as of 2.1+,
+we have a **new** function that lets you do this easily. Instead of using
+`create_training_dataset` you will run `create_training_model_comparison` (see the
+docstrings by `deeplabcut.create_training_model_comparison?` or run the Project Manager
+GUI - `deeplabcut.launch_dlc()`- for assistance.
diff --git a/docs/recipes/pose_cfg_file_breakdown.md b/docs/recipes/pose_cfg_file_breakdown.md
new file mode 100644
index 0000000000..881ccb6f4d
--- /dev/null
+++ b/docs/recipes/pose_cfg_file_breakdown.md
@@ -0,0 +1,231 @@
+---
+deeplabcut:
+ last_content_updated: '2025-02-28'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
+# The `pose_cfg.yaml` Guideline Handbook
+
+::::{warning}
+The following is specific to Tensorflow-based models. To read the equivalent explanations for Pytorch-based models,
+click [here](dlc3-pytorch-config)
+::::
+
+👋 Hello! Mabuhay! Hola! This recipe was written by the [2023 DLC AI Residents](https://www.deeplabcutairesidency.org/)!
+
+When you train, evaluate, and run inference with a neural network there are hyperparatmeters you must consider. While DLC attempts to set the "globally good for everyone" parameters, you might want to change them. Therefore, in this recipe we will review the pose config parameters related to neural network models' and the related data augmentation!
+
+# 1. What is the *pose_cfg.yml* file?
+
+- The `pose_cfg.yaml` file offers easy access to a range of training parameters that the user may want or have to adjust depending on the used dataset and task.
+- You will find the file in the dlc-models > test and train sub-directories. There is also a button in the GUI to directly open this file.
+- This recipe is aimed at giving an average user an intuition on those hyperparameters and situations in which addressing them can be useful.
+
+# 2. Quick start: full parameter list TOC
+
+- [2. Full parameter list](#2-full-parameter-list)
+ - [2.1 Training Hyperparameters](#21-training-hyperparameters)
+ - [2.1.A `max_input_size` and `min_input_size`](#21a-max_input_size-and-min_input_size)
+ - [2.1.B `global_scale`](#21b-global_scale)
+ - [2.1.C `batch_size`](#21c-batch_size)
+ - [2.1.D `pos_dist_thresh`](#21d-pos_dist_thresh)
+ - [2.1.E `pafwidth`](#21e-pafwidth)
+ - [2.2 Data augmentation parameters](#22-data-augmentation-parameters)
+ - [Geometric transformations](#geometric-transformations)
+ - [2.2.1 `scale_jitter_lo` and `scale_jitter_up`](#221-scale_jitter_lo-and-scale_jitter_up)
+ - [2.1.2 `rotation`](#212-rotation)
+ - [2.2.3 `rotratio` (rotation ratio)](#223-rotratio-rotation-ratio)
+ - [2.2.4 `fliplr` (or a horizontal flip)](#224-fliplr-or-a-horizontal-flip)
+ - [2.2.5 `crop_size`](#225-crop_size)
+ - [2.2.6 `crop_ratio`](#226-crop_ratio)
+ - [2.2.7 `max_shift`](#227-max_shift)
+ - [2.2.8 `crop_sampling`](#228-crop_sampling)
+ - [Kernel transformations](#kernel-transformations)
+ - [2.2.9 `sharpening` and `sharpenratio`](#229-sharpening-and-sharpenratio)
+ - [2.2.10 `edge`](#2210-edge)
+- [References](#references)
+
+
+## 2.1 Training Hyperparameters
+
+
+### 2.1.A `max_input_size` and `min_input_size`
+The default values are `1500` and `64`, respectively.
+
+💡Pro-tip:💡
+- change `max_input_size` when the resolution of the video is higher than 1500x1500 or when `scale_jitter_up` will possibly go over that value
+- change `min_input_size` when the resolution of the video is smaller than 64x64 or when `scale_jitter_lo` will possibly go below that value
+
+
+### 2.1.B `global_scale`
+The default value is `0.8`. It's the most basic, first scaling that happens to all images in the training queue.
+
+💡Pro-tip:💡
+- With images that are low resolution or lack detail, it may be beneficial to increase the `global_scale` to 1, to keep the original size and retain as much information as possible.
+
+### 2.1.C `batch_size`
+
+
+The default for single animal projects is 1, and for maDLC projects it's `8`. It's the number of frames used per training iteration.
+
+In both cases, you can increase the batchsize up to the limit of your GPU memory and train for a lower number of iterations. The relationship between the number of iterations and `batch_size` is not linear, so `batch_size: 8` doesn't mean you can train for 8x less iterations, but like with every training, plateauing loss can be treated as an indicator of reaching optimal performance.
+
+💡Pro-tip:💡
+- Having a higher `batch_size` can be beneficial in terms of models' generalization
+
+___________________________________________________________________________________
+
+Values mentioned above and the augmentation parameters are often intuitive, and knowing our own data, we are able to decide on what will and won't be beneficial. Unfortunately, not all hyperparameters are this simple or intuitive. Two parameters that might require some tuning on challenging datasets are `pafwidth` and `pos_dist_thresh`.
+
+
+### 2.1.D `pos_dist_thresh`
+The default value is `17`. It's the size of a window within which detections are considered positive training samples, meaning they tell the model that it's going in the right direction.
+
+
+### 2.1.E `pafwidth`
+The default value is `20`. PAF stands for part affinity fields. It is a method of learning associations between pairs of bodyparts by preserving the location and orientation of the limb (the connection between two keypoints). This learned part affinity helps in proper animal assembly, making the model less prone to associating bodyparts of one individual with those of another. [1](#ref1)
+
+
+## 2.2 Data augmentation parameters
+In the simplest form, we can think of data augmentation as something similar to imagination or dreaming. Humans imagine different scenarios based on experience, ultimately allowing us to better understand our world. [2, 3, 4](#references)
+
+Similarly, we train our models to different types of "imagined" scenarios, which we limit to the foreseeable ones, so we ultimately get a robust model that can more likely handle new data and scenes.
+
+Classes of data augmentations, characterized by their nature, are given by:
+- [**Geometric transformations**](#geometric)
+ 1. [`scale_jitter_lo` and `scale_jitter_up`](#scale_jitter)
+ 2. [`rotation`](#rot)
+ 3. [`rotratio`](#rotratio)
+ 4. [`mirror`](#mirror)
+ 5. [`crop size`](#crop_size)
+ 6. [`crop ratio`](#crop_ratio)
+ 7. [`max shift`](#max_shift)
+ 8. [`crop sampling`](#crop_sampling)
+- [**Kernel transformations**](#kernel)
+ 9. [`sharpening` and `sharpen_ratio`](#sharp)
+ 10. [`edge_enhancement`](#edge)
+
+
+### Geometric transformations
+**Geometric transformations** such as *flipping*, *rotating*, *translating*, *cropping*, *scaling*, and *injecting noise*, which are very good for positional biases present in the training data.
+
+
+### 2.2.1 `scale_jitter_lo` and `scale_jitter_up`
+*Scale jittering* resizes an image within a given resize range. This allows the model to learn from different sizes of objects in the scene, therefore increasing its robustness to generalize, especially on newer scenes or object sizes.
+
+The image below, retrieved from [3](#ref3), illustrates the difference between two scale jittering methods.
+
+
+
+During training, each image is randomly scaled within the range `[scale_jitter_lo, scale_jitter_up]` to augment training data. The default values for these two parameters are:
+- `scale_jitter_lo = 0.5`
+- `scale_jitter_up = 1.25`
+
+💡Pro-tips:💡
+- ⭐⭐⭐ If the target animal/s do not have an incredibly high variance in size throughout the video (e.g., jumping or moving towards the static camera), keeping the **default** values **unchanged** will give just enough variability in the data for the model to generalize better ✅
+
+- ⭐⭐However, you may want to adjust these parameters if you want your model to:
+ - handle new data with possibly **larger (25% bigger than original)** animal subjects ➡️ in this scenario, increase the value of *scale_jitter_up*
+ - handle new data with possibly **smaller (50% smaller than the original)** animal subjects ➡️ in this scenario, decrease the value of *scale_jitter_lo*
+ - **generalize well in new set-ups/environments** with minimal to no pre-training
+ ⚠️ But as a consequence, **training time will take longer**.😔🕒
+- ⭐If you have a fully static camera set-up and the sizes of the animals do not vary much, you may also try to **shorten** this range to **reduce training time**.😃🕒(⚠️ but, as a consequence, your model might only fit your data and not generalize well)
+
+
+### 2.1.2 `rotation`
+*Rotation augmentations* are done by rotating the image right or left on an axis between $1^{\circ}$ and $359^{\circ}$. The safety of rotation augmentations is heavily determined by the rotation degree parameter. Slight rotations such as between $+1^{\circ}$ and $+20^{\circ}$ or $-1^{\circ}$ to $-20^{\circ}$ is generally an acceptable range. Keep in mind that as the rotation degree increases, the precision of the label placement can decrease
+
+The image below, retrieved from [2](#ref2), illustrates the difference between the different rotation degrees.
+
+
+
+During training, each image is rotated $+/-$ the `rotation` degree parameter set. By default, this parameter is set to `25`, which means that the images are augmented with a $+25^{\circ}$ rotation of itself and a $-25^{\circ}$ degree rotation of itself. Should you want to opt out of this augmentation, set the rotation value to `False`.
+
+💡Pro-tips:💡
+- ⭐If you have labelled all the possible rotations of your animal/s, keeping the **default** value **unchanged** is **enough** ✅
+
+- However, you may want to adjust this parameter if you want your model to:
+ - handle new data with new rotations of the animal subjects
+ - handle the possibly unlabelled rotations of your minimally-labeled data
+ - But as a consequence, the more you increase the rotation degree, the more the original keypoint labels may not be preserved
+
+
+### 2.2.3 `rotratio` (rotation ratio)
+This parameter in the DLC module is given by the percentage of sampled data to be augmented from your training data. The default value is set to `0.4` or $40\%$. This means that there is a $40\%$ chance that images within the current batch will be rotated.
+
+💡Pro-tip:💡
+- ⭐ Generally, keeping the **default** value **unchanged** is **enough** ✅
+
+
+### 2.2.4 `fliplr` (or a horizontal flip)
+**Mirroring**, otherwise called **horizontal axis fipping**, is much more common than flipping the vertical axis. This augmentation is one of the easiest to implement and has proven useful on datasets such as CIFAR-10 and ImageNet. However, on datasets involving text recognition, such as MNIST or SVHN, this is not a label-preserving transformation.
+
+The image below is an illustration of this property (shown on the right-most column).
+
+
+
+This parameter randomly flips an image horizontally to augment training data.
+By default, this parameter is set to `False` especially on poses with mirror symmetric joints (for example, so the left hand and right hand are not swapped).
+
+💡Pro-tip:💡
+- ⭐ If you work with labels with symmetric joints, keep the **default** value **unchanged** - unless the dataset is biased (animal moves mostly in one direction, but sometimes in the opposite)✅
+- Keeping the default value to `False` will work well in most cases.
+
+
+ ### 2.2.5 `crop_size`
+ Cropping consists of removing unwanted pixels from the image, thus selecting a part of the image and discarding the rest, reducing the size of the input.
+
+ In DeepLabCut *pose_config.yaml* file, by default, `crop_size` is set to (`400,400`), width, and height, respectively. This means it will cut out parts of an image of this size.
+
+ 💡Pro-tip:💡
+ - If your images are very large, you could consider increasing the crop size. However, be aware that you'll need a strong GPU, or you will hit memory errors!
+ - If your images are very small, you could consider decreasing the crop size.
+
+
+ ### 2.2.6 `crop_ratio`
+ Also, the number of frames to be cropped is defined by the variable `cropratio`, which is set to `0.4` by default. That means that there is a $40\%$ the images within the current batch will be cropped. By default, this value works well.
+
+
+ ### 2.2.7 `max_shift`
+
+ The crop shift between each cropped image is defined by `max_shift` variable, which explains the max relative shift to the position of the crop centre. By default is set to `0.4`, which means it will be displaced 40% max from the center to not apply identical cropping each time the same image is encountered during training - this is especially important for `density` and `hybrid` cropping methods.
+
+ The image below is modified from
+ [2](#references).
+
+
+
+
+ ### 2.2.8 `crop_sampling`
+ Likewise, there are different cropping sampling methods (`crop_sampling`), we can use depending on how our image looks like.
+
+ 💡Pro-tips💡
+ - For highly crowded scenes, `hybrid` and `density` approaches will work best.
+ - `uniform` will take out random parts of the image, disregarding the annotations completely
+ - 'keypoint' centers on a random keypoint and crops based on that location - might be best in preserving the whole animal (if reasonable `crop_size` is used)
+
+
+ ### Kernel transformations
+ Kernel filters are very popular in image processing to sharpen and blur images. Intuitively, blurring an image might increase the motion blur resistance during testing. Otherwise, sharpening for data enhancement could result in capturing more detail on objects of interest.
+
+
+ ### 2.2.9 `sharpening` and `sharpenratio`
+ In DeepLabCut *pose_config.yaml* file, by default, `sharpening` is set to `False`, but if we want to use this type of data augmentation, we can set it `True` and specify a value for `sharpenratio`, which by default is set to `0.3`. Blurring is not defined in the *pose_config.yaml*, but if the user finds it convenient, it can be added to the data augmentation pipeline.
+
+ The image below is modified from
+ [2](#references).
+
+
+
+
+ ### 2.2.10 `edge`
+ Concerning sharpness, we have an additional parameter, `edge` enhancement, which enhances the edge contrast of an image to improve its apparent sharpness. Likewise, by default, this parameter is set `False`, but if you want to include it, you just need to set it `True`.
+
+
+# References
+
+ Cao, Z., Simon, T., Wei, S. E., & Sheikh, Y. (2017). Realtime multi-person 2d pose estimation using part affinity fields. In Proceedings of the IEEE conference on Computer Vision and Pattern Recognition (pp. 7291-7299).https://openaccess.thecvf.com/content_cvpr_2017/html/Cao_Realtime_Multi-Person_2D_CVPR_2017_paper.html
+ Mathis, A., Schneider, S., Lauer, J., & Mathis, M. W. (2020). A Primer on Motion Capture with Deep Learning: Principles, Pitfalls, and Perspectives. In Neuron (Vol. 108, Issue 1, pp. 44-65). https://doi.org/10.1016/j.neuron.2020.09.017
+ Ghiasi, G., Cui, Y., Srinivas, A., Qian, R., Lin, T.-Y., Cubuk, E. D., Le, Q. V., & Zoph, B. (2020). Simple Copy-Paste is a Strong Data Augmentation Method for Instance Segmentation (Version 2). arXiv. https://doi.org/10.48550/ARXIV.2012.07177
+ Shorten, C., & Khoshgoftaar, T. M. (2019). A survey on Image Data Augmentation for Deep Learning. In Journal of Big Data (Vol. 6, Issue 1). https://doi.org/10.1186/s40537-019-0197-0
+
diff --git a/docs/recipes/post.md b/docs/recipes/post.md
index cbcb78c673..1ebdf09ec5 100644
--- a/docs/recipes/post.md
+++ b/docs/recipes/post.md
@@ -1,3 +1,9 @@
+---
+deeplabcut:
+ last_content_updated: '2022-06-08'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
# Some data processing recipes!
## Flagging frames with abnormal bodypart distances
diff --git a/docs/recipes/publishing_notebooks_into_the_DLC_main_cookbook.md b/docs/recipes/publishing_notebooks_into_the_DLC_main_cookbook.md
new file mode 100644
index 0000000000..99d754c731
--- /dev/null
+++ b/docs/recipes/publishing_notebooks_into_the_DLC_main_cookbook.md
@@ -0,0 +1,146 @@
+---
+deeplabcut:
+ last_content_updated: '2025-06-30'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
+# Publishing Notebooks into the Main DLC Cookbook
+### Your Recipe Guide to Contributing to the DLC Cookbook
+
+## Introduction
+Hey there, DLC enthusiast! 🌟 Ready to sprinkle your magic into the main DLC cookbook? Whether you're introducing a zesty new dish or giving an old one a twist, this guide's got your back. We'll walk you through how to publish a new notebook or spice up an existing one in the DLC cookbook. Let's get cooking! 🍲📘
+
+## Preliminary Checks
+### Check Existing Recipes or Tutorials
+ - **Search and Review**: Before you start writing a new recipe, go through the existing DLC Jupyter book to ensure there isn't a tutorial or recipe that covers the topic you have in mind.
+ - **Expand Existing Content**: If your content is related to an existing topic, like I/O manipulations, consider expanding or refining that section instead of creating an entirely new recipe. This ensures that the Jupyter book remains concise and that related information is found in one place.
+ - **Locate and Review**: Navigate to the particular recipe or tutorial you wish to update in the DLC Jupyter book.
+ - **Consider Minor vs. Major Changes**: If you're adding a new section or significantly altering the current content, it might be worth noting the changes at the beginning or end of the recipe for clarity.
+ - **Maintain Consistency**: Ensure your updates adhere to the current style, tone, and structure of the existing content to maintain a seamless reading experience.
+
+
+## Structure of a Recipe
+ When crafting your recipe, adhere to the following structure:
+ - **Introduction**: Begin with an introductory paragraph that highlights the importance and relevance of the recipe. This sets the stage and gives readers context.
+
+ - **Examples/Workflow**: Provide step-by-step instructions or a workflow, supported by examples. This makes it easy for readers to understand and follow along.
+
+ - **Conclusion**: Conclude with a summary or highlight the key takeaways of your recipe. You can also provide references or further reading.
+
+
+Now, let's dive into the process of contributing your content to the DLC Jupyter book.
+## Steps
+
+1. **Set-up your local environment.** You need `deeplabcut[docs]` installed:
+ You can do this by running the following command:
+ ```
+ pip install deeplabcut[docs]
+ ```
+
+This command installs DeepLabCut along with the dependencies required to build the documentation.
+
+2. **Fork the DLC Repository**:
+ - Go to the DeepLabCut GitHub repository: [https://github.com/DeepLabCut/DeepLabCut](https://github.com/DeepLabCut/DeepLabCut)
+
+
+ - Click on the `Fork` button on the top-right corner of the page. This will create a copy of the repository in your own GitHub account.
+3. **Clone your forked repository**:
+ - Navigate to your forked repo on GitHub.
+ - Click the `Code` button and copy the URL.
+ - Clone the repository to your local machine:
+ ```
+ git clone [REPO_URL]
+ ```
+4. **Create a new branch**:
+ It's a good practice to create a new branch for each new feature or change:
+ ```
+ cd [YOUR_REPO_DIRECTORY]
+ git checkout -b my-new-notebook
+ ```
+5. **Create a new notebook** or **update an existing one**.
+ - **Creating a new notebook**
+ - **Choose Your Topic Wisely:** Before you start, make sure your topic fits the DLC Jupyter book's theme and brings value to its readers. A novel topic or a unique twist on an existing topic can be particularly impactful.
+ - **Craft with Care:** Remember, your notebook will be a reference for many. Begin with an engaging introduction, followed by well-structured content, and wrap it up with a conclusion.
+ - **Interactive Elements:** One of the strengths of Jupyter notebooks is the ability to combine code, visuals, and narrative. Use interactive plots, widgets, or any other tools that enhance the content and make it engaging.
+ - **Save Regularly:** Jupyter auto-saves your work, but it's a good habit to manually save your notebook frequently, especially after making significant changes.
+ - **Naming Convention:** Name your notebook in a way that reflects its content and is consistent with other notebook titles in the DLC Jupyter book. This makes it easier for readers to understand the topic at a glance.
+ - **Updating an existing notebook**
+ - Navigate to the location of the existing recipe within the directory:
+ ```
+ [YOUR_REPO_DIRECTORY]/docs/recipes/
+ ```
+ - Open the corresponding Jupyter notebook (.ipynb file) you wish to update.
+ - Make the necessary changes or additions to the content.
+ - Save the notebook once your updates are finalized.
+ - Proceed to **Step 6** *(Proofreading)* and **9** *(Testing the documentation)* (skip Steps 7 and 8).
+6. **Proofread:** Double check for spelling and grammatical errors by using Jupyter notebook's spellcheck extension called `spellchecker` (or your preferred spell-checker).
+ ```
+ jupyter nbextension enable spellchecker/main
+ ```
+ Once installed, restart your notebook, and when you load your notebook again, you will see the incorrectly spelled words highlighted in red.
+7. **Add your notebook** to the recipe directory at `[YOUR_REPO_DIRECTORY]/docs/recipes/`
+
+ - Navigate to the appropriate directory where the Jupyter notebooks are stored for the Jupyter book.
+ - Add your Jupyter notebook (.ipynb file) to this directory.
+
+ To copy via terminal:
+
+ - Unix-based OS users
+
+ ```
+ cp [YOUR_NOTEBOOK_FILENAME].ipynb [YOUR_REPO_DIRECTORY]/docs/recipes
+ ```
+
+ - WinOS users:
+ ```
+ copy new_recipe.ipynb [YOUR_REPO_DIRECTORY]\docs\recipes
+
+ ```
+
+8. **Update `[YOUR_REPO_DIRECTORY]/_toc.yml`** by adding under the *Tutorials & Cookbook* section a **new line** containing the path to your notebook. This creates a link to your notebook on the main DLC book sidebar.
+
+ * For example:
+ ```
+ - file: docs/recipes/[YOUR_NOTEBOOK_FILENAME]
+ ```
+
+9. **Test the documentation:**
+
+ - Build your notebook into the DLC recipe book
+ ```
+ jupyter book build [YOUR_REPO_DIRECTORY]
+ ```
+ - Once build is successful, the newly built book can be accessed at `[YOUR_REPO_DIRECTORY]/_build/html/`.
+ - Open `index.html` and check whether your notebook was rendered properly and if the links are working.
+
+10. **Commit your changes:**
+ When everything is a-okay, commit your changes to your branch. If not, edit your file and go to back to step 1.
+
+ ```
+ git add [YOUR_NOTEBOOK_FILENAME]
+ git commit -m "Added a new notebook about [YOUR_TOPIC]"
+ ```
+
+11. **Push your branch to your fork:**
+
+ ```
+ git push origin my-new-notebook
+ ```
+
+
+12. **Submit a Pull Request (PR):**
+
+ - Go to your forked repository on GitHub.
+ - You'll likely see a message prompting you to create a pull request from your recently pushed branch. Click `Compare & pull request`.
+ - Fill out the PR form with a descriptive title and comments describing your notebook. This will help the maintainers understand the context and purpose of your notebook.
+ - Click `Create pull request`.
+
+13. **Make Necessary Changes**: The DeepLabCut maintainers will then review your PR and provide feedback. If changes are required, make the necessary changes on your local branch, commit them, and push the branch again. The PR will automatically update.
+
+14. **🎉PR Approval:🎉** Once your PR is approved, the maintainers will merge it into the main repository. Your notebook will then be a part of the DeepLabCut Jupyter book! Yay!
+
+Remember to always check the [DLC contributing guidelines](https://github.com/DeepLabCut/DeepLabCut/blob/main/CONTRIBUTING.md).
+
+
+## Wrap-Up 🎉
+Alright! 🌟 By now, you've got the playbook to jazz up the DeepLabCut Jupyter book. Remember, it's not just about cooking up new recipes but also spicing up the old ones. Dive in, have fun, and let's make this book a flavor-packed feast for all DLC enthusiasts out there. High-five for joining the party! 🙌🎈
diff --git a/docs/roadmap.md b/docs/roadmap.md
index b271470877..04228cb56e 100644
--- a/docs/roadmap.md
+++ b/docs/roadmap.md
@@ -1,8 +1,26 @@
+---
+deeplabcut:
+ last_content_updated: '2025-02-28'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
(dev-roadmap)=
## A development roadmap for DeepLabCut
-:loudspeaker: :hourglass_flowing_sand: :construction:
+📢 ⏳ 🚧
+
+**General Enhancements:**
+- [ ] DeepLabCut PyTorch & Model Zoo --> DLC 3.0 🔥
+- [X] DLC-CookBook v0.1
+- [X] DLC BLog for releases and user-highlights
+- [X] New Docker containers into main repo / linked to Docker hub and repo(s)
+- [ ] 3D >2 camera support --> better 3D in PyTorch version 🔥
+
+**General NN Improvements:**
+- [X] EfficientNet backbones added (currently SOTA on ImageNet). https://openaccess.thecvf.com/content/WACV2021/html/Mathis_Pretraining_Boosts_Out-of-Domain_Robustness_for_Pose_Estimation_WACV_2021_paper.html https://github.com/DeepLabCut/DeepLabCut/commit/96da2cacf837a9b84ecdeafb50dfb4a93b402f33
+- [X] New multi-fusion multi-scale networks; DLCRNet_ms5
+- [ ] BUCTD Integration, see ICCV 2023 paper at https://arxiv.org/abs/2306.07879
**deeplabcut 2.2: multi-animal pose estimation and tracking with DeepLabCut**
- [X] alpha testing complete (early May 2020)
@@ -17,35 +35,26 @@
**real-time module with DEMO for how to set up on your camera system, integration with our [Camera Control Software]**(https://github.com/AdaptiveMotorControlLab/Camera_Control)
- [X] Integration with Bonsai completed! See: https://github.com/bonsai-rx/deeplabcut
-- [X] Integreation with Auto-pi-lot. See: https://auto-pi-lot.com/
+- [X] Integration with Auto-pi-lot. See: https://auto-pi-lot.com/
- [X] DeepLabCut-live! released Aug 5th, 2020: preprint & code: https://www.biorxiv.org/content/10.1101/2020.08.04.236422v1
- [X] DeepLabCut-live! published in eLife
**DeepLabCut Model Zoo: a collection of pretrained models for plug-in-play DLC and community crowd-sourcing.**
-- [X] BETA release with 2.1.8b0: http://www.mousemotorlab.org/dlc-modelzoo
-- [X] full release with 2.1.8.1 http://www.mousemotorlab.org/dlc-modelzoo
-- [ ] Manuscript forthcoming!
+- [X] BETA release with 2.1.8b0: https://www.mackenziemathislab.org/deeplabcut
+- [X] full release with 2.1.8.1 https://www.mackenziemathislab.org/deeplabcut
+- [X] Manuscript forthcoming! --> see arXiv https://arxiv.org/abs/2203.07436
- [X] new models added; horse, cheetah
-- [ ] TopView_Mouse model
+- [X] TopView_Mouse model
+- [X] Quadruped model
- [ ] contribution module
+- [ ] PyTorch Model zoo code
**DeepLabCut GUI and DeepLabCut-core:**
- [X] to make DLC more modular, we will move core functions to https://github.com/DeepLabCut/DeepLabCut-core
- [X] DLC-core depreciated, and core is now simply `pip install deeplabcut` GUI is with `pip install deeplabcut[gui]`
-- [ ] new GUI for DeepLabCut; due to extended issues with wxPython, we will be moving to release a napari plugin https://github.com/napari/napari
-- [ ] New project management GUI
+- [X] new GUI for DeepLabCut; due to extended issues with wxPython, we will be moving to release a napari plugin https://github.com/napari/napari
+- [X] New project management GUI
- [X] tensorflow 2.2 support in DeepLabCut-core: https://github.com/DeepLabCut/DeepLabCut/issues/601
- [X] DeepLabCut-Core to be depreciated; TF2 will go into main repo.
- [X] TF2 support while also maintaining TF1 support until 2022.
- [ ] Web-based GUI for labeling --> Colab training pipeline for users (full no-install DLC)
-
-**General NN Improvements:**
-- [X] EfficientNet backbones added (currently SOTA on ImageNet). https://openaccess.thecvf.com/content/WACV2021/html/Mathis_Pretraining_Boosts_Out-of-Domain_Robustness_for_Pose_Estimation_WACV_2021_paper.html https://github.com/DeepLabCut/DeepLabCut/commit/96da2cacf837a9b84ecdeafb50dfb4a93b402f33
-- [X] New multi-fusion multi-scale networks; DLCRNet_ms5
-
-**General Enhancements:**
-- [ ] DeepLabCut PyTorch model zoo
-- [X] DLC-CookBook v0.1
-- [X] DLC BLog for releases and user-highlights
-- [X] New Docker containers into main repo / linked to Docker hub and repo(s)
-- [ ] 3D >2 camera support
diff --git a/docs/standardDeepLabCut_UserGuide.md b/docs/standardDeepLabCut_UserGuide.md
index e46cb0f2e8..59985ea3bb 100644
--- a/docs/standardDeepLabCut_UserGuide.md
+++ b/docs/standardDeepLabCut_UserGuide.md
@@ -1,71 +1,142 @@
+---
+deeplabcut:
+ last_content_updated: '2025-06-30'
+ last_metadata_updated: '2026-03-06'
+ ignore: false
+---
(single-animal-userguide)=
# DeepLabCut User Guide (for single animal projects)
-This document covers single/standard DeepLabCut use. If you have a complicated multi-animal scenario (i.e., they look the same), then please see our [maDLC user guide](multi-animal-userguide).
+This document covers single/standard DeepLabCut use. If you have a complicated multi-animal scenario (i.e., they look
+the same), then please see our [maDLC user guide](multi-animal-userguide).
To get started, you can use the GUI, or the terminal. See below.
## DeepLabCut Project Manager GUI (recommended for beginners)
+
+
**GUI:**
-Simply ``python -m deeplabcut`` or MacOS: ``pythonw -m deeplabcut``. The below functions are available to you in an easy-to-use graphical user interface. While most functionality is available, advanced users might want the additional flexibility that command line interface offers. Read more [here](https://deeplabcut.github.io/DeepLabCut/docs/PROJECT_GUI.html).
+
+To begin, navigate to Anaconda Prompt Terminal and right-click to "open as admin "(Windows), or simply launch
+"Terminal" (unix/MacOS) on your computer. We assume you have DeepLabCut installed (if not, see
+[install docs](how-to-install)!). Next, launch your conda env (i.e., for example `conda activate DEEPLABCUT`). Then,
+simply run `python -m deeplabcut`. The below functions are available to you in an easy-to-use graphical user interface.
+While most functionality is available, advanced users might want the additional flexibility that command line interface
+offers. Read more below.
+```{Hint}
+🚨 If you use Windows, please always open the terminal with administrator privileges! Right click, and "run as administrator".
+```
-As a reminder, the core functions are described in our [Nature Protocols](https://www.nature.com/articles/s41596-019-0176-0) paper (published at the time of 2.0.6). Additional functions and features are continually added to the package. Thus, we recommend you read over the protocol and then please look at the following documentation and the doctrings. Thanks for using DeepLabCut!
+As a reminder, the core functions are described in our
+[Nature Protocols paper](https://www.nature.com/articles/s41596-019-0176-0) (published at the time of 2.0.6).
+Additional functions and features are continually added to the package. Thus, we recommend you read over the protocol
+and then please look at the following documentation and the doctrings. Thanks for using DeepLabCut!
+
+## DeepLabCut in the Terminal/Command line interface:
-## DeepLabCut in the Terminal:
+To begin, navigate to Anaconda Prompt Terminal and right-click to "open as admin "(Windows), or simply launch
+"Terminal" (unix/MacOS) on your computer. We assume you have DeepLabCut installed (if not, see Install docs!). Next,
+launch your conda env (i.e., for example `conda activate DEEPLABCUT`) and then type `ipython`. Then type:
+```python
+import deeplabcut
+```
-To begin, navigate to anaconda prompt and right-click to "open as admin "(windows), or simply launch "terminal" (unix/MacOS) on your computer. We assume you have DeepLabCut installed (if not, go here). Next, launch your conda env (i.e., for example `conda activate DLC-CPU`) and then type (windows/unix) `ipython` or (macOS) `pythonw`. Then type `import deeplabcut`.
+```{Hint}
+🚨 If you use Windows, please always open the terminal with administrator privileges! Right click, and "run as administrator".
+```
### (A) Create a New Project
-The function **create\_new\_project** creates a new project directory, required subdirectories, and a basic project configuration file. Each project is identified by the name of the project (e.g. Reaching), name of the experimenter (e.g. YourName), as well as the date at creation.
+The function `create_new_project` creates a new project directory, required subdirectories, and a basic project
+configuration file. Each project is identified by the name of the project (e.g. Reaching), name of the experimenter
+(e.g. YourName), as well as the date at creation.
-Thus, this function requires the user to input the name of the project, the name of the experimenter, and the full path of the videos that are (initially) used to create the training dataset.
+Thus, this function requires the user to input the name of the project, the name of the experimenter, and the full
+path of the videos that are (initially) used to create the training dataset.
-Optional arguments specify the working directory, where the project directory will be created, and if the user wants to copy the videos (to the project directory). If the optional argument working\_directory is unspecified, the project directory is created in the current working directory, and if copy\_videos is unspecified symbolic links for the videos are created in the videos directory. Each symbolic link creates a reference to a video and thus eliminates the need to copy the entire video to the video directory (if the videos remain at the original location).
+Optional arguments specify the working directory, where the project directory will be created, and if the user wants
+to copy the videos (to the project directory). If the optional argument `working_directory` is unspecified, the
+project directory is created in the current working directory, and if `copy_videos` is unspecified symbolic links
+for the videos are created in the videos directory. Each symbolic link creates a reference to a video and thus
+eliminates the need to copy the entire video to the video directory (if the videos remain at the original location).
```python
-deeplabcut.create_new_project('Name of the project', 'Name of the experimenter', ['Full path of video 1', 'Full path of video2', 'Full path of video3'], working_directory='Full path of the working directory', copy_videos=True/False, multianimal=True/False)
+deeplabcut.create_new_project(
+ "Name of the project",
+ "Name of the experimenter",
+ ["Full path of video 1", "Full path of video2", "Full path of video3"],
+ working_directory="Full path of the working directory",
+ copy_videos=True/False,
+ multianimal=False
+)
```
**Important path formatting note**
-Windows users, you must input paths as: ``r'C:\Users\computername\Videos\reachingvideo1.avi' `` or
-
-`` 'C:\\Users\\computername\\Videos\\reachingvideo1.avi'``
-
- (TIP: you can also place ``config_path`` in front of ``deeplabcut.create_new_project`` to create a variable that holds the path to the config.yaml file, i.e. ``config_path=deeplabcut.create_new_project(...)``)
-
-
-This set of arguments will create a project directory with the name **Name of the project+name of the experimenter+date of creation of the project** in the **Working directory** and creates the symbolic links to videos in the **videos** directory. The project directory will have subdirectories: **dlc-models**, **labeled-data**, **training-datasets**, and **videos**. All the outputs generated during the course of a project will be stored in one of these subdirectories, thus allowing each project to be curated in separation from other projects. The purpose of the subdirectories is as follows:
-
-**dlc-models:** This directory contains the subdirectories *test* and *train*, each of which holds the meta information with regard to the parameters of the feature detectors in configuration files. The configuration files are YAML files, a common human-readable data serialization language. These files can be opened and edited with standard text editors. The subdirectory *train* will store checkpoints (called snapshots in TensorFlow) during training of the model. These snapshots allow the user to reload the trained model without re-training it, or to pick-up training from a particular saved checkpoint, in case the training was interrupted.
-
-**labeled-data:** This directory will store the frames used to create the training dataset. Frames from different videos are stored in separate subdirectories. Each frame has a filename related to the temporal index within the corresponding video, which allows the user to trace every frame back to its origin.
-
-**training-datasets:** This directory will contain the training dataset used to train the network and metadata, which contains information about how the training dataset was created.
-
-**videos:** Directory of video links or videos. When **copy\_videos** is set to ``False``, this directory contains symbolic links to the videos. If it is set to ``True`` then the videos will be copied to this directory. The default is ``False``. Additionally, if the user wants to add new videos to the project at any stage, the function **add\_new\_videos** can be used. This will update the list of videos in the project's configuration file.
+Windows users, you must input paths as: `r'C:\Users\computername\Videos\reachingvideo1.avi'` or
+` 'C:\\Users\\computername\\Videos\\reachingvideo1.avi'`
+
+TIP: you can also place `config_path` in front of `deeplabcut.create_new_project` to create a variable that holds
+the path to the config.yaml file, i.e. `config_path=deeplabcut.create_new_project(...)`
+
+This set of arguments will create a project directory with the name
+**
++** in the **Working directory** and
+creates the symbolic links to videos in the **videos** directory. The project directory will have subdirectories:
+**dlc-models**, **dlc-models-pytorch**, **labeled-data**, **training-datasets**, and **videos**. All the outputs
+generated during the course of a project will be stored in one of these subdirectories, thus allowing each project to be
+curated in separation from other projects. The purpose of the subdirectories is as follows:
+
+**dlc-models** and **dlc-models-pytorch** have a similar structure; the first contains files for the TensorFlow engine
+while the second contains files for the PyTorch engine. At the top level in these directories, there are directories
+referring to different iterations of label refinement (see below): **iteration-0**, **iteration-1**, etc.
+The iteration directories store shuffle directories, where each shuffle directory stores model data related to a
+particular experiment: trained and tested on a particular training and testing sets, and with a particular model
+architecture. Each shuffle directory contains the subdirectories *test* and *train*, each of which holds the meta
+information with regard to the parameters of the feature detectors in configuration files. The configuration files are
+YAML files, a common human-readable data serialization language. These files can be opened and edited with standard text
+editors. The subdirectory *train* will store checkpoints (called snapshots) during training of the model. These
+snapshots allow the user to reload the trained model without re-training it, or to pick-up training from a particular
+saved checkpoint, in case the training was interrupted.
+
+**labeled-data:** This directory will store the frames used to create the training dataset. Frames from different videos
+are stored in separate subdirectories. Each frame has a filename related to the temporal index within the corresponding
+video, which allows the user to trace every frame back to its origin.
+
+**training-datasets:** This directory will contain the training dataset used to train the network and metadata, which
+contains information about how the training dataset was created.
+
+**videos:** Directory of video links or videos. When **copy\_videos** is set to `False`, this directory contains
+symbolic links to the videos. If it is set to `True` then the videos will be copied to this directory. The default is
+`False`. Additionally, if the user wants to add new videos to the project at any stage, the function
+**add\_new\_videos** can be used. This will update the list of videos in the project's configuration file.
```python
-deeplabcut.add_new_videos('Full path of the project configuration file*', ['full path of video 4', 'full path of video 5'], copy_videos=True/False)
+deeplabcut.add_new_videos(
+ "Full path of the project configuration file*",
+ ["full path of video 4", "full path of video 5"],
+ copy_videos=True/False
+)
```
-*Please note, *Full path of the project configuration file* will be referenced as ``config_path`` throughout this protocol.
+*Please note, *Full path of the project configuration file* will be referenced as `config_path` throughout this
+protocol.
-The project directory also contains the main configuration file called *config.yaml*. The *config.yaml* file contains many important parameters of the project. A complete list of parameters including their description can be found in Box1.
+The project directory also contains the main configuration file called *config.yaml*. The *config.yaml* file contains
+many important parameters of the project. A complete list of parameters including their description can be found in
+Box1.
-The ``create_new_project`` step writes the following parameters to the configuration file: *Task*, *scorer*, *date*, *project\_path* as well as a list of videos *video\_sets*. The first three parameters should **not** be changed. The list of videos can be changed by adding new videos or manually removing videos.
+The `create_new_project` step writes the following parameters to the configuration file: *Task*, *scorer*, *date*,
+*project\_path* as well as a list of videos *video\_sets*. The first three parameters should **not** be changed. The
+list of videos can be changed by adding new videos or manually removing videos.
-
-
-
+
-#### API Docs
+### API Docs
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -77,34 +148,61 @@ The ``create_new_project`` step writes the following parameters to the configura
-Next, open the **config.yaml** file, which was created during **create\_new\_project**. You can edit this file in any text editor. Familiarize yourself with the meaning of the parameters (Box 1). You can edit various parameters, in particular you **must add the list of *bodyparts* (or points of interest)** that you want to track. You can also set the *colormap* here that is used for all downstream steps (can also be edited at anytime), like labeling GUIs, videos, etc. Here any [matplotlib colormaps](https://matplotlib.org/tutorials/colors/colormaps.html) will do!
+Next, open the **config.yaml** file, which was created during **create\_new\_project**. You can edit this file in any
+text editor. Familiarize yourself with the meaning of the parameters (Box 1). You can edit various parameters, in
+particular you **must add the list of *bodyparts* (or points of interest)** that you want to track. You can also set the
+*colormap* here that is used for all downstream steps (can also be edited at anytime), like labeling GUIs, videos, etc.
+Here any [matplotlib colormaps](https://matplotlib.org/tutorials/colors/colormaps.html) will do!
Please DO NOT have spaces in the names of bodyparts.
**bodyparts:** are the bodyparts of each individual (in the above list).
- ### (C) Data Selection (extract frames)
-
-**CRITICAL:** A good training dataset should consist of a sufficient number of frames that capture the breadth of the behavior. This ideally implies to select the frames from different (behavioral) sessions, different lighting and different animals, if those vary substantially (to train an invariant, robust feature detector). Thus for creating a robust network that you can reuse in the laboratory, a good training dataset should reflect the diversity of the behavior with respect to postures, luminance conditions, background conditions, animal identities,etc. of the data that will be analyzed. For the simple lab behaviors comprising mouse reaching, open-field behavior and fly behavior, 100−200 frames gave good results [Mathis et al, 2018](https://www.nature.com/articles/s41593-018-0209-y). However, depending on the required accuracy, the nature of behavior, the video quality (e.g. motion blur, bad lighting) and the context, more or less frames might be necessary to create a good network. Ultimately, in order to scale up the analysis to large collections of videos with perhaps unexpected conditions, one can also refine the data set in an adaptive way (see refinement below).
-
-The function `extract_frames` extracts frames from all the videos in the project configuration file in order to create a training dataset. The extracted frames from all the videos are stored in a separate subdirectory named after the video file’s name under the ‘labeled-data’. This function also has various parameters that might be useful based on the user’s need.
+ ### (C) Select Frames to Label
+
+**CRITICAL:** A good training dataset should consist of a sufficient number of frames that capture the breadth of the
+behavior. This ideally implies to select the frames from different (behavioral) sessions, different lighting and
+different animals, if those vary substantially (to train an invariant, robust feature detector). Thus for creating a
+robust network that you can reuse in the laboratory, a good training dataset should reflect the diversity of the
+behavior with respect to postures, luminance conditions, background conditions, animal identities,etc. of the data that
+will be analyzed. For the simple lab behaviors comprising mouse reaching, open-field behavior and fly behavior, 100−200
+frames gave good results [Mathis et al, 2018](https://www.nature.com/articles/s41593-018-0209-y). However, depending on
+the required accuracy, the nature of behavior, the video quality (e.g. motion blur, bad lighting) and the context, more
+or less frames might be necessary to create a good network. Ultimately, in order to scale up the analysis to large
+collections of videos with perhaps unexpected conditions, one can also refine the data set in an adaptive way (see
+refinement below).
+
+The function `extract_frames` extracts frames from all the videos in the project configuration file in order to create
+a training dataset. The extracted frames from all the videos are stored in a separate subdirectory named after the video
+file’s name under the ‘labeled-data’. This function also has various parameters that might be useful based on the user’s
+need.
```python
-deeplabcut.extract_frames(config_path, mode='automatic/manual', algo='uniform/kmeans', userfeedback=False, crop=True/False)
+deeplabcut.extract_frames(
+ config_path,
+ mode="automatic/manual",
+ algo="uniform/kmeans",
+ crop=True/False,
+ userfeedback=False
+)
```
**CRITICAL POINT:** It is advisable to keep the frame size small, as large frames increase the training and
inference time. The cropping parameters for each video can be provided in the config.yaml file (and see below).
-When running the function extract_frames, if the parameter crop=True, then you will be asked to draw a box within the GUI (and this is written to the config.yaml file).
-
-`userfeedback` allows the user to check which videos they wish to extract frames from. In this way, if you added more videos to the config.yaml file it does not, by default, extract frames (again) from every video. If you wish to disable this question, set `userfeedback = True`.
-
-The provided function either selects frames from the videos that are randomly sampled from a uniform distribution (uniform), by clustering based on visual appearance (k-means), or by manual selection. Random
-selection of frames works best for behaviors where the postures vary across the whole video. However, some behaviors
-might be sparse, as in the case of reaching where the reach and pull are very fast and the mouse is not moving much
-between trials (thus, we have the default set to True, as this is best for most use-cases we encounter). In such a case, the function that allows selecting frames based on k-means derived quantization would
-be useful. If the user chooses to use k-means as a method to cluster the frames, then this function downsamples the
-video and clusters the frames using k-means, where each frame is treated as a vector. Frames from different clusters
-are then selected. This procedure makes sure that the frames look different. However, on large and long videos, this
-code is slow due to computational complexity.
+When running the function extract_frames, if the parameter crop=True, then you will be asked to draw a box within the
+GUI (and this is written to the config.yaml file).
+
+`userfeedback` allows the user to specify which videos they wish to extract frames from. When set to `"True"`, a dialog
+will be initiated, where the user is asked for each video if (additional/any) frames from this video should be
+extracted. Use this, e.g. if you have already labeled some folders and want to extract data for new videos.
+
+The provided function either selects frames from the videos that are randomly sampled from a uniform distribution
+(uniform), by clustering based on visual appearance (k-means), or by manual selection. Random uniform selection of
+frames works best for behaviors where the postures vary across the whole video. However, some behaviors might be sparse,
+as in the case of reaching where the reach and pull are very fast and the mouse is not moving much between trials. In
+such a case, the function that allows selecting frames based on k-means derived quantization would be useful. If the
+user chooses to use k-means as a method to cluster the frames, then this function downsamples the video and clusters the
+frames using k-means, where each frame is treated as a vector. Frames from different clusters are then selected. This
+procedure makes sure that the frames look different. However, on large and long videos, this code is slow due to
+computational complexity.
**CRITICAL POINT:** It is advisable to extract frames from a period of the video that contains interesting
behaviors, and not extract the frames across the whole video. This can be achieved by using the start and stop
@@ -112,19 +210,22 @@ parameters in the config.yaml file. Also, the user can change the number of fram
the numframes2extract in the config.yaml file.
However, picking frames is highly dependent on the data and the behavior being studied. Therefore, it is hard to
-provide all purpose code that extracts frames to create a good training dataset for every behavior and animal. If the user feels specific frames are lacking, they can extract hand selected frames of interest using the interactive GUI
+provide all purpose code that extracts frames to create a good training dataset for every behavior and animal. If the
+user feels specific frames are lacking, they can extract hand selected frames of interest using the interactive GUI
provided along with the toolbox. This can be launched by using:
```python
-deeplabcut.extract_frames(config_path, 'manual')
+deeplabcut.extract_frames(config_path, "manual")
```
The user can use the *Load Video* button to load one of the videos in the project configuration file, use the scroll
-bar to navigate across the video and *Grab a Frame* (or a range of frames, as of version 2.0.5) to extract the frame(s). The user can also look at the extracted frames and e.g. delete frames (from the directory) that are too similar before reloading the set and then manually annotating them.
+bar to navigate across the video and *Grab a Frame* (or a range of frames, as of version 2.0.5) to extract the frame(s).
+The user can also look at the extracted frames and e.g. delete frames (from the directory) that are too similar before
+reloading the set and then manually annotating them.
-#### API Docs
+### API Docs
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -134,43 +235,42 @@ bar to navigate across the video and *Grab a Frame* (or a range of frames, as of
### (D) Label Frames
-The toolbox provides a function **label_frames** which helps the user to easily label all the extracted frames using
-an interactive graphical user interface (GUI). The user should have already named the body parts to label (points of
-interest) in the project’s configuration file by providing a list. The following command invokes the labeling toolbox.
+The toolbox provides a function **label_frames** which helps the user to easily label
+all the extracted frames using an interactive graphical user interface (GUI). The user
+should have already named the bodyparts to label (points of interest) in the
+project’s configuration file by providing a list. The following command invokes the
+napari-deeplabcut labelling GUI. Checkout the [napari-deeplabcut docs](file:napari-gui-landing) for
+more information about the labelling workflow.
+
```python
deeplabcut.label_frames(config_path)
```
-The user needs to use the *Load Frames* button to select the directory which stores the extracted frames from one of
-the videos. Subsequently, the user can use one of the radio buttons (top right) to select a body part to label. RIGHT click to add the label. Left click to drag the label, if needed. If you label a part accidentally, you can use the middle button on your mouse to delete! If you cannot see a body part in the frame, skip over the label! Please see the ``HELP`` button for more user instructions. This auto-advances once you labeled the first body part. You can also advance to the next frame by clicking on the RIGHT arrow on your keyboard (and go to a previous frame with LEFT arrow).
-Each label will be plotted as a dot in a unique color.
-The user is free to move around the body part and once satisfied with its position, can select another radio button
-(in the top right) to switch to the respective body part (it otherwise auto-advances). The user can skip a body part if it is not visible. Once all the visible body parts are labeled, then the user can use ‘Next Frame’ to load the following frame. The user needs to save the labels after all the frames from one of the videos are labeled by clicking the save button at the bottom right. Saving the labels will create a labeled dataset for each video in a hierarchical data file format (HDF) in the
-subdirectory corresponding to the particular video in **labeled-data**. You can save at any intermediate step (even without closing the GUI, just hit save) and you return to labeling a dataset by reloading it!
+[🎥 DEMO](https://youtu.be/hsA9IB5r73E)
+
+HOT KEYS IN THE Labeling GUI (also see "help" in GUI):
+
+```
+Ctrl + C: Copy labels from previous frame.
+Keyboard arrows: advance frames.
+Delete key: delete label.
+```
+
+
**CRITICAL POINT:** It is advisable to **consistently label similar spots** (e.g., on a wrist that is very large, try
to label the same location). In general, invisible or occluded points should not be labeled by the user. They can
simply be skipped by not applying the label anywhere on the frame.
OPTIONAL: In the event of adding more labels to the existing labeled dataset, the user need to append the new
-labels to the bodyparts in the config.yaml file. Thereafter, the user can call the function **label_frames**. As of 2.0.5+: then a box will pop up and ask the user if they wish to display all parts, or only add in the new labels. Saving the labels after all the images are labelled will append the new labels to the existing labeled dataset.
+labels to the bodyparts in the config.yaml file. Thereafter, the user can call the function **label_frames**. As of
+2.0.5+: then a box will pop up and ask the user if they wish to display all parts, or only add in the new labels.
+Saving the labels after all the images are labelled will append the new labels to the existing labeled dataset.
-HOT KEYS IN THE Labeling GUI (also see "help" in GUI):
-```
-Ctrl + C: Copy labels from previous frame. With multi-animal DLC, only the keypoints of the animal currently selected are duplicated.
-Keyboard arrows: advance frames
-delete key: delete label
-```
+For more information, checkout the [napari-deeplabcut docs](file:napari-gui-landing) for
+more information about the labelling workflow.
-#### API Docs
-````{admonition} Click the button to see API Docs
-:class: dropdown
-```{eval-rst}
-.. include:: ./api/deeplabcut.label_frames.rst
-```
-````
-
-### (E) Check Annotated Frames
+### (E) Check Annotated Frames
OPTIONAL: Checking if the labels were created and stored correctly is beneficial for training, since labeling
is one of the most critical parts for creating the training dataset. The DeepLabCut toolbox provides a function
@@ -179,9 +279,12 @@ is one of the most critical parts for creating the training dataset. The DeepLab
deeplabcut.check_labels(config_path, visualizeindividuals=True/False)
```
-For each video directory in labeled-data this function creates a subdirectory with **labeled** as a suffix. Those directories contain the frames plotted with the annotated body parts. The user can double check if the body parts are labeled correctly. If they are not correct, the user can reload the frames (i.e. `deeplabcut.label_frames`), move them around, and click save again.
+For each video directory in labeled-data this function creates a subdirectory with **labeled** as a suffix. Those
+directories contain the frames plotted with the annotated body parts. The user can double check if the body parts are
+labeled correctly. If they are not correct, the user can reload the frames (i.e. `deeplabcut.label_frames`), move them
+around, and click save again.
-#### API Docs
+### API Docs
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -189,97 +292,228 @@ For each video directory in labeled-data this function creates a subdirectory wi
```
````
-### (F) Create Training Dataset(s)
+(create-training-dataset)=
+### (F) Create Training Dataset
-**CRITICAL POINT:** Only run this step **where** you are going to train the network. If you label on your laptop but move your project folder to Google Colab or AWS, lab server, etc, then run the step below on that platform! If you labeled on a Windows machine but train on Linux, this is fine as of 2.0.4 onwards it will be done automatically (it saves file sets as both Linux and Windows for you).
+**CRITICAL POINT:** Only run this step **where** you are going to train the network. If you label on your laptop but
+move your project folder to Google Colab or AWS, lab server, etc, then run the step below on that platform! If you
+labeled on a Windows machine but train on Linux, this is fine as of 2.0.4 onwards it will be done automatically (it
+saves file sets as both Linux and Windows for you).
-- If you move your project folder, you must **only** change the `project_path` in the main config.yaml file - that's it - no need to change the video paths, etc! Your project is fully portable.
+- If you move your project folder, you must only change the `project_path` (which is done automatically) in the main
+config.yaml file - that's it - no need to change the video paths, etc! Your project is fully portable.
-- If you run this on the cloud, before importing `deeplabcut` you need to suppress GUIs. As you can see in our [demo notebooks]((https://github.com/DeepLabCut/DeepLabCut/blob/master/examples/COLAB_DEMO_mouse_openfield.ipynb) for running DLC training, evaluation, and novel video analysis on the Cloud, you must first suppress GUIs - server computers don't have a screen you can interact with. So, before you launch ipython, run `export DLClight=True` (see more tips in the full PDF user-guide).
+- Be aware you select your neural network backbone at this stage. As of DLC3+ we support PyTorch (and TensorFlow, but
+this will be phased out).
-**OVERVIEW:** This function combines the labeled datasets from all the videos and splits them to create train and test datasets. The training data will be used to train the network, while the test data set will be used for evaluating the network. The function **create_training_dataset** performs those steps.
+**OVERVIEW:** This function combines the labeled datasets from all the videos and splits them to create train and test
+datasets. The training data will be used to train the network, while the test data set will be used for evaluating the
+network.
```python
-deeplabcut.create_training_dataset(config_path, augmenter_type='imgaug')
-```
-
-- OPTIONAL: If the user wishes to benchmark the performance of the DeepLabCut, they can create multiple
-training datasets by specifying an integer value to the `num_shuffles`; see the docstring for more details.
-
-- Each iteration of the creation of a training dataset will create a ``.mat`` file, which is used by the feature detectors,
-and a ``.pickle`` file that contains the meta information about the training dataset. This also creates two subdirectories
-within **dlc-models** called ``test`` and ``train``, and these each have a configuration file called pose_cfg.yaml.
-Specifically, the user can edit the **pose_cfg.yaml** within the **train** subdirectory before starting the training. These
-configuration files contain meta information with regard to the parameters of the feature detectors. Key parameters
-are listed in Box 2.
-
-- At this step, the ImageNet pre-trained networks (i.e. ResNet-50, ResNet-101 and ResNet-152, etc) weights will be downloaded. If they do not download (you will see this downloading in the terminal, then you may not have permission to do so (something we have seen with some Windows users - see the **[docs for more help!](https://deeplabcut.github.io/DeepLabCut/docs/recipes/nn.html)**).
-
-**CRITICAL POINT:** At this step, for **create_training_dataset** you select the network you want to use, and any additional data augmentation (beyond our defaults). You can set ``net_type`` and ``augmenter_type`` when you call the function.
-
-**DATA AUGMENTATION:** At this stage you can also decide what type of augmentation to use. The default loaders work well for most all tasks (as shown on www.deeplabcut.org), but there are many options, more data augmentation, intermediate supervision, etc. Please look at the [**pose_cfg.yaml**](https://github.com/DeepLabCut/DeepLabCut/blob/master/deeplabcut/pose_cfg.yaml) file for a full list of parameters **you might want to change before running this step.** There are several data loaders that can be used. For example, you can use the default loader (introduced and described in the Nature Protocols paper), [TensorPack](https://github.com/tensorpack/tensorpack) for data augmentation (currently this is easiest on Linux only), or [imgaug](https://imgaug.readthedocs.io/en/latest/). We recommend `imgaug`. You can set this by passing:``` deeplabcut.create_training_dataset(config_path, augmenter_type='imgaug') ```
-
-The differences of the loaders are as follows:
-- `imgaug`: a lot of augmentation possibilities, efficient code for target map creation & batch sizes >1 supported. You can set the parameters such as the `batch_size` in the `pose_cfg.yaml` file for the model you are training. This is the recommended DEFAULT!
-- `crop_scale`: our standard DLC 2.0 introduced in Nature Protocols variant (scaling, auto-crop augmentation)
-- `tensorpack`: a lot of augmentation possibilities, multi CPU support for fast processing, target maps are created less efficiently than in imgaug, does not allow batch size>1
-- `deterministic`: only useful for testing, freezes numpy seed; otherwise like default.
-
-Alternatively, you can set the loader (as well as other training parameters) in the **pose_cfg.yaml** file of the model that you want to train. Note, to get details on the options, look at the default file: [**pose_cfg.yaml**](https://github.com/DeepLabCut/DeepLabCut/blob/master/deeplabcut/pose_cfg.yaml).
-
-**MODEL COMPARISON:** You can also test several models by creating the same test/train split for different networks. You can easily do this in the Project Manager GUI, or use the function ``deeplabcut.create_training_model_comparison``.
+deeplabcut.create_training_dataset(config_path)
+```
+
+- OPTIONAL: If the user wishes to benchmark the performance of the DeepLabCut, they can create multiple training
+datasets by specifying an integer value to the `num_shuffles`; see the docstring for more details.
+
+The function creates a new shuffle(s) directory in the **dlc-models-pytorch** directory
+(**dlc-models** if using Tensorflow), in the current "iteration" directory.
+The `train` and `test` directories each have a configuration file
+(**pytorch_config.yaml** in **train** and **pose_cfg.yaml** in **test** for Pytorch models,
+**pose_cfg.yaml** in **train** and **test** for Tensorflow models).
+Specifically, the user can edit the **pytorch_config.yaml** (or **pose_cfg.yaml**) within the **train** subdirectory
+before starting the training. These configuration files contain meta information with regard to the parameters
+of the feature detectors. For more information about the **pytorch_config.yaml** file, see [here](dlc3-pytorch-config)
+(for TensorFlow-based models, see key parameters
+[here](https://github.com/DeepLabCut/DeepLabCut/blob/main/deeplabcut/pose_cfg.yaml)).
+
+**CRITICAL POINT:** At this step, for **create_training_dataset** you select the network you want to use, and any
+additional data augmentation (beyond our defaults). You can set `net_type`, `detector_type` (if using a detector)
+and `augmenter_type` when you call the function.
+
+- Networks: ImageNet pre-trained networks OR SuperAnimal pre-trained networks weights will be downloaded, as you
+select. You can decide to do transfer-learning (recommended) or "fine-tune" both the backbone and the decoder head. We
+suggest seeing our [dedicated documentation on models](dlc3-architectures) for more information (
+or the [this page on selecting models](what-neural-network-should-i-use) for the TensorFlow engine).
+
+```{Hint}
+🚨 If they do not download (you will see this downloading in the terminal), then you may not have permission to do
+so - be sure to open your terminal "as an admin" (This is only something we have seen with some Windows users - see
+the **[docs for more help!](tf-training-tips-and-tricks)**).
+```
+
+**DATA AUGMENTATION:** At this stage you can also decide what type of augmentation to
+use. Once you've called `create_training_dataset`, you can edit the
+[**pytorch_config.yaml**](dlc3-pytorch-config) file that was created (or for the
+TensorFlow engine, the [**pose_cfg.yaml**](
+https://github.com/DeepLabCut/DeepLabCut/blob/main/deeplabcut/pose_cfg.yaml) file).
+
+- PyTorch Engine: [Albumentations](https://albumentations.ai/docs/) is used for data
+augmentation. Look at the [**pytorch_config.yaml**](dlc3-pytorch-config) for more
+information about image augmentation options.
+- TensorFlow Engine: The default augmentation works well for most tasks (as shown on
+www.deeplabcut.org), but there are many options, more data augmentation, intermediate
+supervision, etc. Here are the available loaders:
+ - `imgaug`: a lot of augmentation possibilities, efficient code for target map creation & batch sizes >1 supported.
+ You can set the parameters such as the `batch_size` in the `pose_cfg.yaml` file for the model you are training. This
+ is the recommended default!
+ - `crop_scale`: our standard DLC 2.0 introduced in Nature Protocols variant (scaling, auto-crop augmentation)
+ - `tensorpack`: a lot of augmentation possibilities, multi CPU support for fast processing, target maps are created
+ less efficiently than in imgaug, does not allow batch size>1
+ - `deterministic`: only useful for testing, freezes numpy seed; otherwise like default.
+
+**MODEL COMPARISON**: You can also test several models by creating the same train/test
+split for different networks.
+You can easily do this in the Project Manager GUI (by selecting the "Use an existing
+data split" option), which also lets you compare PyTorch and TensorFlow models.
+
+````{versionadded} 3.0.0
+You can now create new shuffles using the same train/test split as
+existing shuffles with `create_training_dataset_from_existing_split`. This allows you to
+compare model performance (between different architectures or when using different
+training hyper-parameters) as the shuffles were trained on the same data, and evaluated
+on the same test data!
+
+Example usage - creating 3 new shuffles (with indices 10, 11 and 12) for a ResNet 50
+pose estimation model, using the same data split as was used for shuffle 0:
-Please also consult the following page on selecting models: https://deeplabcut.github.io/DeepLabCut/docs/recipes/nn.html#what-neural-network-should-i-use-trade-offs-speed-performance-and-considerations
-
- See Box 2 on how to specify **which network is loaded for training (including your own network, etc):**
-
-
-
-
+```python
+deeplabcut.create_training_dataset_from_existing_split(
+ config_path,
+ from_shuffle=0,
+ shuffles=[10, 11, 12],
+ net_type="resnet_50",
+)
+```
+````
-#### API Docs for deeplabcut.create_training_dataset
-````{admonition} Click the button to see API Docs
+````{admonition} Click the button to see API Docs for deeplabcut.create_training_dataset
:class: dropdown
```{eval-rst}
.. include:: ./api/deeplabcut.create_training_dataset.rst
```
````
-#### API Docs for deeplabcut.create_training_model_comparison
-````{admonition} Click the button to see API Docs
+````{admonition} Click the button to see API Docs for deeplabcut.create_training_model_comparison
:class: dropdown
```{eval-rst}
.. include:: ./api/deeplabcut.create_training_model_comparison.rst
```
````
+````{admonition} Click the button to see API Docs for deeplabcut.create_training_dataset_from_existing_split
+:class: dropdown
+```{eval-rst}
+.. include:: ./api/deeplabcut.create_training_dataset_from_existing_split.rst
+```
+````
+
### (G) Train The Network
The function ‘train_network’ helps the user in training the network. It is used as follows:
```python
deeplabcut.train_network(config_path)
```
-The set of arguments in the function starts training the network for the dataset created for one specific shuffle. Note that you can change the loader (imgaug/default/etc) as well as other training parameters in the **pose_cfg.yaml** file of the model that you want to train (before you start training).
+The set of arguments in the function starts training the network for the dataset created
+for one specific shuffle. Note that you can change training parameters in the
+[**pytorch_config.yaml**](dlc3-pytorch-config) file (or **pose_cfg.yaml** for TensorFlow
+models) of the model that you want to train (before you start training).
-Example parameters that one can call:
-```python
-deeplabcut.train_network(config_path, shuffle=1, trainingsetindex=0, gputouse=None, max_snapshots_to_keep=5, autotune=False, displayiters=100, saveiters=15000, maxiters=30000, allow_growth=True)
-```
+At user specified iterations during training checkpoints are stored in the subdirectory
+*train* under the respective iteration & shuffle directory.
-By default, the pretrained networks are not in the DeepLabCut toolbox (as they are around 100MB each), but they get downloaded before you train. However, if not previously downloaded from the TensorFlow model weights, it will be downloaded and stored in a subdirectory *pre-trained* under the subdirectory *models* in *Pose_Estimation_Tensorflow*.
-At user specified iterations during training checkpoints are stored in the subdirectory *train* under the respective iteration directory.
+````{admonition} Tips on training models with the PyTorch Engine
+:class: dropdown
-If the user wishes to restart the training at a specific checkpoint they can specify the full path of the checkpoint to
-the variable ``init_weights`` in the **pose_cfg.yaml** file under the *train* subdirectory (see Box 2).
+Example parameters that one can call:
-**CRITICAL POINT:** It is recommended to train the ResNets or MobileNets for thousands of iterations until the loss plateaus (typically around **500,000**) if you use batch size 1. If you want to batch train, we recommend using Adam, see more here: https://deeplabcut.github.io/DeepLabCut/docs/recipes/nn.html#using-custom-image-augmentation.
+```python
+deeplabcut.train_network(
+ config_path,
+ shuffle=1,
+ trainingsetindex=0,
+ device="cuda:0",
+ max_snapshots_to_keep=5,
+ displayiters=100,
+ save_epochs=5,
+ epochs=200,
+)
+```
+
+Pytorch models in DeepLabCut 3.0 are trained for a set number of epochs, instead of a
+maximum number of iterations (which is what was used for TensorFlow models). An epoch
+is a single pass through the training dataset, which means your model has seen each
+training image exactly once. So if you have 64 training images for your network, an
+epoch is 64 iterations with batch size 1 (or 32 iterations with batch size 2, 16 with
+batch size 4, etc.).
+
+By default, the pretrained networks are not in the DeepLabCut toolbox (as they can be
+more than 100MB), but they get downloaded automatically before you train.
+
+If the user wishes to restart the training at a specific checkpoint they can specify the
+full path of the checkpoint to the variable ``resume_training_from`` in the [
+**pytorch_config.yaml**](
+dlc3-pytorch-config) file (checkout the "Restarting Training at a Specific Checkpoint"
+section of the docs) under the *train* subdirectory.
+
+**CRITICAL POINT:** It is recommended to train the networks **until the loss plateaus**
+(depending on the dataset, model architecture and training hyper-parameters this happens
+after 100 to 250 epochs of training).
+
+The variables ``display_iters`` and ``save_epochs`` in the [**pytorch_config.yaml**](
+dlc3-pytorch-config) file allows the user to alter how often the loss is displayed
+and how often the weights are stored. We suggest saving every 5 to 25 epochs.
+````
+
+````{admonition} Tips on training models with the TensorFlow Engine
+:class: dropdown
-The variables ``display_iters`` and ``save_iters`` in the **pose_cfg.yaml** file allows the user to alter how often the loss is displayed and how often the weights are stored.
+Example parameters that one can call:
-**maDeepLabCut CRITICAL POINT:** For multi-animal projects we are using not only different and new output layers, but also new data augmentation, optimization, learning rates, and batch training defaults. Thus, please use a lower ``save_iters`` and ``maxiters``. I.e. we suggest saving every 10K-15K iterations, and only training until 50K-100K iterations. We recommend you look closely at the loss to not overfit on your data. The bonus, training time is much less!!!
+```python
+deeplabcut.train_network(
+ config_path,
+ shuffle=1,
+ trainingsetindex=0,
+ gputouse=None,
+ max_snapshots_to_keep=5,
+ autotune=False,
+ displayiters=100,
+ saveiters=25000,
+ maxiters=300000,
+ allow_growth=True,
+)
+```
+
+By default, the pretrained networks are not in the DeepLabCut toolbox (as they are
+around 100MB each), but they get downloaded before you train. However, if not previously
+downloaded from the TensorFlow model weights, it will be downloaded and stored in a
+subdirectory *pre-trained* under the subdirectory *models* in
+*Pose_Estimation_Tensorflow*. At user specified iterations during training checkpoints
+are stored in the subdirectory *train* under the respective iteration directory.
+
+If the user wishes to restart the training at a specific checkpoint they can specify the
+full path of the checkpoint to the variable ``init_weights`` in the **pose_cfg.yaml**
+file under the *train* subdirectory (see Box 2).
+
+**CRITICAL POINT:** It is recommended to train the networks for thousands of iterations
+until the loss plateaus (typically around **500,000**) if you use batch size 1. If you
+want to batch train, we recommend using Adam,
+[see more here](tf-custom-image-augmentation).
+
+The variables ``display_iters`` and ``save_iters`` in the **pose_cfg.yaml** file allows
+the user to alter how often the loss is displayed and how often the weights are stored.
+
+**maDeepLabCut CRITICAL POINT:** For multi-animal projects we are using not only
+different and new output layers, but also new data augmentation, optimization, learning
+rates, and batch training defaults. Thus, please use a lower ``save_iters`` and
+``maxiters``. I.e. we suggest saving every 10K-15K iterations, and only training until
+50K-100K iterations. We recommend you look closely at the loss to not overfit on your
+data. The bonus, training time is much less!!!
+````
-#### API Docs
-````{admonition} Click the button to see API Docs
+````{admonition} Click the button to see API Docs for train_network
:class: dropdown
```{eval-rst}
.. include:: ./api/deeplabcut.train_network.rst
@@ -289,34 +523,50 @@ The variables ``display_iters`` and ``save_iters`` in the **pose_cfg.yaml** file
### (H) Evaluate the Trained Network
It is important to evaluate the performance of the trained network. This performance is measured by computing
-the mean average Euclidean error (MAE; which is proportional to the average root mean square error) between the
-manual labels and the ones predicted by DeepLabCut. The MAE is saved as a comma separated file and displayed
-for all pairs and only likely pairs (>p-cutoff). This helps to exclude, for example, occluded body parts. One of the
-strengths of DeepLabCut is that due to the probabilistic output of the scoremap, it can, if sufficiently trained, also
-reliably report if a body part is visible in a given frame. (see discussions of finger tips in reaching and the Drosophila
-legs during 3D behavior in [Mathis et al, 2018]). The evaluation results are computed by typing:
+the average root mean square error (RMSE) between the manual labels and the ones predicted by DeepLabCut.
+The RMSE is saved as a comma separated file and displayed for all pairs and only likely pairs (>p-cutoff).
+This helps to exclude, for example, occluded body parts. One of the strengths of DeepLabCut is that due to the
+probabilistic output of the scoremap, it can, if sufficiently trained, also reliably report if a body part is visible
+in a given frame. (see discussions of finger tips in reaching and the Drosophila legs during 3D behavior in
+[Mathis et al, 2018]). The evaluation results are computed by typing:
+
```python
-deeplabcut.evaluate_network(config_path,Shuffles=[1], plotting=True)
+deeplabcut.evaluate_network(config_path, Shuffles=[1], plotting=True)
```
-Setting ``plotting`` to true plots all the testing and training frames with the manual and predicted labels. The user
+
+Setting `plotting` to true plots all the testing and training frames with the manual and predicted labels. The user
should visually check the labeled test (and training) images that are created in the ‘evaluation-results’ directory.
Ideally, DeepLabCut labeled unseen (test images) according to the user’s required accuracy, and the average train
-and test errors are comparable (good generalization). What (numerically) comprises an acceptable MAE depends on
-many factors (including the size of the tracked body parts, the labeling variability, etc.). Note that the test error can
-also be larger than the training error due to human variability (in labeling, see Figure 2 in Mathis et al, Nature Neuroscience 2018).
+and test errors are comparable (good generalization). What (numerically) comprises an acceptable RMSE depends on
+many factors (including the size of the tracked body parts, the labeling variability, etc.). Note that the test error
+can also be larger than the training error due to human variability (in labeling, see Figure 2 in Mathis et al, Nature
+Neuroscience 2018).
**Optional parameters:**
-```
- Shuffles: list, optional -List of integers specifying the shuffle indices of the training dataset. The default is [1]
- plotting: bool, optional -Plots the predictions on the train and test images. The default is `False`; if provided it must be either `True` or `False`
+- `Shuffles: list, optional` - List of integers specifying the shuffle indices of the training dataset.
+The default is [1]
- show_errors: bool, optional -Display train and test errors. The default is `True`
+- `plotting: bool, optional` - Plots the predictions on the train and test images. The default is `False`;
+if provided it must be either `True` or `False`
- comparisonbodyparts: list of bodyparts, Default is all -The average error will be computed for those body parts only (Has to be a subset of the body parts).
+- `show_errors: bool, optional` - Display train and test errors. The default is `True`
- gputouse: int, optional -Natural number indicating the number of your GPU (see number in nvidia-smi). If you do not have a GPU, put None. See: https://nvidia.custhelp.com/app/answers/detail/a_id/3751/~/useful-nvidia-smi-queries
-```
+- `comparisonbodyparts: list of bodyparts, Default is all` - The average error will be computed for those body parts
+only (Has to be a subset of the body parts).
+
+- `gputouse: int, optional` - Natural number indicating the number of your GPU (see number in nvidia-smi). If you do not
+have a GPU, put None. See: https://nvidia.custhelp.com/app/answers/detail/a_id/3751/~/useful-nvidia-smi-queries
+
+- `pcutoff: float | list[float] | dict[str, float], optional`
+(Only applicable when using the PyTorch engine. For TensorFlow, set `pcutoff` in the `config.yaml` file.)
+Specifies the cutoff value(s) used to compute evaluation metrics.
+ - If `None` (default), the cutoff will be loaded from the project configuration.
+ - To apply a single cutoff value to all bodyparts, provide a `float`.
+ - To specify different cutoffs per bodypart, provide either:
+ - A `list[float]`: one value per bodypart, with an additional value for each unique bodypart if applicable.
+ - A `dict[str, float]`: where keys are bodypart names and values are the corresponding cutoff values.
+If a bodypart is not included in the provided dictionary, a default `pcutoff` of `0.6` will be used for that bodypart.
The plots can be customized by editing the **config.yaml** file (i.e., the colormap, scale, marker size (dotsize), and
transparency of labels (alphavalue) can be modified). By default each body part is plotted in a different color
@@ -325,9 +575,10 @@ plotted as plus (‘+’), DeepLabCut’s predictions either as ‘.’ (for con
’x’ for (likelihood <= `pcutoff`).
The evaluation results for each shuffle of the training dataset are stored in a unique subdirectory in a newly created
-directory ‘evaluation-results’ in the project directory. The user can visually inspect if the distance between the labeled
-and the predicted body parts are acceptable. In the event of benchmarking with different shuffles of same training
-dataset, the user can provide multiple shuffle indices to evaluate the corresponding network.
+directory ‘evaluation-results-pytorch’ (‘evaluation-results’ for tensorflow models) in the project directory.
+The user can visually inspect if the distance between the labeled and the predicted body parts are acceptable.
+In the event of benchmarking with different shuffles of same training dataset, the user can provide multiple shuffle
+indices to evaluate the corresponding network.
Note that with multi-animal projects additional distance statistics aggregated over animals or bodyparts are also stored
in that directory. This aims at providing a finer quantitative evaluation of multi-animal prediction performance
before animal tracking. If the generalization is not sufficient, the user might want to:
@@ -346,7 +597,7 @@ deeplabcut.extract_save_all_maps(config_path, shuffle=shuffle, Indices=[0, 5])
```
you can drop "Indices" to run this on all training/testing images (this is slow!)
-#### API Docs
+### API Docs
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -354,26 +605,42 @@ you can drop "Indices" to run this on all training/testing images (this is slow!
```
````
-### (I) Novel Video Analysis:
+### (I) Analyze new Videos
-The trained network can be used to analyze new videos. The user needs to first choose a checkpoint with the best
-evaluation results for analyzing the videos. In this case, the user can enter the corresponding index of the checkpoint
-to the variable snapshotindex in the config.yaml file. By default, the most recent checkpoint (i.e. last) is used for
-analyzing the video. Novel/new videos **DO NOT have to be in the config file!** You can analyze new videos anytime by simply using the following line of code:
+The trained network can be used to analyze new videos. Novel/new videos **DO NOT have to be in the config file!**.
+You can analyze new videos anytime by simply using the following line of code:
```python
-deeplabcut.analyze_videos(config_path, ['fullpath/analysis/project/videos/reachingvideo1.avi'], save_as_csv=True)
+deeplabcut.analyze_videos(
+ config_path, ["fullpath/analysis/project/videos/reachingvideo1.avi"],
+ save_as_csv=True
+)
```
There are several other optional inputs, such as:
```python
-deeplabcut.analyze_videos(config_path, videos, videotype='avi', shuffle=1, trainingsetindex=0, gputouse=None, save_as_csv=False, destfolder=None, dynamic=(True, .5, 10))
-```
-The labels are stored in a [MultiIndex Pandas Array](http://pandas.pydata.org), which contains the name of the network, body part name, (x, y) label position in pixels, and the likelihood for each frame per body part. These
-arrays are stored in an efficient Hierarchical Data Format (HDF) in the same directory, where the video is stored.
-However, if the flag ``save_as_csv`` is set to ``True``, the data can also be exported in comma-separated values format
-(.csv), which in turn can be imported in many programs, such as MATLAB, R, Prism, etc.; This flag is set to ``False``
-by default. You can also set a destination folder (``destfolder``) for the output files by passing a path of the folder you wish to write to.
-
-#### API Docs
+deeplabcut.analyze_videos(
+ config_path,
+ videos,
+ videotype="avi",
+ shuffle=1,
+ trainingsetindex=0,
+ gputouse=None,
+ save_as_csv=False,
+ destfolder=None,
+ dynamic=(True, .5, 10)
+)
+```
+The user can choose a checkpoint for analyzing the videos. For this, the user can enter the corresponding index of the
+checkpoint to the variable snapshotindex in the config.yaml file. By default, the most recent checkpoint (i.e. last) is
+used for analyzing the video.
+The labels are stored in a MultiIndex [Pandas](http://pandas.pydata.org) Array, which contains the name of the network,
+body part name, (x, y) label position in pixels, and the likelihood for each frame per body part. These arrays are
+stored in an efficient Hierarchical Data Format (HDF) in the same directory, where the video is stored.
+However, if the flag `save_as_csv` is set to `True`, the data can also be exported in comma-separated values format
+(.csv), which in turn can be imported in many programs, such as MATLAB, R, Prism, etc.; This flag is set to `False`
+by default. You can also set a destination folder (`destfolder`) for the output files by passing a path of the folder
+you wish to write to.
+
+### API Docs
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -383,28 +650,58 @@ by default. You can also set a destination folder (``destfolder``) for the outpu
### Novel Video Analysis: extra features
-#### Dynamic-cropping of videos:
+### Dynamic-cropping of videos:
-As of 2.1+ we have a dynamic cropping option. Namely, if you have large frames and the animal/object occupies a smaller fraction, you can crop around your animal/object to make processing speeds faster. For example, if you have a large open field experiment but only track the mouse, this will speed up your analysis (also helpful for real-time applications). To use this simply add ``dynamic=(True,.5,10)`` when you call ``analyze_videos``.
+As of 2.1+ we have a dynamic cropping option. Namely, if you have large frames and the animal/object occupies a smaller
+fraction, you can crop around your animal/object to make processing speeds faster. For example, if you have a large open
+field experiment but only track the mouse, this will speed up your analysis (also helpful for real-time applications).
+To use this simply add `dynamic=(True,.5,10)` when you call `analyze_videos`.
```python
dynamic: triple containing (state, detectiontreshold, margin)
- If the state is true, then dynamic cropping will be performed. That means that if an object is detected (i.e., any body part > detectiontreshold), then object boundaries are computed according to the smallest/largest x position and smallest/largest y position of all body parts. This window is expanded by the margin and from then on only the posture within this crop is analyzed (until the object is lost; i.e., detectiontreshold),
+ then object boundaries are computed according to the smallest/largest x position and
+ smallest/largest y position of all body parts. This window is expanded by the margin
+ and from then on only the posture within this crop is analyzed (until the object is lost;
+ i.e., < detectiontreshold). The current position is utilized for updating the crop window
+ for the next frame (this is why the margin is important and should be set large enough
+ given the movement of the animal).
```
-### (J) Filter pose data data (RECOMMENDED!):
+### (J) Filter Pose Data
You can also filter the predictions with a median filter (default) or with a [SARIMAX model](https://www.statsmodels.org/dev/generated/statsmodels.tsa.statespace.sarimax.SARIMAX.html), if you wish. This creates a new .h5 file with the ending *_filtered* that you can use in create_labeled_data and/or plot trajectories.
```python
-deeplabcut.filterpredictions(config_path, ['fullpath/analysis/project/videos/reachingvideo1.avi'])
+deeplabcut.filterpredictions(
+ config_path,
+ ["fullpath/analysis/project/videos/reachingvideo1.avi"]
+)
```
An example call:
- ```python
-deeplabcut.filterpredictions(config_path,['fullpath/analysis/project/videos'], videotype='.mp4',filtertype= 'arima',ARdegree=5,MAdegree=2)
- ```
+```python
+deeplabcut.filterpredictions(
+ config_path,
+ ["fullpath/analysis/project/videos"],
+ videotype=".mp4",
+ filtertype="arima",
+ ARdegree=5,
+ MAdegree=2
+)
+```
Here are parameters you can modify and pass:
```python
-deeplabcut.filterpredictions(config_path, ['fullpath/analysis/project/videos/reachingvideo1.avi'], shuffle=1, trainingsetindex=0, comparisonbodyparts='all', filtertype='arima', p_bound=0.01, ARdegree=3, MAdegree=1, alpha=0.01)
+deeplabcut.filterpredictions(
+ config_path,
+ ["fullpath/analysis/project/videos/reachingvideo1.avi"],
+ shuffle=1,
+ trainingsetindex=0,
+ filtertype="arima",
+ p_bound=0.01,
+ ARdegree=3,
+ MAdegree=1,
+ alpha=0.01
+)
```
Here is an example of how this can be applied to a video:
@@ -412,7 +709,7 @@ deeplabcut.filterpredictions(config_path, ['fullpath/analysis/project/videos/rea
-#### API Docs
+### API Docs
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -420,7 +717,7 @@ deeplabcut.filterpredictions(config_path, ['fullpath/analysis/project/videos/rea
```
````
-### (K) Plot Trajectories:
+### (K) Plot Trajectories
The plotting components of this toolbox utilizes matplotlib. Therefore, these plots can easily be customized by
the end user. We also provide a function to plot the trajectory of the extracted poses across the analyzed video, which
@@ -430,14 +727,18 @@ can be called by typing:
deeplabcut.plot_trajectories(config_path, [‘fullpath/analysis/project/videos/reachingvideo1.avi’])
```
-It creates a folder called ``plot-poses`` (in the directory of the video). The plots display the coordinates of body parts vs. time, likelihoods vs time, the x- vs. y- coordinate of the body parts, as well as histograms of consecutive coordinate differences. These plots help the user to quickly assess the tracking performance for a video. Ideally, the likelihood stays high and the histogram of consecutive coordinate differences has values close to zero (i.e. no jumps in body part detections across frames). Here are example plot outputs on a demo video (left):
+It creates a folder called `plot-poses` (in the directory of the video). The plots display the coordinates of body parts
+vs. time, likelihoods vs time, the x- vs. y- coordinate of the body parts, as well as histograms of consecutive
+coordinate differences. These plots help the user to quickly assess the tracking performance for a video. Ideally, the
+likelihood stays high and the histogram of consecutive coordinate differences has values close to zero (i.e. no jumps in
+body part detections across frames). Here are example plot outputs on a demo video (left):
-#### API Docs
+### API Docs
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -445,53 +746,90 @@ It creates a folder called ``plot-poses`` (in the directory of the video). The p
```
````
-### (L) Create Labeled Videos:
+### (L) Create Labeled Videos
Additionally, the toolbox provides a function to create labeled videos based on the extracted poses by plotting the
-labels on top of the frame and creating a video. There are two modes to create videos: FAST and SLOW (but higher quality!). If you want to create high-quality videos, please add ``save_frames=True``. One can use the command as follows to create multiple labeled videos:
+labels on top of the frame and creating a video. There are two modes to create videos: FAST and SLOW (but higher
+quality!). One can use the command as follows to create multiple labeled videos:
```python
-deeplabcut.create_labeled_video(config_path, ['fullpath/analysis/project/videos/reachingvideo1.avi','fullpath/analysis/project/videos/reachingvideo2.avi'], save_frames = True/False)
-```
- Optionally, if you want to use the filtered data for a video or directory of filtered videos pass ``filtered=True``, i.e.:
+deeplabcut.create_labeled_video(
+ config_path,
+ ["fullpath/analysis/project/videos/reachingvideo1.avi",
+ "fullpath/analysis/project/videos/reachingvideo2.avi"],
+ save_frames = True/False
+)
+```
+ Optionally, if you want to use the filtered data for a video or directory of filtered videos pass `filtered=True`,
+ i.e.:
```python
-deeplabcut.create_labeled_video(config_path, ['fullpath/afolderofvideos'], videotype='.mp4', filtered=True)
-```
-You can also optionally add a skeleton to connect points and/or add a history of points for visualization. To set the "trailing points" you need to pass ``trailpoints``:
+deeplabcut.create_labeled_video(
+ config_path,
+ ["fullpath/afolderofvideos"],
+ videotype=".mp4",
+ filtered=True
+)
+```
+You can also optionally add a skeleton to connect points and/or add a history of points for visualization. To set the
+"trailing points" you need to pass `trailpoints`:
```python
-deeplabcut.create_labeled_video(config_path, ['fullpath/afolderofvideos'], videotype='.mp4', trailpoints=10)
-```
-To draw a skeleton, you need to first define the pairs of connected nodes (in the ``config.yaml`` file) and set the skeleton color (in the ``config.yaml`` file). There is also a GUI to help you do this, use by calling `deeplabcut.SkeletonBuilder(config+path)`!
-
-Here is how the ``config.yaml`` additions/edits should look (for example, on the Openfield demo data we provide):
+deeplabcut.create_labeled_video(
+ config_path,
+ ["fullpath/afolderofvideos"],
+ videotype=".mp4",
+ trailpoints=10
+)
+```
+To draw a skeleton, you need to first define the pairs of connected nodes (in the `config.yaml` file) and set the
+skeleton color (in the `config.yaml` file). There is also a GUI to help you do this, use by calling
+`deeplabcut.SkeletonBuilder(configpath)`!
+
+Here is how the `config.yaml` additions/edits should look (for example, on the Openfield demo data we provide):
```python
# Plotting configuration
-skeleton: [['snout', 'leftear'], ['snout', 'rightear'], ['leftear', 'tailbase'], ['leftear', 'rightear'], ['rightear','tailbase']]
+skeleton:
+ - ["snout", "leftear"]
+ - ["snout", "rightear"]
+ - ["leftear", "tailbase"]
+ - ["leftear", "rightear"]
+ - ["rightear", "tailbase"]
skeleton_color: white
pcutoff: 0.4
dotsize: 4
alphavalue: 0.5
colormap: jet
```
-Then pass ``draw_skeleton=True`` with the command:
+Then pass `draw_skeleton=True` with the command:
```python
-deeplabcut.create_labeled_video(config_path,['fullpath/afolderofvideos'], videotype='.mp4', draw_skeleton = True)
+deeplabcut.create_labeled_video(
+ config_path,
+ ["fullpath/afolderofvideos"],
+ videotype=".mp4",
+ draw_skeleton=True
+)
```
-**NEW** as of 2.2b8: You can create a video with only the "dots" plotted, i.e., in the [style of Johansson](https://link.springer.com/article/10.1007/BF00309043), by passing `keypoints_only=True`:
+**NEW** as of 2.2b8: You can create a video with only the "dots" plotted, i.e., in the
+[style of Johansson](https://link.springer.com/article/10.1007/BF00309043), by passing `keypoints_only=True`:
```python
-deeplabcut.create_labeled_video(config_path,['fullpath/afolderofvideos'], videotype='.mp4', keypoints_only=True)
+deeplabcut.create_labeled_video(
+ config_path,["fullpath/afolderofvideos"],
+ videotype=".mp4",
+ keypoints_only=True
+)
```
-**PRO TIP:** that the **best quality videos** are created when ``save_frames=True`` is passed. Therefore, when ``trailpoints`` and ``draw_skeleton`` are used, we **highly** recommend you also pass ``save_frames=True``!
+**PRO TIP:** that the **best quality videos** are created when `fastmode=False` is passed. Therefore, when
+`trailpoints` and `draw_skeleton` are used, we **highly** recommend you also pass `fastmode=False`!
-This function has various other parameters, in particular the user can set the ``colormap``, the ``dotsize``, and ``alphavalue`` of the labels in **config.yaml** file.
+This function has various other parameters, in particular the user can set the `colormap`, the `dotsize`, and
+`alphavalue` of the labels in **config.yaml** file.
-#### API Docs
+### API Docs
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -499,15 +837,25 @@ This function has various other parameters, in particular the user can set the `
```
````
-#### Extract "Skeleton" Features:
+### Extract "Skeleton" Features:
-NEW, as of 2.0.7+: You can save the "skeleton" that was applied in ``create_labeled_videos`` for more computations. Namely, it extracts length and orientation of each "bone" of the skeleton as defined in the **config.yaml** file. You can use the function by:
+NEW, as of 2.0.7+: You can save the "skeleton" that was applied in `create_labeled_videos` for more computations.
+Namely, it extracts length and orientation of each "bone" of the skeleton as defined in the **config.yaml** file. You
+can use the function by:
```python
-deeplabcut.analyzeskeleton(config, video, videotype='avi', shuffle=1, trainingsetindex=0, save_as_csv=False, destfolder=None)
-```
-
-#### API Docs
+deeplabcut.analyzeskeleton(
+ config,
+ video,
+ videotype="avi",
+ shuffle=1,
+ trainingsetindex=0,
+ save_as_csv=False,
+ destfolder=None
+)
+```
+
+### API Docs
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -515,6 +863,7 @@ deeplabcut.analyzeskeleton(config, video, videotype='avi', shuffle=1, trainingse
```
````
+(active-learning)=
### (M) Optional Active Learning -> Network Refinement: Extract Outlier Frames
While DeepLabCut typically generalizes well across datasets, one might want to optimize its performance in various,
@@ -530,39 +879,48 @@ where the decoder might make large errors.
All this can be done for a specific video by typing (see other optional inputs below):
```python
-deeplabcut.extract_outlier_frames(config_path, ['videofile_path'])
+deeplabcut.extract_outlier_frames(config_path, ["videofile_path"])
```
We provide various frame-selection methods for this purpose. In particular
the user can set:
```
-outlieralgorithm: 'fitting', 'jump', or 'uncertain'``
+outlieralgorithm: "fitting", "jump", or "uncertain"
```
-• select frames if the likelihood of a particular or all body parts lies below *pbound* (note this could also be due to
-occlusions rather than errors); (``outlieralgorithm='uncertain'``), but also set ``p_bound``.
+• `outlieralgorithm="uncertain"`: select frames if the likelihood of a particular or all body parts lies below `p_bound`
+(note this could also be due to occlusions rather than errors).
-• select frames where a particular body part or all body parts jumped more than *\uf* pixels from the last frame (``outlieralgorithm='jump'``).
+• `outlieralgorithm="jump"`: select frames where a particular body part or all body parts jumped more than `epsilon`
+pixels from the last frame.
-• select frames if the predicted body part location deviates from a state-space model fit to the time series
-of individual body parts. Specifically, this method fits an Auto Regressive Integrated Moving Average (ARIMA)
-model to the time series for each body part. Thereby each body part detection with a likelihood smaller than
-pbound is treated as missing data. Putative outlier frames are then identified as time points, where the average body part estimates are at least *\uf* pixel away from the fits. The parameters of this method are *\uf*, *pbound*, the ARIMA parameters as well as the list of body parts to average over (can also be ``all``).
+• `outlieralgorithm="fitting"`: select frames if the predicted body part location deviates from a state-space model fit
+to the time series of individual body parts. Specifically, this method fits an Auto Regressive Integrated Moving Average
+(ARIMA) model to the time series for each body part. Thereby each body part detection with a likelihood smaller than
+`p_bound` is treated as missing data. Putative outlier frames are then identified as time points, where the average
+body part estimates are at least `epsilon` pixels away from the fits. The parameters of this method are `epsilon`,
+`p_bound`, the ARIMA parameters as well as the list of body parts to average over (can also be `all`).
-• manually select outlier frames based on visual inspection from the user (``outlieralgorithm='manual'``).
+• `outlieralgorithm="manual"`: manually select outlier frames based on visual inspection from the user.
As an example:
```python
-deeplabcut.extract_outlier_frames(config_path, ['videofile_path'], outlieralgorithm='manual')
+deeplabcut.extract_outlier_frames(config_path, ["videofile_path"], outlieralgorithm="manual")
```
In general, depending on the parameters, these methods might return much more frames than the user wants to
-extract (``numframes2pick``). Thus, this list is then used to select outlier frames either by randomly sampling from this
-list (``extractionalgorithm='uniform'``), by performing ``extractionalgorithm='k-means'`` clustering on the corresponding frames.
-
-In the automatic configuration, before the frame selection happens, the user is informed about the amount of frames satisfying the criteria and asked if the selection should proceed. This step allows the user to perhaps change the parameters of the frame-selection heuristics first (i.e. to make sure that not too many frames are qualified). The user can run the extract_outlier_frames iteratively, and (even) extract additional frames from the same video. Once enough outlier frames are extracted the refinement GUI can be used to adjust the labels based on user feedback (see below).
-
-#### API Docs
+extract (`numframes2pick`). Thus, this list is then used to select outlier frames either by randomly sampling from
+this list (`extractionalgorithm="uniform"`), by performing `extractionalgorithm="kmeans"` clustering on the
+corresponding frames.
+
+In the automatic configuration, before the frame selection happens, the user is informed about the amount of frames
+satisfying the criteria and asked if the selection should proceed. This step allows the user to perhaps change the
+parameters of the frame-selection heuristics first (i.e. to make sure that not too many frames are qualified). The user
+can run the `extract_outlier_frames` method iteratively, and (even) extract additional frames from the same video.
+Once enough outlier frames are extracted the refinement GUI can be used to adjust the labels based on user feedback
+(see below).
+
+### API Docs
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -594,7 +952,7 @@ deeplabcut.refine_labels(config_path)
```
This will launch a GUI where the user can refine the labels.
-Use the ‘Load Labels’ button to select one of the subdirectories, where the extracted frames are stored. Every label will be identified by a unique color. For better chances to identify the low-confidence labels, specify the threshold of the likelihood. This changes the body parts with likelihood below this threshold to appear as circles and the ones above as solid disks while retaining the same color scheme. Next, to adjust the position of the label, hover the mouse over the labels to identify the specific body part, left click and drag it to a different location. To delete a specific label, middle click on the label (once a label is deleted, it cannot be retrieved).
+Please refer to the [napari-deeplabcut docs](file:napari-gui-landing) for more information about the labelling workflow.
After correcting the labels for all the frames in each of the subdirectories, the users should merge the data set to
create a new dataset. In this step the iteration parameter in the config.yaml file is automatically updated.
@@ -603,15 +961,18 @@ deeplabcut.merge_datasets(config_path)
```
Once the dataset is merged, the user can test if the merging process was successful by plotting all the labels (Step E).
Next, with this expanded training set the user can now create a novel training set and train the network as described
-in Steps F and G. The training dataset will be stored in the same place as before but under a different ``iteration #``
-subdirectory, where the ``#`` is the new value of ``iteration`` variable stored in the project’s configuration file (this is
-automatically done).
+in Steps F and G. The training dataset will be stored in the same place as before but under a different `iteration-#`
+subdirectory, where the ``#`` is the new value of `iteration` variable stored in the project’s configuration file
+(this is automatically done).
-Now you can run ``create_training_dataset``, then ``train_network``, etc. If your original labels were adjusted at all, start from fresh weights (the typically recommended path anyhow), otherwise consider using your already trained network weights (see Box 2).
+Now you can run `create_training_dataset`, then `train_network`, etc. If your original labels were adjusted at all,
+start from fresh weights (the typically recommended path anyhow), otherwise consider using your already trained network
+weights (see Box 2).
-If after training the network generalizes well to the data, proceed to analyze new videos. Otherwise, consider labeling more data.
+If after training the network generalizes well to the data, proceed to analyze new videos. Otherwise, consider labeling
+more data.
-#### API Docs for deeplabcut.refine_labels
+### API Docs for deeplabcut.refine_labels
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -619,7 +980,7 @@ If after training the network generalizes well to the data, proceed to analyze n
```
````
-#### API Docs for deeplabcut.merge_datasets
+### API Docs for deeplabcut.merge_datasets
````{admonition} Click the button to see API Docs
:class: dropdown
```{eval-rst}
@@ -630,12 +991,15 @@ If after training the network generalizes well to the data, proceed to analyze n
### Jupyter Notebooks for Demonstration of the DeepLabCut Workflow
We also provide two Jupyter notebooks for using DeepLabCut on both a pre-labeled dataset, and on the end user’s
-own dataset. Firstly, we prepared an interactive Jupyter notebook called run_yourowndata.ipynb that can serve as a
-template for the user to develop a project. Furthermore, we provide a notebook for an already started project with
-labeled data. The example project, named as Reaching-Mackenzie-2018-08-30 consists of a project configuration file
-with default parameters and 20 images, which are cropped around the region of interest as an example dataset. These
-images are extracted from a video, which was recorded in a study of skilled motor control in mice. Some example
-labels for these images are also provided. See more details [here](https://github.com/DeepLabCut/DeepLabCut/blob/master/examples).
+own dataset. Firstly, we prepared an interactive Jupyter notebook called
+[Demo_yourowndata.ipynb](https://github.com/DeepLabCut/DeepLabCut/blob/main/examples/JUPYTER/Demo_yourowndata.ipynb)
+that can serve as a template for the user to develop a project. Furthermore, we provide a notebook for an already
+started project with labeled data. The example project, named as
+[Reaching-Mackenzie-2018-08-30](https://github.com/DeepLabCut/DeepLabCut/tree/main/examples/Reaching-Mackenzie-2018-08-30)
+consists of a project configuration file with default parameters and 20 images, which are cropped around the region of
+interest as an example dataset. These images are extracted from a video, which was recorded in a study of skilled motor
+control in mice. Some example labels for these images are also provided. See more details
+[here](https://github.com/DeepLabCut/DeepLabCut/tree/main/examples).
## 3D Toolbox
diff --git a/examples/COLAB/COLAB_3miceDemo.ipynb b/examples/COLAB/COLAB_3miceDemo.ipynb
index f11abedf2d..75c741ad40 100644
--- a/examples/COLAB/COLAB_3miceDemo.ipynb
+++ b/examples/COLAB/COLAB_3miceDemo.ipynb
@@ -1,258 +1,276 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "view-in-github"
- },
- "source": [
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "TGChzLdc-lUJ"
- },
- "source": [
- "# DeepLabCut 2.2 Toolbox Demo on 3 mice data\n",
- "\n",
- "\n",
- "https://github.com/DeepLabCut/DeepLabCut\n",
- "\n",
- "### This notebook illustrates how to use COLAB for a multi-animal DeepLabCut (maDLC) Demo 3 mouse project:\n",
- "- load our mini-demo data that includes a pretrained model and unlabeled video.\n",
- "- analyze a novel video.\n",
- "- assemble animals and tracklets.\n",
- "- create quality check plots and video.\n",
- "\n",
- "### To create a full maDLC pipeline please see our full docs: https://deeplabcut.github.io/DeepLabCut/docs/intro.html \n",
- "- Of interest is a full how-to for maDLC: https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html\n",
- "- a quick guide to maDLC: https://deeplabcut.github.io/DeepLabCut/docs/tutorial.html\n",
- "- a demo COLAB for how to use maDLC on your own data: https://github.com/DeepLabCut/DeepLabCut/blob/master/examples/COLAB_maDLC_TrainNetwork_VideoAnalysis.ipynb\n",
- "\n",
- "### To get started, please go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\"\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "HoNN2_0Z9rr_"
- },
- "outputs": [],
- "source": [
- "# Install the latest DeepLabCut version:\n",
- "!pip install https://github.com/DeepLabCut/DeepLabCut/archive/master.zip"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "Wid0GTGMAEnZ"
- },
- "source": [
- "No information needs edited in the cells below, you can simply click run on each:\n",
- "\n",
- "### Download our Demo Project from our server:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "PusLdqbqJi60"
- },
- "outputs": [],
- "source": [
- "# Download our demo project:\n",
- "import requests\n",
- "from io import BytesIO\n",
- "from zipfile import ZipFile\n",
- "\n",
- "url_record = 'https://zenodo.org/api/records/7883589'\n",
- "response = requests.get(url_record)\n",
- "if response.status_code == 200:\n",
- " file = response.json()['files'][0]\n",
- " title = file['key']\n",
- " print(f\"Downloading {title}...\")\n",
- " with requests.get(file['links']['self'], stream=True) as r:\n",
- " with ZipFile(BytesIO(r.content)) as zf:\n",
- " zf.extractall(path='/content')\n",
- "else:\n",
- " raise ValueError(f'The URL {url_record} could not be reached.')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "8iXtySnQB0BE"
- },
- "source": [
- "## Analyze a novel 3 mouse video with our maDLC DLCRNet, pretrained on 3 mice data (i.e., here you extract detections and association costs):"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "odYrU3o8BSAr"
- },
- "outputs": [],
- "source": [
- "import deeplabcut as dlc\n",
- "import os\n",
- "\n",
- "project_path = \"/content/demo-me-2021-07-14\"\n",
- "config_path = os.path.join(project_path, \"config.yaml\")\n",
- "video = os.path.join(project_path, \"videos\", \"videocompressed1.mp4\")\n",
- "\n",
- "dlc.analyze_videos(config_path,[video], shuffle=0, videotype=\"mp4\",auto_track=False )"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "zmdSLRTOER00"
- },
- "source": [
- "### Next, you compute the local, spatio-temporal grouping and track body part assemblies frame-by-frame:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "QxYwpFB8EVGw"
- },
- "outputs": [],
- "source": [
- "TRACK_METHOD = \"ellipse\" # Could also be \"box\", but \"ellipse\" was found to be more robust on this dataset.\n",
- "\n",
- "dlc.convert_detections2tracklets(\n",
- " config_path,\n",
- " [video],\n",
- " videotype='mp4',\n",
- " shuffle=0,\n",
- " track_method=TRACK_METHOD,\n",
- " ignore_bodyparts=[\"tail1\", \"tail2\", \"tailend\"], # Some body parts can optionally be ignored during tracking for better assembly (but they are used later)\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "nlpGe9obEvFa"
- },
- "source": [
- "### Reconstruct full animal trajectories (tracks from tracklets):"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "mKWi7JQIEvbn"
- },
- "outputs": [],
- "source": [
- "dlc.stitch_tracklets(\n",
- " config_path,\n",
- " [video],\n",
- " videotype='mp4',\n",
- " shuffle=0,\n",
- " track_method=TRACK_METHOD,\n",
- " n_tracks=3,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "F-d6kXqnGeUP"
- },
- "source": [
- "## Create a pretty video output:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "aTRbuUQ1FBO0"
- },
- "outputs": [],
- "source": [
- "#Filter the predictions to remove small jitter, if desired:\n",
- "dlc.filterpredictions(config_path, \n",
- " [video], \n",
- " shuffle=0,\n",
- " videotype='mp4', \n",
- " track_method = TRACK_METHOD)\n",
- "\n",
- "dlc.create_labeled_video(\n",
- " config_path,\n",
- " [video],\n",
- " videotype='mp4',\n",
- " shuffle=0,\n",
- " color_by=\"individual\",\n",
- " keypoints_only=False,\n",
- " draw_skeleton=True,\n",
- " filtered=True,\n",
- " track_method=TRACK_METHOD,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "AYNlrgeNUG4U"
- },
- "source": [
- "Now, on the left panel if you click the folder icon, you will see the project folder \"demo-me..\"; click on this and go into \"videos\" and you can find the \"..._id_labeled.mp4\" video, which you can double-click on to download and inspect!"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "n7GWMBJUA9x5"
- },
- "source": [
- "### Create Plots of your data:\n",
- "\n",
- "> after running, you can look in \"videos\", \"plot-poses\" to check out the trajectories! (sometimes you need to click the folder refresh icon to see it). Within the folder, for example, see plotmus1.png to vide the bodyparts over time vs. pixel position.\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "7w9BDIA7BB_i"
- },
- "outputs": [],
- "source": [
- "dlc.plot_trajectories(config_path, [video], shuffle=0,videotype='mp4', track_method=TRACK_METHOD)"
- ]
- }
- ],
- "metadata": {
- "accelerator": "GPU",
- "colab": {
- "collapsed_sections": [],
- "include_colab_link": true,
- "name": "Copy of 3micedemo.ipynb",
- "provenance": []
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- },
- "language_info": {
- "name": "python"
- }
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "view-in-github"
+ },
+ "source": [
+ " "
+ ]
},
- "nbformat": 4,
- "nbformat_minor": 0
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "TGChzLdc-lUJ"
+ },
+ "source": [
+ "# DeepLabCut MultiMouse Data Demo\n",
+ "\n",
+ "\n",
+ "https://github.com/DeepLabCut/DeepLabCut\n",
+ "\n",
+ "Note: this Colab notebook was written to accompany the Nature Methods publication [_Multi-animal pose estimation, identification and tracking with DeepLabCut_](https://www.nature.com/articles/s41592-022-01443-0) with the TensorFlow engine. To learn about DeepLabCut 3.0+ and the PyTorch engine, you can check out our other notebooks (such as [`COLAB_YOURDATA_maDLC_TrainNetwork_VideoAnalysis.ipynb`](https://github.com/DeepLabCut/DeepLabCut/blob/main/examples/COLAB/COLAB_YOURDATA_maDLC_TrainNetwork_VideoAnalysis.ipynb)).\n",
+ "\n",
+ "## This notebook illustrates how to use COLAB for a multi-animal DeepLabCut (maDLC) Demo 3 mouse project:\n",
+ "\n",
+ "- load our mini-demo data that includes a pretrained model and unlabeled video.\n",
+ "- analyze a novel video.\n",
+ "- assemble animals and tracklets.\n",
+ "- create quality check plots and video.\n",
+ "\n",
+ "- To create a full maDLC pipeline please see our full docs: https://deeplabcut.github.io/DeepLabCut/README.html\n",
+ "\n",
+ "- Of interest is a full how-to for maDLC: https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html\n",
+ "- a quick guide to maDLC: https://deeplabcut.github.io/DeepLabCut/docs/quick-start/tutorial_maDLC.html\n",
+ "- a demo COLAB for how to use maDLC on your own data: https://github.com/DeepLabCut/DeepLabCut/blob/main/examples/COLAB/COLAB_YOURDATA_maDLC_TrainNetwork_VideoAnalysis.ipynb\n",
+ "\n",
+ "### To get started, please go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Install the correct (older) version of DeepLabCut\n",
+ "!pip install \"deeplabcut[tf]\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Important - Restart the Runtime for the updated packages to be imported!\n",
+ "\n",
+ "PLEASE, click \"restart runtime\" from the output above before proceeding!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Wid0GTGMAEnZ"
+ },
+ "source": [
+ "No information needs edited in the cells below, you can simply click run on each:\n",
+ "\n",
+ "## Download our Demo Project from our server:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "PusLdqbqJi60"
+ },
+ "outputs": [],
+ "source": [
+ "# Download our demo project:\n",
+ "from io import BytesIO\n",
+ "from zipfile import ZipFile\n",
+ "\n",
+ "import requests\n",
+ "\n",
+ "url_record = \"https://zenodo.org/api/records/7883589\"\n",
+ "response = requests.get(url_record)\n",
+ "if response.status_code == 200:\n",
+ " file = response.json()[\"files\"][0]\n",
+ " title = file[\"key\"]\n",
+ " print(f\"Downloading {title}...\")\n",
+ " with requests.get(file[\"links\"][\"self\"], stream=True) as r:\n",
+ " with ZipFile(BytesIO(r.content)) as zf:\n",
+ " zf.extractall(path=\"/content\")\n",
+ "else:\n",
+ " raise ValueError(f\"The URL {url_record} could not be reached.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "8iXtySnQB0BE"
+ },
+ "source": [
+ "## Analyze a novel 3 mouse video with our maDLC DLCRNet, pretrained on 3 mice data (i.e., here you extract detections and association costs):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "odYrU3o8BSAr"
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "import deeplabcut as dlc\n",
+ "\n",
+ "project_path = \"/content/demo-me-2021-07-14\"\n",
+ "config_path = os.path.join(project_path, \"config.yaml\")\n",
+ "video = os.path.join(project_path, \"videos\", \"videocompressed1.mp4\")\n",
+ "\n",
+ "dlc.analyze_videos(config_path, [video], shuffle=0, videotype=\"mp4\", auto_track=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "zmdSLRTOER00"
+ },
+ "source": [
+ "## Next, you compute the local, spatio-temporal grouping and track body part assemblies frame-by-frame:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "QxYwpFB8EVGw"
+ },
+ "outputs": [],
+ "source": [
+ "TRACK_METHOD = \"ellipse\" # Could also be \"box\", but \"ellipse\" was found to be more robust on this dataset.\n",
+ "\n",
+ "dlc.convert_detections2tracklets(\n",
+ " config_path,\n",
+ " [video],\n",
+ " videotype=\"mp4\",\n",
+ " shuffle=0,\n",
+ " track_method=TRACK_METHOD,\n",
+ " ignore_bodyparts=[\n",
+ " \"tail1\",\n",
+ " \"tail2\",\n",
+ " \"tailend\",\n",
+ " ], # Some body parts can optionally be ignored during tracking for better assembly (but they are used later)\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "nlpGe9obEvFa"
+ },
+ "source": [
+ "## Reconstruct full animal trajectories (tracks from tracklets):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "mKWi7JQIEvbn"
+ },
+ "outputs": [],
+ "source": [
+ "dlc.stitch_tracklets(\n",
+ " config_path,\n",
+ " [video],\n",
+ " videotype=\"mp4\",\n",
+ " shuffle=0,\n",
+ " track_method=TRACK_METHOD,\n",
+ " n_tracks=3,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "F-d6kXqnGeUP"
+ },
+ "source": [
+ "## Create a pretty video output:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "aTRbuUQ1FBO0"
+ },
+ "outputs": [],
+ "source": [
+ "# Filter the predictions to remove small jitter, if desired:\n",
+ "dlc.filterpredictions(config_path, [video], shuffle=0, videotype=\"mp4\", track_method=TRACK_METHOD)\n",
+ "\n",
+ "dlc.create_labeled_video(\n",
+ " config_path,\n",
+ " [video],\n",
+ " videotype=\"mp4\",\n",
+ " shuffle=0,\n",
+ " color_by=\"individual\",\n",
+ " keypoints_only=False,\n",
+ " draw_skeleton=True,\n",
+ " filtered=True,\n",
+ " track_method=TRACK_METHOD,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "AYNlrgeNUG4U"
+ },
+ "source": [
+ "Now, on the left panel if you click the folder icon, you will see the project folder \"demo-me..\"; click on this and go into \"videos\" and you can find the \"..._id_labeled.mp4\" video, which you can double-click on to download and inspect!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "n7GWMBJUA9x5"
+ },
+ "source": [
+ "## Create Plots of your data:\n",
+ "\n",
+ "> after running, you can look in \"videos\", \"plot-poses\" to check out the trajectories! (sometimes you need to click the folder refresh icon to see it). Within the folder, for example, see plotmus1.png to vide the bodyparts over time vs. pixel position.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "7w9BDIA7BB_i"
+ },
+ "outputs": [],
+ "source": [
+ "dlc.plot_trajectories(config_path, [video], shuffle=0, videotype=\"mp4\", track_method=TRACK_METHOD)"
+ ]
+ }
+ ],
+ "metadata": {
+ "accelerator": "GPU",
+ "colab": {
+ "collapsed_sections": [],
+ "include_colab_link": true,
+ "name": "Copy of 3micedemo.ipynb",
+ "provenance": []
+ },
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2026-02-10",
+ "last_metadata_updated": "2026-03-06"
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
}
diff --git a/examples/COLAB/COLAB_BUCTD_and_CTD_tracking.ipynb b/examples/COLAB/COLAB_BUCTD_and_CTD_tracking.ipynb
new file mode 100644
index 0000000000..38460552ae
--- /dev/null
+++ b/examples/COLAB/COLAB_BUCTD_and_CTD_tracking.ipynb
@@ -0,0 +1,2388 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "87e3afb1",
+ "metadata": {
+ "id": "87e3afb1"
+ },
+ "source": [
+ "# DeepLabCut - Tutorial for BUCTD models\n",
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ "\n",
+ "**This tutorial introduces the use of [bottom-up conditioned top-down](https://openaccess.thecvf.com/content/ICCV2023/papers/Zhou_Rethinking_Pose_Estimation_in_Crowds_Overcoming_the_Detection_Information_Bottleneck_ICCV_2023_paper.pdf) pose estimation models (also named BUCTD or CTD) in DeepLabCut. This architecture is state-of-the-art in crowded images (when animals are interacting closely with one another), and carry the huge advantage that they can be used to directly track animals, removing the need for tracklet creation or stitching.**\n",
+ "\n",
+ "Some resources that can be useful:\n",
+ "\n",
+ "- The original paper: [Zhou, Stoffl, Mathis, Mathis. \"Rethinking Pose Estimation in Crowds: Overcoming the Detection Information Bottleneck and Ambiguity.\" Proceedings of the IEEE/CVF International Conference on Computer Vision (ICCV). 2023](https://openaccess.thecvf.com/content/ICCV2023/papers/Zhou_Rethinking_Pose_Estimation_in_Crowds_Overcoming_the_Detection_Information_Bottleneck_ICCV_2023_paper.pdf)\n",
+ "- Multi-animal user guide: [DeepLabCut's Documentation: User Guide for Multi-Animal projects](https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html)\n",
+ "\n",
+ "Note: In this notebook, we first train a BU model. Typically, you would already have a BU model, that is not performant enough. That's why you go for the BUCTD approach.\n",
+ "\n",
+ "## Introduction\n",
+ "\n",
+ "This notebook is an introduction to training and using CTD models in DeepLabCut, through the [maDLC Tri-Mouse Benchmark Dataset](https://zenodo.org/records/5851157) presented Lauer et al. 2022 (Nature Methods). For more information, you can check out the [DeepLabCut Benchmark Datasets](https://benchmark.deeplabcut.org/datasets.html).\n",
+ "\n",
+ "In this notebook, we'll\n",
+ "\n",
+ "- train an bottom-up model that can provide conditions for the CTD model\n",
+ "- evaluate the bottom-up model\n",
+ "- (optional/advanced) learn how the CTD model is trained with generative sampling\n",
+ "- train the CTD model\n",
+ "- evaluate the CTD model\n",
+ "- **(Nice feature of CTD models)** use the CTD model to track individuals\n",
+ "\n",
+ "Note: This notebook **can also be run locally**. However, using a GPU is recommended to train the models and run video inference. Just skip the _Installing DeepLabCut on COLAB_ section"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ad548ee1",
+ "metadata": {
+ "id": "ad548ee1"
+ },
+ "source": [
+ "### ⚠️⚠️ Change the Runtime type to use a GPU!⚠️⚠️\n",
+ "\n",
+ "First, go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\"."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2d41bf5e",
+ "metadata": {
+ "id": "2d41bf5e"
+ },
+ "source": [
+ "### Installing DeepLabCut on COLAB"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4428d550",
+ "metadata": {
+ "id": "4428d550"
+ },
+ "source": [
+ "Let's install the latest version of DeepLabCut, straight from GitHub."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "ae9ebeae",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 1000
+ },
+ "collapsed": true,
+ "executionInfo": {
+ "elapsed": 134753,
+ "status": "ok",
+ "timestamp": 1744357127034,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "ae9ebeae",
+ "outputId": "6643acbb-c848-42c3-b222-d9797a67249b"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Collecting deeplabcut\n",
+ " Cloning https://github.com/DeepLabCut/DeepLabCut.git (to revision lucas/buctd_v2) to /tmp/pip-install-p_2aupou/deeplabcut_38affa993eac4f18a4fcf05ba8f80e79\n",
+ " Running command git clone --filter=blob:none --quiet https://github.com/DeepLabCut/DeepLabCut.git /tmp/pip-install-p_2aupou/deeplabcut_38affa993eac4f18a4fcf05ba8f80e79\n",
+ " Running command git checkout -b lucas/buctd_v2 --track origin/lucas/buctd_v2\n",
+ " Switched to a new branch 'lucas/buctd_v2'\n",
+ " Branch 'lucas/buctd_v2' set up to track remote branch 'lucas/buctd_v2' from 'origin'.\n",
+ " Resolved https://github.com/DeepLabCut/DeepLabCut.git to commit 12cf3fa01b91bbc8c73c1efcecebf83815164da0\n",
+ " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n",
+ " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n",
+ " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n",
+ "Collecting albumentations<=1.4.3 (from deeplabcut)\n",
+ " Downloading albumentations-1.4.3-py3-none-any.whl.metadata (37 kB)\n",
+ "Collecting dlclibrary>=0.0.7 (from deeplabcut)\n",
+ " Downloading dlclibrary-0.0.7-py3-none-any.whl.metadata (4.2 kB)\n",
+ "Requirement already satisfied: einops in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (0.8.1)\n",
+ "Collecting filterpy>=1.4.4 (from deeplabcut)\n",
+ " Downloading filterpy-1.4.5.zip (177 kB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m178.0/178.0 kB\u001b[0m \u001b[31m10.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
+ "Collecting ruamel.yaml>=0.15.0 (from deeplabcut)\n",
+ " Downloading ruamel.yaml-0.18.10-py3-none-any.whl.metadata (23 kB)\n",
+ "Collecting imgaug>=0.4.0 (from deeplabcut)\n",
+ " Downloading imgaug-0.4.0-py2.py3-none-any.whl.metadata (1.8 kB)\n",
+ "Requirement already satisfied: imageio-ffmpeg in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (0.6.0)\n",
+ "Requirement already satisfied: numba>=0.54 in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (0.60.0)\n",
+ "Collecting matplotlib!=3.7.0,!=3.7.1,<3.9,>=3.3 (from deeplabcut)\n",
+ " Downloading matplotlib-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (5.8 kB)\n",
+ "Requirement already satisfied: networkx>=2.6 in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (3.4.2)\n",
+ "Collecting numpy<2.0.0,>=1.18.5 (from deeplabcut)\n",
+ " Downloading numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (61 kB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m61.0/61.0 kB\u001b[0m \u001b[31m5.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hRequirement already satisfied: pandas!=1.5.0,>=1.0.1 in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (2.2.2)\n",
+ "Requirement already satisfied: scikit-image>=0.17 in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (0.25.2)\n",
+ "Requirement already satisfied: scikit-learn>=1.0 in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (1.6.1)\n",
+ "Requirement already satisfied: scipy>=1.9 in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (1.14.1)\n",
+ "Requirement already satisfied: statsmodels>=0.11 in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (0.14.4)\n",
+ "Collecting tables==3.8.0 (from deeplabcut)\n",
+ " Downloading tables-3.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (2.2 kB)\n",
+ "Requirement already satisfied: timm in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (1.0.15)\n",
+ "Requirement already satisfied: torch>=2.0.0 in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (2.6.0+cu124)\n",
+ "Requirement already satisfied: torchvision in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (0.21.0+cu124)\n",
+ "Requirement already satisfied: tqdm in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (4.67.1)\n",
+ "Requirement already satisfied: pycocotools in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (2.0.8)\n",
+ "Requirement already satisfied: pyyaml in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (6.0.2)\n",
+ "Requirement already satisfied: Pillow>=7.1 in /usr/local/lib/python3.11/dist-packages (from deeplabcut) (11.1.0)\n",
+ "Requirement already satisfied: cython>=0.29.21 in /usr/local/lib/python3.11/dist-packages (from tables==3.8.0->deeplabcut) (3.0.12)\n",
+ "Requirement already satisfied: numexpr>=2.6.2 in /usr/local/lib/python3.11/dist-packages (from tables==3.8.0->deeplabcut) (2.10.2)\n",
+ "Collecting blosc2~=2.0.0 (from tables==3.8.0->deeplabcut)\n",
+ " Downloading blosc2-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (11 kB)\n",
+ "Requirement already satisfied: packaging in /usr/local/lib/python3.11/dist-packages (from tables==3.8.0->deeplabcut) (24.2)\n",
+ "Requirement already satisfied: py-cpuinfo in /usr/local/lib/python3.11/dist-packages (from tables==3.8.0->deeplabcut) (9.0.0)\n",
+ "Requirement already satisfied: typing-extensions>=4.9.0 in /usr/local/lib/python3.11/dist-packages (from albumentations<=1.4.3->deeplabcut) (4.13.1)\n",
+ "Requirement already satisfied: opencv-python-headless>=4.9.0 in /usr/local/lib/python3.11/dist-packages (from albumentations<=1.4.3->deeplabcut) (4.11.0.86)\n",
+ "Requirement already satisfied: huggingface-hub in /usr/local/lib/python3.11/dist-packages (from dlclibrary>=0.0.7->deeplabcut) (0.30.1)\n",
+ "Requirement already satisfied: six in /usr/local/lib/python3.11/dist-packages (from imgaug>=0.4.0->deeplabcut) (1.17.0)\n",
+ "Requirement already satisfied: opencv-python in /usr/local/lib/python3.11/dist-packages (from imgaug>=0.4.0->deeplabcut) (4.11.0.86)\n",
+ "Requirement already satisfied: imageio in /usr/local/lib/python3.11/dist-packages (from imgaug>=0.4.0->deeplabcut) (2.37.0)\n",
+ "Requirement already satisfied: Shapely in /usr/local/lib/python3.11/dist-packages (from imgaug>=0.4.0->deeplabcut) (2.1.0)\n",
+ "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.7.0,!=3.7.1,<3.9,>=3.3->deeplabcut) (1.3.1)\n",
+ "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.7.0,!=3.7.1,<3.9,>=3.3->deeplabcut) (0.12.1)\n",
+ "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.7.0,!=3.7.1,<3.9,>=3.3->deeplabcut) (4.57.0)\n",
+ "Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.7.0,!=3.7.1,<3.9,>=3.3->deeplabcut) (1.4.8)\n",
+ "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.7.0,!=3.7.1,<3.9,>=3.3->deeplabcut) (3.2.3)\n",
+ "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.7.0,!=3.7.1,<3.9,>=3.3->deeplabcut) (2.8.2)\n",
+ "Requirement already satisfied: llvmlite<0.44,>=0.43.0dev0 in /usr/local/lib/python3.11/dist-packages (from numba>=0.54->deeplabcut) (0.43.0)\n",
+ "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/dist-packages (from pandas!=1.5.0,>=1.0.1->deeplabcut) (2025.2)\n",
+ "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/dist-packages (from pandas!=1.5.0,>=1.0.1->deeplabcut) (2025.2)\n",
+ "Collecting ruamel.yaml.clib>=0.2.7 (from ruamel.yaml>=0.15.0->deeplabcut)\n",
+ " Downloading ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (2.7 kB)\n",
+ "Requirement already satisfied: tifffile>=2022.8.12 in /usr/local/lib/python3.11/dist-packages (from scikit-image>=0.17->deeplabcut) (2025.3.30)\n",
+ "Requirement already satisfied: lazy-loader>=0.4 in /usr/local/lib/python3.11/dist-packages (from scikit-image>=0.17->deeplabcut) (0.4)\n",
+ "Requirement already satisfied: joblib>=1.2.0 in /usr/local/lib/python3.11/dist-packages (from scikit-learn>=1.0->deeplabcut) (1.4.2)\n",
+ "Requirement already satisfied: threadpoolctl>=3.1.0 in /usr/local/lib/python3.11/dist-packages (from scikit-learn>=1.0->deeplabcut) (3.6.0)\n",
+ "Requirement already satisfied: patsy>=0.5.6 in /usr/local/lib/python3.11/dist-packages (from statsmodels>=0.11->deeplabcut) (1.0.1)\n",
+ "Requirement already satisfied: filelock in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->deeplabcut) (3.18.0)\n",
+ "Requirement already satisfied: jinja2 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->deeplabcut) (3.1.6)\n",
+ "Requirement already satisfied: fsspec in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->deeplabcut) (2025.3.2)\n",
+ "Collecting nvidia-cuda-nvrtc-cu12==12.4.127 (from torch>=2.0.0->deeplabcut)\n",
+ " Downloading nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\n",
+ "Collecting nvidia-cuda-runtime-cu12==12.4.127 (from torch>=2.0.0->deeplabcut)\n",
+ " Downloading nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\n",
+ "Collecting nvidia-cuda-cupti-cu12==12.4.127 (from torch>=2.0.0->deeplabcut)\n",
+ " Downloading nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl.metadata (1.6 kB)\n",
+ "Collecting nvidia-cudnn-cu12==9.1.0.70 (from torch>=2.0.0->deeplabcut)\n",
+ " Downloading nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl.metadata (1.6 kB)\n",
+ "Collecting nvidia-cublas-cu12==12.4.5.8 (from torch>=2.0.0->deeplabcut)\n",
+ " Downloading nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\n",
+ "Collecting nvidia-cufft-cu12==11.2.1.3 (from torch>=2.0.0->deeplabcut)\n",
+ " Downloading nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\n",
+ "Collecting nvidia-curand-cu12==10.3.5.147 (from torch>=2.0.0->deeplabcut)\n",
+ " Downloading nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\n",
+ "Collecting nvidia-cusolver-cu12==11.6.1.9 (from torch>=2.0.0->deeplabcut)\n",
+ " Downloading nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl.metadata (1.6 kB)\n",
+ "Collecting nvidia-cusparse-cu12==12.3.1.170 (from torch>=2.0.0->deeplabcut)\n",
+ " Downloading nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl.metadata (1.6 kB)\n",
+ "Requirement already satisfied: nvidia-cusparselt-cu12==0.6.2 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->deeplabcut) (0.6.2)\n",
+ "Requirement already satisfied: nvidia-nccl-cu12==2.21.5 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->deeplabcut) (2.21.5)\n",
+ "Requirement already satisfied: nvidia-nvtx-cu12==12.4.127 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->deeplabcut) (12.4.127)\n",
+ "Collecting nvidia-nvjitlink-cu12==12.4.127 (from torch>=2.0.0->deeplabcut)\n",
+ " Downloading nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\n",
+ "Requirement already satisfied: triton==3.2.0 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->deeplabcut) (3.2.0)\n",
+ "Requirement already satisfied: sympy==1.13.1 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->deeplabcut) (1.13.1)\n",
+ "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.11/dist-packages (from sympy==1.13.1->torch>=2.0.0->deeplabcut) (1.3.0)\n",
+ "Requirement already satisfied: safetensors in /usr/local/lib/python3.11/dist-packages (from timm->deeplabcut) (0.5.3)\n",
+ "Requirement already satisfied: msgpack in /usr/local/lib/python3.11/dist-packages (from blosc2~=2.0.0->tables==3.8.0->deeplabcut) (1.1.0)\n",
+ "Requirement already satisfied: requests in /usr/local/lib/python3.11/dist-packages (from huggingface-hub->dlclibrary>=0.0.7->deeplabcut) (2.32.3)\n",
+ "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.11/dist-packages (from jinja2->torch>=2.0.0->deeplabcut) (3.0.2)\n",
+ "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests->huggingface-hub->dlclibrary>=0.0.7->deeplabcut) (3.4.1)\n",
+ "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/dist-packages (from requests->huggingface-hub->dlclibrary>=0.0.7->deeplabcut) (3.10)\n",
+ "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/dist-packages (from requests->huggingface-hub->dlclibrary>=0.0.7->deeplabcut) (2.3.0)\n",
+ "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/dist-packages (from requests->huggingface-hub->dlclibrary>=0.0.7->deeplabcut) (2025.1.31)\n",
+ "Downloading tables-3.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.5 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m6.5/6.5 MB\u001b[0m \u001b[31m105.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading albumentations-1.4.3-py3-none-any.whl (137 kB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m137.0/137.0 kB\u001b[0m \u001b[31m13.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading dlclibrary-0.0.7-py3-none-any.whl (16 kB)\n",
+ "Downloading imgaug-0.4.0-py2.py3-none-any.whl (948 kB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m948.0/948.0 kB\u001b[0m \u001b[31m52.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading matplotlib-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (11.6 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m11.6/11.6 MB\u001b[0m \u001b[31m103.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (18.3 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m18.3/18.3 MB\u001b[0m \u001b[31m95.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading ruamel.yaml-0.18.10-py3-none-any.whl (117 kB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m117.7/117.7 kB\u001b[0m \u001b[31m11.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl (363.4 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m363.4/363.4 MB\u001b[0m \u001b[31m4.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl (13.8 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m13.8/13.8 MB\u001b[0m \u001b[31m99.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl (24.6 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m24.6/24.6 MB\u001b[0m \u001b[31m88.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl (883 kB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m883.7/883.7 kB\u001b[0m \u001b[31m54.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl (664.8 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m664.8/664.8 MB\u001b[0m \u001b[31m1.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl (211.5 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m211.5/211.5 MB\u001b[0m \u001b[31m3.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl (56.3 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m56.3/56.3 MB\u001b[0m \u001b[31m22.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl (127.9 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m127.9/127.9 MB\u001b[0m \u001b[31m7.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl (207.5 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m207.5/207.5 MB\u001b[0m \u001b[31m5.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl (21.1 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m21.1/21.1 MB\u001b[0m \u001b[31m41.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading blosc2-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.9 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.9/3.9 MB\u001b[0m \u001b[31m91.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hDownloading ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (739 kB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m739.1/739.1 kB\u001b[0m \u001b[31m51.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hBuilding wheels for collected packages: deeplabcut, filterpy\n",
+ " Building wheel for deeplabcut (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n",
+ " Created wheel for deeplabcut: filename=deeplabcut-3.0.0rc8-py3-none-any.whl size=2135196 sha256=5fed7cbc1c688b63dba5c4ec999ec8cd64ff183633f52f61dae83c99bef61086\n",
+ " Stored in directory: /tmp/pip-ephem-wheel-cache-mksbbfnl/wheels/f5/b8/31/9da4b9cf29c390764ce8fb3cda190fa42dce894367ddf37cc9\n",
+ " Building wheel for filterpy (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
+ " Created wheel for filterpy: filename=filterpy-1.4.5-py3-none-any.whl size=110460 sha256=ba55d3e7bb10d2a01b1ad749dd6776e6fe11dc542fa55874879db636b2932478\n",
+ " Stored in directory: /root/.cache/pip/wheels/12/dc/3c/e12983eac132d00f82a20c6cbe7b42ce6e96190ef8fa2d15e1\n",
+ "Successfully built deeplabcut filterpy\n",
+ "Installing collected packages: ruamel.yaml.clib, nvidia-nvjitlink-cu12, nvidia-curand-cu12, nvidia-cufft-cu12, nvidia-cuda-runtime-cu12, nvidia-cuda-nvrtc-cu12, nvidia-cuda-cupti-cu12, nvidia-cublas-cu12, numpy, blosc2, ruamel.yaml, nvidia-cusparse-cu12, nvidia-cudnn-cu12, tables, nvidia-cusolver-cu12, matplotlib, dlclibrary, imgaug, filterpy, albumentations, deeplabcut\n",
+ " Attempting uninstall: nvidia-nvjitlink-cu12\n",
+ " Found existing installation: nvidia-nvjitlink-cu12 12.5.82\n",
+ " Uninstalling nvidia-nvjitlink-cu12-12.5.82:\n",
+ " Successfully uninstalled nvidia-nvjitlink-cu12-12.5.82\n",
+ " Attempting uninstall: nvidia-curand-cu12\n",
+ " Found existing installation: nvidia-curand-cu12 10.3.6.82\n",
+ " Uninstalling nvidia-curand-cu12-10.3.6.82:\n",
+ " Successfully uninstalled nvidia-curand-cu12-10.3.6.82\n",
+ " Attempting uninstall: nvidia-cufft-cu12\n",
+ " Found existing installation: nvidia-cufft-cu12 11.2.3.61\n",
+ " Uninstalling nvidia-cufft-cu12-11.2.3.61:\n",
+ " Successfully uninstalled nvidia-cufft-cu12-11.2.3.61\n",
+ " Attempting uninstall: nvidia-cuda-runtime-cu12\n",
+ " Found existing installation: nvidia-cuda-runtime-cu12 12.5.82\n",
+ " Uninstalling nvidia-cuda-runtime-cu12-12.5.82:\n",
+ " Successfully uninstalled nvidia-cuda-runtime-cu12-12.5.82\n",
+ " Attempting uninstall: nvidia-cuda-nvrtc-cu12\n",
+ " Found existing installation: nvidia-cuda-nvrtc-cu12 12.5.82\n",
+ " Uninstalling nvidia-cuda-nvrtc-cu12-12.5.82:\n",
+ " Successfully uninstalled nvidia-cuda-nvrtc-cu12-12.5.82\n",
+ " Attempting uninstall: nvidia-cuda-cupti-cu12\n",
+ " Found existing installation: nvidia-cuda-cupti-cu12 12.5.82\n",
+ " Uninstalling nvidia-cuda-cupti-cu12-12.5.82:\n",
+ " Successfully uninstalled nvidia-cuda-cupti-cu12-12.5.82\n",
+ " Attempting uninstall: nvidia-cublas-cu12\n",
+ " Found existing installation: nvidia-cublas-cu12 12.5.3.2\n",
+ " Uninstalling nvidia-cublas-cu12-12.5.3.2:\n",
+ " Successfully uninstalled nvidia-cublas-cu12-12.5.3.2\n",
+ " Attempting uninstall: numpy\n",
+ " Found existing installation: numpy 2.0.2\n",
+ " Uninstalling numpy-2.0.2:\n",
+ " Successfully uninstalled numpy-2.0.2\n",
+ " Attempting uninstall: blosc2\n",
+ " Found existing installation: blosc2 3.2.1\n",
+ " Uninstalling blosc2-3.2.1:\n",
+ " Successfully uninstalled blosc2-3.2.1\n",
+ " Attempting uninstall: nvidia-cusparse-cu12\n",
+ " Found existing installation: nvidia-cusparse-cu12 12.5.1.3\n",
+ " Uninstalling nvidia-cusparse-cu12-12.5.1.3:\n",
+ " Successfully uninstalled nvidia-cusparse-cu12-12.5.1.3\n",
+ " Attempting uninstall: nvidia-cudnn-cu12\n",
+ " Found existing installation: nvidia-cudnn-cu12 9.3.0.75\n",
+ " Uninstalling nvidia-cudnn-cu12-9.3.0.75:\n",
+ " Successfully uninstalled nvidia-cudnn-cu12-9.3.0.75\n",
+ " Attempting uninstall: tables\n",
+ " Found existing installation: tables 3.10.2\n",
+ " Uninstalling tables-3.10.2:\n",
+ " Successfully uninstalled tables-3.10.2\n",
+ " Attempting uninstall: nvidia-cusolver-cu12\n",
+ " Found existing installation: nvidia-cusolver-cu12 11.6.3.83\n",
+ " Uninstalling nvidia-cusolver-cu12-11.6.3.83:\n",
+ " Successfully uninstalled nvidia-cusolver-cu12-11.6.3.83\n",
+ " Attempting uninstall: matplotlib\n",
+ " Found existing installation: matplotlib 3.10.0\n",
+ " Uninstalling matplotlib-3.10.0:\n",
+ " Successfully uninstalled matplotlib-3.10.0\n",
+ " Attempting uninstall: albumentations\n",
+ " Found existing installation: albumentations 2.0.5\n",
+ " Uninstalling albumentations-2.0.5:\n",
+ " Successfully uninstalled albumentations-2.0.5\n",
+ "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n",
+ "thinc 8.3.6 requires numpy<3.0.0,>=2.0.0, but you have numpy 1.26.4 which is incompatible.\u001b[0m\u001b[31m\n",
+ "\u001b[0mSuccessfully installed albumentations-1.4.3 blosc2-2.0.0 deeplabcut-3.0.0rc8 dlclibrary-0.0.7 filterpy-1.4.5 imgaug-0.4.0 matplotlib-3.8.4 numpy-1.26.4 nvidia-cublas-cu12-12.4.5.8 nvidia-cuda-cupti-cu12-12.4.127 nvidia-cuda-nvrtc-cu12-12.4.127 nvidia-cuda-runtime-cu12-12.4.127 nvidia-cudnn-cu12-9.1.0.70 nvidia-cufft-cu12-11.2.1.3 nvidia-curand-cu12-10.3.5.147 nvidia-cusolver-cu12-11.6.1.9 nvidia-cusparse-cu12-12.3.1.170 nvidia-nvjitlink-cu12-12.4.127 ruamel.yaml-0.18.10 ruamel.yaml.clib-0.2.12 tables-3.8.0\n"
+ ]
+ },
+ {
+ "data": {
+ "application/vnd.colab-display-data+json": {
+ "id": "d5d69df769e143178b2cb062f2044022",
+ "pip_warning": {
+ "packages": [
+ "matplotlib",
+ "mpl_toolkits"
+ ]
+ }
+ }
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "!pip install --pre deeplabcut"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "dd0a07bc",
+ "metadata": {
+ "id": "dd0a07bc"
+ },
+ "source": [
+ "**(Be sure to click \"RESTART RUNTIME\" if it is displayed above before moving on !)** You will see this button at the output of the cells above ^."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7c35856e",
+ "metadata": {
+ "id": "7c35856e"
+ },
+ "source": [
+ "### Imports"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "0d2ca689",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "executionInfo": {
+ "elapsed": 30067,
+ "status": "ok",
+ "timestamp": 1744357191552,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "0d2ca689",
+ "outputId": "c1dcdaaa-0ec9-475b-bb5d-a3fa59712727"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Loading DLC 3.0.0rc8...\n",
+ "DLC loaded in light mode; you cannot use any GUI (labeling, relabeling and standalone GUI)\n"
+ ]
+ }
+ ],
+ "source": [
+ "import shutil\n",
+ "from io import BytesIO\n",
+ "from pathlib import Path\n",
+ "from zipfile import ZipFile\n",
+ "\n",
+ "import matplotlib.pyplot as plt\n",
+ "import numpy as np\n",
+ "import requests\n",
+ "\n",
+ "import deeplabcut\n",
+ "import deeplabcut.pose_estimation_pytorch as dlc_torch\n",
+ "import deeplabcut.utils.auxiliaryfunctions as auxiliaryfunctions"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "36a04338",
+ "metadata": {
+ "id": "36a04338"
+ },
+ "source": [
+ "### Downloading the Tri-Mouse Dataset\n",
+ "\n",
+ "This cell downloads the Tri-Mouse dataset from Zenodo into the current working directory (or `cwd`), which should be the directory you launched the jupyter server from."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "8d1c71ab",
+ "metadata": {
+ "executionInfo": {
+ "elapsed": 14,
+ "status": "ok",
+ "timestamp": 1744357194674,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "8d1c71ab"
+ },
+ "outputs": [],
+ "source": [
+ "download_path = Path.cwd()\n",
+ "config = str(download_path / \"trimice-dlc-2021-06-22\" / \"config.yaml\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "784ed973",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "executionInfo": {
+ "elapsed": 42401,
+ "status": "ok",
+ "timestamp": 1744357238563,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "784ed973",
+ "outputId": "fb682110-14b4-4a09-cdb6-6524f7433d11"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Downloading the tri-mouse dataset into /content\n",
+ "Downloading trimice-dlc-2021-06-22.zip...\n",
+ "Config path: /content/trimice-dlc-2021-06-22/config.yaml\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"Downloading the tri-mouse dataset into {download_path}\")\n",
+ "\n",
+ "url_record = \"https://zenodo.org/api/records/5851157\"\n",
+ "response = requests.get(url_record)\n",
+ "if response.status_code == 200:\n",
+ " file = response.json()[\"files\"][0]\n",
+ " title = file[\"key\"]\n",
+ " print(f\"Downloading {title}...\")\n",
+ " with requests.get(file[\"links\"][\"self\"], stream=True) as r:\n",
+ " with ZipFile(BytesIO(r.content)) as zf:\n",
+ " zf.extractall(path=download_path)\n",
+ "else:\n",
+ " raise ValueError(f\"The URL {url_record} could not be reached.\")\n",
+ "\n",
+ "\n",
+ "# Check that the config was downloaded correctly\n",
+ "print(f\"Config path: {config}\")\n",
+ "if not Path(config).exists():\n",
+ " print(f\"Could not find config at {config}: check that the dataset was downloaded correctly!\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "414d1700",
+ "metadata": {
+ "id": "414d1700"
+ },
+ "source": [
+ "## Training a CTD Model in DeepLabCut\n",
+ "\n",
+ "BUCTD (or bottom-up conditioned top-down), as its name suggests, requires a bottom-up model to provide conditions (or **pose proposals**) for the CTD model to fix. So the first step in getting a CTD model that can be used to run inference is to train a bottom-up model to provide conditions!\n",
+ "\n",
+ "We'll also **ensure that we're training the bottom-up and CTD models on the same train/test splits!** This is important: if you're training the models on different training images and evaluating them on different test images, then their results aren't comparable!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "8e808c35",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "executionInfo": {
+ "elapsed": 81,
+ "status": "ok",
+ "timestamp": 1744357248719,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "8e808c35",
+ "outputId": "1a7777ba-a4d5-4777-973f-02497878617d"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Using 70% of the data in the training set.\n"
+ ]
+ }
+ ],
+ "source": [
+ "cfg = auxiliaryfunctions.read_config(config)\n",
+ "train_frac = cfg[\"TrainingFraction\"][0]\n",
+ "print(f\"Using {int(100 * train_frac)}% of the data in the training set.\")\n",
+ "\n",
+ "num_images = 112\n",
+ "train_images = int(train_frac * num_images)\n",
+ "\n",
+ "seed = 0\n",
+ "rng = np.random.default_rng(seed)\n",
+ "\n",
+ "train_indices = rng.choice(num_images, size=train_images, replace=False, shuffle=False).tolist()\n",
+ "test_indices = [idx for idx in range(num_images) if idx not in train_indices]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d2165546",
+ "metadata": {
+ "id": "d2165546"
+ },
+ "source": [
+ "### Training a BU Model\n",
+ "\n",
+ "We'll take the simplest approach possible here and train a ResNet pose estimation model. As the CTD model will be used to improve the predictions made by the BU model, we want something light and fast rather than something heavy and slow!\n",
+ "\n",
+ "We'll start by **creating the shuffle for the bottom-up model (with index 1) with the selected train/test split**."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "8329fce1",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "executionInfo": {
+ "elapsed": 427,
+ "status": "ok",
+ "timestamp": 1744357252179,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "8329fce1",
+ "outputId": "643bddfa-da4d-49d0-e241-849500d06fc1"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Utilizing the following graph: [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [0, 8], [0, 9], [0, 10], [0, 11], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1, 8], [1, 9], [1, 10], [1, 11], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 9], [2, 10], [2, 11], [3, 4], [3, 5], [3, 6], [3, 7], [3, 8], [3, 9], [3, 10], [3, 11], [4, 5], [4, 6], [4, 7], [4, 8], [4, 9], [4, 10], [4, 11], [5, 6], [5, 7], [5, 8], [5, 9], [5, 10], [5, 11], [6, 7], [6, 8], [6, 9], [6, 10], [6, 11], [7, 8], [7, 9], [7, 10], [7, 11], [8, 9], [8, 10], [8, 11], [9, 10], [9, 11], [10, 11]]\n",
+ "You passed a split with the following fraction: 70%\n",
+ "Creating training data for: Shuffle: 1 TrainFraction: 0.7\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 78/78 [00:00<00:00, 1613.55it/s]"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "The training dataset is successfully created. Use the function 'train_network' to start training. Happy training!\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "BU_SHUFFLE = 1\n",
+ "\n",
+ "deeplabcut.create_training_dataset(\n",
+ " config,\n",
+ " Shuffles=[BU_SHUFFLE],\n",
+ " trainIndices=[train_indices],\n",
+ " testIndices=[test_indices],\n",
+ " net_type=\"resnet_50\",\n",
+ " engine=deeplabcut.Engine.PYTORCH,\n",
+ " userfeedback=False,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9bb714ec",
+ "metadata": {
+ "id": "9bb714ec"
+ },
+ "source": [
+ "We can then train the model defined in the created bottom-up shuffle. To make running this notebook a bit quicker, we'll **only train the BU model for 100 epochs**. The model should still perform well enough, and as we're less interested in the BU model than the CTD model we'll save a bit of time and compute here. Training the model should **take 10 to 20 minutes**, depending on your CPU and GPU performance.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "4ec8e03c",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 1000,
+ "referenced_widgets": [
+ "2e74326b630a4d4fa879ec3a154eecb4",
+ "3880c2d826f440d485bd49b562c58a6f",
+ "4aea0735770d4196bfb9b036017f65b2",
+ "069424f702f446cb8737e94a28b18588",
+ "da21a61d52cd46d79d88bb458b1cda45",
+ "7e4bae5e01234b49a350583d99998956",
+ "affa8c3978fa4b118d1fd4b5c055d464",
+ "c4fc0083fdd24badafea3df30ff9b2a5",
+ "5e9c160affc945748cbbfe8f6b76df3f",
+ "6a24ded1648a40e0926f6a66d2501207",
+ "1d6e05a559e34b389ff57424ad7ce3d1"
+ ]
+ },
+ "executionInfo": {
+ "elapsed": 1182867,
+ "status": "ok",
+ "timestamp": 1744358458536,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "4ec8e03c",
+ "outputId": "59489e99-078c-4ef7-c98b-930c34cf82e2"
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "Training with configuration:\n",
+ "data:\n",
+ " bbox_margin: 20\n",
+ " colormode: RGB\n",
+ " inference:\n",
+ " normalize_images: True\n",
+ " train:\n",
+ " affine:\n",
+ " p: 0.5\n",
+ " rotation: 30\n",
+ " scaling: [0.5, 1.25]\n",
+ " translation: 0\n",
+ " crop_sampling:\n",
+ " width: 448\n",
+ " height: 448\n",
+ " max_shift: 0.1\n",
+ " method: hybrid\n",
+ " gaussian_noise: 12.75\n",
+ " motion_blur: True\n",
+ " normalize_images: True\n",
+ "device: auto\n",
+ "metadata:\n",
+ " project_path: /content/trimice-dlc-2021-06-22\n",
+ " pose_config_path: /content/trimice-dlc-2021-06-22/dlc-models-pytorch/iteration-0/trimiceJun22-trainset70shuffle1/train/pytorch_config.yaml\n",
+ " bodyparts: ['snout', 'leftear', 'rightear', 'shoulder', 'spine1', 'spine2', 'spine3', 'spine4', 'tailbase', 'tail1', 'tail2', 'tailend']\n",
+ " unique_bodyparts: []\n",
+ " individuals: ['mus1', 'mus2', 'mus3']\n",
+ " with_identity: None\n",
+ "method: bu\n",
+ "model:\n",
+ " backbone:\n",
+ " type: ResNet\n",
+ " model_name: resnet50_gn\n",
+ " output_stride: 16\n",
+ " freeze_bn_stats: False\n",
+ " freeze_bn_weights: False\n",
+ " backbone_output_channels: 2048\n",
+ " heads:\n",
+ " bodypart:\n",
+ " type: DLCRNetHead\n",
+ " predictor:\n",
+ " type: PartAffinityFieldPredictor\n",
+ " num_animals: 3\n",
+ " num_multibodyparts: 12\n",
+ " num_uniquebodyparts: 0\n",
+ " nms_radius: 5\n",
+ " sigma: 1.0\n",
+ " locref_stdev: 7.2801\n",
+ " min_affinity: 0.05\n",
+ " graph: [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [0, 8], [0, 9], [0, 10], [0, 11], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1, 8], [1, 9], [1, 10], [1, 11], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 9], [2, 10], [2, 11], [3, 4], [3, 5], [3, 6], [3, 7], [3, 8], [3, 9], [3, 10], [3, 11], [4, 5], [4, 6], [4, 7], [4, 8], [4, 9], [4, 10], [4, 11], [5, 6], [5, 7], [5, 8], [5, 9], [5, 10], [5, 11], [6, 7], [6, 8], [6, 9], [6, 10], [6, 11], [7, 8], [7, 9], [7, 10], [7, 11], [8, 9], [8, 10], [8, 11], [9, 10], [9, 11], [10, 11]]\n",
+ " edges_to_keep: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65]\n",
+ " apply_sigmoid: True\n",
+ " clip_scores: False\n",
+ " target_generator:\n",
+ " type: SequentialGenerator\n",
+ " generators: [{'type': 'HeatmapPlateauGenerator', 'num_heatmaps': 12, 'pos_dist_thresh': 17, 'heatmap_mode': 'KEYPOINT', 'gradient_masking': False, 'generate_locref': True, 'locref_std': 7.2801}, {'type': 'PartAffinityFieldGenerator', 'graph': [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [0, 8], [0, 9], [0, 10], [0, 11], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1, 8], [1, 9], [1, 10], [1, 11], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 9], [2, 10], [2, 11], [3, 4], [3, 5], [3, 6], [3, 7], [3, 8], [3, 9], [3, 10], [3, 11], [4, 5], [4, 6], [4, 7], [4, 8], [4, 9], [4, 10], [4, 11], [5, 6], [5, 7], [5, 8], [5, 9], [5, 10], [5, 11], [6, 7], [6, 8], [6, 9], [6, 10], [6, 11], [7, 8], [7, 9], [7, 10], [7, 11], [8, 9], [8, 10], [8, 11], [9, 10], [9, 11], [10, 11]], 'width': 20}]\n",
+ " criterion:\n",
+ " heatmap:\n",
+ " type: WeightedBCECriterion\n",
+ " weight: 1.0\n",
+ " locref:\n",
+ " type: WeightedHuberCriterion\n",
+ " weight: 0.05\n",
+ " paf:\n",
+ " type: WeightedHuberCriterion\n",
+ " weight: 0.1\n",
+ " heatmap_config:\n",
+ " channels: [2048, 12]\n",
+ " kernel_size: [3]\n",
+ " strides: [2]\n",
+ " locref_config:\n",
+ " channels: [2048, 24]\n",
+ " kernel_size: [3]\n",
+ " strides: [2]\n",
+ " paf_config:\n",
+ " channels: [2048, 132]\n",
+ " kernel_size: [3]\n",
+ " strides: [2]\n",
+ " num_stages: 5\n",
+ "net_type: resnet_50\n",
+ "runner:\n",
+ " type: PoseTrainingRunner\n",
+ " gpus: None\n",
+ " key_metric: test.mAP\n",
+ " key_metric_asc: True\n",
+ " eval_interval: 10\n",
+ " optimizer:\n",
+ " type: AdamW\n",
+ " params:\n",
+ " lr: 0.0005\n",
+ " scheduler:\n",
+ " type: LRListScheduler\n",
+ " params:\n",
+ " lr_list: [[0.0001], [1e-05]]\n",
+ " milestones: [90, 120]\n",
+ " snapshots:\n",
+ " max_snapshots: 5\n",
+ " save_epochs: 25\n",
+ " save_optimizer_state: False\n",
+ "train_settings:\n",
+ " batch_size: 8\n",
+ " dataloader_workers: 0\n",
+ " dataloader_pin_memory: False\n",
+ " display_iters: 500\n",
+ " epochs: 100\n",
+ " seed: 42\n",
+ "Loading pretrained weights from Hugging Face hub (timm/resnet50_gn.a1h_in1k)\n",
+ "/usr/local/lib/python3.11/dist-packages/huggingface_hub/utils/_auth.py:94: UserWarning: \n",
+ "The secret `HF_TOKEN` does not exist in your Colab secrets.\n",
+ "To authenticate with the Hugging Face Hub, create a token in your settings tab (https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session.\n",
+ "You will be able to reuse this secret in all of your notebooks.\n",
+ "Please note that authentication is recommended but still optional to access public models or datasets.\n",
+ " warnings.warn(\n"
+ ]
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "2e74326b630a4d4fa879ec3a154eecb4",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "model.safetensors: 0%| | 0.00/102M [00:00, ?B/s]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "[timm/resnet50_gn.a1h_in1k] Safe alternative available for 'pytorch_model.bin' (as 'model.safetensors'). Loading weights using safetensors.\n",
+ "Data Transforms:\n",
+ " Training: Compose([\n",
+ " Affine(always_apply=False, p=0.5, interpolation=1, mask_interpolation=0, cval=0, mode=0, scale={'x': (0.5, 1.25), 'y': (0.5, 1.25)}, translate_percent=None, translate_px={'x': (0, 0), 'y': (0, 0)}, rotate=(-30, 30), fit_output=False, shear={'x': (0.0, 0.0), 'y': (0.0, 0.0)}, cval_mask=0, keep_ratio=True, rotate_method='largest_box'),\n",
+ " PadIfNeeded(always_apply=True, p=1.0, min_height=448, min_width=448, pad_height_divisor=None, pad_width_divisor=None, position=PositionType.CENTER, border_mode=0, value=None, mask_value=None),\n",
+ " KeypointAwareCrop(always_apply=True, p=1.0, width=448, height=448, max_shift=0.1, crop_sampling='hybrid'),\n",
+ " MotionBlur(always_apply=False, p=0.5, blur_limit=(3, 7), allow_shifted=True),\n",
+ " GaussNoise(always_apply=False, p=0.5, var_limit=(0, 162.5625), per_channel=True, mean=0),\n",
+ " Normalize(always_apply=False, p=1.0, mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], max_pixel_value=255.0),\n",
+ "], p=1.0, bbox_params={'format': 'coco', 'label_fields': ['bbox_labels'], 'min_area': 0.0, 'min_visibility': 0.0, 'min_width': 0.0, 'min_height': 0.0, 'check_each_transform': True}, keypoint_params={'format': 'xy', 'label_fields': ['class_labels'], 'remove_invisible': False, 'angle_in_degrees': True, 'check_each_transform': True}, additional_targets={}, is_check_shapes=True)\n",
+ " Validation: Compose([\n",
+ " Normalize(always_apply=False, p=1.0, mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], max_pixel_value=255.0),\n",
+ "], p=1.0, bbox_params={'format': 'coco', 'label_fields': ['bbox_labels'], 'min_area': 0.0, 'min_visibility': 0.0, 'min_width': 0.0, 'min_height': 0.0, 'check_each_transform': True}, keypoint_params={'format': 'xy', 'label_fields': ['class_labels'], 'remove_invisible': False, 'angle_in_degrees': True, 'check_each_transform': True}, additional_targets={}, is_check_shapes=True)\n",
+ "Using 78 images and 34 for testing\n",
+ "\n",
+ "Starting pose model training...\n",
+ "--------------------------------------------------\n",
+ "Epoch 1/100 (lr=0.0005), train loss 0.08699\n",
+ "Epoch 2/100 (lr=0.0005), train loss 0.02512\n",
+ "Epoch 3/100 (lr=0.0005), train loss 0.02266\n",
+ "Epoch 4/100 (lr=0.0005), train loss 0.02117\n",
+ "Epoch 5/100 (lr=0.0005), train loss 0.02038\n",
+ "Epoch 6/100 (lr=0.0005), train loss 0.01967\n",
+ "Epoch 7/100 (lr=0.0005), train loss 0.01796\n",
+ "Epoch 8/100 (lr=0.0005), train loss 0.01739\n",
+ "Epoch 9/100 (lr=0.0005), train loss 0.01615\n",
+ "Training for epoch 10 done, starting evaluation\n",
+ "Epoch 10/100 (lr=0.0005), train loss 0.01435, valid loss 0.01307\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 52.10\n",
+ " metrics/test.rmse_pcutoff: 42.00\n",
+ " metrics/test.mAP: 28.63\n",
+ " metrics/test.mAR: 37.16\n",
+ "Epoch 11/100 (lr=0.0005), train loss 0.01340\n",
+ "Epoch 12/100 (lr=0.0005), train loss 0.01192\n",
+ "Epoch 13/100 (lr=0.0005), train loss 0.01072\n",
+ "Epoch 14/100 (lr=0.0005), train loss 0.01012\n",
+ "Epoch 15/100 (lr=0.0005), train loss 0.00917\n",
+ "Epoch 16/100 (lr=0.0005), train loss 0.00888\n",
+ "Epoch 17/100 (lr=0.0005), train loss 0.00848\n",
+ "Epoch 18/100 (lr=0.0005), train loss 0.00810\n",
+ "Epoch 19/100 (lr=0.0005), train loss 0.00742\n",
+ "Training for epoch 20 done, starting evaluation\n",
+ "Epoch 20/100 (lr=0.0005), train loss 0.00693, valid loss 0.00716\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 31.48\n",
+ " metrics/test.rmse_pcutoff: 29.16\n",
+ " metrics/test.mAP: 56.98\n",
+ " metrics/test.mAR: 63.14\n",
+ "Epoch 21/100 (lr=0.0005), train loss 0.00636\n",
+ "Epoch 22/100 (lr=0.0005), train loss 0.00645\n",
+ "Epoch 23/100 (lr=0.0005), train loss 0.00596\n",
+ "Epoch 24/100 (lr=0.0005), train loss 0.00534\n",
+ "Epoch 25/100 (lr=0.0005), train loss 0.00545\n",
+ "Epoch 26/100 (lr=0.0005), train loss 0.00507\n",
+ "Epoch 27/100 (lr=0.0005), train loss 0.00502\n",
+ "Epoch 28/100 (lr=0.0005), train loss 0.00492\n",
+ "Epoch 29/100 (lr=0.0005), train loss 0.00456\n",
+ "Training for epoch 30 done, starting evaluation\n",
+ "Epoch 30/100 (lr=0.0005), train loss 0.00456, valid loss 0.00507\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 15.23\n",
+ " metrics/test.rmse_pcutoff: 11.35\n",
+ " metrics/test.mAP: 79.63\n",
+ " metrics/test.mAR: 83.04\n",
+ "Epoch 31/100 (lr=0.0005), train loss 0.00453\n",
+ "Epoch 32/100 (lr=0.0005), train loss 0.00434\n",
+ "Epoch 33/100 (lr=0.0005), train loss 0.00425\n",
+ "Epoch 34/100 (lr=0.0005), train loss 0.00434\n",
+ "Epoch 35/100 (lr=0.0005), train loss 0.00403\n",
+ "Epoch 36/100 (lr=0.0005), train loss 0.00402\n",
+ "Epoch 37/100 (lr=0.0005), train loss 0.00389\n",
+ "Epoch 38/100 (lr=0.0005), train loss 0.00377\n",
+ "Epoch 39/100 (lr=0.0005), train loss 0.00365\n",
+ "Training for epoch 40 done, starting evaluation\n",
+ "Epoch 40/100 (lr=0.0005), train loss 0.00368, valid loss 0.00457\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 9.55\n",
+ " metrics/test.rmse_pcutoff: 8.73\n",
+ " metrics/test.mAP: 84.75\n",
+ " metrics/test.mAR: 86.86\n",
+ "Epoch 41/100 (lr=0.0005), train loss 0.00369\n",
+ "Epoch 42/100 (lr=0.0005), train loss 0.00369\n",
+ "Epoch 43/100 (lr=0.0005), train loss 0.00350\n",
+ "Epoch 44/100 (lr=0.0005), train loss 0.00338\n",
+ "Epoch 45/100 (lr=0.0005), train loss 0.00321\n",
+ "Epoch 46/100 (lr=0.0005), train loss 0.00337\n",
+ "Epoch 47/100 (lr=0.0005), train loss 0.00328\n",
+ "Epoch 48/100 (lr=0.0005), train loss 0.00354\n",
+ "Epoch 49/100 (lr=0.0005), train loss 0.00315\n",
+ "Training for epoch 50 done, starting evaluation\n",
+ "Epoch 50/100 (lr=0.0005), train loss 0.00304, valid loss 0.00439\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 7.60\n",
+ " metrics/test.rmse_pcutoff: 6.95\n",
+ " metrics/test.mAP: 86.14\n",
+ " metrics/test.mAR: 88.43\n",
+ "Epoch 51/100 (lr=0.0005), train loss 0.00314\n",
+ "Epoch 52/100 (lr=0.0005), train loss 0.00312\n",
+ "Epoch 53/100 (lr=0.0005), train loss 0.00306\n",
+ "Epoch 54/100 (lr=0.0005), train loss 0.00308\n",
+ "Epoch 55/100 (lr=0.0005), train loss 0.00303\n",
+ "Epoch 56/100 (lr=0.0005), train loss 0.00313\n",
+ "Epoch 57/100 (lr=0.0005), train loss 0.00320\n",
+ "Epoch 58/100 (lr=0.0005), train loss 0.00286\n",
+ "Epoch 59/100 (lr=0.0005), train loss 0.00284\n",
+ "Training for epoch 60 done, starting evaluation\n",
+ "Epoch 60/100 (lr=0.0005), train loss 0.00284, valid loss 0.00423\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 8.43\n",
+ " metrics/test.rmse_pcutoff: 7.91\n",
+ " metrics/test.mAP: 84.27\n",
+ " metrics/test.mAR: 86.76\n",
+ "Epoch 61/100 (lr=0.0005), train loss 0.00271\n",
+ "Epoch 62/100 (lr=0.0005), train loss 0.00283\n",
+ "Epoch 63/100 (lr=0.0005), train loss 0.00293\n",
+ "Epoch 64/100 (lr=0.0005), train loss 0.00282\n",
+ "Epoch 65/100 (lr=0.0005), train loss 0.00273\n",
+ "Epoch 66/100 (lr=0.0005), train loss 0.00286\n",
+ "Epoch 67/100 (lr=0.0005), train loss 0.00299\n",
+ "Epoch 68/100 (lr=0.0005), train loss 0.00299\n",
+ "Epoch 69/100 (lr=0.0005), train loss 0.00260\n",
+ "Training for epoch 70 done, starting evaluation\n",
+ "Epoch 70/100 (lr=0.0005), train loss 0.00268, valid loss 0.00412\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 6.21\n",
+ " metrics/test.rmse_pcutoff: 5.85\n",
+ " metrics/test.mAP: 89.50\n",
+ " metrics/test.mAR: 90.88\n",
+ "Epoch 71/100 (lr=0.0005), train loss 0.00254\n",
+ "Epoch 72/100 (lr=0.0005), train loss 0.00270\n",
+ "Epoch 73/100 (lr=0.0005), train loss 0.00281\n",
+ "Epoch 74/100 (lr=0.0005), train loss 0.00269\n",
+ "Epoch 75/100 (lr=0.0005), train loss 0.00262\n",
+ "Epoch 76/100 (lr=0.0005), train loss 0.00273\n",
+ "Epoch 77/100 (lr=0.0005), train loss 0.00260\n",
+ "Epoch 78/100 (lr=0.0005), train loss 0.00250\n",
+ "Epoch 79/100 (lr=0.0005), train loss 0.00270\n",
+ "Training for epoch 80 done, starting evaluation\n",
+ "Epoch 80/100 (lr=0.0005), train loss 0.00259, valid loss 0.00393\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 5.40\n",
+ " metrics/test.rmse_pcutoff: 5.14\n",
+ " metrics/test.mAP: 91.43\n",
+ " metrics/test.mAR: 92.25\n",
+ "Epoch 81/100 (lr=0.0005), train loss 0.00278\n",
+ "Epoch 82/100 (lr=0.0005), train loss 0.00270\n",
+ "Epoch 83/100 (lr=0.0005), train loss 0.00266\n",
+ "Epoch 84/100 (lr=0.0005), train loss 0.00266\n",
+ "Epoch 85/100 (lr=0.0005), train loss 0.00257\n",
+ "Epoch 86/100 (lr=0.0005), train loss 0.00250\n",
+ "Epoch 87/100 (lr=0.0005), train loss 0.00270\n",
+ "Epoch 88/100 (lr=0.0005), train loss 0.00247\n",
+ "Epoch 89/100 (lr=0.0005), train loss 0.00220\n",
+ "Training for epoch 90 done, starting evaluation\n",
+ "Epoch 90/100 (lr=0.0001), train loss 0.00224, valid loss 0.00405\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 8.61\n",
+ " metrics/test.rmse_pcutoff: 8.10\n",
+ " metrics/test.mAP: 88.81\n",
+ " metrics/test.mAR: 90.20\n",
+ "Epoch 91/100 (lr=0.0001), train loss 0.00224\n",
+ "Epoch 92/100 (lr=0.0001), train loss 0.00227\n",
+ "Epoch 93/100 (lr=0.0001), train loss 0.00205\n",
+ "Epoch 94/100 (lr=0.0001), train loss 0.00229\n",
+ "Epoch 95/100 (lr=0.0001), train loss 0.00206\n",
+ "Epoch 96/100 (lr=0.0001), train loss 0.00214\n",
+ "Epoch 97/100 (lr=0.0001), train loss 0.00192\n",
+ "Epoch 98/100 (lr=0.0001), train loss 0.00197\n",
+ "Epoch 99/100 (lr=0.0001), train loss 0.00208\n",
+ "Training for epoch 100 done, starting evaluation\n",
+ "Epoch 100/100 (lr=0.0001), train loss 0.00187, valid loss 0.00378\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 4.67\n",
+ " metrics/test.rmse_pcutoff: 4.59\n",
+ " metrics/test.mAP: 91.37\n",
+ " metrics/test.mAR: 91.96\n"
+ ]
+ }
+ ],
+ "source": [
+ "deeplabcut.train_network(\n",
+ " config,\n",
+ " shuffle=BU_SHUFFLE,\n",
+ " epochs=100,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "dd158943",
+ "metadata": {
+ "id": "dd158943"
+ },
+ "source": [
+ "And finally we evaluate it! If you trained for 100 epochs, you should get an mAP around 90, and RMSE around 4-5 pixels. When calling `evaluate_network`, the PAF graph is pruned (as described in [Lauer et al. 2022 (Nature Methods)](https://www.nature.com/articles/s41592-022-01443-0)) to boost performance."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "0a02cf7c",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "executionInfo": {
+ "elapsed": 22610,
+ "status": "ok",
+ "timestamp": 1744358489662,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "0a02cf7c",
+ "outputId": "0548ab4e-ead0-4f73-8660-a56170f9c137"
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 78/78 [00:05<00:00, 13.04it/s]\n",
+ "100%|██████████| 34/34 [00:05<00:00, 6.33it/s]\n",
+ "100%|██████████| 78/78 [00:05<00:00, 15.49it/s]\n",
+ "100%|██████████| 34/34 [00:02<00:00, 16.35it/s]\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Evaluation results for DLC_Resnet50_trimiceJun22shuffle1_snapshot_080-results.csv (pcutoff: 0.01):\n",
+ "train rmse 2.42\n",
+ "train rmse_pcutoff 2.42\n",
+ "train mAP 97.23\n",
+ "train mAR 97.52\n",
+ "test rmse 3.95\n",
+ "test rmse_pcutoff 3.95\n",
+ "test mAP 92.69\n",
+ "test mAR 93.04\n",
+ "Name: (0.7, 1, 80, -1, 0.01), dtype: float64\n"
+ ]
+ }
+ ],
+ "source": [
+ "deeplabcut.evaluate_network(config, Shuffles=[BU_SHUFFLE])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9742e6a8",
+ "metadata": {
+ "id": "9742e6a8"
+ },
+ "source": [
+ "### Training the CTD Model\n",
+ "\n",
+ "As for the BU model, we need to start by creating the shuffle for the CTD model. We'll use `create_training_dataset_from_existing_split` to create a shuffle with the same train/test split as the BU shuffle. You could equivalently call `create_training_dataset(..., trainIndices=[train_indices], testIndices=[test_indices], ...)` again, as done above for the BU shuffle.\n",
+ "\n",
+ "In this notebook, we'll use a preNet CTD architecture. You can check out the paper for more information on how preNet models are designed!\n",
+ "\n",
+ "We'll also specify which model we want to use to provide conditions with the `ctd_conditions` parameter. As is indicated in the docstring:\n",
+ "\n",
+ "```\n",
+ "ctd_conditions: int | str | Path | tuple[int, str] | tuple[int, int] | None, default = None,\n",
+ " If using a conditional-top-down (CTD) net_type, this argument should be specified. It defines the\n",
+ " conditions that will be used with the CTD model. It can be either:\n",
+ " * A shuffle number (ctd_conditions: int), which must correspond to a bottom-up (BU) network type.\n",
+ " * A predictions file path (ctd_conditions: string | Path), which must correspond to a\n",
+ " .json or .h5 predictions file.\n",
+ " * A shuffle number and a particular snapshot (ctd_conditions: tuple[int, str] | tuple[int, int]),\n",
+ " which respectively correspond to a bottom-up (BU) network type and a particular snapshot name or\n",
+ " index.\n",
+ "```\n",
+ "\n",
+ "We'll use the index of the BU shuffle defined above, and the best snapshot that was saved (indicated through a -1). You can edit which model is used to provide conditions through the `pytorch_config` for the `CTD_SHUFFLE` (in this case shuffle `2`):\n",
+ "\n",
+ "```yaml\n",
+ "# Example: Loading the predictions for snapshot-250.pt of shuffle 1.\n",
+ "inference:\n",
+ " conditions:\n",
+ " shuffle: 1\n",
+ " snapshot: snapshot-250.pt\n",
+ "\n",
+ "# Example: Loading the predictions for the last snapshot of shuffle 1.\n",
+ "inference:\n",
+ " conditions:\n",
+ " shuffle: 1\n",
+ " snapshot_index: -1\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "0f75947d",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "executionInfo": {
+ "elapsed": 267,
+ "status": "ok",
+ "timestamp": 1744358497035,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "0f75947d",
+ "outputId": "a99698bd-eb7d-4042-f72f-7ae7e1ed6a46",
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Utilizing the following graph: [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [0, 8], [0, 9], [0, 10], [0, 11], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1, 8], [1, 9], [1, 10], [1, 11], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 9], [2, 10], [2, 11], [3, 4], [3, 5], [3, 6], [3, 7], [3, 8], [3, 9], [3, 10], [3, 11], [4, 5], [4, 6], [4, 7], [4, 8], [4, 9], [4, 10], [4, 11], [5, 6], [5, 7], [5, 8], [5, 9], [5, 10], [5, 11], [6, 7], [6, 8], [6, 9], [6, 10], [6, 11], [7, 8], [7, 9], [7, 10], [7, 11], [8, 9], [8, 10], [8, 11], [9, 10], [9, 11], [10, 11]]\n",
+ "You passed a split with the following fraction: 70%\n",
+ "Creating training data for: Shuffle: 2 TrainFraction: 0.7\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 78/78 [00:00<00:00, 8165.62it/s]"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "The training dataset is successfully created. Use the function 'train_network' to start training. Happy training!\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "CTD_SHUFFLE = 2\n",
+ "\n",
+ "deeplabcut.create_training_dataset_from_existing_split(\n",
+ " config,\n",
+ " from_shuffle=BU_SHUFFLE,\n",
+ " shuffles=[CTD_SHUFFLE],\n",
+ " net_type=\"ctd_coam_w32\",\n",
+ " engine=deeplabcut.Engine.PYTORCH,\n",
+ " ctd_conditions=(BU_SHUFFLE, -1),\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b2829415",
+ "metadata": {
+ "id": "b2829415"
+ },
+ "source": [
+ "#### (Optional/Advanced) Learning and visualizing generative sampling during training\n",
+ "\n",
+ "You can skip this section (and move on to _Training and Evaluating the CTD Model_) as it's simply to visualize how CTD models are trained, if you aren't interested in learning about it."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "odk3LbpOI0B3",
+ "metadata": {
+ "id": "odk3LbpOI0B3"
+ },
+ "source": [
+ "This section **uses some internal DeepLabCut functions which may seem a bit complicated if you're not used to using them; you can ignore most of the code and just read the text/comments and look at the outputs if you're more comfortable with that.**\n",
+ "\n",
+ "Conditional top-down models are trained using _generative sampling_, as introduced in PoseFix \\[1\\]. For every ground truth pose, we'll add some errors. The errors that can be introduced are:\n",
+ "\n",
+ "- Jitter error is defined as a small displacement from the GT keypoint.\n",
+ "- Swap error represents a confusion between the same or similar parts which belong to different persons.\n",
+ "- Inversion error occurs when a pose estimation model is confused between semantically similar parts that belong to the same instance.\n",
+ "- Miss error represents a large displacement from the GT keypoint position.\n",
+ "\n",
+ "It's important that \"enough\" generative sampling is applied (so the model can learn how to correct errors), but applying too much can be bad too! You want the model to learn to correct errors that are realistic (w.r.t. the task at hand), not just receive random points and have to learn by itself where the keypoints go. **The default parameters should work well on most datasets.**\n",
+ "\n",
+ "The way these keypoints are \"sampled\" can be visuallized below. We'll create a `dataset` (which is used by DeepLabCut for training) and sample some data from this dataset. You can see that every time we sample an image, we get different keypoint conditions that will be given to the model. This ensures that the model is well trained to deal with a variety of mistakes that can be made by the bottom up model. On the left side of the plots, you have an image with the ground truth keypoints annotated. On the right side of the plots, you have the pose conditions that the CTD model will receive and will be tasked with fixing.\n",
+ "\n",
+ "> \\[1\\]: Moon, Gyeongsik, Ju Yong Chang, and Kyoung Mu Lee. \"Posefix: Model-agnostic general human pose refinement network.\" Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition. 2019"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "2a43ec8e",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 989
+ },
+ "executionInfo": {
+ "elapsed": 2099,
+ "status": "ok",
+ "timestamp": 1744358505139,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "2a43ec8e",
+ "outputId": "892a8a22-db67-4e64-a441-667e90faeecd",
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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17zmNXjuc5+Ln//5nweQHkru9/u4+OKLcf755+Od73wnvvSlL+HP/uzPsHXrVrzmNa85oPT+5Cc/QbFYxOzsLLZt24bHPvaxaDabuPTSSzE/P4/LL78cz372s/HJT34Sz33ucwEAH/7wh3HppZfi+c9/Pn7jN34DKysr+OEPf4hvfetbeMlLXgIA2LZtGx796EcnEF9YWMC//Mu/4JJLLsG+ffvwhje84YDS63Idi7r55psBAPPz8wBWWXb55Zfj+c9/Pt70pjfhW9/6Ft7+9rfj2muvxWc+8xkAq42Vpz3taVhYWMDv/M7vYHZ2Frfeeis+/elPp8J+1atehY9+9KN4+ctfjksvvRS33HIL3vve9+I///M/ceWVVwZHBJrNJr785S/jiU98Ih784AePzMN6+f/1r38dn/70p/Ha174WU1NTePe7343nPe95uP3225Ny+NGPfpTk8S1veQt6vR7e/OY3Y/PmzZlpeeITn4hLL710aPoOT+NhtVotXHjhhbjpppvw+te/HieddBI+8YlP4GUvexn27NmD3/iN30i5//jHP47FxUW86lWvQhRF+JM/+RP8/M//PH7yk58k5fm85z0PP/7xj/Hrv/7rOPHEE3Hvvffi//2//4fbb799aBjadRAUu3Jr7969MYD4Oc95ztC53bt3x9u3b0/+ms1mcu6lL31pDCD+nd/5nZSfz372szGA+A//8A9Tx5///OfHURTFN910UxzHcXzLLbfEAOKPfOQjQ/ECiN/85jcnv9/85jfHAOJXvOIVKXfPfe5z4/n5+eT397///RhA/NrXvjbl7iUveclQmKP0iU98IgYQf/WrXx1Kx4tf/OIh9xdccEF8wQUXDB1/6UtfGp9wwgnJ7+3btwfTImX61re+NXX8kY98ZHzOOeeMTPMFF1wQn3rqqcn1uvbaa+NLL700BhA/61nPiuM4jt/whjfEAOJ///d/T/wtLi7GJ510UnziiSfG/X4/juM4/rmf+7n49NNPz4zvkksuiY877rh4x44dqeMvetGL4pmZmdT94nK5VvWRj3wkBhB/6Utfirdv3x7fcccd8d/93d/F8/Pzcb1ej++8886EZa985StTfn/zN38zBhB/5StfieM4jj/zmc/EAOLvfOc7wfj+/d//PQYQf+xjH0sd/8IXvmAeZ/3gBz+IAcS/8Ru/kStvefkfx6uc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OF784hfjlFNOSd7wEccxbrnlFnz84x9HoVAYmt9n6VnPehae9KQn4fd+7/dw66234qyzzsIXv/hF/OM//iPe8IY3pObjvfKVr8Qf//Ef45WvfCUe9ahH4d/+7d9www03HHA+zj77bLz4xS/G+9//fuzduxePfexj8eUvfxk33XTTAYeZV694xSvw53/+57j44otxySWX4N5778Vf/uVf4vTTT08mTQOrQ8annXYa/v7v/x4Pe9jDsGHDBpxxxhk444wzDnkaAeB3fud38H/+z//B05/+dFx66aXYsGEDLr/8ctxyyy341Kc+lQzjP+1pT8OWLVvwuMc9Dps3b8a1116L9773vXjmM5+ZzOf54z/+Y3z1q1/F+eefj1/91V/Faaedhl27duF73/sevvSlL2HXrl33S55crnHTWWedhZe+9KX40Ic+hD179uCCCy7At7/9bVx++eV4znOegyc96UkAgMsvvxzvf//78dznPhdbt27F4uIiPvzhD2N6ejppQF5wwQV41atehbe//e34/ve/j6c97Wkol8u48cYb8YlPfAL/83/+Tzz/+c8PpuWxj30s3ve+9+G1r30tTj311NQbPr72ta/hn/7pn/CHf/iHANbH/7y67LLL8IUvfAFPeMIT8NrXvjZpkJ1++ukjXzt69tlno1gs4h3veAf27t2LarWKJz/5yeaoxK/92q/hgx/8IF72spfhu9/9Lk488UR88pOfxJVXXol3vetduRe9iG644QY85SlPwQtf+EKcdtppKJVK+MxnPoNt27bhRS960brCcuXUYVljPAa66aab4te85jXxQx/60LhWq8X1ej0+9dRT41e/+tXx97///ZTbl770pfHExIQZzuLiYvzGN74xfsADHhCXy+X45JNPjv/0T/80HgwGKXfNZjO+5JJL4pmZmXhqaip+4QtfGN97773BrV62b9+e8i9bJtxyyy3JsVarFV966aXx/Px8PDExET/rWc+K77jjjoO61YtOh+hv//Zv44c85CFxpVKJzz777PiKK64Y2uoljuP4P/7jP+JzzjknrlQqqXSFylTiHaULLrhg5PYscRzHN998c/z85z8/np2djWu1WnzeeefFn/vc51JuPvjBD8ZPfOIT4/n5+bharcZbt26Nf+u3fiveu3dvyt22bdvi173udfHxxx8fl8vleMuWLfFTnvKU+EMf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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAn8AAAFECAYAAABWG1gIAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOz9efhsWVXfj7/W2sM5VfX53KGbpgHFpmlmUPABAXEAFeEriIIgAmoYIwikxaDG6C8BHIIoKgFEAR+BRIgGEYwEwYA4Jg5IcAKUGRRpupu+9zNUnXP23mv9/ji3r1y6gWbs9KVez3Pv89Spc6r2qU/td62999rvJe7ubNmyZcuWLVu2bPmCQK/tBmzZsmXLli1btmz5/LEN/rZs2bJly5YtW76A2AZ/W7Zs2bJly5YtX0Bsg78tW7Zs2bJly5YvILbB35YtW7Zs2bJlyxcQ2+Bvy5YtW7Zs2bLlC4ht8Ldly5YtW7Zs2fIFxDb427Jly5YtW7Zs+QJiG/xt2bJly5YtW7Z8AbEN/rZcBRHhqU996rXdjE/IIx7xCHZ2dq7tZmzZsmXL//M89alPRUTOOHaTm9yERzziEdfo+nvc4x7c4x73+Ow3bMu1xjb4+zR5z3vewxOf+ERucYtbsFwuWS6X3OY2t+EJT3gCf/3Xf31tN+9zyj3ucQ9E5JP++0wDyPV6zVOf+lR+//d//7PS7o/mY+/hnHPO4Su+4iv4lV/5Fczss/5+W7Zs+dR58YtffEY/7fueW9ziFjzxiU/kkksuubab93F5y1vewnd913dx4xvfmK7rOOecc7jnPe/Ji170Ilpr13bzrpa3vvWtPPWpT+W9733vtd2ULZ8H4rXdgOsir371q/mO7/gOYox853d+J7e//e1RVd7+9rfzm7/5m/ziL/4i73nPe7jggguu7aZ+TvjRH/1RHvOYx5x+/Bd/8Rc8+9nP5kd+5Ee49a1vffr4l33Zl31G77Ner3na054G8DkZdX7xF38xT3/60wG49NJL+S//5b/w6Ec/mn/4h3/gp37qpz7r77dly5ZPjx/7sR/jwgsvZBgG/viP/5hf/MVf5DWveQ1/+7d/y3K5vLabdwa//Mu/zOMe9zjOP/98vvu7v5ub3/zm7O/v84Y3vIFHP/rR/PM//zM/8iM/cm03k7//+79H9V/mf9761rfytKc9jXvc4x7c5CY3OePc3/3d3/08t27L55pt8Pcp8q53vYuHPOQhXHDBBbzhDW/ghje84RnPP+MZz+B5z3veGZ3q6jg8PGS1Wn0um/o54xu/8RvPeNz3Pc9+9rP5xm/8xk8YpP2/ds9Hjx7lu77ru04/fuxjH8stb3lLnvvc5/LjP/7jpJSuxdZt2bLlSr7pm76JO93pTgA85jGP4dxzz+Xnfu7n+K3f+i0e+tCHXsut+xf+9E//lMc97nF85Vd+Ja95zWvY3d09/dyTnvQk3vSmN/G3f/u312IL/4Wu667xuTnnz2FLtlwbbJd9P0V++qd/msPDQ170ohddJfADiDFy8cUXc+Mb3/j0sSvz0971rndxn/vch93dXb7zO78TmAOiJz/5yaeXB255y1vyzGc+E3c/ff173/teRIQXv/jFV3m/j11evTK3453vfCePeMQjOHbsGEePHuWRj3wk6/X6jGvHceT7v//7Oe+889jd3eVbvuVb+Md//MfP8BM6sx1vfetbedjDHsbx48f56q/+auDj54884hGPOD3ifO9738t5550HwNOe9rSPu5T8T//0T9z//vdnZ2eH8847jx/4gR/4tJdVlssld73rXTk8POTSSy8F4N3vfjff/u3fzjnnnHP6+f/5P//nVa59znOew21ve1uWyyXHjx/nTne6Ey972cuu0tZHPepRnH/++XRdx21ve1t+5Vd+5dNq65YtX8h8/dd/PTCn3wDUWvnxH/9xLrroIrqu4yY3uQk/8iM/wjiOZ1z3pje9iXvf+95c73rXY7FYcOGFF/KoRz3qjHPMjGc961nc9ra3pe97zj//fB772MdyxRVXfNJ2XalVL33pS88I/K7kTne60xl5dtdE/2HW+Sc+8Ym86lWv4na3u91p/Xjta197lff44z/+Y77iK76Cvu+56KKLeP7zn3+1bf3onL8Xv/jFfPu3fzsAX/d1X3dab69Mubk6zf7whz/Mox/9aM4//3z6vuf2t789L3nJS84458rfrmc+85m84AUvOP33+Yqv+Ar+4i/+4oxzP/ShD/HIRz6SL/7iL6brOm54wxvyrd/6rdtl6M8R25m/T5FXv/rV3OxmN+Mud7nLp3RdrZV73/vefPVXfzXPfOYzWS6XuDvf8i3fwhvf+EYe/ehHc4c73IHXve51/OAP/iD/9E//xM///M9/2u188IMfzIUXXsjTn/503vzmN/PLv/zLXP/61+cZz3jG6XMe85jH8Ku/+qs87GEP4253uxu/93u/x33ve99P+z2vjm//9m/n5je/Of/pP/2nqwjaJ+K8887jF3/xF/ne7/1eHvCAB/Bt3/ZtwJlLya017n3ve3OXu9yFZz7zmbz+9a/nZ3/2Z7nooov43u/93k+rve9+97sJIXDs2DEuueQS7na3u7Fer7n44os599xzeclLXsK3fMu38Bu/8Rs84AEPAOCFL3whF198MQ960IP4vu/7PoZh4K//+q/5sz/7Mx72sIcBcMkll3DXu971tIifd955/M7v/A6PfvSj2dvb40lPetKn1d4tW74Qede73gXAueeeC8xa9pKXvIQHPehBPPnJT+bP/uzPePrTn87b3vY2XvnKVwJzsHKve92L8847jx/+4R/m2LFjvPe97+U3f/M3z3jtxz72sbz4xS/mkY98JBdffDHvec97eO5zn8v//b//lz/5kz/5uCsC6/WaN7zhDXzt134tX/IlX/JJ7+FT1f8//uM/5jd/8zd5/OMfz+7uLs9+9rN54AMfyPvf//7Tn8Pf/M3fnL7Hpz71qdRaecpTnsL555//CdvytV/7tVx88cVXSd/56DSej2az2XCPe9yDd77znTzxiU/kwgsv5OUvfzmPeMQjOHHiBN/3fd93xvkve9nL2N/f57GPfSwiwk//9E/zbd/2bbz73e8+/Xk+8IEP5O/+7u/4N//m33CTm9yED3/4w/yv//W/eP/733+VZegtnwV8yzXm5MmTDvj973//qzx3xRVX+KWXXnr633q9Pv3cwx/+cAf8h3/4h8+45lWvepUD/hM/8RNnHH/Qgx7kIuLvfOc73d39Pe95jwP+ohe96CrvC/hTnvKU04+f8pSnOOCPetSjzjjvAQ94gJ977rmnH7/lLW9xwB//+Mefcd7DHvawq7zmJ+PlL3+5A/7GN77xKu146EMfepXz7373u/vd7373qxx/+MMf7hdccMHpx5deeunHbcuVn+mP/diPnXH8y7/8y/2Od7zjJ23z3e9+d7/VrW51+u/1tre9zS+++GIH/H73u5+7uz/pSU9ywP/oj/7o9HX7+/t+4YUX+k1uchNvrbm7+7d+67f6bW9720/4fo9+9KP9hje8oV922WVnHH/IQx7iR48ePeP7smXLlpkXvehFDvjrX/96v/TSS/0DH/iA/9qv/Zqfe+65vlgs/B//8R9Pa9ljHvOYM679gR/4AQf8937v99zd/ZWvfKUD/hd/8Rcf9/3+6I/+yAF/6Utfesbx1772tVd7/KP5q7/6Kwf8+77v+67RvV1T/XefdT7nfMaxK9/vOc95zulj97///b3ve3/f+953+thb3/pWDyH4x/7cX3DBBf7whz/89OOr0/Er+VjNftaznuWA/+qv/urpY9M0+Vd+5Vf6zs6O7+3tufu//Hade+65/pGPfOT0ub/1W7/lgP/2b/+2u8+/n4D/zM/8zCf6yLZ8Ftku+34K7O3tAVytxcg97nEPzjvvvNP/fuEXfuEq53zsbNRrXvMaQghcfPHFZxx/8pOfjLvzO7/zO592Wx/3uMed8fhrvuZruPzyy0/fw2te8xqAq7z3Z3sG6mPb8dnm6u7z3e9+9zW69u1vf/vpv9etb31rnvOc53Df+9739FLsa17zGu585zufXq6G+W//Pd/zPbz3ve/lrW99KwDHjh3jH//xH6+yjHEl7s4rXvEK7ne/++HuXHbZZaf/3fve9+bkyZO8+c1v/nRuf8uWLwjuec97ct5553HjG9+YhzzkIezs7PDKV76SL/qiLzqtZf/23/7bM6558pOfDHA6TePYsWPAvHpTSrna93n5y1/O0aNH+cZv/MYz+ukd73hHdnZ2eOMb3/hx23iltl7dcu/V8anq/z3veU8uuuii04+/7Mu+jCNHjpzWu9Yar3vd67j//e9/xszjrW99a+5973tfozZdU17zmtdwgxvc4Ix8y5QSF198MQcHB/zBH/zBGed/x3d8B8ePHz/9+Gu+5msATrd9sViQc+b3f//3r9Hy+pbPnO2y76fAlZ364ODgKs89//nPZ39/n0suueSMTQRXEmPki7/4i8849r73vY8b3ehGVxGLK6fa3/e+933abf3YZYcrO94VV1zBkSNHeN/73oeqniEmALe85S0/7fe8Oi688MLP6ut9NH3fn84LvJLjx49fY/G4yU1uwgtf+MLTFhI3v/nNuf71r3/6+fe9731Xu7z/0X+f293udvy7f/fveP3rX8+d73xnbnazm3Gve92Lhz3sYXzVV30VMO8kPnHiBC94wQt4wQtecLVt+fCHP3yN2rxlyxciv/ALv8AtbnELYoycf/753PKWtzy9qe5KLbvZzW52xjU3uMENOHbs2Gkdvfvd784DH/hAnva0p/HzP//z3OMe9+D+978/D3vYw05vfnjHO97ByZMnz9CBj+YT9dMjR44AsL+/f43u6VPV/6tbSv5ovbv00kvZbDbc/OY3v8p5t7zlLU8HyZ8N3ve+93Hzm9/8Khsbr2nbP/r3CObNJ894xjN48pOfzPnnn89d73pXvvmbv5l/9a/+FTe4wQ0+a+3e8i9sg79PgaNHj3LDG97wandrXRkkfLzk1K7rPukO4I/Hx5pzXskn2tgQQrja4/4p5N19NlgsFlc5JiJX245PdaPGx7vHa8pqteKe97znZ/QaMAve3//93/PqV7+a1772tbziFa/gec97Hv/xP/5Hnva0p532Dfyu7/ouHv7wh1/ta3ymtjhbtpzN3PnOdz692/fj8fF08qOf/43f+A3+9E//lN/+7d/mda97HY961KP42Z/9Wf70T/+UnZ0dzIzrX//6vPSlL73a1/jYweZHc7Ob3YwYI3/zN3/zyW/o0+D/FU3/dLgmbX/Sk57E/e53P171qlfxute9jv/wH/4DT3/60/m93/s9vvzLv/zz1dQvGLbLvp8i973vfXnnO9/Jn//5n3/Gr3XBBRfwwQ9+8Cojxbe//e2nn4d/GSWdOHHijPM+k5nBCy64ADM7nTh9JX//93//ab/mNeX48eNXuRe46v18MjH/XHPBBRdc7efxsX8fmAPJ7/iO7+BFL3oR73//+7nvfe/LT/7kTzIMw+nd1K017nnPe17tv48307Bly5ZPzJVa9o53vOOM45dccgknTpy4it/qXe96V37yJ3+SN73pTbz0pS/l7/7u7/i1X/s1AC666CIuv/xyvuqrvupq++ntb3/7j9uO5XLJ13/91/OHf/iHfOADH7hG7b4m+n9NOe+881gsFlf5HOCa6fqnorcXXHAB73jHO65iiP/ptv1KLrroIp785Cfzu7/7u/zt3/4t0zTxsz/7s5/Wa235xGyDv0+RH/qhH2K5XPKoRz3qah3mP5VR2H3ucx9aazz3uc894/jP//zPIyJ80zd9EzAvJ1zvetfjD//wD88473nPe96ncQczV772s5/97DOOP+tZz/q0X/OactFFF/H2t7/9tJ0KwF/91V/xJ3/yJ2ecd6V569UFip8P7nOf+/Dnf/7n/J//839OHzs8POQFL3gBN7nJTbjNbW4DwOWXX37GdTlnbnOb2+DulFIIIfDABz6QV7ziFVc7a/zRn8OWLVs+Ne5zn/sAV9Wun/u5nwM47WBwxRVXXEWf73CHOwCctoR58IMfTGuNH//xH7/K+9RaP6kWPeUpT8Hd+e7v/u6rTQ/6y7/8y9N2KNdU/68pIQTufe9786pXvYr3v//9p4+/7W1v43Wve90nvf5KD9Zrorf3uc99+NCHPsSv//qvnz5Wa+U5z3kOOzs73P3ud/+U2r5erxmG4YxjF110Ebu7u1ex69ny2WG77PspcvOb35yXvexlPPShD+WWt7zl6Qof7s573vMeXvayl6GqV8nvuzrud7/78XVf93X86I/+KO9973u5/e1vz+/+7u/yW7/1WzzpSU86Ix/vMY95DD/1Uz/FYx7zGO50pzvxh3/4h/zDP/zDp30fd7jDHXjoQx/K8573PE6ePMnd7nY33vCGN/DOd77z037Na8qjHvUofu7nfo573/vePPrRj+bDH/4wv/RLv8Rtb3vb00nTMC8Z3+Y2t+HXf/3XucUtbsE555zD7W53O253u9t9ztsI8MM//MP8t//23/imb/omLr74Ys455xxe8pKX8J73vIdXvOIVp5fx73Wve3GDG9yAr/qqr+L888/nbW97G8997nO5733vezqf56d+6qd44xvfyF3uchf+9b/+19zmNrfhIx/5CG9+85t5/etfz0c+8pHPyz1t2XK2cfvb356HP/zhvOAFL+DEiRPc/e5358///M95yUtewv3vf3++7uu+DoCXvOQlPO95z+MBD3gAF110Efv7+7zwhS/kyJEjpwPIu9/97jz2sY/l6U9/Om95y1u4173uRUqJd7zjHbz85S/nP//n/8yDHvSgj9uWu93tbvzCL/wCj3/847nVrW51RoWP3//93+d//I//wU/8xE8An5r+X1Oe9rSn8drXvpav+Zqv4fGPf/zpgOy2t73tJy07eoc73IEQAs94xjM4efIkXdfx9V//9Ve7KvE93/M9PP/5z+cRj3gEf/mXf8lNbnITfuM3foM/+ZM/4VnPetY13vRyJf/wD//AN3zDN/DgBz+Y29zmNsQYeeUrX8kll1zCQx7ykE/ptbZcQ66VPcZnAe985zv9e7/3e/1mN7uZ933vi8XCb3WrW/njHvc4f8tb3nLGuQ9/+MN9tVpd7evs7+/793//9/uNbnQjTyn5zW9+c/+Zn/kZN7Mzzluv1/7o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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAn8AAAFECAYAAABWG1gIAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAADXYUlEQVR4nOz9ebxsR3Udjq/T852nN0igEUkINIAMCDCTBMboZzA2GIwBD2BEwuTIOMSO43wTg+0EY2NDGI1IDHIMJsEDHkLAYbIBDyDJjEISAqGBQdKb77tTd98+vz/u26fXWb3rdD/pivfO7Vrvc193n1Pjrl3r1D61qypJ0zRFRERERERERETEWKByogsQERERERERERHx/UMc/EVEREREREREjBHi4C8iIiIiIiIiYowQB38REREREREREWOEOPiLiIiIiIiIiBgjxMFfRERERERERMQYIQ7+IiIiIiIiIiLGCHHwFxERERERERExRoiDv4iIiIiIiIiIMUIc/EUMIEkSvPa1rz3RxSjEi1/8YkxPT5/oYkRERESc9Hjta1+LJEly18466yy8+MUvHin+5Zdfjssvv3z7CxZxwhAHf/cSt956K37hF34BD37wgzE5OYnJyUlccMEFeNWrXoUvfelLJ7p49ysuv/xyJEky9O++DiBXV1fx2te+Fp/61Ke2pdwMrcPi4iIuvfRS/OEf/iF6vd625xcREXH8eO9735vrp61WCw9+8IPxC7/wC7jrrrtOdPGC+MIXvoCf+Zmfwemnn45ms4nFxUU89alPxXve8x5sbm6e6OK5uOGGG/Da174W3/rWt050USK+D6id6AKUEX/zN3+Dn/qpn0KtVsNP//RP4+EPfzgqlQpuvPFG/Pmf/zne+c534tZbb8WZZ555oot6v+A//sf/iJe+9KXZ789//vN4y1vegl/7tV/DQx/60Oz6wx72sPuUz+rqKl73utcBwP1idZ522ml4/etfDwC455578Ed/9Ee48sorcfPNN+O3f/u3tz2/iIiIe4ff+I3fwNlnn4319XV85jOfwTvf+U58+MMfxle+8hVMTk6e6OLl8N//+3/Hy1/+cuzduxc/+7M/i/POOw/Ly8v4+Mc/jiuvvBLf/e538Wu/9msnupi46aabUKn03//ccMMNeN3rXofLL78cZ511Vi7s3/7t336fSxdxfyMO/o4T3/jGN/D85z8fZ555Jj7+8Y/j1FNPzd1/wxvegHe84x25TuVhZWUFU1NT92dR7zf88A//cO53q9XCW97yFvzwD/9w4SDtZKvz3NwcfuZnfib7/bKXvQznn38+3va2t+E3f/M3Ua/XT2DpIiIiDD/yIz+CRz3qUQCAl770pVhaWsLv//7v4y//8i/xghe84ASXro9/+qd/wstf/nL84A/+ID784Q9jZmYmu/fqV78a1157Lb7yla+cwBL20Ww2Rw7baDTux5JEnAjEad/jxO/8zu9gZWUF73nPewYGfgBQq9Vw1VVX4fTTT8+umX/aN77xDTz96U/HzMwMfvqnfxrA1oDoNa95TTY9cP755+ONb3wj0jTN4n/rW99CkiR473vfO5CfTq+ab8ctt9yCF7/4xZifn8fc3Bx+/ud/Hqurq7m4Gxsb+KVf+iXs3r0bMzMz+LEf+zHceeed91FC+XLccMMNeOELX4iFhQU84QlPABD2H3nxi1+cWZzf+ta3sHv3bgDA6173uuBU8re//W0861nPwvT0NHbv3o1/9+/+3b2eVpmcnMRjH/tYrKys4J577gEAfPOb38RP/uRPYnFxMbv/f/7P/xmI+9a3vhUXXnghJicnsbCwgEc96lF4//vfP1DWl7zkJdi7dy+azSYuvPBC/OEf/uG9KmtExDjjKU95CoAt9xsA6Ha7+M3f/E2cc845aDabOOuss/Brv/Zr2NjYyMW79tprccUVV2DXrl2YmJjA2WefjZe85CW5ML1eD29+85tx4YUXotVqYe/evXjZy16GgwcPDi2XcdX73ve+3MDP8KhHPSrnZzcK/wNbPP8Lv/AL+NCHPoSLLroo44+PfOQjA3l85jOfwaWXXopWq4VzzjkH73rXu9yyss/fe9/7XvzkT/4kAODJT35yxrfmcuNx9t13340rr7wSe/fuRavVwsMf/nBcc801uTD27HrjG9+Iq6++OmufSy+9FJ///OdzYb/3ve/h53/+53Haaaeh2Wzi1FNPxY//+I/Haej7CfHN33Hib/7mb3DuuefiMY95zHHF63a7uOKKK/CEJzwBb3zjGzE5OYk0TfFjP/Zj+OQnP4krr7wSl1xyCT760Y/il3/5l/Htb38bb3rTm+51OZ/3vOfh7LPPxutf/3pcf/31+O///b9jz549eMMb3pCFeelLX4o//uM/xgtf+EI87nGPwyc+8Qk84xnPuNd5evjJn/xJnHfeefiv//W/DhBaEXbv3o13vvOdeMUrXoFnP/vZ+Imf+AkA+ankzc1NXHHFFXjMYx6DN77xjfjYxz6G3/u938M555yDV7ziFfeqvN/85jdRrVYxPz+Pu+66C4973OOwurqKq666CktLS7jmmmvwYz/2Y/jTP/1TPPvZzwYAvPvd78ZVV12F5z73ufjFX/xFrK+v40tf+hL++Z//GS984QsBAHfddRce+9jHZiS+e/du/N//+39x5ZVX4siRI3j1q199r8obETGO+MY3vgEAWFpaArDFZddccw2e+9zn4jWveQ3++Z//Ga9//evxta99DX/xF38BYGuw8rSnPQ27d+/Gr/7qr2J+fh7f+ta38Od//ue5tF/2spfhve99L37+538eV111FW699Va87W1vw7/8y7/gs5/9bHBGYHV1FR//+MfxpCc9CWecccbQOhwv/3/mM5/Bn//5n+OVr3wlZmZm8Ja3vAXPec5zcPvtt2dy+PKXv5zV8bWvfS263S5+/dd/HXv37i0sy5Oe9CRcddVVA+477MbDWFtbw+WXX45bbrkFv/ALv4Czzz4bH/zgB/HiF78Yhw4dwi/+4i/mwr///e/H8vIyXvaylyFJEvzO7/wOfuInfgLf/OY3M3k+5znPwVe/+lX8m3/zb3DWWWfh7rvvxv/7f/8Pt99++8A0dMQ2II0YGYcPH04BpM961rMG7h08eDC95557sr/V1dXs3ote9KIUQPqrv/qruTgf+tCHUgDpb/3Wb+WuP/e5z02TJElvueWWNE3T9NZbb00BpO95z3sG8gWQ/vqv/3r2+9d//ddTAOlLXvKSXLhnP/vZ6dLSUvb7C1/4QgogfeUrX5kL98IXvnAgzWH44Ac/mAJIP/nJTw6U4wUveMFA+Msuuyy97LLLBq6/6EUvSs8888zs9z333BMsi8n0N37jN3LXf+AHfiB95CMfObTMl112WfqQhzwka6+vfe1r6VVXXZUCSJ/5zGemaZqmr371q1MA6ac//eks3vLycnr22WenZ511Vrq5uZmmaZr++I//eHrhhRcW5nfllVemp556arpv377c9ec///np3NxcTl8iIiK28J73vCcFkH7sYx9L77nnnvSOO+5IP/CBD6RLS0vpxMREeuedd2Zc9tKXvjQX99/9u3+XAkg/8YlPpGmapn/xF3+RAkg///nPB/P79Kc/nQJI3/e+9+Wuf+QjH3GvM774xS+mANJf/MVfHKluo/J/mm7xfKPRyF2z/N761rdm1571rGelrVYrve2227JrN9xwQ1qtVlN93J955pnpi170ouy3x+MG5ew3v/nNKYD0j//4j7Nr7XY7/cEf/MF0eno6PXLkSJqm/WfX0tJSeuDAgSzsX/7lX6YA0r/+679O03Tr+Qkg/d3f/d0ikUVsI+K073HgyJEjAOBuMXL55Zdj9+7d2d/b3/72gTD6NurDH/4wqtUqrrrqqtz117zmNUjTFP/3//7fe13Wl7/85bnfT3ziE7F///6sDh/+8IcBYCDv7X4DpeXYbnj1/OY3vzlS3BtvvDFrr4c+9KF461vfimc84xnZVOyHP/xhPPrRj86mq4Gttv/X//pf41vf+hZuuOEGAMD8/DzuvPPOgWkMQ5qm+LM/+zM885nPRJqm2LdvX/Z3xRVX4PDhw7j++uvvTfUjIsYCT33qU7F7926cfvrpeP7zn4/p6Wn8xV/8BR74wAdmXPZv/+2/zcV5zWteAwCZm8b8/DyArdmbTqfj5vPBD34Qc3Nz+OEf/uFcP33kIx+J6elpfPKTnwyW0bjVm+71cLz8/9SnPhXnnHNO9vthD3sYZmdnM77b3NzERz/6UTzrWc/KvXl86EMfiiuuuGKkMo2KD3/4wzjllFNy/pb1eh1XXXUVjh49ir/7u7/Lhf+pn/opLCwsZL+f+MQnAkBW9omJCTQaDXzqU58aaXo94r4jTvseB6xTHz16dODeu971LiwvL+Ouu+7KLSIw1Go1nHbaablrt912Gx7wgAcMkIW9ar/tttvudVl12sE63sGDBzE7O4vbbrsNlUolRyYAcP7559/rPD2cffbZ25oeo9VqZX6BhoWFhZHJ46yzzsK73/3ubAuJ8847D3v27Mnu33bbbe70PrfPRRddhH//7/89Pvaxj+HRj340zj33XDztaU/DC1/4Qjz+8Y8HsLWS+NChQ7j66qtx9dVXu2W5++67RypzRMQ44u1vfzse/OAHo1arYe/evTj//POzRXXGZeeee24uzimnnIL5+fmMRy+77DI85znPwete9zq86U1vwuWXX45nPetZeOELX5gtfvj617+Ow4cP53iAUdRPZ2dnAQDLy8sj1el4+d+bSma+u+eee7C2tobzzjtvINz555+fDZK3A7fddhvOO++8gYWNo5adn0fA1uKTN7zhDXjNa16DvXv34rGPfSx+9Ed/FD/3cz+HU045ZdvKHdFHHPwdB+bm5nDqqae6q7VskBByTm02m0NXAIegm3MaihY2VKtV93p6HH5324GJiYmBa0mSuOU43oUaoTqOiqmpKTz1qU+9T2kAW4R300034W/+5m/wkY98BH/2Z3+Gd7zjHfjP//k/43Wve122b+DP/MzP4EUvepGbxn3dFiciYifj0Y9+dLbaN4QQT/L9P/3TP8U//dM/4a//+q/x0Y9+FC95yUvwe7/3e/inf/onTE9Po9frYc+ePXjf+97npqHGJuPcc89FrVbDl7/85eEVuhc4WTj93mCUsr/61a/GM5/5THzoQx/CRz/6Ufyn//Sf8PrXvx6f+MQn8AM/8APfr6KODeK073HiGc94Bm655RZ87nOfu89pnXnmmfjOd74zYCneeOON2X2gbyUdOnQoF+6+vBk888wz0ev1Msdpw0033XSv0xwVCwsLA3UBBuszjMzvb5x55pmuPLR9gK2B5E/91E/hPe95D26//XY84xnPwH/5L/8F6+vr2Wrqzc1NPPWpT3X/Qm8aIiIiimFc9vWvfz13/a677sKhQ4cG9lt97GMfi//yX/4Lrr32Wrzvfe/DV7/6VXzgAx8AAJxzzjnYv38/Hv/4x7v99OEPf3iwHJOTk3jKU56Cv//7v8cdd9wxUrlH4f9RsXv3bkxMTAzIARiN14+Hb88880x8/etfH9gQ/96W3XDOOefgNa95Df72b/8WX/nKV9But/F7v/d79yqtiGLEwd9x4ld+5VcwOTmJl7zkJe4O88djhT396U/H5uYm3va2t+Wuv+lNb0KSJPiRH/kRAFvTCbt27cLf//3f58K94x3vuBc12IKl/Za3vCV3/c1vfvO9TnNUnHPOObjxxhuz7VQA4Itf/CI++9nP5sLZ5q3eQPH7gac//en43Oc+h3/8x3/Mrq2srODqq6/GWWedhQsuuAAAsH///ly8RqOBCy64AGmaotPpoFqt4jnPeQ7+7M/+zH1rzHKIiIg4Pjz96U8HMMhdv//7vw8A2Q4GBw8eHODnSy65BACyLWGe97znYXNzE7/5m785kE+32x3KRb/+67+ONE3xsz/7s6570HXXXZdthzIq/4+KarWKK664Ah/60Idw++23Z9e/9rWv4aMf/ejQ+LYH6yh8+/SnPx3f+9738L/+1//KrnW7Xbz1rW/F9PQ0LrvssuMq++rqKtbX13PXzjnnHMzMzAxs1xOxPYjTvseJ8847D+9///vxghe8AOeff352wkeaprj11lvx/ve/H5VKZcC/z8Mzn/lMPPnJT8Z//I//Ed/61rfw8Ic/HH/7t3+Lv/zLv8SrX/3qnD/eS1/6Uvz2b/82XvrSl+JRj3oU/v7v/x4333zzva7HJZdcghe84AV4xzvegcOHD+Nxj3scPv7xj+OWW26512mOipe85CX4/d//fVxxxRW48sorcffdd+MP/uAPcOGFF2ZO08DWlPEFF1yA//W//hce/OAHY3FxERdddBEuuuii+72MAPCrv/qr+JM/+RP8yI/8CK666iosLi7immuuwa233oo/+7M/y6bxn/a0p+GUU07B4x//eOzduxdf+9rX8La3vQ3PeMYzMn+e3/7t38YnP/lJPOYxj8G/+lf/ChdccAEOHDiA66+/Hh/72Mdw4MCB70udIiJ2Gh7+8IfjRS96Ea6++mocOnQIl112GT73uc/hmmuuwbOe9Sw8+clPBgBcc801eMc73oFnP/vZOOecc7C8vIx3v/vdmJ2dzQaQl112GV72spfh9a9/Pb7whS/gaU97Gur1Or7+9a/jgx/8IP7bf/tveO5znxssy+Me9zi8/e1vxytf+Uo85CEPyZ3w8alPfQp/9Vd/hd/6rd8CcHz8Pype97rX4SMf+Qie+MQn4pWvfGU2ILvwwguHHjt6ySWXoFqt4g1veAMOHz6MZrOJpzzlKe6sxL/+1/8a73rXu/DiF78Y1113Hc466yz86Z/+KT772c/izW9+88iLXgw333wzfuiHfgjPe97zcMEFF6BWq+Ev/uIvcNddd+H5z3/+caUVMSJOyBrjHYBbbrklfcUrXpGee+65aavVSicmJtKHPOQh6ctf/vL0C1/4Qi7si170onRqaspNZ3l5Of2lX/ql9AEPeEBar9fT8847L/3d3/3dtNfr5cKtrq6mV155ZTo3N5fOzMykz3ve89K77747uNXLPffck4tvWybceuut2bW1tbX0qquuSpeWltKpqan0mc98ZnrHHXds61YvWg7DH//xH6cPetCD0kajkV5yySXpRz/60YGtXtI0Tf/hH/4hfeQjH5k2Go1cuUIytXyH4bLLLhu6PUuapuk3vvGN9LnPfW46Pz+ftlqt9NGPfnT6N3/zN7kw73rXu9InPelJ6dLSUtpsNtNzzjkn/eVf/uX08OHDuXB33XVX+qpXvSo9/fTT03q9np5yyinpD/3QD6VXX3310HJERIwjjLeKtmdJ0zTtdDrp6173uvTss89O6/V6evrpp6f/4T/8h3R9fT0Lc/3116cveMEL0jPOOCNtNpvpnj170h/90R9Nr7322oH0rr766vSRj3xkOjExkc7MzKQXX3xx+iu/8ivpd77znZHKfd1116UvfOELM15fWFhIf+iHfii95pprsi2i0nR0/geQvupVrxrIR7drSdM0/bu/+7uMMx/0oAelf/AHf+Dyohf33e9+d/qgBz0o2xrGON3bnuuuu+5Kf/7nfz7dtWtX2mg00osvvnhgOzLb6sXbwoX5fN++femrXvWq9CEPeUg6NTWVzs3NpY95zGPS//2///dAvIjtQZKmJfAWjYiIiIiIiIiI2BZEn7+IiIiIiIiIiDFCHPxFRERERERERIwR4uAvIiIiIiIiImKMEAd/ERERERERERFjhDj4i4iIiIiIiIgYI8TBX0RERERERETEGCEO/iIiIiIiIiIixggjn/Dx//3yy+/Pchw35uZ34QFnnIO5ha2Dtjc3N3PnDCZJglqthm63CwCoVCpIkgRpmmZH/HQ6nSys/fV6PaRpmoW1NO00hzRNUa1Ws3tJkmRp93q97I9RrVZRrVaRJElWTjtH0cLyby4rn7fI8aws/FmpVLJyshy43lperjeXm8vA+Wm5OW+7Z3W0PDc3N9HpdLK/druNjY0NrK+vY21tDSsrK9jY2ECn08Hm5maWXrVaRa1WQ6PRQKvVwuTkJCYmJtBoNFCv17PDwq2tq9UqKpVK1jaVSgW1Wg21Wi3XxlZu++t2u6hUKqjX61l8SyNNAT7y0uTHMBmwrE1Glv7q6ioOHjyY/S0vL2NtbS2TlZX3WEKApMFlNtmrvmd1ApBQ2bytPOdnJ3HaqUuYn50auHci8Vu/+wcnugj3KyKPRh6NPBp59P7GKDxa3uPdkq3/uNMAeQXqdrtZR+MOb5+1Wm2g43Kn4U9O28KwArNyWfrWAZgAOYyRKhOXdURP0bjDaT4eQvt3a+dl8rXyekTOabK8jZgsbSYE7sCbm5vodrtot9tYX1/H6upq9tfpdNDtdrG5uZkrQ7VaRb1ex8bGBtrtNjqdDiYnJ3PEVa1Ws/y9hwL/5jIxgVcqlYG4W2WA/O5/Z7mwLNM0zR4g/L2obex+9lCih5bK0q6pfmVpBeqazzNBn9oixhaRRyOPRh4dSx4t7+BP+qMqASuXdriBpOg6d1ZtcO5MnvWgyl2pVAYsO8C3Bovy5fpoh9M6cDjtZEw0bJlzOl7ZQvLlzhSSl8mm1+uh2+1mlioT1sbGBrrdbkZaSgZJkqBer6PVamF9fR3r6+uYmJhAs9nMyMusTSY7fgjpw8CIhElLZcgyUVnoA5DvKZHZb++BFHo4af29tK1sXlt55crnUVbKithWRB6NPBp5dCx5tLyDv2Ngy4R/s7WlCJEXx/dgVgKHUWuCLQ8Lz3mqJRjKc9g1JiTvmpKc1oPjGLlq51Xlt3qGyFg7F7+uZ2vViGt9fT2bpuh2u9l0hVmtRnaWZ71ex+rqKo4ePYqpqSlMTEwMTGHYNAc/AHgKiB8oTFgqP48gtJ6WfuhBwXIr0qth7aPXvPKEHjLeva37QKpP/oixReRRuNcij0YeDd3bul9eHt0xg7+Qdec1nl035Q6lad+VCD1F0jiWR63WF3HIUvHqUFRfLy+1PLnDhqxj9X1h2aistNwhuVq6Fs/IiP9s6sGmH+xPrVazctlfaGVlBY1GA5OTk5iZmcHMzEw2hWEWrJXBfIrYqmby4geNEg7XKedHgsEHisZVEvR0yMMwovHaaGCaA8UE179fTsKKuH8QeTTyaOTR8eLR0g/+tIHUguVrFj5EZmbteK/L1RL08gwRgHed02WlL7KYOC21hLUunL7WkctjnVtlU2RlherklcUsUCMkIysjLLvOfxyeCayXAr1dD0KlOYdmdwOL+/ZnFq8RsDk3hyzO0INGH3ih70z4Gsa7r3HZUg6Va1TyAgad1zWNkCUbEcGIPDo+PGqDnVqthrW1NWxsbEQeHUMeLfngL3WVDUCQCAwe0Wgn9ywOz2Lj8AyPRELx3doF6qZx1GLkPHXFG5M6W2NeBwnJS52UQ3/mK2J/vEqNrVWPtNhxudvtorP3QnQufhYwOQ8AWAZwYP0wdt/xKfQO7gOAjLDsL2RVG/l5VqRH+io7r008mKxUh0II5cNvAI6H0Iry2QHcFbFtiDyqZdqpPMoD8kqlkotvA63Io6Oh7Dxa6sFfmvaVrFarZT4OwCABMamwgqrCsgOvEgOTQoigPDIMXed4mg/fUwItsni083mkq/LY3NzMlvcDeWufLTN7rV+tVrMVgGoZ2m8jm2zwJlMWSkrqq8KWbnvPQ9F99IsG2r/XnMVd5/4Y0q//FXBwX241GBMXk6jKjLc58OrC9TY56qo3bksDb5fg+Td5eqfhvIecwtNRr+w7wVKNuH8QeRS5eFo3lQOnXyYetcEft423OCTy6HjwaGkHfylSpGl/+Tn7MwDFndh+q6J41ow3dVGkQKYktjqK43iKq0qpRMtpcP28fNU3g9PziBnY6ty62s2rr+Vh5MH3PeI1PxP7s6kF3mrAiKrdbmcklJvm7fWw2UvRfdizLTOtOJCm2HfG5Zj+8h9me1k1Gg1sbGwMELE6katMQ22k0zk6RaAyNjnpw8ezWL2Ho+pj6KGjbaRt6D3Y+uHSMrurRGwTIo8O5rsTeTTjUxno8dswk0vkUQzEY+wUHi3t4A/H9tcJWQ5KCrqUW4nHUw5Oj4nRUyrO1/JjCyuUJqejZVOi9erBeTOhaKfizqsdjMvBYTl/swbZwgvJ0ciILVVbncak5U1J6BRHd+EsYHIBQSQJeq057KvMo3LgQG66wjYltT2s+IHE7RJ64Kn159XVszLtnj48Qml7cT2C4zD8m/NSooyIKEbkUb63U3lUB4MsL28QFHl05/NoaQd/CYCkkgxYIdl9R5HUElTrb5glqlYIp6eWmymzt0WCEo1aLdqp+NOzcjkvJV+z2jQNteQ4DndmlQnLzOrGZKK+JWbhsa8KE9XAmz4hqrQ147aJYi2t48iRI7md7Ov1OpIkQbPZHCBqdRj29EW/8zWPPEwGaoFanqF4XlsPIxzNQwlct5oYTC9BaTeoitg2RB4dEx5N01xalp836OG3f5FHdy6PlnbwBwAJ8o7E3HmyMI5lyI6t/OqZLRclOEtL02WFUUIxclCiM6hCq7WhSscKrkpolpl2AK8uXsfwrGOvk3H5in5r3h5pMVlZu+j0BNYOD+Tvobu8HysrK6hUKmg0Gmg2mxlhs89IqN7qj6LWrcqmqF2t7pubm648WTZ8L4QiAgvd86zrwUDBZCPGCJFHSRY7lEe9AYxeYzlGHvWv7yQeLe/gLwGSJD+VoATE+0+pglmHYEUOWW6GXq+X22+Kwyjh8DW7HvKj8dLxnIatY1u9NB9zzNW02G9GwQ7eXsdhmVgYfThwPN4R3vxVOp0O1tfXB7YlUMLyiCy95xakKweByXm3fGnaA1YPofe9m7BGpG1TFSZDs2J1x3r2LbI28qaE9OFYFIbTZaveI6vB+gw+WFRns7AY1DvVCe8BZ+VKy8paEduHyKPjwaMyaPUGgADQ7XaxtrYWeXQMeLS8g79jGDbq9xS9yD/DwNZt0ehf/VFUmXk6YFga5gvCabLyWxwmZ66bZ7Gr1cTWr103S1fDWH04TT63MeQ3Y2U00uItCfjgeCYlu67TFUhTJP/yQaSP/1cDxJqmPQAJup/7ANDtAmmKdruN1dVV7N+/P5fHzMxMpguNRgMKI/yQrw9/5xVoXCZ+E+JZsVpnRuihMQzclqY7Xh0USZIgKet8RcS2I/LozuZRLqsOfEDyMEQe3fk8WurBX5rC7aiq6J6SeK+TVQktHKevnYetW7UuOJ5aIp5FomlxWexzc3MzZ0WG/qwDcnk1HyNU++TysEw8uebbIU+YvV4vtwO9HT+kPizaJmqtZrjzi8Bn3g084ieBqYX+9ZWD6HzuA+jdfn1Wpm63i/X1dRw5cgRpmmZ51uv17G0D52/l7Xa7GXnzQ0HrazLQh5WRhuqMHtSuUzCGooev6oInv1Banpy3wgxcihhTRB4dEx5F/81pxnFp/70V5x95dOfzaKkHfwyvc+aU/BhU0ZTcNE3u4EXk4imGEkCoA3g+FB65aRoWl8Ow5a1WeKgcmndotZbdY5Kw+9ZBdb8pW5nGUxQeaXjfc7K98wvAt7+I3q5zkLZm0Vs5iN5dNyMVsux2uzmZAkCj0cDMzAxarZZLiHpN46tcvfbwZOq1j+qL91Dg3155FSHyKgqbEulHRBgij+5wHkWe37zBdeTR8eDR8g7+0uw/AIMWoF1jS9K7Z3E4DS9N73qo42snY8JThK6FiJD9b7zOot+57l5HG6bkCk5D/UyYsPjwcbZWPT8US4PT4zBZfQBU939z697m5tbLdqmDlaXdbmfyWl5exvLyckZcoQeM5e21mfewYjl795i09AFnfxpey+OVUdvC+15Ux63vAEpLWxHbhsij48ejSZKrv/fWN/LozufR8g7+AMCZa/cUzSMdtby8sEpyHF+v5SwBx8Ly8iiylkPQ43Y8i9jK7ZGVQjsfX1fLiju2XVMfE1uNZv4pfGak/nmd2LvPJMsWq9deds8s13a7jbW1NSwvL2NlZQWtViu3d5UnlyJ5qX8OQ4nLyE/l7LWd1wb8W98QeOUN6a4XtqyEFXF/IPJo5NHIo+PGo+Ud/CXF8+2e5ab3PSvWUwjP8rV7XnoWzjqYdg6PWDwC9Mpb5Bys5S6SB9eN0y8qh5GUxVPH4uw4Ntp8lHedL7JKQwQWkp+Wl+ti5TTSWl9fx/LyMg4dOoRGo5GdXckOxeZnEoL3AOM21HbiabKithjlWsj61N8hMgvdj4iIPDpYTy13kTy4bpx+5FEfkUdPHpR28Jcg34nNOmBrQy077ihGKLqsn0mBOw1bH0pc9r1Wq+UsFMvT4hiU9LSsWR3lu+6hxPeso+qqNltyz/4nXOZqtZpzprXX61Zu9ZVgMvGISA8Y52kMDW9xjAg961OJPuS7ofI1ediqtUOHDqFerw9sQ8EkxmXg/Pi3yl8JlMuoaWm7abn1eqieoxAQE55PcCiz0RqxTYg8Gnk0pw+RR910dyKPlnbwl2JL8LysP3dfOqf+BvLTCdZ5LCzgO+Uy1EJSZ1a1YrUM9Xo9RzRpmmaWlJXJ68jmT6FTIfybLTDrqB4xct1UlmoRcnibguB0eENRs1z14HJvyoK3LVDZcr2UEPhhY/f5uk1brK+vu/E5/U6nkx1fxLJi0ve2mvD8kHq9Xu580BDRslytLBrO5OSF53j6INWwg+RVUsaK2FZEHo08Gnm0H2+ceLS0gz8gQZL0O7YpnCqlKjJbfqEOoB2VLd40ze9YbmF4l3uzfjqdTo6QspIn+eXslqblydYix7H7TEiexaNlU3Lj72qh63eWG29y6hEAgMw6takKm7ZgQuM/tla13ULw5KlpcFt2Oh0kSZJtW8BEYHVrNBquHwq3B7c9W75G2NauvE+UxQlNh4QsWG07lZ1eV7louoP9ocy0FbF9iDzK13OSiTwaeXQH82hpB38J/a+vlFnJPIuCFVMb3dLh9DhNja/hOR8+vFs7mnUs7+gaJgr97uVj33X5vmfFuLJMBv0+ePqHpxR0ukenG4yIlKx0WkOnLbwHi0LDqjyLCKfdbg9MZ7EMp6amsuvmxMx/mnaRZc919MhYidXkqBaxV0++x1CL3/SewxalFzGeiDwaeTTyaB/jxKOlHfxtOSrnLS5POey7KikAdxWRWq2ssKZYWRGSwdVuXloDRafr3io2TZ9JL2SJeMqufhkKewXPJMR1trjsZxIqm65SM8Ly/rhDaz08GRV1fI1fRFwqKyZhloluA+HJm+OojJnMFFoerw6h9uL8PHl5ded7ZSeriPsBkUeDeUUezcsn8ujO4tHyDv4AJMmgAvbv5VcK8XVuTJ5OYFLwiMUjDUXIughZSmbVaifg8nHenuXN17XDeP4VXC/vFb3Jjq/zb3NuVotYV6npnlVMWixzz7LTOvL9UGe3snvE4pEk521yNpnUarXsocZ6YrLkMy05b/7ktlHiKNKJIrLhB4rWySMurS9dQXknLCK2E5FHI496sos8OoidxKPlHfylQK/nj/5ZUVipmJhUCRme0qVpmrNkDEoSTKJemnw/pNCeAvPvkGVraWjdQmXSdC2s11F4ekLfFNiUha0K87Yl8F7fe1Mdavl7RBMirKJ6WTk98uO25fMmbdqCiV8J0WuzSqWSW/nHDxSPqEJvFUJEXlSOkAyOV24RY4LIo7m0I48W18vKGXl0UC5lQ3kHfwJTOCD/GpqtVv4MERbDGpZXhLEFOYq1YCSicay8HtmqkjHx6hSEdnZ21rbwRZa7R5ZKJJ7s9M8IiLcoYOuUy+r9eXLje0w42j6h3949fsjw6jVbHWiyNt+Ver2eaxd96HA5WU/0rYO2F7eD+plomT3dYh1XQvV0w5VNeXkr4n5C5NHIo5FHx4NHSzv4S5GfYvCISK04thDV0tUG53tA/zggViCvQ3vkEJoS8MqsHUEJlwlQy2vfbZ8sIytPYT2r1+TC9faIme+xtWpE5ZHWMKLiehaVwX4Pe+BoeM4jTfsHhW9sbCBJEhw4cCC3YSmXS98AWNn4oTNs2itEuCGiLSJyr05euuqjExGhiDwaeXRURB7dWTxa2sHf1nRFL+c8bJ2UrQTtdHw/OJLHoLXgWUxFBOUREcdRS0UtISY6Lremp/eZoLUDaZ08wvM+izqutYH6o9gfy47j6XevXiE5c7n0gaMPsRCYcDudDlZXV3Hw4MHccUVsEYZWzYXS1oefR94h/fPIbRSZhMoSTGt03o/YqYg86tYr8mjk0ZHTKimPlnbwlwYcLfl1dJqmAzuOc0dWy8JbXcRhOa5aKJwGh9E0uVNpXvZbicFIyLNAlZA8GWgZ+Z5XBs85WolLOwCTAHdQIy6uj5XRW62mZeRr2um9DulZcCp7LouVm3ewN1mbVaublvK0hJentlVoZR6XW8uveurF8R5G/FvrGZJRxPgi8igG0g3JIPLo8fEoEuDA9AE0J5rYtbELD64+OPLoSYTSDv4SJEiSiqvYBlVS7cyhzurmR52XiYXvm4NqyEoIWR+soJqXZ9WGlI4JYZR6WL782p2tM30DoGXVTqIPB3VS1voq8WgZgb6fEOflOUCHZGHfix4OVtdut4vV1dWBKSEe/DF5ee2gUwTsM6X1t3Lww8lDyPrWh5dH/t4DdysuvGd+xJgh8qifDhB5lMtk30fl0cN7DuPux9+N3lQ/zc/0PoOnbDwFFycXRx49CVDewV8lQbWadyBmZeDfrNis/J5Tr90zqOOp3tdObd89hdZOwlYgQ9O3a96UQVG5vLy1rKHOx8TEzs4sM+34lUolc/bVe0pooxKWHh5uVrGthGN5enLQ+mheRor8VmBjYwNHjx7NysKDv1ar5Tojh8nB99Px6h8qY0g/vOuhdJXE07R37K1PxDgj8mjk0e3m0fTcFGs/vDYQdyVZwV/jr5FsJLi4dXHk0ROM8g7+jlms+gqdH9i82kqtPQsD5K2KIotU92TSsExEoxDLQJ2IIHjKUYmB89ffRm48mPGUnMnL8jFrkMPwIesalmVdr9dRr9ezA77VMdwjMO1kGt4Iy9K09up2u6hWq7ntEDhdlWmRrC2Per2e5WFvHtbW1nD48OHsWqVSGQjLOmNl8AiL/Xl4OsdzJub4HJZ1z/vN9Q9NB+XyKydnRWwjIo9GHt1OHq01aug+6dhgUqMkAFLg4+nHcX7n/MijJxilHfylGGx0JiK7F9oeoF6vZ9ft2iidSC02S2NzczN37iCXh9P28uQ4aZpmnT40VaFxlJCZgAF/GsbC1Gp9FTCSApDbiZ4tNHZAtns2IGo0GqjX67npDu6c6sti8tEHhhGekaFtFGrytzxYFnbwuXZyr96cj5Gilb/RaKBSq6C9t432dBudbgfJoa10qtUqms1mrp4sc20rfoBw23lkosRuv/Wh4+mOR9ZapogID5FH8/lwmJ3Eo816DZfuaWP3xCYOdhr4ypFZbN4PPFo5s4LeTHi6HMnWG8Bvtr+JC5oXRB49gSjv4I8UNOQ0qq/NtRF7vd4AyXn5GJHovk1s9RnJsCKzJav5c4fl8KaoQN5a9axOr6NYGXQahOXC0zeelc7EaXVWK0zly9MKTNRMIvow4Gu8NxSX2R4IHEbJzcrW6XTc+irUWq1UKhnhds7q4MijjuR8Vb639j2c9c2zMLMyg5WVlezwcs2LZQH0id8INfTntWdR2T15cnzWT+9BeSylQcs8YuwQeXTn8+hTH7iGX73kIE6Z6L+NvHu9jrd94zR86q7ZbeXR2mwNHXSCcQwHNg5gvb4eefQEorSDPyBBkuQbzBqJLTUmNW5sPghcLZyQBaAkw/l6HR/Id9CQdaOvvNV64byMPDUcK6dnwRZ1CJUhp2dkzCSt1qHWM0RWWheOx9a+Xc8svbSHRyxu4JQp4Eg6ga8uz6FLVrGed1kElo0Rlw38umd3cehJhwbidFtd3HLBLWjd2sLs6iymp6czMtKBqJU5tCu/tqtapUyGw0jMIzCGtkE+//JOV0RsJyKP7mQefeoD1/D7j903EG9Xs4PXXXArXpucg09+b3rbeLTarmINg/5+ispaBZ1mJ/LoCURpB38J/T84Gh983av3zLLjxlQrapjV413TfFQZlRi8dDyS9UhZyZPz1LBaTs9BmqGkove8+x5hjSIrJXyWwQ89YBW/cvE+7CWr9Z6NBt75rbPwqbtmtvxMyJG5qM08GWSWdr2KQ5ceOnZTAwNIgZsfeDPO+/Z5GWlbnbX8OhWhcmPZaDhPhzV86AGoYUL3vHwjxhORR3cuj1aQ4lcvObj1XaJWEqCXAq865w58dt9F6G4Tjzb3N7GyuoLNiU3/jVgKNNoNzB6ZRXc68uiJRHhNfknhNTowSGxqZXqEw38h34IQcYSmUDRO0eqqkJKH8uW8lNy0M7MzM/8VWbeq8CoDIyxvR3qWS6gNdFXaUx+4ht+99C7sbm3myrHUaOM/PfhmXLbnSG7w562OC8mW869UKuju7WJzMkBYAJAA7WYb361+F+12231rYPmE6sv3vQdSqE1Dcb36eaSk37MwgapGREQeLT+PXrq3i1MmNgcGflnZE2BPs41LFte2jUerSRUL/7JwLLBG3vo48xtnor3Rjjx6grHjBn+MkMVm9wxqPSnhqPUXIgPvuqdgen8UxVfHXAbfVyvcO0R91I4dIhe1StM0HZgy0Nf0RTKwchsB1et1NOs1/PJFW9MVntWaAnjF2behXu1vi+D5CYWgMu5NFk9zGI6mR7G+vp6tNitK08rkOYoPK5dnpYbIZ9S4A20wcqkixhmRR8vJo6dMDRTNxa5WN7fTwX3h0SRJMPWdKez5pz2oruWnoGtrNZx2/WmYvWsWGxsbkUdPMEo77YvEtxYA5DqWKbRnJZjSKRl4lljIqvXiM0KWEqfL0xMchqcUQqvtPELRDhMio1B5Q6Sr5O0Rlh1Irp0mJA8mLF759QMLK9g70UUIfat1FZ9d87dDGNYuRqpJkqC2PlpXqKxVsF5Zx8bGBprNpjvlpbLyVrMV6a2WMVR+L71h5Fb0II8YQ0Qe3bE8emizGSwf4/BmKzfde1941P6mvzuNye9MYm3XGjabm6i365g+NI1atYbN5tb+guvrkUdPJEr75i/BYMdiSypJ8n4qHuFoB+TVW0XhWRHVQVXLwOVj2Kt5by8ny0/JjJe6c7oc1oia997y6mPQaRi1stTqtE7O6TNhdTqd3Mq2Ydaalcu2CrCtVPZOjvYyfbHecWXB7cT5MLnb4em9Xg+NexqorlbD7/BToL5Wx+SBSayuruLo0aNZXXXLBWtbK5d9qhXP5eawXMYse4fsVV882XJYLkeapqXdnDRi+xB5dOfy6NdWFnD3Rh29QDfvpcA97SZuODqfXbuvPMrpJEgwcc8EZr49g6kDU6hW+quX7RzgyKMnDqUd/KUIK6n9sQOrwRTLlqbrdd2yQJXKOi2D8/QsEV62r2F4dZZHGFYPu8ZL+ZlIsjdYx87g5LIrEbLCW9pqmXL6fM3KqdeMtGyzUDuc3IvP8rFys79fvV7HkXTCafVB7G/XcrLz6sDlNjKwvG2X+163h8UvLB4rmBZ06+O0W05Db7OH1dVVLC8vY21tLXtIcBuxjvF1bQ8Oq2VWC9jzK+Kw3oo09RnidCIigMijlv5O5NFqvYF3fetsJMDAALCXbk1Xvue756OH/IKa+8KjWlYbjLZaLUxOTqLVamUrrSOPnliUd9o33fqPCcUaxJRRN/lUq007jV3jT1OqJEnQ6XQKLQRg8GgjbyrCwJaJWlcWJ2RpWr08ArW4Gt8jMftkObG87LqVn8Pw/kudTgcbGxvY2NhAp9PJTQN4FruWmS3/SqWCG47O456NBpYabddhuZcC+9oNfPHgFNJ0za0vy5nbgH93u12sr6+jVqth8tZJ7MZuHLjkwNbij2NobDRw7h3nYml1Cb361gPCpmXYOldCtHw8i1YJlPXXoHLzfvN1/bO6Wt5FbRAxpog8uqN59B8P78ZvfT3By8+8Fbub7SzsgU4T/+M7D8Y/HlrC5mZ7YNBzX3g0SZLsNBEriw0CG41GbqAdefTEobyDvyT7L4NHPOqPoqTEcdTi0UZWxfKsU73uWXqqfF65mcS0DGrFsDXD4ayzeFaKF96zXJXQgPyRN91uN/PfWFtbyyxWz2L2Oo12Mru2CeAPbjsb/995N6GX5hd9mNX6zlvPRLu7OXDcTwjcbta+m5ub2NjYyEir9c0WTr3zVHT2dpBMJZjoTWBPew9mZ2bRnOv70Hgbx3I+nmXqkTfrmZLfMHj6aZ+he305lNtqjdgmRB7d0TwKAP9wcAn/fGgJF88dxVKzg0PdFm44OofNFNjc7Gb8uZ08mqYp6vV6Vl878aTZbKLZjDx6MqC8gz8ASTJoOTIZseXA4dSHRRvUa3D1P/HCa1wmIyYhTStEFl4+nrXpWYbqX+IRm/3mjVoVXDb9U2t1fX09e/U/jEAY7L/B5PiZ/Qv4LZw/YLXuazfwzlvPxCe+O4V2ez1HlCZvL2+VHddhY2OjT1rdOup3HjtmqVlHe6qNzclNtFotTE1NZZZnq9XKpiOKHmRqmWo7e7IfZmUq0enDmOWgYbfy3vL3ioiIPLqzeTRJEqBSwRcPz5Asu9lbx42Njdz5vtvFo91uF/V6PZN3rVbLpn4jj554lHfwlwLWPqwUPP3Ah2szSQH91+4WRonDA08R6OtmBpNEiBw93xZeccUK5xFNESlwnl5c7rSj+C4oAVpctVp5hZrXSS1eqMzsKG7h/u7uWXzmnofjotllLDU62N+u40uHptDubmbTI2ohZ3VNU1xcq2ExSXAgTfGVXg89DBKL+atYXdnXxtp7YmICaZpmviuNRgMTExO5s4fVgvUIROXP7R16iGpaqnfDiM37naYoraNyxDYi8uhY8Kg3OOIBWxGPerLjT8sn8mi5UN7BH/KWiZGAHRiufhzW0Poanf1DQpaD3fcUU0lQlcsIhsOqomocttrUGiryu+E0TBas2CHFZ0uay+nVq1Kp5IjJOrxZjcOsVJafhbX24jM/eaqlUqng2n0tAK1jeW5kK+KMKJXEn1Ct4RXNJvbQlM89vR7esbGBz/byR1JZ3p1OJ6sTE26lUsl8cNI0Ra1Wy6xVdVBm3x6tB9dbCcfak9+0FJG9tn3ogcsyzutsOQkrYvsReXR8eJTrySuLQzyqeYZ4KPJo+VDqwR9bYKyY+opdyUg7qyqARy5MJF451M+Fy6Np8jW1gLUeHDZJBlehKaGpVa7Wjd2zjulNZbDsLEzIWZl9RdhxeRiJhKxG6/SWhxIW58tbIjBRPKFaw39utQbyXUoS/OdWC7+xsY7PHpui4QcHE1i73c7uWV7d7ta+g6GtJUJvCVTeHsl4lilf14eu+jFpm4XS9/KIGG9EHo086vGohs/kir6nG/Nn5NFyobyDv6TvqxJqcINnHRSF88Kw5aadXMlNycfiqTIz0YxSPl6F51lhw6Ck5HUurxwh0tdX+0xcoc7D8blddGqJrWdNg8lRCTJJU7zimEOxlruSJOilKV7RaOIf11bRcwhL5VWpVHJWsV1PkiQ3veQ9qIxYeLd+lr224TA9Vvl44fRB6T28t36jzEZrxHYh8mjkUYdH+ZO5hCpaOPBTeUUePflQ3sEfAJCjpXZkb0QPDHZODseKpJ2a46hVkiuRKHCo41sYzyJjPxev7F5cr84anqF+MXyfrW8mYk6TrTsmLI+Q7NPiKKklSTIQV9NQgrNPS8sI5WHVam6qV1FJEuxJElxUqeJLvc1cXRQmY91vy67rg0vLqiTkycV7uBWRlkeO3sPBHm7h9ErKWBH3AyKPMiKPYiC8wmvXyKPlQmkHfwkGV2ep9QCEHZAtnk4zAHAb28Jp+pqm/g6FZeXn314drBxMHlwvDe85+3KZmBSVoDw5eOXijmLEpTLWdO27N/0RsnS9TllEcAsjrrxadIiLycuIlEmZpyzUwdvTL+8658XfQ+FDusthNUwoPP9OkgQlXaQWsY2IPBp5NLuWAqcvno/p5jyW1w/hjv03Hvdihsij5UFpB38GVWruvHo0j1oJai141gNbh71eL3jAt1euovuehahlUiIyvxLtMEpEatGElNbCMHGNWq8QoWjYYQTkxR8Wx5uesO8H0rBPEeNAAaGyjJi0bCUek673YPIsWS63V3b7XUR2Go4fHio7LovGK0o7YjwReXS8efTBex+JH77oZzA7sZTFP7K2H3/7lf+Jm757rVsHrz72PfLoyY/SHu+2hb4yq9Oxd82uM7gD6C7jrHhMXqpYXlhL2wNbqp5zsfdK33wzzLLTcnH9lGztk/M03wtehq8rqVSeOd86ul5ERvzACHWoNE1z5MDTA/ynsvGcfr/U7eLuXg+9QMfspSnu7vXw5c3u4D1Kj/PyymXkpTqQJPnd5+3ByXUY5e2CR2ih38OI2UOapmWesYjYVkQeHWcePW/vI/ATj7oKM63FnHxnWgt4zqN+EQ859dLCsinGjkd7KWrr66ivrKK2vg6UZFBY2jd/KVL0en4n4JG8Wg6skHwepIGdgTmtbrfr+rV4nVevq3Vhyl6r1QbeYvF3Vmge8HhkBSDbZJSJ1erJcrJ0zQq375qW5cmE0e12s7TVOdk6uFpkHnEU3R+FZILyThK8fW0Nr52cRC9NUWGLMU2RAHjHxjo20zTXSU1uLHfTA1u1tr6+jvX19WyrAnuDELIYTSZFRMRtrProycHKxbrN7eoNiDVNS6Os0xUR24fIo+PJoxUAD69WsYAKfuCin821bb9tKkjTHp564U/jxu98HkiOpXEsvvfGbNx49JRmHWetraC+erQfv1rF6sI8OpOjnU9/olDawV+CBJVK/zDnnGLT6iC9B/Q3D1U/FtvbSoknSZKBA8wNnK6Rg6VhFou+IuZrdhwOdxS2HDUP3ghTLT6rOyszW+2efOr1evad681pmBVm5WZr0nw4lLh4ryola+58Vm/Psg2Rn5GtR9xpmuLT3Q5eu7qKV01MYA/dvydN8Y61NXxms+vGM70weVgZzVJdW1vDysoK2u32wFsMfhvAZMaWL+fDDzjvgaJy4Lcf3psQJWCTrUdcERGGyKPjx6OPr1bxqtYE9lQqODh/Hv6FpnoVSVLB3OQunLH0ENy2/2sD7TXOPHr6dAuXzLQG3vQlm5uY2rcfK7uWTuoBYGkHf1vw/QNY+YDB1+9GFNqwqtAhstP82CJlC9CzHFRJWXHZ2vRIzsJvbm7myImJzrOO2GpiqIXkWfW6vN7C2T27z87K3Kk84tV7od92TeXndVq99vedNj7d3sDD6nUsJQn2pym+3O1mJ3xoXqw7qhu9Xi87emltbS07fsk2J+VyWjsyUekDxj5VhzwZsG6oxerJZ5g8c3HiuDACQOTR8eHRJ9RqeO3EZHZ9ozE7ENbDVHMuL+9AXuPCowmAS/fMZ9/1Xgpg8uAhHJ5oAY7OnAwo+eAvb3lopws54HJYjs9xlGj4e0gBOYyl4a2C88prYTmuhvUIzcvTI11vSb29QWPrSvO260xQdt3zIwlZnKHfXn5eeMbIZAfgC51OUGbaBl4aNkVjxyCtra1hbW0t56ui5KSyZKvVk4P3QNG66YM3VJ9QXYquR0REHh3Mc6fxaJKmeFVrIiezZvvIgAw8LK8fzNL1MG48umeyial6ePiUYOsNYG1jA13nwIGTAaUf/DG48XmU7ymF3vMGGko2ajko9D4TxChpsbJrWCY17hwKIyGVhSeHok6g8XU6QpfvHy9xDauHPjSGhSm67j0EvHRD7WGOyWy18mo1z19F8y4iag7r6apH7Prp1XkU2UREKCKP7kwefVitNrAH6vyhW9BcP4iN5rz7hipNeziydgC379va9iXy6BYmqqOtlU02R9t94kSg3Kt9qX21owMYsCTUYvM6cWh+Xy1JT5G0Q6ofTZHyKkGFwoTIh8Ppq3IvrHVGIyT2VfHKwdd4ioL9VjhN/vOmaUNtEArnxRslrodhBKJltz/bpmBjY8Pdk4vj2Uo1u66fKteiqQ1NP/R7lDr30x5NVhFjgMijA3Gs3juJR5ccX8sEKc675YNWKaljD0CCj375j9z9/saZR9dGHNSlIw4STwRO3pKNiKJOzp1dfTvUITlk4XkkVzT4UPLwFIunCvS+kibXgX1HPIW2cFoO3XrAy1MtTX3FzmVn0vOmLDziChGEVweuB1/3HkyjYFg7a94Krqs5Zmvb6ZsIYLjctX5e2xUR16ikH5JbHP9FGCKPDobTcpSdR/cHBo979n0RF3313WhuHMpdP7J2AB/83Ju2Vvoi8iiHv3t1AyudbrC+KbZW/XaPHTV6MqLc075OZ9q6PEgSqjTskMpk5MU36BYFGpbJLl/Mwc7ovRrnMF6ZigZBRXUIKbZHrkzonK/myXs2MUlpWb3f3jSBlilNj9maBaQy7FpoKoTDaXuoxahErpYq3w+1u8UdRtocb5g8jxce0Z2cbsgR33dEHnXTDeWpKAuPfnlzE3f3etiVJLktsICtAeDSPV/EbXPn4E2bdRxZP5hN9UYeHSwLkgSfv+sQLnvgElLkudRSXV2Yd6fSTxaU9s2fKYKRj20PwMpjiqJOtpoGK6t+D1mHrMjayYFBEvGcVT0LSr8z4arVrSiysD0n6GEWs9fp7Hen08nt1D6MJLVtRrG0LIRHCKOgqHPzAytkxWpa/MZAHZG1vYBBWY76YFL9GuVBqPf1geY+KE5eXor4PiHy6Pjw6Gaa4u1ra0iAgU3we2mKClK897tfxFe+/Y+4bd/XsqneyKP5+xbn9qNr+Jcj6+hqmtXqSb/NC1DiN38pbU7KyqH7C7F1wRZn0VE8niVoDql2TcnKiIHB4b29rbwyMBF7FrKm6ymn7rPFHcfrqHp8k4Vlorc4vV5/ub6Rlu1yH+p0ISL0wA8OrfuwuCGMRJBCXErwTFpmpYfSsL289K2I96DyvvNeV159rR3sHuugllvj3duBdMTOROTR8eLRz/Y28dq11a19/ujePWmKt6+t4tOdjpueym6UMOPAo3e1u5iZnMIpUy0kmz2k1crWVG8JOLa0g78kqWTOoJ4iKCF4nRXIKwjfV8uToU7ASgacryqgZ1Fyft4GxvxpBBPKm+uv6Wi9lCAqlcoAyav11ev1Mmdd3pTUIyn+rZaXkmnISiy6prIOIfQWQeERPJcvTbdOKLD9qUxWvDmpyY1lbPILWcbcfqwn1h6hsnr1DD0cisgsYnwReXT8ePQz3S4+u3wEF1drWKok2N9L8aVuB8OWMEQeDfBokpy027kUocSDP6BSGezY/fs+AXHj8e70piT2m3cm5zheB7N4TAKeQhZZyHyPiclTbiYju+8Rsl3jPbdCpMxlMJIxWFxbqt9utzNrVV/fK2lxfM7Xs6L5+v2BogdRURy1wG21mu2+z/rE7e09MFR/hulJSC6qY0Vy8++niCs+IiKPjieP9gB8cbMLHBsTHQ8VRB7N1ay0PFrawZ8pECu9NrB2CO/TwvBu9aygHH9zc9MlC86Tf3P5vKkJVuiiI488gtH69nq9bAd7nWKw+0X5qr+J+mXYb5umCBGXpu910NDvUYgrBXK+gKG0isDtNlKeRDZsgZpc7aQAPkGAH2ShdtA0rTzegypEdiGwbvv1KYweMSaIPBp5NPJoGDuZR0s7+EuQIEkGz2bkRtZNOoH8q3w7ZFydeD0Cst/ms2Lp6H1WWiY+i8NEVq1W0el0sqNtjBQtTauDlZfrwB2A07c/nR4ITRfwFIWF4Tzt0+TV7XZx9OjRbINOna7gMrLs9QHjyZfjGgbuFeiExkUKnLHrIZhuLWB57SBuJydmD94D0Hs4aNlZF6y9eZpBLfbQA059kywcp235q054dbE4nFfRaQ0R44fIo5FHPXhxmZOKEHm0HCjt4A9J3nJTPwug30CqiMBWXDuMWy0BJjbtaHoGoRKVpQ1skVCtVsuF46kIszKBwQ1FvT9WRN38kq1srTsTLYdnYmXoXlNGVp1OB6urq1hbW8ssV3NSZouXy6JkWdR5i6CEOKzjPeTUS/G0i38Oc5P9Q8sPr+7HR7703mzfqlGhehOqF7/h0I1fvbcrXA8Ly4fVsxyZwPQBwTIKycXy7odJCwfCEWOCyKORR79PiDx6cqG0W70gHbQE2MqwTs0WaRZVCMezaj2LhS0KzzpTK8ZI0Qtj14CwNef5PvC0BKejzrKGEBGyTPg7kxRbquacbNaqEZZuTKoEqNapyo7Lr+2jbxA0vkd2aZri/FMfhec++tWYnVjM3ZudWMDzHvNv8ZAHXBosi1d2T37mp+K1Wa1WGzhEXkncy8uIq6ju+nDg9L30tI55HRzdZydihyLyaC6dyKODcfl3CJFHy4fSvvlLMdho2kHtuyKk7B7xWHjPqi3KS9Pj6/yp5eJ4PI1i4JV5nI5ay5ofW0ih+jPxsMXabrexvr6OlZWVzGJl0uLpilBHUfmF7t1bZHVDgisufpEriySpIE17+P897EW4+bvXDVhsRQ8WLrs9FD05cjos+yIi9kg5FM77XSR7zn+gjG7pI8YJkUfHlEeTBMmeByOZnEO6ehjpXTcBafGGzoNJFPNf5NGTG6Ud/AF5xeeRvBKJp8z62tcjoWHgcLpVglrFdi1XgzTN7WOk5dc6sGVXRLwcx1NWL54RFE/59Hq9bFXa2tpaRlq8PUGRdeXJPWRBFpF5qPxWX5bbGUsPyU31DsavYG5yF87Y9RDctu9rhXlpubjs+hZEyzWKXPi+51flEZG2a1Feqnec19Z839DqR+x4RB4dkMgO59HkjB9A/dLnI5nqz4ykKwfQ+ec/Qe/263NyUt4pqn8RIo+efCjttO+Wo/KgVXc8pOOO4p2/0H2+5k0tcB5eOppm0fTKsLp6ddGVUnqf46lPBdAnrY2NDaytrWWEZXsz8ev3IoIKdV4vTAhFdWVMtxaC9zRcSFdC6Ws5te28Q8i9NLntGcdDqJye9/Ad/mBIUVJXlYhtROTR8eLRyhmPQO2yVwCTwpOT86g/+ZWonPGIYNyQPkQeHTm7kwqlHfwhGdyfijt2CGadqRJ5JFJEWKqwSjJA36rRTy1PiCi5rJ4yesqq97zrnDaTjn3nqQojrfX1dayvr2NjY8P1Tzle4iq6Fmo3rbuHFTmcPAQvXFEeXFZ9O8IPG9uVXvXB0zmF96AreggPI/pQ+lnckWNG7FhEHh0fHk0SVC/9KbeeSVIBkKL+6OdvTQkn/pterz1DZVVZaXkjj55YlHbad6sx85tumgKNHj8whx+wNtVaZGvQFJPTSdM0W6Vm6SVJfmrF9jay8KHO5E1pqHJzmFGsrjRNc9MTPIVjDstssdqO7Exq3nmfSrraYYcN4rRzq/y8sPZ5+74bcXh1P2YnFo4Rmqbdw5G1A7h9342ufBihBwWXSx9K9r1er6Pb7Q7UgdMJtZNatJaPl0aofMPqFBEBRB4dJx7FnvNyU72KJKkA00tI9pyH9K6bnfv3jjsij56cKO+bv2PTFWYh8J9tVwDkGz5N02wVEbDl9Fur1Qb2m3Jzo8ZmkuEOr3v/WOe3Ds6ruLxpCcDfqsCu2zUlG8vL8vdIRC1DJTi7xuU1PxW2VjudjmuFcvqaX8jy57J71pyGLSL27OGBFB/98jUAEqSpHkDfA5Dgb7/8P+/V8nyTbafTcXfl1/ryZrFMOiZffthym3hTXx4ZFb0JsfLy9/wDvoIdwF8R9xmRR8eGRyfng+2Sa6NAuGEDolERefTkQGnf/CFNkab5jT65URqNBoDwa2rr1GpNeUvsNT6TmymtKY7uN+V14JDlqXlq5w11PstnSyz5faeUSC1MmqbZSjOTlXVC3o9qY2MDGxsbuRVp7NDs/WkH8siriEi8zsayCVlwhhu/83l88HNvwhUXvyi3+OPI2gH87Zf/J278Lu3zx20bKK+Sum3ZoFM99qDkTWaZ1JSMvHbX/PRh44XX754cPSt4m7g8osyIPJorw07mUaweCobNYe1wYZruW6/Io6VDaQd/KVL0evmOyRYAWwqA/8qXdw9XcuE4RlS9Xn8zUU3L8gtdCxGRlZEVmRWe87a6MkFzWblc1kG4M3G+ln6lUhnYYLTX6+WOHrK9qXiqgjvVQNs4JB2Sb6j8XpqjIkkS3PTda3Hzd6/bOuGjOY+jG4dw+74bh77x4/bhdrMyMHHxWwT2UVGSUTIqApN/6D6XT69rXbwH1rFvheWIGA9EHh0fHk3v/jrSlQPA5DxCLjFYOYjesSlfd5A3IiKPnvwo7eCPYcrkWUTcsZS4zLLgZf+e1aDTGKMon5KlWr6cH091eB3Oi6vEYNd0CwMlTY1bqVQyMmIr1CxXezWvr+fZmtLOo9eKrG2vE3I5Qxg2WAS2HmzDtnNhqYbkzN+ZuFiG9jAB8jpn5eOpoJAFqjJTfQ6VjR/Y/HvYILqksxUR9xMij+5wHk1TdD/3AdQufwXStJcbAJpLTOdzHwCGtImHyKPlQ4kHf2HHZO6kIcXQjsxpcYMXKYvFU0uT73kKZffYAg2ly7+tfDqlwnkZmFSsU1ke3HE869PisbMyn1upZ1h68tfO7qVfRP5emBT5jjaMCE0uIXDba9wQydvvkHO2vonwSM7SUKLW+gzTO/6udQg91IbJJGLcEHnUy8uw03g0vf16dD71zq1Vvbz4Y+UgOp/7AHq3X5+Ly3IJIfJoOVHiwV/eovQa0V7zKwGxpaGKq/dDpOcpiu4fxT4jA6UXIvMI2Cuvp+ScjublkToTrYX3VqgxYek0Rchi1fIUEdZxIx02aZvPvyCAJDv4gPIIzcKYXHRnfn4zwQ8sr85KUh5x8u9hJMT5ARjQ/cEHa7mJK2K7EHl03Hi0d9t12Lj9elT2PhiYmAPWDm9N9Q4ZNDkCyP2MPFoulHbwl2DQgrOOzx2VO7o2vEcUTHb2Wz89cuFPJQJN39JivxdOy8vT/tjyUWVlh1j74ykIrzNbeItrTsjmpNxutwc6qE5ZcN1ZztpRizqxXuPfnjU3KkL1BhAcSHpvHpgout1utmrPzqbU8NVqNbeij8vD9fPy47ChcGwRj/Lw0HpERACRR/n6WPFomqL3vZsG6hFC5NGdx6OlHfzZgNsbGKhiFClhqBGZmKxjc3xOn/NUxdJ8OX/P4VjDevAsI3NG5pVQobS0HFZW+7NVWB5hFe1Iz+Tp3R8Vo3ToUdMpuNn/HpCPd83kY6v3eCrHi8syC6Wt5R1GQCEi9KBvNnYCaUVsIyKPRh4dIZ2Cm/3vkUdLhfLu85f2G4s7KltualUZuMPzNbYwef8oJQhNk78bGXmWMH/qdS2bpqudQutg5bf9tiyOVx+Ozyvg7I99VLzX8kXW5rCpDEa6VZDg/fuC0IMgSRIkCJDHCGWxB4Tt3aUHs1s+IZlwWbicw4jH0xN7IA8jI0+fdgB3RWwHIo9GHi1A5NF8WM2jzDxa3jd/BM/6MyuTFcjABDCKtciryDS+Kg1PobBSafpqSXjWr37XzmD3tX4euXqWu4VVx2W2XI20vCkKhXbUUD2yNwkITxloulyv48GwzjxKeix3k4FN5zCpc/3VwmTZqU54bTNMxvelPsdCjxguYlwQeTTyaAiRR4O1GjHcyYdSD/7M4gopphGX16Htfigeh1fLUOOx5RcqJ396+Xjk4hEu+yhYeCULtmY8Z+kQuai1yp+jWK38an5UEkaaAgGZqjzse1HHPF5SC5luRe1tpMWEzvl75dR20ryU5LROIbLTcJouE34exz+NFLEzEXk08qgi8mg+3Z3Io+Ud/CVb+qbWn7cqayBq0l9Gbr8Naj141iWDOzz/VjC5qmXDiqoEaeU0QuAyhJSU06pWq7md5Dl/S9euWWe0Y4jsVXyRkzKn401VFFlXo1hYRRZnkQxCYYelV5S3ErFOUfBbC5arPiC89tY29d5OcNohwvJ0gx9i/fKPLIaInYzIo5FHC2QQCht5tPw8WtrBX5r2O5oRVa1WyzUavx5mxeHd3dWaZCVSC8GsP2+lmH23lWd85qCVxz7tr16vZ2FNyXmKha1PJjkrO6fLBKfXuNzsx8NpGCmaH8b6+jrW1tbQ6XTcDqgy1qkMzcOuZfKg32maXykXIuXQtQzWDgXhjpe48skPTkupjoyatlc+9THidvTehvAqRPutD0MuF99LkqTMMxYR24TIo5FHBxB5dCx4tLSDP5O3dY5qtZp1clMcc9r1LFOgP/r3FC1kLVg6Rga6TYKGr9VquWOBOA0Lz8TiEa0SXrfbzSm4wYjDiE+tXN45XcvG1ura2lpmsRqRseWqJDXq9ESu4wU6N3c8bYt+1OGWbiVJcNrcDKYadax2Ovj2kRX07qWZ5j3IKpUKGo0G6vV67iFjRxNxOVX/+E2BheHtKrz6KZGzLD39tXT04HPvYRwxvog8Gnm0CDogGyVOUVqRR08elHbwhySvBKxM3MB29BArgjWmOjLzbwAD97lT6lYAHM+zNKxsfF87PJMXx2cr1dJjsg2RG0OtFduGwNLY3NzExsYGVldXsbKyklmrel6lV/4iH5Zck3FndNLicFxuz+ovwnm75vGUc87AbKuRXVveaOMTt9yBr+8/lI/PbyRG6MjWFtVqFa1WC/V6PXs4KlFy2+tbDvWh4nqGCIX1vUgOquuch32maa/M7ioR24XIo5FHjwOhAWnk0fKhvFu9bC00R7VazaYpPALyLBf7zpaD7j/lkZVBiZHT4ficl5aD09YOy2XnrQVCHdswjAAMTEJGXmaprq6uYnV1Nbf3UugsSsuriDRVDio7xrD2GtapAeC8pXn8+AXnYKZZz12fbtTxYxc8COctzavQcvIL/ek0UK1Wy/6MxPh4KSYh9evxyMbTA62nylVl6BGVl09ERB+RRxWRR30UDUgjj5YPJR78bRkb3LG3roVfxXqdK59euLE1n1CaXp6hcnB5vQ7LHVx9FUKdq6jOTLD2Z4Rllurq6mq267p3JBHXaVh+Kr8iS2xUItJwOfkBeMq5pwfDAcCTzzltZBeNkHz5QWLfQw7yRe0zCsl4aXGcUeIeL8lHjBcij0Ye9WQXQuEgsCB85NGTC6Ue/AH5Ts7KG+pYbDWYVaHW4EAuQixscdr9kCUWIg2vA3jxLS+epvDS8hSa66t/fPQQk9ba2lpu36XQ1gReZxxG4Fwn/QxZaF68UPqnzU1jttkoJMfZVhOnz8/cq47M5TSflPvykCyqC+d5vOXjdEOD4IiIPiKPRh51Mxiaf+TR8qK8Pn8OQg1m5MTg+X67zyRm4LRCPgKqLBzH+85l8khQ43hEoK/P+dMrOxMdOx7b+Yo2TbG2tpbbbV23N+C0j4ewPJmx3Ee1JIuIfbpRD0XLYepYuKwMkr7XsVnG9qDhEwhUj3RaQss7KorkqW84OLynw/cm/4jxQ+TR8eZRq/3xpBV5tHwo8Zu/FOppGVJotYz43rAOw0rKlmOwVNKRvU6gZKWExwSj14reapmvBMtACVVXlylpeT4qKiMlQS2jJweO68lgGOlpvbxrK+1OYVwDh/M6Pv/2yms64Tkoc5m0LUMI5TMKQuFD1n0/XIKyblEQsZ2IPKppjjuPFiJJ+n8iN+87l1HLG3n0xKLUb/64PaxD27YD1pl46bfBGplXenE6HN7SLFIiU0wljSJLwSyeTqeTs3JYAfW3V4dQh1EiZBLicLYyzazVjY0N95idUL2tnCF/Fq/+bL1zOO3YauVrvnr/zsNHsbzRxnSjHpTL8kYbdxxaDpJXSNb2afpgv9lXhQmY2zRE/N7DNNTeCn7geXLS/LhMERGMyKORRz0ZF3JFmiLFIEdFHi0PSvvmL0F4pG6vkT0LSn09gPyZk0parHh2XTsN5+H5rHgWKeddZBWZZcSEqFDHWZaJTjVw57Jjh2x12sbGRjZV4VmgNtVRRGgqN5WB25ZDOqlH1ixfQwrgE7fc4d879vsT37gj2xg1lGeoPCZfWxmpq9I0T4/MvfS4fB7JqV549dfrHkkOPFTKz18R9xGRR/uIPNpHML80zVkLkUfLy6OlHfylSJGm+b2RgEErIhdHOq9aG6GGN0UqSk/JhhWSCY//2ELTlXZKfvrnKS93MlZaJhs7S7HT6aDdbmNtbW1gM1IO78mW8wxZQF4nC8Hqw3HvLW7efwh/dcM3cVSmgJc32vjLG76Br+87dFx56AOEtyPwtqpI03Tg5ACP0BjDCLSozfWh4j1s7LOI7CPGE5FHI4+6aSE8eNPyjYLIoycfSj3ty/AsVGukEGmwo6kppjY6N7R9D22HwN95F3i1WDgel81+e2RnYOX16sqWiZKuOSYbYa2vr+Po0aNYXV3NEVZo9RuXQa12/n48hKC/h3VwL24ufJri6/sP4RsHDuOBc9OYqtdwdKODOw8v32cDjWVcr9cHVqopgfGKwBDJe/IKydN7cHnkFLKO8xbwfRRGxI5E5NHIo8d+ANIu2zXYiTx6cqDUgz+1JqzTeY3pWaOhThaKo+QH9Mky9IrZ82Fg0rG4Wma+zvXVMupvJSu2QLkTmbW6srKS808J+VZwml7H8DoYl9kj7TTtn0UZeqXvgWXlyQDYslzvPHy0fy1JctMV9xbWpo1GI5uyUOLSKTH1wQnp5jDCDj0svHD8qeU/9u246h2xcxF5dLDMO5lHK0mCS/Y8FLsm5nHP6kF84e6v5Y6+DMX3ynBvEXn0xKPUgz9rE2tMnnbgzutt8mmdBchbshYmTQfPCWRrkknP/vjsSU7T8lIS63a7WViOox3e8gOQnb2p17nja309C9Z2pOcNSD2SCpEgE5iVlS3+UTui1iMEfeBwnKIOHCLNYfkqEVn9arUams0m6vV6zmq19tVpDM5H5cL564NrmGxCDxTWDe9hqTKMiIg8Oj48+uQzHo1fetSLsHdqKbt218o+/N7n3otP3fH5yKNjxKOl9fmrJJVMYawBzJcAQEY6tVot+80NqlMB3Dm5A5rPhoVjh2HtEHZMDZOn3Wdl9awzvq5+NqrgnD7721h9dTsFj3jMX4X3oGIfFW9PKi2nWlzso2OdV/2AWC5qjXthvPw92WkcJdxjmWz9YfBBxWmEZGt/tVoNk5OTuaOJjMRM/p5fk1cmaytPBiobJUNNywujD+Iyk1XE9iPy6Pjw6OWnX4rXP+mXsHtyMVeW3ZOLeMPlr8Hlp18aeRTjw6OlHfz10hS9Xn/JPb+K59f8nr+HIWSpeh3Irqu1yBawXeO07RqThqXFq50srKZj13jlnaXvKSgTgv2xFWWWcrvdzm1CqtaoV262yD2Zhqw8zl87nX16Fp4HJZlh4bkeCeBuYBoiCfuu5FWv19FqtTLLVeXL5MCWpFqWSjihOgyQL11jmXB+egA6l23rWlBcEWOEyKPjwaOVJMEvPepFSI99Z1SSClKk+LePfnHhJGbk0Z3Fo6Ud/AF+I4YaVpXUPkexmkLWhcIUhUnQs0BCabBy5Woq9dD7TGBeuTgvIy2zWL1jhzx4pOal79WHO75HTh7RD0NRGwLItq4dZp+FyCpUh3q9jmaziUajgUajMbBiTdvQ00n+tDyKVkBqPO+hwr89nWN5RUT0EXmU4+1UHr1kz0Owd2ppYOBnqCQVnDK1C5fseWi+nog8ulN5tNQ+f8AgGTBhsBWhjeaRUshq8uJ5VgKHtWuesui90PTFoJUxuPllmqYDCq9lVWtJjyUa5iTMnYPz92Q2Cjl79fTS4HryPa+DD+hBPsFg/kXlU6vVrNVms4lms5mbmrA/1gXvQcoE6OVdREpF8B5k/J3JLCIij8ijO51Hd00sBOMydk8u5Pk3n2Bh3Mij5UKpB38mf6+j2HduKP3OFigjpCRMEJ4FwdYKO54WWQtqZXjWDROlEm1fFv2pFO48vHpP/4oOG/fK6HXAUGfxBmQqA6/eKrPQAFDTKMrDK+uonZdJplarodFoYGJiAhMTEzm/JCYtzs8rU5FOcjzvgRV6CPLnaPUrP3lFbA8ij7IsdiaP7ls75DXFAPavHx4og+YbeTRXs5HqfzKixNO+QJqGX+WrkgxbOTSYdt4nxK4xIYQsEU3X6+yeA6+Wi9P1/rzyahyWhWe1ekcVeTJiHK/VEyLtIjLXehSFCYUPyWFYPTQuE1ar1cLU1BRardaAMzbHZ50J6ckwOaoua1x9UxERcW8QeXSwvBqHZVFGHv3Svptw18p+9FL/bNxe2sP3VvbhC3d/LfLomKDUtU7hK4MuEw8pBzsNe6/8OS0g76sChF/HsyLzdwtvPg8hUvGsGI2vvilcfy6fWa1WD96ewKYqdKsBjl/0xs3bqkDDhjqtphUilyKiLiIjT37DBpJe3uyg3Gg0MDU1henp6Wx/KpW3Wpoh67PoQWUI+fd49dPvwxB6KxMxfog8uvN5NAXw367/n0iQDAwAe2kPCRK8+do/yu33F3l0OMrMo+We9j3mq8LOooD/ipo7EeCvBjMlNQVif4J6vQ4A2Z5SHM46na4288pjMGtRw1p4z3G41+uhVqtl9bIymLJWq9XsPEmzQm1LBetgRlrmpGz56b5T3ion7hRs6SqpapxQB+F6sO+OWvsc3q5tbVT6ECxNLGAfbVQamoLSNPS7d48fBGyxTkxMoF6vD1ilFp6ng6ydTdbcvpYP65nJwWTikRnH54cuP4QtDjvLqzwjIoDIo+PCo39357X4tU+/Ga9+5M/l9vm7e/UA3nztH+GTt38uuxZ5dOfzaGkHf2xN2G++lxsoOP4lHM4a2euEXifVNPi1NRNerVbLiInDszWr90MWK4Cc1elZtwCyPK0Mm5ubOYuZO5OloedPcifw5Mn14E7ihRnWSVgGKnsvbpIkuOz0S/FLQmB3rezHm669Bn9357W5drA8itLzrFt+I2AWq21NMDU1lTkq88NS62UPGCaOonJ4Vr9n2XL7eOSmuq8P1yxMed1VIrYJkUfHi0f/7s5r8elvX4eH734Illpz2Ld2CF+850b0KF9OM/LozuXR8g7+kCcta0CDTld4DW/xWLlZsZTcVAEM1ul1Y1JdrWRgMsjVSeIxKTABaZqhdJQ8eHWaWVVMGKHVamqJenmGwITmlZctVibuENlddtqj8F+f+OqBrQd2Ty7g9U/6JfyHT78Zf3fH53Ny0LJ75MDhuQ3tt21AalsTmMWqUwr88APyMuV29crB0Pbl3157hOKG9GTE5ovY4Yg8Wl4erVQq2LVrF1qtFlZXV3H33XcPnH/rlaOXpviXu78WHDxpeSOP7kweLe3gD2RJeEquDatWl/2FCESheXidhJXDs/Y4HbUqlGhCRFFEVhZP62rkNOz4oVBdLY1hpDKsXIyQdRsaICZJgkqS4NWP/LngRqW9tIdXP/Jn8ek7r81ZsqG8PZl5BMb+Kuyfom2rCK0AHEY6RXLSe3zdHnLaVhERQUQeLSWPPvCBD8QjHvEITE5OZvdWVlZw7bXX4rbbbivkUe97KD++Hnl0Z6G0Cz4SbI26Wbmye05Dq9KErEkOD+St2BARsZXDaXoOwCErigmJrWMujyqpltdzOjYCDZGgysCrp3Ysll0RtJN6ndbLR61Fk+3Dd4++Uakn59BvJkK+z4TlxdO3B9x+aZoOTHEdD4aVPxTek6V97kQCi7hviDw6WN6TnUcf+MAH4vGPfzwmJiZy4SYnJ/GkJz0JZ5xxxkC++maN4XGHxyUc1vsdebRcKO2bvxQprA28zsdTC+yvYeCpAEvDvheRkxKKWZys3MMsFAtn051Kiup/w+STk4FYb+xrwnnZlITWR5VYy+vJxrOEUiDn9sDxh3UUrpv38OH8libmC9My7JpcyKUxDEUd3SNA7xgilZG9JSjyI/Hkatf0gaVl5TTUXyVkEWv9IiIij5aLR5MkwSMe8YhcuiyTNE1x6aWX4s4773Tjh7ghBK1DESKPlgslfvOXwGSvlqg2oDnnhixItTb5PpOgXedrrMCe1exZGtwRWfE0fyYrdj7mcJyf+u5wGXkFnVcOb0WU1wm0s221RR6heBom9FDwZJYkCQ6sH8Io2L92aCCtUNqhDhwKZ/Xitlb5hfTRI8ZRrEiPVL3f3nf97Nep3MQVsT2IPFouHt21axcmJycLeWtqagp79+4N8igPvlW+OriPPLpzebS0b/4AIE39Rmclst/W+blThyw2YFCp2DoxpVXFZkuXLcdRkSQJNjc3kST97Q48C4SvcWcwy0VXfekrd43PHUytYE8mo2KYDIqsUC/PL+27GXev7seuiQVUkkG7pZf2cM/qQXxp3025h0uSJNkCkSSQNpejiOSUlDiOR1oqV34QhcoQuu5Zrvrw5a0esrqn+bcYERGMyKPl4dFWqzVSnImJiQEOVBmxfL1BGtctu2b3EXm07Cjtm78thRhseFYg75opkPcaWTsz52WfrCiWjjkB80org1ksHoycVLFDFqSRmpe2dq6+jPIdTJ2WrcxeXpqOpq3wwg27HloZ5xFHCuAtX3h/4Ualb/3C+wCHcBL0bbQQIdk99o/xVi5qm+qDQdtvmL+K6qxXJv2uhOm9NQnJ/djdwjJFjAcij5aLR9fW1lwZKCxcaBDGg25vVa53HYg8Oojy8mip3/wxTDm44Tzrk/9YGdRPhGHhTDE9q07jFlmbFk9/p2k6kIaVjQmTO0KIjHu9HjqdTkZy7XY725DUOl3oTEqui0d8WnaVgxJe6Jr+Dnewfuf87Hf/Bf/5H9+Of3PJC7FncjG7f8/aQbztC3+CT3/n+kwHWCe8NvfSVxJUIvIIiz9VfzzZugPbgroXwWsHrr/qYP97+S3XiO1H5NGTm0f37duH1dXV7M2e136rq6u45557Bu5Zmlx/GzRrfobIozuXR8s7+Euy/wY6ExOKN/pXBfIancOHLBe+H7KUvXB2zQjDIyori5IahxmwyoTUOp0O1tbWsLm5iU6ng9XVVWxsbAyQFx9YbsSp1pm+oSuyhorIYZSBnkKJ4bPf+xf840e+gIt3PRhLrTnsXz+ML++7GT3kTxPQvLyHhsrOtXaddufTEOy+xvPkFpIPy3wUeXgPDq2HTj+xLCIiAEQeRbl4NE1TXH/99Xj84x8f7NvXXXedm6byaIijOa3IozuXR8s7+HPAHTbUoAZTELZ8QmHZAuDX+3zPI0cuk5c/MLgiLqRUqtRKll4avV4PGxsb2d/q6irW19fRbrczGTHBazlNltr5ixRfSXtYmCJwOTS9FMCXD3y9H7iSoIJwm3sWOLcrv+733lqob1Ko3UN5WRyv7kx4o4T15MTkxLri6d/W7/KTV8T9g8ijJzeP3nnnnfjMZz6DRzziEZiamsqur66u4rrrrsMdd9zhytCTn/fbK1vk0Z3Ho+Ud/B2TOTc4O+qqo69n9eiIXjsx0FdWsz44TU4H8JeuWxgjAIaVOVctKq8SH9fRg9W7UumfoVir1TKS6nQ6aLfbOWdotry0k3lW6jAH79B1lSuH8SxDlRN/Vyufwdc5HFtwXr4ha5XTtWOI9OHIvi38MPB0gevhPcRC4TmOld2bNvFkoW0YEZEh8ugAysCjd9xxB+68807s3r0bExMTWF1dxb59+3IDlcijkUeLUN7BnyiXNaB9qnXFBGVnBVpHBfKHgrPfiikHTy2wkqqymMKZMzBbEd4AqFarDZCFt0JNFVytJSa0SqWCiYkJ1Go19Ho91Ot1JEmCdrs9cI6iycN8WrwzNLle2qGKysgoctb1LGGvE2p6IYLhNEJWr35qe3I723mUdiQRkxe3lR6txM7rmh+XkR9IdlZoiND0QWy6oG1juqcy4GtJif1VIrYJkUdLy6NpmuKuu+6CIvJo5NFRUNrBXwIgSQanBdhqZVJimMJbPCY+S8N+Z/klSc6yMzDpsQVjymzlypWdrinJqqXKSmqExHtNeZYsd7xWq5U7GLvb7aLdbmNtbQ1JkqDRaKDT6WTkyXLwiMojLM8K1HqqvDwCD4VTazZkzRWhiNj0kx8IRuqNRgMTExOYmJhAvV7PyUh10BzA7dPablj5+WHJeuHpT2j6SnU4mEZJCStiexF5NPKoyrQIkUd3Do+WdvCXIkWvlz9eyBude2SQpilqtRo6nY772lvTMpgy8zYBFr7b7Wb5sEXJiuqlyRYNl0XLbp/VajW3UsqzgDh8o9EAsLXvkxHWxsYG1tfXsbGxgc3NzVwn5PopoVqaRR1qVCgJepblnj17MDk5ibW1tWxKQ+VRZLFyeqHpDSVZtlJtiqJer6PVamF6ehrT09Oo1WpZ2t4UlMmI27SojCHfEi0jE58ns1D9tP3SNC2xp0rEdiLy6M7mUa2T9zvy6HjyaGkHf0gHOw1bajpqVzLja3rdg1o0A8VxrDbP4tN8Q9c85QyVy6sPl9Ws0YmJCUxPT2ektb6+nq1Ys/CdTidnJYby9Kx2JVqVyTCCs/unn346Lr300pwz88rKCq677jp8+9vfzj0A1FrUchb99urFxGVTFK1WC1NTU5iZmcHk5CQajYY7teXlNazOTHJeeH7weekW6bD2h/wbmMJiRYwLIo9m93YajxaByxZ5dDx5tLyDP0HIutTpBVX0YYrsxfEcQNXvhTvXMItC8/AI0KCr2vTVuqf01WoVANBsNjE5OZlZrWtraznfCPvrdDq5KR+v83tkrIQ9DB7Bn3HGGbjssssGwk5OTuKJT3wiPvOZz+DOO+90ydojfr3ndX6uu/2Zf8rExARmZmYwNzeXIy2bAlLy5PSUKEIy8dqs6MGgYfVBUfQQo18o8YxFxP2EyKM7g0dZJqHv3sBPw2r6kUd3Do/umMEf4DcsKwArlIYLgYnBrGBv2T6Tltep2HoYpWNrWkqASlgWJ6TEZrma9WX+KioTS8dIy4grND3ilbsozDACv/TSS918rC6PeMQj8J3vfCd3vSi9Yde8tqpWq6jVamg2m5iensbCwgIWFxcxOzuLiYmJIGlxGjylNKysRUSqD9xhbwo4LLeb5l+plNdROeL+ReTRcvOo3g8N5kZtw8ijO5NHSz34Yx3Q/ZY8pfa+ex2Bw/AfW2lqsXkd3657Fq6Wx36HOrUSXahDahmYVNkK63a7ma+Okp+RMhOXpe35S/ADIVRGtW69+3v27MlN9Xr1m5qawu7du7Md7IeR5ChEYX/sp9JoNDA5OYm5uTns2rULS0tLmJ2dRavVGjjgXVe4mUyMuLw21bbydId/c3h9OBXpTJ6oRn/4RIwPIo8Ohikzj3p1Z87gT70fQuTRncej5R38Jf1Gse0D2Irw/FWAsP8IMHisECufjv41nV6vl00L6Ctjj/B4OoXT9MKrJQT0SZpXrVkZrDxsrdo9c0rm1UwmNy1HmvaX2TN5mSOzkohnXYWIWDshsDW1OwparZZruWnao5JZmqa5vbrMsp+ensbi4iJ2796NpaWlnJMyt522o7URb1EQdFi232l/z6kQgXtl599FcuY0tz7LO10RsY2IPLrjeFSvK0KDncij48Wj5R38AUjTfgezPZi2rg+uXmMCSJIkc841eA3PxMD7U2mHYILgvI3EDByXCcXy9f48peOyKSkYsXB43ZvL6sukZeRXq9VQq9WwsrLiWl7dbndgeXxRJ1OiCZFJkiRYXV1101DY9gqhvEPWoGdJW1gjLFuRNjU1hcXFxcxSta0JTEY6+OSVffybiV/LmyTJlhKLrLxwWk/WH9Y5jqv1tPbub9FRUtaK2FZEHt1ZPDrAMdR+XvhQ3pFHdzaPlnjwN2iV8P5NXsNxZ9MDrdXCYqvM4rLzLoBMCYosiFBnZTIMXWMfGQBZmc1qtPRVYa18vJKKrV67Zn4XJjNzZF5eXsbRo0dx5MiRnDXLBMREFiIbrhe3hXcPAO6++26srKxgcnLSJao07R9a7nVgrhuXhcMZuN2YsJrNJqampjA/P489e/Zgz549mJubw9TUVM5HxcBvKbgdePVaEWly+dUJPTTVwWkpmYXkxvLPHp5uyhHjhcijO41HGZwel19X2VrYyKN9eXphdxKPlnjwN2jRAcisKmDQYuTw1vE9MlFrMURKfI+VR60InZIwhFYwhc7JNLK1qQfLX61eroeW38LW6/WsDEb2thdTs9lEq9VCvV7P7WSfJAnW1tYAAJ1OJyfTos7FfyxTJbw0TfG5z30Ol19++cADx8Jcd911LmExOA+v7nrfpidsimJpaQl79+7Frl27MsJqNpuo1+uZP4u1k2cZm0zt0HevnbgcJgPP2d2Tp9bVu65vFTR8iQ3WiG1F5NGdxqOh+AYvn8ij48ej5R38pdl/A1ardWr1PbFPa1Dd4V0tGgvPlo12BiYH7WBFhGdlVevSvqsFZGUMdTwvfa9DW/r2yn1zczPnQ2fpsRVnfzatYdOunU4nV2ePUPhhEgKHuf322/GpT30Kj370owcOLb/22mtxxx13uINCqyuTCstE/yxcpVJBo9HAzMwMZmZmsLi4iD179uCUU07B0tISJicn0Ww2s7rzlFOIVHhqR4958spk7W0PUn74eSsiLb6mF8rHR0kZK2J7EXl0R/KoJ0cvbChu5NGdz6PlHfwBUMFbg7GFpR2aG9fObFSlKFJ6DucRoToIe1aI/uZ0OH39br+1Y3uWr30q8XGduJ6NRiPzqTAry1ZrGWGxTwv755nlz8vylbCKLE2t7+23347bb78de/fuzU74uPvuu916DLNoLQzrg5G2bUMwMTGBxcVFLC4uYu/evdizZw8WFxcxPT2dHeukD0EmGstb5c6OylxG+9T25ykoffNQZKmG5BsKGxGRR+RRYOfxKA+oQ3LwrkceDcuFw5Yd5R38JcdeufKlpO+Iaw2kis8dyTqZWjEcl6+r74AqABMUkLdIOU8lPS5baOqBCa9IQRWav1rlZiVVKls7sRtpAcimL9iKVRlUKhV0Op3sDEaVu3Y+u6bkzfHs+ve+972BB4gHlpsHfXCZszFvPHrqqadiYWEBu3btwuLiYjZFwZaqTSOFDppXAjJZssO7R0r2x35L+vAZ1vZK9F4eHCdoyEaMFyKPBsuhZfLqb/dOZh49ngFg5NHx4dHyDv62jiTPNQa/Ti7qAPbJ1pV96iovUzSzJkK7tXsdx1NUzxqx+EZqHF8tZS4T5+0pr057aF4mM7biGo3GQPyJiYmMrDY2NrKd65nYzXcFwICVxvXhMus1vq514XJrHJ3CUVI1wrGHmh0uPjs7i8XFRezatQunnnoq5ubmBpyS+e2HyVKtfQW3JctIy+a9seC/kKOyXvPS9eTE94GktJuTRmwnIo9y3pFHI4+G5KYoO4+WePDXt1h1RO5ZBh6KLCC7p4qnHZ/zZcKzcvXLOtgBvfS4XF7HCFkuWicOp1MWuqUCE5ftX2Vp9Xo9NJvNzCK1lW2NRiOzUFVu3GGLZO49XEJh9TrLiPP1fHl46wVbiWd+Kbt27cLu3buxe/duTE1NZb4pIUdwy1OnK7zyhkjW0rC0uR34DYrVw5NJESmpfCMiihB5NFynyKORR3cqj5Z28GfN5TWcRyxKbBwuS1MsI73HHZzDKBHxd14Jp/fMKtGycvk4LLCl7HyOpBIqXx+m2GodW0c0J2Ym7m63m5EV79NkJOcRlpKS16k9y2wYEXhp8G8mBLNW7WDx6elpzM3NYWFhAUtLS1haWsLi4iLm5uaylWg2RWHyVmJhYmTd0HIUvX3g8pncvT3TVC7eg3BU5MOnSLHzCC3i+BB5NPKoytx+Rx71sVN4tLSDP9uZXqHWkhILX/PCFBEbW0TD0rLvpszq08BlDXXSEGmFiIive9sjeFazlpGvW5zNzc1spZoRl73Kr9VqOZ8M3bdKLfgiEhqVuOxaESEbGbCVOjs7i/n5+WzTUSMr23yUd6f3LF9uP81bH0qexa6Ew38sc5U/Xx9mpXL9VZ7aHhERkUcdkUQezRB5dOfyaHkHfwRP8Y1gQg1VZD2pNemRgV63NJQUmAiKlJSve/e9/LTsFobrpqTI6XB+3ut3K5NNVXikZRahrfhjXx6vTiGEyMsrt5YvZA3yoeLz8/MZUS0tLWFhYSEjq8nJSTQaDQD9szuVoIYRFZdPiZv/vHqHpnY0XChMSD/0er4NyuurEnH/IPLoycWj1WqCBz0oxcwMcPBQgptuTAFEHo08uj0o7eAvTYFeL80RS0jxvRG/kgMTVZFVoNaEKoSmV+TQ6pXby5utVFbyEFF51g77dLDMeKWal3+aprk9qnjLAiMtnhbg36HtEUIYZpFpna38+mfTKK1WCzMzM9i1axf27t2L3bt3Z2Q1MzODycnJrB71en3gwcL5hPJVmadp36ndZG1/ugot9ODi/JR4+HeRruo9DVepJCgpZ0VsIyKPnpw8evHFXfzoMzewsNAfZBw4UMGfvL+H664rFEXk0cijI6G0gz8cU4hut4skSXI7rZulGLI6rMFZuSwcd26gvyGpbTdggxpWSrb6torW92lgZeTy2TXehT5EXmw5KmEqCSm5cTiGdaBGo4Fer3+YupVRrfUkSTIL0Dq6wYgiTbf8XDqdTq7+lqY+MIqb1w+jVqmSph2vZKvQdu3ahVNOOQWnn356tvVAq9XKzpjUMulbS20HzsfiecSiFmtWL2xxBd9T/yfWQ24HHUzr9BLnw07PWrat7yVlrIjtReTRk45HH3z+Cp773KMDTTU/D7zyVRW84+1bA8DIo5FH7wtKO/hLkVdsc/JUK5E7rIXlMx+NjHAsvWq1mjtA2uJpZ7HwRn72yQpn8KxBC8cWjXUGfn3NU6lGzEpWVpbNzc3MyZYJyw4RZ6db7iRWXquHXbOy8JREvV7H5ORkZsHa0TtcfpW/Pgw8q5jrpfe930xUbHWaX4odL/TABz4Qp512Gvbs2YOJiQk0m83M6rZ0Q34pLCe1Ur0HDcPajWXqIU37O9hzmgNklw6uavRkFLofyj9ivBF59OTi0TTdxBVXHDxWvnxbVSoJer0Uz39BBddfvwkdeEQejTx6PCjt4A9IkSSDJMId0YhAG9RG/9qQ1tGZ9DjtTqeTveHSV/6cP1srvFmqwfL3iFCtRT6E3AhHfSEsLB+WzXUMWV6q4Eya+mrdrtkKNftk8rD68nWWi+XN6TLUutY3DlZ/dii2/aZsz6mpqSksLCxgYWEBe/fuxd69e7G0tITp6ekcuVm7mKx5SwKF3eO3INwW3O7qrM31yOorbzC4/ixvbbNRp9G9Nxxe+iVdpBaxrYg8ejLx6ANPa2N2th9HUakkWFoCzj8/wU039dNlRB6NPDoKSjv4S5D3BWEiMYVS6yIXnyw2BhMfdxpWTM8q9awMTdfS0gEQ39MpD06PrWtVaJYBlxtAjvg8UuByhKw2Iyc+n5JJy6x9s2q9DUw9eXhlUTK3Tz0ns9FoZFMTtufU/Pw8du3ahfn5+cwvxRyRbVqFp1uMDFjWSpbed2sHfjvAVj/v8K/1YxmErFAv/DCrNBTeu+bdixg/RB49uXh0Zjo88GPMzydIksijVvfIo8eP0g7+DEwW2umAQYdNIwPAfw2u39la1LCmcKacSnRKOlwGLjun51mXFtbrBFo3A+etZef8LT+2sLRMPMXBG32qX4dZdt1uNzuqiKdNQg8KrQfLT1fC2XTExMRERlbT09OYmZnJtiCYn5/H1NRU5pdiJGtl1HJ7Mgp1ao84tC2YtIqIRR+KXt5FD0HOW+8NQ1Jud5WIbUbk0ZODR1dX6wP19HDkSAVJEnmUw0cePT6UevDnEQa/UlZSsE9VMq/DaMN7PgqqdExUSj7cKdTS9erDZeHvntWqAyrukOqLEyLRkJy0g9s1/bP4tl9Vp9PJ+QP25ZLi3HMTzM0Bhw4BN9+cIE0HLXkjGCMcW3U2NTWV7Sk1PT2dkZOtPLPd5XkjVSMs9dXhfDz/JvtTGXA7sa6wzL1pi1CbFr1R4fuhtx6ahurfKCQWMb6IPHry8Oh3vzuN5eUapqe78MZNaQocPAjcckv4LV/kUQxcjzw6iNIO/rQZVCm1gUON6lkgFo/DmwJ6cVjBPEXR62ppFCkXW53VatU99JvT5e+haxpHO0EISjBMYlZGI219+9fr9fDwS3p43vOAxUUAx84UPXAA+MAHUnzxC/ljemxqxHaUt8PDbZ8pIy2zXicnJ7PvuneWWqlqnYYeNEVtylNjilHJytMB+86ExZ9cLq99PAtay34s9ED8iPFD5NGTjUdr+OxnT8cVV9x6zCjmOmx9/u//nRz7Pigr5s/Io5FHi1DawV9C/wODSmDXPJ+S/Juo8Ctjtk5MCa0DFFkBWhYlUr7GCFkuXD6ziJSQLIx1Jl3BZp+edcsdg98AeBaSheF7Oq1QqVSwubmJdrudvf17+MN7eOm/GqzfwgLwilckuPpdCb785f5u97bibGpqKjtKyHaVt60GeNWZ+a00m80B2TIZhvTEg5KzykzDpWmabeSqxOW9FRgF+gBW8tJwSZJk2yB46BMxSrs5acT2IfLoycejd96xG5/8RB2PfsytmJ7uZHkcOgT86Qdr+OIXtmZQFCYLXsQReXQwr8ijWyjt4M/gjc6LrAmgr0BGXiE/FFYYI6yQdWoOq71eL/PjsDw8JVVLpKgjcYfQVXechzokszx0GofrySTIf6EFG7b03upt9WV/EgBot9vY2NjA5mYHz/1J20JC22/Lon3eT/Vw88111OvN3KHh5nBsPihzc3OZD4oej2Rkxw8JKz9P8XgPC9UDJQiNa9+tnZmweMsBJXnWO07Le1h4RFd0Lbsu4UJ6tbMmMSLuCyKPnlw8+r3vPQB//VenYmbme+j1DuJ7d63jxq910e32UKlsuvKwAbWt3I08Gnm0CKUe/BVZILYnk7esOzTy9z5Z6XXxAk9zaue2Jf6mzGwxmRKr8qofhVqSqtwch8vAsuGOw2kadFWbEhcTVJr291KysttSfx74meW5sbGB9fV1nHnmOhYWitpx6w3gBRc2cPddcxlZmXU6Pz+f7SQ/MTGBVquVbZLKsjPyDFmsSgh2j3+zjFSW9mf7kJl8TI6chk0reXnZJ8ue81Sy8R5gRWCZeA/OLTkA5aWtiO1E5NGTl0dXVmq4554m9u/fj1ptBWnayfLQtjDfvlarlRv0RR5FLjy3URF2Oo+Wd/CX9JURyDdmSFkYvd7g3lUhRWZlUqXhDu4pnSplCJaGZ1l55JUThVMHq5/npMzf1U+DycrCstNtp9PJtnEBkBEHk475mLTbbaytrWFxaTlYb8YDHjCFWvUB2bmRS0tLmJubG5iWsC0K1Oozq9lrM3U092Rv8i8iBt6OgB9mLFvepkDbXdtOrU1PtyyeWqEhXeP6Axh4eBfpYcSYIfKom8fJyKO2ATe/HbVy28DPFm3YoC/yaOTREEo7+EsQdjRO0zR3bI7XSGZFclwgvweUxeUOoApQRIxKpPpKOjSdwmlrXpym1wG4zF55OBx3PM/C5e/2Kp5Ji/387ODvmZmZjFzW1tZw9OhRtDeOADhSWFcAmJw8FWeddRYWFxdze0vZRqi8txQ7NGudeQGKV3+10JlE1bJl2XpWrd5nmakV6xGYEpZ3jT+1TFo34Jgd6hCcFy9ivBF5tDw82ul0kCRJ7q2hDfxsc+aFhQXs2rULu3btijwaebQQpR38pfQ/UGylhixIncpgJVZryAvHlh5bfp7C26dneQD5qYokSXKWHadhK9U8cFksDT4qqciCUXnZNIX9dTqdbBrXSMgsfjuqyByKbdpgZmYG09PTOHRoN44c2YeZmU14xlKaAuvrE5iZ/gEsLu7KVqDx+ZdGXCwjri/Xg+upJBQidG+Kg6en9LtBrVXvurahZ8F6lqjm5z1kvficX4igtqKV23KNuO+IPDqIk5VHu90uarVatpDO5NBoNLKtW0499VTs3r0bi4uLkUcjjxaitIM/IEWvl7c2+Cidotezpuj8qlmtGGBQkdUqZoVh4uIwRoLcyVkpWZEtPp+ZyflavTRvLbddV9LTjqRlsfRtjz61Um36odPpZOVjq3N6ehrT09NZW9gxQZubm/iHfzijcPuCA/t/GKeffmbmhNxsNgfOjeSpCDufk+tjYdQHxH6rX4/Xbmypc3zvIeY9gJjoGZw+kyx/alhuJ21Dbq+iuCGiTJKyrlGL2F5EHuV7JzuP1mo1bGxsZIO/arWavfHbvXs3TjvttGwVb+TRyKNFKPHgrz+IYMU2/wDdSoAVxDqV3mMCBAbJQpXXwnB4/bP0jIyYiOx8S0uf09L8uRxsmXFeRsRarr68wn45nC53ZrvX7XaxsbFxbPXuVodkB2PbKd6IxvxVJiYm0O12cc/dp+HjH6visT94W277gnZ7CgcPPg2zs0/I4ps/Cq8K5JV/Jjf71IeM+qtYnb03FEr2et/isT7V6/WBNrb21RVqnK9a2l7bhspg31Wn9SBzrVvIFykiwhB5tDw8amHtTWSr1cLc3Bz27NmDvXv3Yvfu3ZFHI4+OhFIP/gyechjYX0HD8X3PwvMaWK0h+2QyU2XWNE2B2OlVw+l37Vwha4eJVcuhcazcFo/9K8xKtU7Ybrexvr6eWZxsrZqDcqvVyk74sPxs5VqSJNi37wx8+P+cgVNPPYr5hQqajd2YmHgY5ue3LNVGo5H5pPD0jZGxbcFgDyUmK5VJUTurHO23ESqTihIMtxsTu6XNDsralqE24/bx9EV1gb9zW3J81jNNb+tvoAgRY47Io/nynaw8agPDRqOBmZkZLC4uYvfu3dlxbJFHI4+OgvIO/tJBclAldqMJcWijcziPBLXzW57q4MzxNG0mLe58lk4IHinqPc+/Rb9rp+RTOPh1uxGW+qkYKpVKZrHaflHmS8MOybaNAQA0Gg0ADwTSeUxNbW0/YKvQ+IBz9d3R9tKpIZWN92BhcLt54ZW0VFeY8DVdlqc+/ELE5ZXXa+cQ+E3DKHHKOlURsc2IPOreKwOPTk9PZ6t6I48Oti3nEXl0EKUd/KXwO6+BlVqhCuimT4oZUtJQGqyYNoXihdcOptc8q6nIEjKopcYWFoPrZiTNFpd1QLNW2+12ZtUByCxW2z6ApxTYX8PIrVarZY7JTFh6bmSo3tYGTCKeo3CaphlRetaoyjpk3XmWpYe8JZjf0iGko0owVhfFMPINQR+SgDhPl5m1IrYNkUcjj0YeDWMn82hpB3/AoMJ5RBWyFFRhgrmIcjFxhaxDheZtis1Whn0PKTh/9zqaWm+qtPpaXTu9djQjr263m53U0el0srhGRHYUkFmrRlhmtQLIfE7smKG5ublsh3lbheZZhtpOVnbeV8zaIWQBhtqH5c1t68nfI3xtD20bnrIIYRSd4XDa7ioXL2443aTUxBWxXYg8Gnm03w6RR8eHR8s7+EuBNM13QG5IzxrUhrZw2vk9hfE6QYggR7EumAxHISy1vKxMnoKqtespuhePyYsJq91uo9Pp5CxvtlZ5RZlNb9geVha2Uqlkxw1NTU1hcnIy82FRUvXq78lTSSfURh65szxGkaNXpiIrla1D1UFPv7y8Q/f1QepB9XiwXsGoEeOEyKORRyOPunE5TCjtMvNoeQd/Sd6PABhUWnVOVQXn++YfoeShnSrUcTxy8KxSJY8i52pVenYA9siY0+Qyex2W81JLyxyUeWWaHbJt8c0BWY8IMqJj0rLw5tTM50nadI6VSR2+vXbVNuV2MvnwlAvXk9NTGXnTBcOIkj/Z16doqoLzz00hyH3OLwSuk6e3Ws6+7thfxFgj8miujJFHI4+OC4+WdvBXSSrZGbKmLNZx7M+UUC0LAKjX69l3i88KrMTgLTNnPxQmKSURJhNbPm+dSEmL4/KKM7vnLb/XDmL10C0RQuVi60YJzPakWl9fz1mgTFh2RJDFN4KzsEZavJqN49inTkPYyjR9KLHMTD4mAw1vPiveQ4U7OfvomIVtFqiFZ4LnPPghxWd4MrwHqxIgt4nG5TLrg5LbkB9wSlxc1zTtbb3yiRhrRB6NPBp5dDx5tLSDv9T+HWss68zcoEYSpoCqHGyhqFXE6Vj6ap3Yd4vPr9y5w1p4IwK1mLTMatHYd85f/Sq0E/F3JWCOY2ViK6vX6+9Htba2ltuTqlKpZATE1mqapplPi/m1MJHU6/XcajSWodXFDpFn3xWVcbVaxebmZk62loZ2bo2vsvaIhH/rA4MJg7eBMBhp8bSFxvV0TNuX77MeKOkqKYXiq0/VVrwK4OhExHgh8mjk0cij48mj5R38pSl6m70BBVKFVWLyLDgd5XMHyOVHFqdaiwwmGo8stcMo+XDaSnBMLEpKTEIABjZo5fQtDSMX7mydTgdra2tYWVnJdqLnKRAmrUajkVuZZlMbvEWB7UJv/il8XijLkssXIhJ+WCgxqLxZHjYdZXEZTB4c135b/JAFqO1re3h5+qj15YeV5uvBIyuua+i+X99yWqwR24fIo5FHI4+OJ4+WdvBnUIXjRlOrEvBfF/Mrbw3PYbVDsTJyfK9set2DZ2Fp/hqe47AcmJi4jqzcTID2adar7UllfidpmmYbhzabTUxMTGSbkSZJkjk281YGALIpJQtvPireyrQiXxEO5227oCRhsvPS9OTtPUw0nMHzgWLZMWl7Dywtg5JsSAb829OtUHz/elpWzoq4HxB5NPIo1z/y6M7n0ZIP/vo+FZ6SqRXC9+y6KZw6KjNxWKOztcphTNnV4uVyqdJ4fihqqbKyFVkfXBclVrZ67bfVmadQ7J45KNtZlGZ9mSzY2dj8TcxK06kKs1Ytjvm18D5WLE+FkojJVsmZZVFEOvoGgD91lZyVifVCSV/b0+Sn0xWh9tOHXyhtLdNQXXDysPt92blJRIwlIo9GHo08qnnsdB4t7eAvTfu+AQa2PD1LzsAdgP00+IxIj2yY4Pg3h+eOZgqvK6a4E6lfhubFZVbl5rw1PsPy4M7kWaxGVOaj0m63c/4kSkDmBMx7WGkcPrPS9rAKPTi4DVWeSsRMUGylVioVdLvdATKyuuvUFufDbeRNhbADOB+RxDLlh4DqkdaLyXeYZV304OVPjccIkV3E+CLyaOTRyKPjyaOlHfwZWFlMcQ2sJEoYwOD2ALwFAFuTBrbsOG/Ni/NTq4ytPw3r1UvTVULmTmBOvKrgHmnrNd6Pam1tLfNRMZkmydaZkpOTkzkCMiuNtzJgAqnVarnzKlWuXBZvRR5f43YOWXjed159qGRlRKbXWDf4TQaX08Kxn4/5+ngk4+kj6wfnmcVPEiD196PiB1mo/p6u7BTyitg+RB6NPBp5dLx4tLSDv2NqMKDQTChmVbCFadaG/QaQ6wBF0Ff/hpSUyspQpJzBOlEZPEvKOo9HzBzePjmc2yGoXOyfYv4mevi4bS46MTGRWatGWmaxMvGbhWsOykpaVka2JNk69CzxkGWnDy+vfbi+2haaj8qTp0k8ErR2t6ketWK5biFC0/IBW3o+4GUSsE5VD4vCFqhhxBgh8mg/TuTRyKPjxKOlHfwZWJn09b1adKxc3mtrr0OzxabWAYMVxvLTdDQfz5KxOnFc/uQOYwh1SG9VFstApy46nQ7W19ezg8fZyZkPHm+1WrmVaZ1OJ0dyVp9arZatTuPNSLnOZvUySamVqvXVtub2Zail6cEjIE9+nnMy14NJi2VaRFBe+46CIstTdU3DHU8+EeODyKORR7l9GZFHdyaP+mfBlAApBv0EPAuHLQXuEGpVaidQZS9SELd8pIxqrWheWgbvd4islCi1k3vl41fsTGB89qTdt7z5/ElbmZamae7ooiKnZrVWuWNbu+j0hMrMIwAAuXj68ChqL5axEpHKjqcjtCyclhdGCZHjhMoWuh6ysrXsoYdr/16Ckm5MH7GNiDwaedQQeXS8eLTkb/7yr6g90mJog6rV4XX6os7P97MSOQSiHYnz03ih8nmrszS+dy3UEdjPwhyUzfLk1Wm2LYGdP1mv13OOz3wMkREdx/F2r9c6hmQYqgPXUy1OvsfyYvlpXvyn8mRHZ41jUy28NYE3nTIsX62bxgmRMdfPS2PYvYiILUQe5fiRRyOPchrD7pUVpR78palvaRjUQrRrpozdbjd3jcnBIzIP2vmURO2el0+/HsWvkC1OkdVmUP8UVni7xoRlBKU+KjpVYccQ8fYCdmwRx7Fy2U705tfiOX6zXDw/IC6/ylqtX88XJ9QeKttQG7BuWVk0T5YlH0mk+jDqw1TLXkRAw9IbNXzEeCPyaORR+x15dLQ67QSUevAXet1qjWV+EN5UhK2iYsVky4aVVJXVs4zMumMF1tforMhWLl6yX2SZWR0sjpGGEaC+5ufOanH0Vbp9ttttrK6uYmVlJeenYmnbhqS2Mg3AAGHpZqSNRgNTU1OYnJzMys0bmap8Wf4hGXhWv8lb683t4snWfqvvjAe1Qi2urgg0fx/bnFW3phj28PPan9uwqIzHT1Apyro5acQ2Y4fxaLeX4obNBAeRYAHABZUEtcijkUcjj+ZQ6sFfgrxCA3kFa7fbWWfmTm0KYJ3ZrrGSmTVbq9VQq9UGDvfu9fobmtq1TqczQFJqdXlEop3NI0MjKq2vwQiIrWEmR0uLV5ElSZJtSbC6upoRFh/MbdsSTE9PY2JiIpOXrWhT3xbL33avt2OL+GEA5J2Idb8nb8sA/l2pVNDpdDLC52OOTK78ALG2UgJUEuDjh5gE9O2CZ1nalA/vd8b5WDqatkdIrAuchhKTZ+Uz2OrVNx471ZqNOH7sJB79h06Cd7dr2E8j2iVUcGW9jSc0ksijkUcjjx5DaQd/puyskKroGp5h1h5bdmZN2St660BMBEB+ZRzHZwVUHwf+bWVuNBpZPYBBi4stUVVi/c5KzyTF19Q/pd1uY319HWtrawOWarVazZyMjXzM4rR02LeFpzd4TyoeuHJbqez44WHy4zrwAeRM4CoX3YjV0mIZK5GoXvB17vRcbkvLIyklSE6D21n1yXxf7L43zcZ6pqsQPWJjWPplJqyI7cVO4tFPb/Tw2+3BR9p+AL/TaaBW6+FxlcijkUcjjwIlHvwl6L+mBwZfTZtyq6IZvNflPLXBcZUAszIQOZlCcKe3zqNpsEXlKZHn16FWh2dxWT25zpy/dS4+dqjT6WBlZQWrq6vZa/YkSbIpCiMf3VXe/FzM14XrzX4tXv3U4tM6FtVH72k4k5HGUR8klak6UXvlUuuSH2r8doDrzA/TENFaOro5bsi61AdtSD4q37zsy++wHHHfsVN4tLPZw7vb9axWWksgxdXrFTx2uodqJfKo3tNwkUfz2Ik8WtrBHxLfJy677TSmKo2nDJ5loUoXisP3+DoTKpdNnVq5TBpPFXkUqCVtxMV7Spm1aj4nQP44IduMtF6v52RtRxCxUzOvTOMzK7neKncmfiUhjsPXdArI7hn5KAlZnThNzTdk7bEvi7a1kY2WwdPFUP20jnpvGDzi8uIP6t/IWUTsZOwQHv1KN8H+tEipE+xLga9uApc0Io9a/pFH+2HHjUfLO/hL/YbXwRNf4++qsBbHlNlTJK8zKJExSbHyahm8Py6XhtXrrkgkT2DwAHKeruCzJz2rc2JiYsBatWmKkI+KkZ0dPG472LN8PQvWysqDXH2AWBiWp/epg2bO2757U1wqS48wvbAs+5CFPgxF+mFpeJbu8SAfvsSsFbF92CE8erBw4NfHgfDRrwNlZEQejTzq5VFmHi3v4A8AkA6QDv/2LFILx+HVEvFG/9pp9HWyvpXjdL3pDrvHPhTs46dh2UoLDQa9N4lMVkxYtgu9kRavMms0Gtn2AnyckMGmKdipmUmLd6H3rELPqtJ20HY1eMTH6XE+HgGGrE7NQ8vGnx5hFFmrHuEV1WFYXl4ZQ3lyWoMBg0lEjBXKz6Nz6eZINZ1HD71esVEdeTTyaChtP2AwiZMapR38pQBSsVq1Yxj0mmf9sBJ4lqYqCftDWDjOX69bGh6helay17nZAVk7qcaxP/ZR4akKIyybdlAHZbNWeTNSI2DewJQde21VmxEd18+r1yhWo6YRIj1uN7VSPfmz7NixOPSg88Ay9qxVr06htuJroby4zFoGLWtRP4iIMOwUHn1o0sMSesdW+XpP4xS7EuCCShp5FJFHI4+WePAHOpYIyDt8Asj5LXBD8SBL4+p3C2O/Nzc3s9VrnrLxSioui6XrvdXTDqMdL7Q8XpXRCMdIxCxT+25/dvTQ2toaVlZWskPEkyTJ7UBvu8nzxqIcf2NjI7e5K+9Gz1sTcF1YxuaszXLwOhR3UiZOlo2StNcWLCsLp21iaXvlKCI1Ji29FyJMDaMPPI/cOA0tq0dcoXrsBOKK2C7sDB6tVyt4adLFG9p1bA1p+f5W+i9tdFFNIo9GHo08CpR68LflbKmKoY3OiuItHddX6jq9an9GBqYonK5aPZ7jLf/mMiqxsYLq8n4maSVNHUByOIurPirmoGydvNlsZhuK8lSFdRCLu7q6mpEdgNw+VrOzswOWLpePHy7agXjFn7clhIUNOSMzeEUdt73JWa0+BuuHyU7z6vV6OUte20/bXfWSyzHMqlSdLWp71SFFTgfLzV0R24Kdw6OPq6T4VXTx7k4N+ynKrgR42USKx9UqkUcjj+bSGmceLe3g75iaDCgmW6xslRjsOvvXeY2snYSvhdLVzslpAb41bFabhbfBnsUzYrCOw7+tbGqBexac7UdlG5Ea8ZjVyaQ1PT2d7S1l6Rlp2Z5WbC1VKlt7bdkmprY6LWSlqeXOBONZt/zJ8e2aTntYXPajYcK39DySUQJhXxdGvV7PpoEsDk8NaT5aB+8+v0HhqamQTDwyteuePufLMhAtYgyx03j0cfUUj6l1cEMvwYEesJCkuLAK1CpJ5usXeTQfP/LoePJoaQd/qRxGrtOn3qiflUutgCTp73DOiqgdz3M6DiGkUF5nZiVj4lWLRK03K6OloVMw1onMP4UJq9PpIE3TbC8qIyzbk4rz2tzcxMbGRnZ0ka1qMx8VXtXmkZbK03tAsMXK9WaZeySt8uPvaimrjhQ5KYdIRu+ZfJjE+E/r7JEkl1lJz2tnzypWOes9qYlzLWLcsBN5tJokuLiaIqlt+f9tDVQjj0YeHWzncebR0g7+trYo6A+O1EJL03TgLZpBG98sE9swdCArsYQ8nxO1YO2aZ+1YOuyroXlxOVVx1VLRqQn+bn+dTid3/JCtTDPCmZycxNTUlDtNwX4uR48ezZyUrRy8kalNU3jTEV7nVRnxmwSun3b0EGExdEpdZcm/1a9Jy8kEpxYhy4inlXyyQC5N/h7SFwtT9JuveZZqUVkixhiRR7NrkUcjj/K1nc6j5R38AbDpCrNwzBI1sCKGRvnaoPpdlYgtMbVAVGm8Mmk4JqAiggXyU8IcXztJmqbZ7vNGWBsbG1hfX8f6+vrANAWfOcmExXnYVIXFtXLzlgaTk5MDZRwGazsmK+8BwPXkNuOwTEAhSznUTh5psZw9stV2NuLSNuQ6KIZbloMyYJ309EHTV3I8llphXhHjhMijkUcjj3Ja48Cj5R38JUCl0rcYPGIxeKRlK7NY6VkZ2deFr3mEwpZGSAk9i8nKwOCDub3O4Skup8t7VPV6vcw/xf7MWu31erlVZTZNYRuR8oPArNW1tTW0u13cObuE1UYTM5sdPKi9isnJySyuxbcyJUmSESTLhd8ScB0sT3YEVr8YthTtGk+Vsx5wO2h7cXh27g45NXuEwWSlviuhh57lxzrJU0PeA5Zlx2lrGbW+Hglv/a6U22ElYnsQefSE8KgNKE3O5usXeTTy6PcLpR38JUiQJJWBKQlVFGtQveY1vm7u6VlCei0rD1nOngXrKVloeiPkD6OKzp/8mpw70fr6OlZWVnD06NFsqqLb7aJS2dpI1AiLtxXQ1/JGfl9sTuP/PeoSrDQnsjLNtNfxE8t344yJRu7QciZ+lTmnzw8G7eDcDl5dVYZeG3Abe6vmKpUKOp0OkiTJpqu8B4YSgDeF4rWNfedy6j1Lz3uwcXh9wLJ1rHG0TH7a5bVaI7YHkUe//zy6vr6eycji8xFwkUcjj34/UNrB3xbyo321JDxHYQMrITDonBzMUSwJtmg5XbZ+OJ5niXgWhpaDFZA7ppVb/STMymTnZCOdJOkfOG6Wqu5Ab+nYtgTXVlr40FnnDJR9ud7ENYunY6myiqfSuZVcXyb6kLz4OpMUy0Jlo87QJm9+E2Bpew8SrqsSZIj4tG7cDvpQ0Xz5QeDpgdfuobcknkyKwDLuxymnxRqx3Yg8+v3iUdsQmhd52Fs/8xWsRx6NPPp9QGkHfylS9Hp9clLl1ZG6gZWZO5MqUoi4isKokvN3Lz0l1pCyj1ImtmDMP8Ws1ZWVFaytrWWr0mz3eLNWeT8p7rSZc/LqKv7PngdbIfKZJwmQpvijdBI/5MhfHZa5ncw6ZqvVm2piYuM0PdJRWatMQzqixKkEFJI7O4Ozhe61mZLdKNdDehxK24M+SLaulZWyIrYTkUfz+d6fPLq6uprFN86zk0BsyrfZbA7UJfJo5NH7A2GT7mRHmvcVUEuBG4qtEe4U2qEsPKfBlpV2Pk7fyqL3PSjBct5F6Xl5qsLzBqRmqa6uruY2ITX/lJmZGUxPT6PZbA6sLON0bkQNy41m2LchSbAPCb66OVjOUL35M7Raz7PumLC8B5XK3iMrAJk1vrm5GZxm0nS1bEpWofgch8unbReKr7rI6Xhy4Dy8twMRERkij37feNR8BUNTvhY/1N+9ekcejTx6b1HaN39byI/oQ8pi8DqNFyaEUIdka0e/exYR5+NZUd53i+ORGncYnqZYWVkJEtb09DRmZmayaQadpuB0DvRGU5ODaX4/Kc8S9Opfq9UGVnd5RGO/2dJn+Xptou1m8bltPCs2RBzq2MyEpVMWx0MQng4XPQC9tLUeRQ/ZiIg+Io9+P3iUt4axctjWLqHzf7m8kUeHI/Lo6Cj54K9PDmx52PcixbbX5Hbf4G1Qyq/UDaz0/Jo+9D0rsZDVMJLS6xZHO41uQnr06NHcmZO2omxiYiK3CSlbm0yGPN3RSActSg9L1UHyYFkocVl92GJUgtOwRhwhqzgkR151xx3Ze2AUWZ6WP8sqZLlaGt6DUtMMEUmonqrTRbripRcR0Ufk0e8Hj66vr2dTvpXK1r5+loYt9NApY0Xk0cij24WSD/4G/Q2UiHSllClwtVrNLDld+eNZm5wmK7ulpZaawet0bMlpXE/JmTw1TStzp9PJ/EqOHDmCI0eO5PxT2LF4eno681Gp1WoDFqO9yjcn58XlZUydtbq1ytdV+hS7KwkuricZQXrl1Dbg+lln98hKydBWwtn0CctXV7QZTL52XrJttcCy99rJfjNR2QpA1jF7aHhWq7a7pcnf9SHrPVC9Mnp1VR3TTwBISuupEnF/IPLo/c+ja2tr6PV62QCv2WxidnY2l4aVLfJo5NH7G6Ud/G01bP/MQXX6NUVgImGriZXCiCukvEB/DyzrMKws1lG5IxmZeZaGpc1nPvJ1hrck3escvV4vIy2eprByTExMYHZ2FnNzc9k0hRKmkZXtYr+ysoJ2u40kTfGDX/8iPnbRY4E0lQFgCiDBq2ZqqFUSdEWuAHLbFniExfI3uZjszHq2eCwLTcPS73a7aDQaAJA7I1IdidnKtvyZfFn+3C7VahW1Wi0j9r4+5h+O2u6epa26pLqgxM/1NCgBKhF7lmta0u0JIrYXkUe/fzzKZWs0GlhYWMDc3BympqayVcJa18ijkUfvL5R38Cd+KrrijDu4wZTGOoYpHofzyIM7nCk2W7UWnzsbKzE7SKsyexY1l4OvcxnNSuKNQ48ePYojR47g6NGj2NjYyGRj501OTU1l/iW2F5WSb7vdxsrKCg4fPozl5eVsW4Jz9n8PtRuvxWfPedjW4o9j2F1J8KrpKp7Y7K8gU5l5fyYTq4P3UOFwnjwyXUjzr+u9TsuExOTAhOYRioa39mTneI9kir5r+1uaOtWh9TOds7roQ5bDh9JhWUVERB79/vKoTffOzMxgcXERU1NT7nRv5NHIo/c3Sjv4A/KvugfuJv7qJCY4AAMdwsLwd49MlLAsLFuh2rE4Pb2m+TK4fPaa3axLdkpeXl7G0aNHsba2hs3NzWzn+ZmZGczOzmJmZibon2Kr0oz82M8lTbeciS9eO4LL77oJRx5wBjpTM9hdr+JhjQrq1cHVfixrk7f6qxQRj8q/SC6e/JiEvPwsvuWr1jaTK+CfImBhebpCiUzl4dVPp2qYvFTHuLxcbi/tUP7HfgXDRYwTIo9+v3l0cnISc3Nzmb+gdwycJ+vIo/26ePWLPHp8KO3gL6H/i0hnIJ4orymmKpqFYYuJFYkVmqcPWNk90uJPtXw9a1lJ0pbV27SCbUVgRKM7z09NTWVbEUxNTeV8S9i6Y8vX0uGpl0ajgdnZWSzMzeG8iTqmJ2s58me5hdpD68VkX0RUas2xzDS8J0evXbkcmp6F4/bxCJatXiYsLYfmV0QsXEZ9ixKK55V/lPQjIiKPnhgenZubw+TkZG6FsMo38mjk0fsTpR38bSGvtKM0mjau+iLo1AF3TFZevWfg6QxOl7+zpaTlYuuKldYsIiMXIywjGbZUk2TLp6bVauVWpNmxQWxVW5nN78LOrrQtCYz8JicnMTMzg5mZGbRarYH6KaGolegRscqFLVzPGmPLLtTWFkbbpugtgZKCTi151iw/aPRvFOvR7ntpc5nVQtW6eukU5df/LK+vSsR2I/Jo5NFB+UceDefX/ywvj5Z28Me+KkB+2oGVxrNQ7E8dZt18yDr1LBVTbnb6Zb8IJQju0N5raS4/0He0Vd8UIxjzT7HpBQAZYRnR8D5S2gGMEM1aXV1dzVa2Wfnt+CJzcK7VttSG6x+SuclJpyoY7KSsRKdEovJjWDtwGLuu8lY90Wv8UDK/J9UJr578qeE1DY9sRrU6h8VRYte6pmlaamfliO1B5NHIo5FHw3F2Mo+WdvBn8MhArT2DWox6n8NwOK/DhUiGSQ0YPOtSO6RauJw2+0D0elurtTY3N3NTFMvLywMbkJpjsk1T8AakKgPb3sDS4+OHeFsDS4vPndSO6BEJgAEL3n57DtraPpy2l5/eN+Ly2sjruFrOojJYOh4x6x+HZUt2GJmMAi+sJy99yB67AaQpyuyrErH9iDwaeZTvRx4dLOdO49HSDv5M+XnlEfuVMHkVWbD2el/JRJVMX4Ordby5uZl1Fs7DrD8rG8cPWVRsoapTsk0r2BSFERaAjGSmpqYwNzfn+pVwvmypLi8vZ1Zvt9tFkmxNedjWBryLPZeZ20Nla9fNwvXiAP23DWztM9nr6rIQWRksPk9vaJ4GaxttWyN0r625rTyy0vrzHxNJCKMQWFGeRfETAH3KKi9xRWwPIo9GHuU0I4+OFn8n8Gh5B39IkCT55eum+PbdW95uSqPxBtJ3OoNnifBRPfyp6WgaQN/XgK08TteIy5yS2bK0Q8Zti4VqtZo5Js/OzuY2Dw351HS73WwzUyWser2eEdbs7GxuSwKvsygxW73tO8ubHyT6m4mK5cLWrT449CGjbwlUD9RyrFQqmRy5vXQ6idNXDCOuUBgtkzcFo2G9vDWs9zsXh/6PGF9EHo08Gnl0MI1Q3IE49H/ZUNrBX4p8Q4VG7p4i2at4+85+EPrbs1xV6dU6VgvZrnsWs1pJ9p0tLpuiWF9fz7YhMJ8SALlphdnZWczPz2NmZibbOJTrYmnbYeO2tQGnx9sRzM/PY3Z2Fq1WK9vTi3dl13Kbz47tW8VtxDJTuRa9DeBrRQ8b7y0A5+tZzHydVyN6RBjSL24rD2ylDrNU+b5atqpz9snWs+qml2dfBuW0WCO2D5FHI49GHh1PHi3t4A8pkKY9V8EN3p5CbDVpJxnIwkmbCc/rGBpHlY6hnZY7gTklG2GxU7Lnm2KHjNvUgpEMK7NHWLaZqaVnTs4zMzMZYdmUhycT7tBs5at/ikc0HjkUtYfd85yaeWrCCDDUvvpQ4aOUOC0Lqz4sXCcjLCYujyiK6sRh1LodFo7ro3kV5ZkkZaWsiG1F5NHIo5FHc/XRvHYqj5Z38AcgTfuvuj3FUoQIxuJ4CsMKwQ6w/ErewvLRPJxWyBHZOjxPW9j+UzZNYUcNLS8v4/Dhw1hZWUG3280Iy3xTbEpBCYsJJU3TjLCOHj2KQ4cO4ciRI1hfX0ev18sIa3p6GvPz85ibm8s5OVv51PIz2RtRsLVq10M7udtvk61d9x4CnC+vRmO5cn35WrVadXUFQLaRK8f3dIcfKpyntZWeVanxvfbXciuUgFgmRRZu6HtEhCLyaOTRyKPjx6OlHvwZzMLj1+NssbAFYkcHJUmSbeLJHUIdkg1J0n8Fzx2XLSgmLVMo9ZWxe8DWtABvB9Dr9Vei8VYEvOu8WZb1ej1bjWZOyVNTUxnJWGeyjm0ysmOHDh06hMOHD2N1dTVzsq7X65iensbS0hLm5+cxMTGRm/IA8kcscT3tnh5TpNaUWpFGhtxWJm8Lx4RRq9WylXJcP+8h4bWBWqMGdkrmeNp+FofD89sFJjaWmZEm153z0mkwJXiWJxO2+tOo7BR9AiurvRpxfyHyaOTRyKPjw6PlHvwdk7s2NltQrMSs3PzJr7yV7CwdJjlPGdS6CjnHmtJxnqZ4bLkauRhhLS8vo9PpZKRgUwo2PWGOxFYWzpOtXztv8tChQxlhJcnWSrLp6encgeVGWEwwXoew+rL/D5MXgKzT8it9g1m0Kif7zQRiHZVlqGXQ+rMOeKRl9717PA2i5ep2u7ljouzBwETLDzZuY9Ubq1/IUTn0lmXYd05H40dEAIg8GnnULUPk0Z3No+Ud/CU4tlIt7wCsUwOAP4I3q1N9VrQjsKLq9aLffJ2tF7Nw+B6vSLP9omyjUHZKTtOt1/o2pTA3N5f5kjQajcxSZSsI2LKu2PI9cuRItsKtUqlklur8/DwWFxcxNzeXO2xcyx6almHLXeVtJGN15vtsjfE1IyIO7xEayz3UeTVPjmNyY7DV68Xj60p2Hjmo9allZr1QsF7y/VA+HCdU/ogIAJFHI49GHh1THi3v4O8Y1Dqx73Yv1OBeB+E4nKZHSJo/0Ld4tcNpXvzdLB7ehoAPBbfNQtM0zQ4YN4dk23jUjhtiq4stKHZKthVptiTfLFXzTZmamhpY3RaqO19n61DbReXElqxHIkUd18KHfDs0TbY8Wf7WPloeLQunqXWy60xaoyBEJl4aXl007vESUvZgO65YETsZkUcjj3phtcyRR/NplplHyzv4S/MKqB0sFzQdfH3MvgPaUdRy1d+atqXh/bbvZpGoRWm+Ke12GxsbGxnBMGHZ63mzVHXHeXUMtrR1GwLexZ4dk21Fmm1rwOl59eF6WefnY5mAvAO51b/ICVjT9N4ScFlsekN9iqzMadqftvJIhh8u6swdyleh0xShqQbOOwSPIJXQvDYpKu9wMisrbUVsGyKPRh5F5NFx5NHSDv5SDI72Q9aVNrJasCFLdmgZHIvYs3zZgmRfDd1tfnV1NTtgfH19PVuNZlMKtmeUWqraoY0gbYXbkSNHclsbWKe1aQp2dPaOL1I52ncmGCYojssEEZJhUXtwu3B4tly9twpsRarz87D24rJzmvrWweTMJwio9VpEXMMIUn+zrELyHHZfQo8QJmInI/Jo5FEg8miRnIajnDxa2sGfjbZZMXV1lKcgbEGFrFBLzzqixfcsW73H99l6YyuVFd4sVT4T0ggL6G88OjExgZmZGczNzaHVamW+Kby038qsxw3ZzvO2wq1SqaDZbGJycjLbg8qmKTxrsQganp3DlbgtvF7z2kKJOJS3toWWyWRuv7k9QwSiHZ+tYm4/Ji2+VkQoHvEx+NqwB4eHUR+4SVJWyorYXkQejTwaeVQxDjxa4sFfnhySJO8gawrFDrJsVRlYQfR1cJZT0ncmVcvJs6TUMmKrhhWctyAw35SNjY1cfdg3xVaP2VFDnhOxpbu2toYjR47g4MGDWF1dxcbGRkYmtqfVwsICFhcXMTMzg0ajkauPN7XA0xJmKRcRB6/2046nZMLtoPJTq9TqYdeYADyr2Zu2sHjmsK75a7p63dLW1WnDEHojoqQ3itXOctXPoryyPIeWNmLnI/Jo5NHIo+PIo+Ud/Imi8mqmEEFtResrmhGDdlbucLxlAfsjsGXB+asTrlmQ5pNi5GIOybZibHV1Ndsk1EjBdptn/5R6vZ47FNzKbGXrdrs5wrKtDTzC2rVrF2ZmZnKr2zjdQZFvhbEpDbb+LX3eDd9kpedYqjVrfjNsGVoYkyl3QCYmJY00Td09skzuXnwmSd5awuKYr5DJAOgfNG/bP+hbiaKtLLiOVn6D98bD+23fdRUfp6MP2cHPstJWxLYh8mjk0cijY8mj5R38JVuvXFnJmKh01K9WDNA/xJuvqcXjkZ4qh11nK8Kus0IbqXQ6Hayvr+PQoUOZj4pZqkYsk5OTGWFNTU1hcnIStVptwCrndNvtNjqdTraLvW1mCmytRms2m5iZmcks1fn5+YGtDbSuTE76VoDDmeyYrNT60vuVSiXbVFbbh0mI20WtXEvf9tHiPy4bk5c+3Lz2VXlYeawtmdj4oWH3LH1+cFrZVX+Y4NS6Hwavzbhu2k793wnKO2ERsW2IPBp5NPLoWPJoeQd/x8CWCHcOtqT0FTOAgc0zOT1LSzsHK6OnWNYJ7R5bqp1OJ/NLMWdkIxXzS2k0Gtn0hJFVq9XKnJLZerYObBtkmlPy6uoqDh06hOXl5RxhmZPzwsJC7pBxJWiPrI2sKpUKarVaziJjAuKOx1MV2h7aViZjJROPZCwtbyd5tkLVKuZ6sXXJbyw4DSu7komVx3sTwfG9hwDrV+haSLeY/LWu2l5F6Icrr8Uasf2IPBp5NPLoePFoqQd/3sDes6QYaj3xRqGsFNzxDNwxi5QkTdPsjEKzItkh2f7W19czf5pms5kR1MzMTOaXUq/Xc5ZqtVrNTU10Op2MCNUpGdgiwlarhcXFRSwsLGT7WjWbzQEfDs/CNKKwqQiexlGZWly1QIvkVa1Ws9V4HEYJmsnDPpkcPCtV31AwcYWsQpUBx+d8zEplPyQlKu/NiZeXfi8iLg2r0LYI5VtSvoq4HxB5NPJo5NE8xoFHSz34szbRDsSNq9YY/9mrcgvPPideB+ZwSnA8JcGWTLvdzhyHbQuC9fX13FYBdrakTUvYd9srii03+zQiXFtbyzYztW0IzAK2bQ1mZ2extLSUEaG3rQHQt8rYilNfHiMl/a4Wm0H3ATPwNX2rAPT9gtT3h99ODOvYXC8mUy0Lt6OnL2oh8vSE/ukbD81HiVavKbEOI1ett0LllH0v50xFxP2AyKORRxWRR/PYiTxa3sFf2rdY07R/piBQbClYR7TvwOC2BZ7zJ/vDqIWnvgpMXDyNYD4ptuGoHa49NTWVWamTk5PZFgTmvMsd0Xav5y0IlpeXsbKykjtj0tJdWFjILNVWq5VL08rA/jVqmWqd+VU9nwvJFqV2XCUtvs9vBjwrUTch5fIwkWvaFtd78+DpBcezOirxmNzsbYSFZcLySJPT9x4UnpxHJSSOq9ZyUb5JkiApM3NFbA8ij0YejTyaizsuPFrewV8yaIXya3K2AMwSZYsFyDslhxRGO0vIUZd9J8x3xJyQ2VI1x2WzVM1CNdJqtVq5fadYqW2KYnV1NfN1sTMrNzY2suX21WoVU1NTWFxcxOLiImZnZ7OjhpigrX7aSbm+XFft/Fz/rFmoM7KfCtdFCZAtXy+MvilgslN/I48Yi8oLwN2mwJMJxze58QOL8+P0Qg9Qj4y4vdWyDaXLD1wlKU7L4nt1ihhTRB6NPBp5dCx5tLSDvwSDZGPQRvaIie+pgmuDh5SCLSagv1WArUJbXV3F8vIy1tbWcg7JfLbk5OQkZmZmMqdkXqpv6duf+bscPXo0W4VmaVsnY0vVCKvVauUOF/csqSJSV4Q6lBem1+sv72d55tpS2sCTOZNBCJ6V55Gw144e+XnlVGLX6YpQGdWa1fp69Qjpo1cHLmOoTSIiFJFHI496eer3yKM7D6Ud/AFbvircsJ7is5VVZMWwwrHFq3EsnCqrkRUTllmWvOFovV7PLFUjK/ZL4TysU3S6XdxwTxv7VjpobK5hcfMIVo5Zqjb1Uan0jxliwjKHZLUMud7skMydUr9rx2MfkFAbWLh+m/lTSOyDovlrG+qqNyVP+61pcbkYXP9QGTksO1fzNJXl7dVVCStE3owi8lFC84gtFHbrezDpiDHDuPAoDyhXVlZw5MiR7I1f5NHIoxp2p/NoeQd/SV7Bs8uOVaqd0RTfoA2vRMhxrQPyCiXdgkB9U8yvwwhramoq23B0cnIym56wDsV+D5//bgfvv6mDQxsAUAUwjalKE4+td/CA3hEAfQt4cnIy23fKNjINrRhTWajzMdedZeX5nWiH4fRCHU8fDiGLlMMZSer9UFupJeyB44RIhq+x7pjPStGu9J5lGSKnIuLRNPlB4KWl5FxExBFjjDHh0V6v5w78+Ai4yKORRzXeTubR8g7+aHNFVfoiImMF8awXtSwY7Ihs+0KZX4oRFpOWWZN8riRvOsrEAiBn/djA7w++sgkgzeoKACu9Gj6+cTaeXO/h7MbhzAKenZ3FwsLCwAo3Tx5MROxw7MGzrnRKxevwnJ93XdvCu2ffQx3as1i99ta4avF6JOsRIdfZ2t/bpsAjQtU3LbtHXp78QoRWRHSaVkTEFnY+j9rpEZbm8vIyjh49mr1JtKncyKORR4uue2mVGSUe/OXBiu8pJocD8htoqmXDcThNs0xsXyizUtfX17NtB9bX17GxsZHtD2XHCE1MTGQOyVNTU5iYmMgdAM7THgDQ6XbxgZu7x0qtipYASPH57hl4+K47MD3Vd3aenJzMrXDj/bey2El/GkBf0XO92VrkMMMIwb6HVqfZ91AH1weHhikiMa6j9zALdWzdYNQckY3Q+I2FklWItLgs/MnhhtXFIzqti7aPB1f2Zd6kKuJ+wU7jUdsmRt8kdjqdLN1arYZWq5VbNBJ5tF/HyKPIxRuQfUl5tMSDv3znYn8BID8lYftQ8aqmbreLer0+oFDaMVgRzD+BpybW19ezPaJs+wEjimq1mlmok5OT2fYD5pDM1p4Rlq1i+9r+Lg61+w6+g0hwNG1gZfJUnHvKRM7J2dtywaun+V2wzHISPvZbfWi86QwjyG63i1otr1Zs0VqdrUzWNt1uN0eiWlaGhbfvdqySyc7OwFT90Hqxjnhl5PD8sDLisgeXPWi0jCES4XyUtLh8HEYtZq9eGtfy8h8WPZSUsyK2FTubR+2MXtvCxdI2juLp4+np6cijkUcH4lpeO41Hyzv4S/sNUKn0j8vhjpUk+bMKWTFZWTmOm9Uxha1Wq9lO87rZqK0WszyNVMyatD2nzC/FiGBjYwNA/wgjW81215EegKnhYmjOYHZ2Mjtb04iIiYVJlC2/SqWCTqeTHXlkMmKnYSYIfUCYLHkfqEajkVl/mpfXQavVKjqdTkY0nuWoeRisQ3J5PAubfUk8647vcRpGvp1OJ7d7vumMXbffnDbrmpKUQmXjWdZq4XLaoXQ137xswqQaMUbY4Txqp3a02+2M12yK2AZ9PKCMPBp5VLFTebS8gz8BN7gRFhOShTGSs9fRpqQWV8Nap7DX00ZQfLSQEU21WkWtVsumJoxU7ExJdka26Qy2gMwKbrfbaGwmGGXwt2u6v5eVlZ+nCaxM3Nl1moLJgq+xvDw5c3p2T1f7aZk4Dfv0rDsvnj5ULL6Gsf2mOA3bId9ra77P94ysuUwsG12hZmXitLkcnJ+Sj618KwKTjhIR/2ay4rhcnrISVsT9i53GozywSJIkGzja8W86mOTyRx6NPMry4TrtBB4t7+DvmMw9S1QbyMLpNX6tDeTPOTTLzYhPjxcynxSzZOr1OhqNRm7D0ampqeyQcbUeLF1bxWZTH0aOp1Q3MV2dx9HNar+ygsVWggv3NN1Ob7LQ3e1NBiwTj4i869pBOZ1hRKdWJcdna5njsKWrUGvV8mQr18KEHkp2zcKz5egRAJOWWazeeZRqras1yuXSNtAwXnk9Qtd7FseTXXbPvRMxVtjhPGqDMZvWtDN67fxfW9DB+/cpIo9GHt2JPFrawV+CBJXKoFLZd8BvRO00rDT2Wp+tVPNJMJLiqQkjFtsiwFahmcXaarWy6QMlK0vXnI+NFK081UoFT9t1GH9+1yIgq30NP3vhBCqO5ch+OWydZ7ITiyonV5Ehx2PH3cLOIJ1FCYnz4TcLbDGqZcbxlCA8AuI6eHVTQlb9UT3hOluZTT88q7VIHkpKfI+tf0+2Xn08ePlanK1r9hcxztjpPGoDNxv42eDPvvMRbIbIo5FHi/K1OGXn0dIO/rYwOLJXZ1rAn4YwxePr9tusVT5eyByRbSrBrFslLCOrZrOJRqORs2Z4WsLSXVlZydIzQjCyumQKmJ5q46/ubOBQu1/rxVaCn7mghUtPrWdl547ldd4Q0bCjck6yTgfn9DhfL5xaZJ78tUxF7WXX7TsfJm/18yzSYQh17GGWobWnrlA7nvz0u8pS73MaSnxKgjwVN6osIsYVO5tHLV2b3rVVvMp9kUcjj44Tj5Z28Jdi8DU4w2ssVjYAAw3OxGIWpW0RwMf/pGma+YAYsRhpeYeJ6wamtqXBysoK1tbWcumZk3Oz2USr1cJjd7XwhLPruOUwcLRbxcJEBQ9ZquXeAxoB8Gt6lUHImiuyXu2ahlEfn1AcJijNFxjcJsLS1jj8nR8sel1Jpqh8Spj6kDMSZPJlfSsiLK/sxdbjoH+QR+YeWXly1Tz8++X1VYnYPowLjzZbLXxrpY4jR4FdU1U8dFcNFalb5NHIo+PEo6Ud/CHN+ylwR7Xv+upXR/Ta+XgawTYaNYKxLQKMXOr1es53xCxLdUhmK5W3M+ADyo2sLE1Lz8irWq3iYafkpz3Y6jOfFba0tOMVyUMRIoyQBed1WCUttag4DLebWotKPOxgzvft4aBkzPdVR6x9dEpH68xkavmEpio8ueVktXVza6fGAkJXwtQ0lUxDVrfKoJ9nijITV8Q2YQx49IYjDfzvL6U4uNE5VukOFlsJfu6iCTzqlHrkUUQeHUceLe/gD0CSDFprQL/xeXsCb4TP19lSZYdkc0a2FURMLLYxaLPZzDYL5dfmNrVhpMf7TZmTq+5ab74uut8UW7/2O0mSzGdFV6HxNIRHGkogRgShqRCLw3nzfcvXs7z01Xmoo3qOxlY2+21hlNz4QVSr1YJkZZ9M5HydH2beFIj9MWkpvPyysprcRb4an+VuhKqDfpWfl0aobFu3dtY0RsS9w07m0a8eqeNdXx1c/XlgPcWbr13Fqx81hcc8sBl5NPLoQP5aR69sZebR8g7+krBVYBZctVrN7ZXEjWgWHm8yadsCmKVqVmqv18ssVVt9ZlMTduC3pW2vr42szBmZ97Iyh+hKpX+k0PT0dObnwgQI9B2EmaC1wxoBapw0TQeIiOMymGD4Gndwtoytg1vHUnK08nIamrcRJe+J5VnAXBaPHK18Vgc+askjaPtt9eC4TFgha9EeckVnUiqhKfl46ep3feCqjF05bUUcSHMA5eSsiO3EDubRaq2GD9zUcetm+J9fXcNjTmsBiDwaeXS8eLS8g79j0JG7NSiveuKRPv8265T3hrI/W4WWJEnmJGyOyDYly3tDAcheYxthMVkpAdrh5LOzs5mlymmylaODPT1DUsNxfdlqVEvQyszp68o3DstvIFmO/N3zA+Jd+DlN7sB8n+vBZGYEo51bO3XId8SzLpnklHw5HfZR4fNIeRsLLRcTtspJy2319upj8dQSLiQkBwPhyzlbEXE/YCfy6M2HgIMbxX1k/1oPNx3o4aI9tcijkUdHwk7h0dIP/rgx9bo2EiugnflofilmoRq5WLq855RNUfCbOVZmIz7zRTHCMsVOkiRzQG61WjnL186u5Dd7+pbPCKXIAlWri0mdiUs7koGJxbNsLV0lICZIvq7pa7uFOrGV13vYKEF4RKvxvI5u14wsuL780FOysDcSujcVy5OtSo+wPNl5stH6KOFr/BCRDd5PS2uxRmw/diKPHukMHhfm4XA7P1ixOkYejTzqyTt/v7w8Wt7BXwr0ev0OyoshtBOrFWvWhhEU7zCvVqr5o/DeUJlFs9nBzIGvoL6+HyuVGexLTsPK2nrm42L7TVWr1cwa5T2sJiYmMgdl9jnx/DHsj52gDazI2sF0NZYSFO/YrvLjAaf95q0Bhlm4lp8dom5pcGczy5CnPbyOHbLyPEvdI0Qvfy0D32c5aJ6mQzr1EIJH4J6crBxcX8Uo+Sk8B+zjTCJip+Jk4NFe/4ivbrebvem7rzy6ODGaCBYnqrm6Rh6NPOphp/FoaQd/KVKkad6pFMhPV9h1syx4qwCzLM1KtR3mbduBZrOZWah2ZiOf9bh092fxkFvfi4nOgaxMy8ksPlZ/Kr7aOydnodZqtYz82Oq1FW02oOPBn4Gv6XXAt1DYwdbrjDy4ND8LPhNSZcl5bW5u5hyB7brJ3NLhqQG2lnXKGig+M9KzBoH+qQKeVc3THpwGT6er3JSoeGrGdMPqYeW16Qq1HPnNLZffIxy1cllelqZn2XtTG5qukSCnHRHBONE8atO7nIa96eN9AO8Nj164p4bF1hoOrA9OUxp2TVZx4Z4WvO4ReTTy6E7m0dIO/gxscbAlBWytEuNOZFMUfK6kEUylUskIi4/+se0D7MzAbreL3ff8Iy75xpsHyjKdHsGzNv4c7eRH8Y3GhbntBngFr5GfHimUJEluoYdZ4gCyTqcOvWqhmvVn6ZpFyuQV6pRAfnd77kQAMmves4D4t74t4HSN2Pjhwm8btN62OpBJhQfB1q4qE88iZL8cJXB2ajdY3jotZQ++IkLyiEanWRSaDtdDiXZYWhZG04mI8HAieJTP+OXpXXsLmCRJLp17w6Mv+YEZvPEfDwfr/dJHzqNayftPA5FHI48iF0bT2Qko7eAvQYIkyftiqFICyCkZbw66traWvXI2x2E+99EIy9Iza7e9sY6H3nbNsTJomYA0AX4k+Xv8z5nHYHJ6Jtu3yqYr2Fpky08tNJ7+BfqdXklI41oH819R+w7GOtVj8bz0TaZep1RS8ciLy8IEq/BIQN9GcDhuc36zoASg8mAy92TEJG8y7fV6OcfzUNmVaDSPYSiySDlNlb+i6C1xxHjjhPHosdM5bMsWPvUDgLs45N7w6A+eNoFfeXwF/+P6I9i/1vcB3DVZxUsfMY/HPrDVl0Xk0cijY8SjpR38AYC2DyuQNSbvCG8EYyvTAGT+J7ahsh0BxITCUxwLh7+Kye7BcJkAzKZHcF7zHhyZecDWlgO06zxbVd6qXe40/NpdCcTCeFajFzZEMpa+vtY2MvEUXr9retxh9Y2C1jHUkXS6I0QqnnUXuu/5tPBg20vHq6M9vHRXeq6b1wYqOw8hAuW09LqXvleOnUJaEduLE8GjvKjDtm1J0zTz3bMFHfy2797y6GMe0MSjH7AHN+7v4NBGisWJralepIObW3MakUcjj+5kHi3v4C/J/huADZz0KCC1MsyHhKcm2NoxheTpiaWVu0cq3kKtjc4xa1UXcmRVSPoO1h75WF1CJKFhPWvF4oR+Fym1huMOr+XRNDgMW3uh/EMdytpBLWF1EvYIN5SPxvOmciwdj7BMr7yd6fmNREiWw8APpJCsi+QVur6TiCtim3CCeNQGfzbtZ+H5xA8eSN5XHq0kwEV7mvktZXqRRyOPji+PlnbwlyBBpZJfYcSkwFamHS1kZ0pWKpWMUMyyrNfrWaOyU7PtOWXOyId7oy0h603tzs6m5M6gFqV1aM/S9Cww+66v7JlUOA5bol76XmfNZOyE4ylnTtvimgWvluQo5GThuYPpn8pE66pp8f1QGbwyctswefV6vWzqS31bNJ3jQejNhVfXonBaR+9BExFhOFE8qj6CNs1rb/3st3Fo5NHIo6Mg8ujoKO3gT3em58bp9Xo5K1UJy6xLsyyNWHg1m61C07MHv9c4G8uVWUz3jrj2cgpgvbGE5cWLB3xTPNLSzyLC0o6ZpukA0SiBmXMxy0kJzsqmyu3F4+tKJPbJZKYE4lnnSsJ8XWWgzuh63/L3yGOUjltkpRuB2VuQ0JmUKt9R8lUC8ohvWDrD5Je/n5Z1e6qI7cQJ4lFbqGF+gq1GHQ/ofAtTvaPo9JZwsPlQ1OqN3F6AkUcjjw5D5NHRUdrBX5pu7U+VJPm9qXq9XmZh8soxoO9EzD4pFtesEItvimkdoL+VQBPX1X8Sl931PwaOdDYluOXcK1FvtHK7net0BTv7+vXzfUNCHdIsSIMuj+e01IrRtDyfHw5ri044LhOYTiXwdAUTnLUXr2j2SI59aZgIQuVnWfDDwpMxl1l9WbgMRlg8BWbX9K3AMFLVdlfi9wje0xMlOU82mmYWLwVQWtqK2C6cOB5Nsrhnrn4JP3DHn2CiczAr13pjCV8/90oc2PuEyKORRwfy4rprm9i9yKPFKPHgr4fNzW5uPyWgv2u4WatmsZl1aoRljchTEkxcbKHatINtMbA/eTyunZzCRd/+Y0y0+/v8bTR34RvnvRSHT3kiao7lqZ1Hy80dZ6uOebKy9FjBLTxPIzBZeCvALK6RtebHC000/9A5n5q/koxt3WBl5q0BuDz99g2TE1vtGo/9jMz3iC1d+27pGWkyMVraTN52jY+y0p3pVbbcRpaePgS4Xt5DLGT9Wrr6BsMjK5ZTpj9JgqSs5xJFbBtOJI8mSYIzjn4Bj7rjHQPlarb346Ibfgc31uo4sPfxkUcjj0Ye3WaUdvCHdMtqNcUBthqOjxtKkiQ77kenJmxPJvNnYb8DJiqemrAd5iuVCg7NPgn/eNqTsHDka2i0D6LdWMDh+QtRqdZRwaCyeZ0L2LKibX8lvs4kEPIhsXCWV61Wy+qtFiPHs3pbvt1uN3eYeZIkOZ8T6xx6/qJaeBZXrVOuhxK3R3BaT65/pVIZ2I+K43rkWqvVBs62tO+e87ha3VZGnsIyXVMy8axMlgfXV61TrbcSp8qniOC43L4OpiWesIjYNpxIHk2Ai296HwD422YBeNBNV+PQKY9DivyGyYbIo8jKEnk08ujxoLyDv2TQ58IUyawV8xfhLQfYQjWfFlMg9kPhPz543FadWfjlpUuyItXoOoOVSInLFD+sXIOWmVnhOrgE+ju298XUt8y5nl5eXjmVUKzzexadkhKn690zMjOr0dIOHTFlYELicAz2fbG2tvYdRjTen5G2+i5xOkzS+vDxSMRrY5aN6otn7Xp6FZIJ61FaTr6K2G6cQB5dPHIDWu394aIBaG7cg9mDX8HhhYdFHo08Gnl0G1HewV8AtgKtVtuqmlmdQP8sQT5OhokqSZLcXlLW0ew+0LcKuBOaIoQsOCCvSEqyfJ/jsWVlys+7untKz4NPJSN+Xe6lz2HtOsfzwrPcOW0uF8uBrwH5VW1KdvxbCUGJm+97JOKRn77u13bi+qvFqqvxtB08i3cYih4eReG939xWmnZExDB8P3i0ST5+Rait7QcWtr5HHo08OgyRR0dDaQd/rEysjN6GnxZOFdqmJfgwcCMtPjIoSZKMBHlqwdLhMnHZtJNwGPvuWST2XRWuyJfF6mnxuMNpWTUek6Cn1NqR2T/GC6v15k6sZbE0WF4eafCZmZqPtreWx/utUzgqD42jxOVtT8C/1TL1HjJa5lAbeDL1oA+kUYgyYrxxInm03VgYqYztxkLhAzryaORRLkPk0dFQ2sEf0hS93mbWkU0JmWh4OsDABGSWqJFVyEK1a5yXWo32nR1i7XqoI3CZ+Lp2aA7Hfg5eOl6ZvM5t6bPVxmHUauJ62INBnXuHgd8SeHKy/LUMTGhMzFpuLpPK3KaxWFc4HZMdty2nw0RtzuweyXAbqJ9J6IGgn6GHzSjwXA40nZ1gtUZsE04gjx5ZuAgbzSU0NvYHt83aaO7CofkLgMBbp8ijkUe53pFHR0d5B3/HwIrjNZjX0fSTrVRWUoNaf9qx+LcqKn9qRwrFtS0VtMNwh/QwiqWqipskyYATr+XFFqLF4TKoBeZZv94gmMNo/XWqxb6zT4/K18DWvMqZy8zkZfmpJaxtzOXwLFaLV0RM+ttL3yNBvVf0IPLys3D56+UmrojtxQnh0UoN33jwy/DQL//X4LZZ3zjvX20t9qB+EXk08qjKXtOPPDocJR/8DVo3rPSm4Lz/EZDvnEZo/EaPrRZO2+IqVHk5bEixeepBlVWnHTiu16k5nFlVnnKHyMzKYnF4ysfrHDwIVvLR/IG8Y7WmpTJSQtT25e/aFtqx2SGbw7Pfj0coIeKx6Qo7j9IL68UNgeXmGRaefHL5JAnglX/ry5ByjW4FR+x0nDge3b/ncfjaxb+Gc26+Gs2NfVmJbNusfbt/cGvlb+TRyKMBRB69dyj54G8LrIxMDPadpx6A/h5GSgxqMSlZedYWh/WsQU3Tc4xlhbXyKUExabEV6ZGJ15G4jlwf69yeczOQX/WWplsWG+/5pGlxebWNPKLt9XoDe4wp0WscfUBx/T2rV9uZ0zad4QebZ5FbOdnRXcmZ89Nr3A5ch1A9i5DpGQantLK2DpSBHxxJiYkrYvtxonh03+4fxL5dj8bswa+isXEA7eYiDs9fkL3xizyab6PIo5FHtwOlHfyl9L+O+oE+gXk+LKxU3hSHEp/lYfc5D8/C5bQsHlttShLaCZlgvHy9TmHxudOy9civ11UGRZ1ZrWYNw1ME3lSC1dezckMEofIe1rm9h4uVZ9hAXcEy1zi89xmTFpetyMLm71pfbjevrhwvKx/6kw5e/TWPfLppiScsIrYLJxOPHll8WP4e9ZnIo5FHI49uL0o7+NsalA927v798BmQTErawNyRil7L6z0vj1BcKxvnOUzR+J7G1XT4U8OYb06SJDn/jKIOxrJVy88jdq/MJi9vikadllWmnlwtzZD155GJRxYcnutrFiyHseOIdJNWLpempfe98qmsuM4a1tPzUFpeO/XLA6C0tBWxXYg8Gnk08uh48mh5B39IkCSDb9ysoWylWVGHMjBRAXBJyzo57+Cu8XR7BIurCssdeJjVo2mpj0uILD0S8spWBM9q147AMtL6e52W/XA0nJKtV1Yl5RApaP30wcZlN+LktxyWthGUEZYeRK7TBZyfR7AMT29DD62QXD14DxsnVMG9iHFB5NHIo5FHfex0Hi3t4C/FYGfkT5sSUKXShjTLw+5px9HOpFMISmxMaEp8Btsdv16vD0wL6DSGxu10OqjX664lbOXxtgDgMrOlytavyjGTNclCX9PbpzqDs3w868lLn+Wm19k/xyM+z8plMtQ4Wi7e9JWtUZuiaLfb2NjYyKYrmPCU1D0yNV3gumn7qb+OkpP3APTk5z24gPxDMk3TMvNWxDYh8mjk0cij48mj5R38UYewxmCHV88aUALpdrvulIb6K2jn0PQt7U6nk4XRzmzpKHmoj4K+gmcLmTuTEiaQP8/SOoNZ7dpZWI6alu3Kb2Xk+lgZeIm/5VOpVDJrzjphtVpFp9MJOkKzbPk4Is6Lw1uafMySB4+MNV92WLdzOTmMOSbbtgRGWkagKn99aPJ5qZyfllsfdF4b8Sfn5T0ANH0mYw0fMd6IPBp5NPLoePJoaQd/W0rQbziPfGx6QS1MthIMbMmx4nDnN0LzrAlLQ0kpTbcOxPYsGwvH8fiTj0uyMBMTE9mB2Byeicd+82cIanF7dVTSZ7JSa1ktP+24ijRN0e12Ua/Xc3my3O1hxHXVtLQ91aoumi5QYtb26Xa7WF9fx/r6OjY2NnKHuWvdmHiYqLhtuC6ePDQNrg/LG0kCDNFDrmeI6CLGF5FHI48yIo/m09jJPFrawR9SwGSvis7WinUaJSJgcIWaQUf21pHtHndYtSzU+rA4ISW1sN40g1plIWtGlZLDWhgGp8N5cpr8qavnQnXRaR+PRDW+lUMtd46j4dVfR+WgZeK3GV6bcxmUGNRqbbfbA28OQulpPZiMNLw+xEJtp8Tu5W/66pFUP15pZysithORR3N1t/tc5sijkUc1jZ3Ao+Ud/B2DKi13SO5knoXI3/U+gzuJl6+WR/Mv6rBq8WkcfvUPbPmqeIrq5cOv40MEonXxiImv2z1PjkwyoU7lEZDXZqH6heTO10KWqcqY0+SHhKXR6/U3I93Y2MhZqzyVMqy8HoGFwms9i9I5XtIc1IGy0lbEdiPy6KAsLJ/Io4P1izzK18rJo6Ue/KXkrMw+Dfq6OBdHlN5reCUT/h2yBJkcvM7jdUgN55GohrdX6wqus5bPs645zRDBc7qcNlum6rCrcmNSCpFFEbl7HdXzv9Gyal289ID+2wk99si+m5Vq0xWexVpUVp02KWoHlYsnK76upBnSS65nLkw5OStimxF5tI/Io5FHx4VHSzv4S5Ei7aUDZAXkfSpYyUMkUdS5tdF19ReH4biWlk4FcL7c6ZWIQgRpJKDEonGtw3B5vXp6RDmMOL3O533XTs0E5qXrvRHgdLx7od9KsJw/hzMCYv8fu97tdtHpdDIflfX19dx5lN60Fsf35BEqM9dT0+S8jpe0uDx5Pe2VeHvSiO1C5NHIo0W/I4/mr+8kHi3t4A9pfhVT7pZ0cCBvXanTKzemrVTSuCFFtO9KIl4HHGZheeC8i4hE09UO5JET+2YYIWudVAb2F/Lz4bihzsOyGFYPJVYrt97jOOaXonXXdLhM7FCdpmnON8UIi3elD51JafGVuA2cB8vBq4snA77vPQQ4X42br3N5LdaIbUTk0YH0I49GHh0HHi3t4M+UjR1ctWOq4niNrZ0zpIQWhrc10LCedcjxtcMAg0SmSuctX+d0OR3usLxdgbf030g8TbdW0XllsLIxQfF0iVcWk421jX1XeWteVsaQ/4cSFrC1xYTK2L5b3ZQkvGtGWkZcRlj2t7a2hrW1tYy4bBsGTwaD5NAbkH0RKYXk6pGbVwaN5yFLo6ysFbFtiDwaeTTy6HjyaGkHf0h8/wNWbm/FGIdXUrOOr9Yn0N/LytLme5wnW4G2b5a3qs3CMaGwVc0kZN/Zr8LS57wtHnd862C6NxJbqdxhPEuSZQn0N7nUMEaQtiWDYXNzMyNGbQ+uP79R4HpzWyn5e9a4WqxGRCorm5IwkuIDx9laPXr0KA4fPozV1VV0Op1saqPb7Q7oAIOtWm53wzDCUnkVkZpHniH5Zb/LyVkR24nIo5FHEXl0HHm0vIO/Y7AGUqsVGHydq43ISsOWlhIX58UdXq0ktlC8DUe9dD2lVoUD8geq63mJnBbQ7yxscYb8KjS8dXZvCwD7bZ1VCdby4ZV0JgfeDZ/lxxaoPkQ4rJKT5aEdXsPab8ufpxps13lbiWbTEbYR6f+/vbPbbRUJgnBjTJyLRLnK+79iJMeJEy9mL84pKMrddlbKXmCqJMsYmP/pj2lmwJimOB6P8f7+Hvv9Pj4/P+P7+/sC9Bkc+CKmeczarbq46jmqynPN0ppdbC67uLVSmaPzuCLMUXN0CnOPHF384I87dSXtDByWv3HurXgq48/CZp2N09XOr16ueqk69cDxMbQYQApv9rwzDxtxZEbEcNWwmQfGQFPjhbLF3EiX/wYJ9YC0eT9+w+PkxcS8j7d5SgKgYm8V2/yaAoBNy1dd5DKwVn0lC5v1EW2La32h3l6ou2r9LzJHzVFzdF0cXe7gb7hsOG1QGAsfv2bw2Yg/83AZAAwE/s7S0nR1GoPhy4aYlS0zmiz/GqeWBfkHsDQ/7HmrB8tx8/SCGqt6dCgXhOkJBSWAw5ACRBg+8Eh5P8IhXj6/+s3vnsKUBOLgNDifeBO/wl0volU/qI5lQKrgxe3D51UX2HHfcrll/ZbM0TEec9Qc1X33zNHFDv6GGGIYpsWws2NiMPhWzy1bz6JGxtIpEYUJvEn2zrjzKmj0aTmdUtAO17bt7D8f2fPVp8bY0Hl6Yxim6Qb2frnc+mb/rG5xDnuUyAuXR6cOEIZfBAoY8f89ctwABqYU+AkyBo8uNtY0OD/q7eo32o2hhW2uA/7rKJyftXt10dR21n6i/U2PZedpnNf6tLVumaPmqDka6Xka571xdLGDv4iIYZh7B9w43FnVm0CY6lUFKo0v8wjYkHUBq8YNYLBxKOS0g1aw5TghnqbgOuA8wStTj5TT4rpTQ0YcOn2AvMEDZCNmj089R0wX8DQDgyILz8fUsDMDz9pNvXlt8+yigvJjUbZCC+cp0DOQ8G8Vh+OyZBdMjSO7i6EXp6WuVbF+V+boPI+QOWqO3jNHFzz4a1LgZAanjcoGXd32ZQOHqs4BZR2UOxnABqicTqeLPF8DJ77ZMJCWets4h0GkXgvyo3EBvNjH3h+MEd96y59v98O7xrmZR6lTBwxGBVDmUWp96W9eQ8PeOOqIz9Pw3K+yCw9vIx2e9uG8M0AyoOjvrP2zczRuzRv/npenTMpalcxRc9QcXSNHFzz4+zNdoR1DYVV5C/jG7fEMUte8WfUauKNm8am3x/vY+9O0Oc6+70dvkI24gjKXLYsP37oeBCBB/LytHqTCSD1SBg7/5vxzebSO1QCr9tD6ZkhhH3vnDHgFUwZIvVNR5QUXpaZpZtMlWZ5VlQerffIWqH+WxhBLXati/abMUXN0kjn6M90DRxc7+PsDnMkQ2APhzqhrLbhzo2Nx5+bOzGkpDDMDQ7zcubNXJ2hnxj6Ns8oDQ0MNK4MLx8fecURcLPZl8DDE2Jir9DRdNVjs04sBA1/hU3naCgE1bC43zkVbc3j9qKfMcM76F+cfa4LQ7piS0r7B7a71UbV9Jc6HhmPdOm6tU+aoOWqOrpOjix78nc/9zCtDp6rWoHCD8eJdDldJgVV5i3xO9uh9ZhwMAD5Pw2HtR7ZWQ0EDI1VoaZ2oN1pBicPoPgVIVZasHTgcP7XHht+27cXCaAaplg/bVf6ytLPtDFzIJ44DhJxnbPNLWvXiqWlWyvoBvitvXuvCsiqZo+aoObpOji588HeedRoYChr01miej8EI9A3uCMdrOhgabKAKJL1lHTEZNE8R6GLbzOjP5/MILZ1e4DgyD5LLrt4RjEkNVYF8DdBav2po6o2qAAHUM8Pq4eEhdrtdtG0bfd+Pfw3EEKguDFznmkcGDB/nc1A/aHP8DVLlXXNeuBxol1ueqZahqs+s3OwNa3g+PrVhGqW1Mpmj5ijnoWpX1Lk5ej8cXfDgL8bpCta129bcCfAkma5l6LpuPBeP8OMYOje/wBLvNFKYMRQUJn3fz7zPCkBqPCirgkkNktPnYxm0L+v1b7iI8SEmhnjm/Wm8esFAPSv8tE14X9u28fj4GE9PT/H8/Bzb7Ta+vr7i7e1thMctmGZTHRw/wJLVF/oNp6UXKPZSsQ9PrCFuXrtS1TdDsCpPFb7SbdAtmFrWr8kcNUfN0Vr3zNEFD/7O0ff/jE96Tfun9Snb7Ta9TRwRcT4P0TQxe7kkbi9vt9voum7sjIBS13Xx8fERx+MxDofDuA0Pkv/XENsAJ3d4vd2efaCmaSKaZnQxsFiZvdG/J84gEzF5TQA0A2IY5i8ejaaJ4Ux/qdRcrqvg/Azn+boarn8YGN8FyIxOPUW9SOx2u3h5eYnX19foui72+32cTqc4HA6zcrBGL3KzGetjfA9XRGwIZNzu/BsXpmGYniREWtyOm81m7DN8HG2UheM65bJmZch+a3mri1NWN/O07nc6w/q5zFFz1BxdJ0eb4b8Mgy3LsizLsqxFq16Za1mWZVmWZd2dPPizLMuyLMtakTz4syzLsizLWpE8+LMsy7Isy1qRPPizLMuyLMtakTz4syzLsizLWpE8+LMsy7Isy1qRPPizLMuyLMtakTz4syzLsizLWpH+Bb6caOQp4jb7AAAAAElFTkSuQmCC",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "def plot_generative_sampling(dataset: dlc_torch.PoseDataset) -> None:\n",
+ " # Sample the same image 3 times and plot the results\n",
+ " for _i in range(3):\n",
+ " item = dataset[0]\n",
+ "\n",
+ " # Remove ImageNet normalization from the image so it displays well\n",
+ " mean = np.array([0.485, 0.456, 0.406])\n",
+ " std = np.array([0.229, 0.224, 0.225])\n",
+ " img = item[\"image\"].transpose((1, 2, 0))\n",
+ " img = np.clip(img * std + mean, 0, 1)\n",
+ "\n",
+ " # Get the ground trouth and \"conditional pose\"\n",
+ " gt_pose = item[\"annotations\"][\"keypoints\"][0]\n",
+ " gen_samples = item[\"context\"][\"cond_keypoints\"][0]\n",
+ "\n",
+ " fig, axs = plt.subplots(1, 2, figsize=(8, 4))\n",
+ " for ax in axs:\n",
+ " ax.imshow(img)\n",
+ " ax.axis(\"off\")\n",
+ "\n",
+ " # plot the ground truth on the left and conditions on the right\n",
+ " for ax, title, keypoints in zip(\n",
+ " axs,\n",
+ " [\"Ground Truth Pose\", \"Pose Conditions\"],\n",
+ " [gt_pose, gen_samples],\n",
+ " strict=False,\n",
+ " ):\n",
+ " ax.set_title(title)\n",
+ " for x, y, vis in keypoints:\n",
+ " if vis > 0:\n",
+ " ax.scatter([x], [y])\n",
+ "\n",
+ "\n",
+ "ctd_loader = dlc_torch.DLCLoader(config, shuffle=CTD_SHUFFLE)\n",
+ "\n",
+ "transform = dlc_torch.build_transforms(ctd_loader.model_cfg[\"data\"][\"train\"])\n",
+ "dataset = ctd_loader.create_dataset(transform, mode=\"train\", task=ctd_loader.pose_task)\n",
+ "\n",
+ "# Fix the seeds for reproducibility; you can change the seed from `0` to another value\n",
+ "# to change the results\n",
+ "dlc_torch.fix_seeds(0)\n",
+ "plot_generative_sampling(dataset)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "50d4a150",
+ "metadata": {
+ "id": "50d4a150"
+ },
+ "source": [
+ "The generative sampling can be parameterized through the `pytorch_config.yaml` as well. Let's play around with these parameters a bit and see how that changes the conditions that will be given to the model.\n",
+ "\n",
+ "First, we'll just lower the `keypoint_sigmas`, which impacts how much pose conditions can move during jittering."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "d39ee88c",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 989
+ },
+ "executionInfo": {
+ "elapsed": 1669,
+ "status": "ok",
+ "timestamp": 1744358509345,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "d39ee88c",
+ "outputId": "58637ccc-53b2-42e9-9eff-4235da1e939b"
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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LXVM8nv729+eHHvmM58ZNxqN+I477kiOXX/99XG5XI717f6EE06IX/SiFyW/LR4XaM5+61vfGgOI/+7v/i451ul04kc96lHx+Ph4vG/fvjiOV+5dmzdvju+///4k7D/+4z/GAOJPfvKTcRwv3z8BxH/5l3+ZV2WONYQv+64C+/btAwBzi5GLLroIW7duTT7vfOc7M2H0bNSnP/1plMtlXH755anjr3nNaxDHMf7lX/7loG19+ctfnvr9uMc9Drt27UrK8OlPfxoAMnmv9QyUtmOtYZXzxz/+8VBxb7zxxuR6nX766Xj729+Opz/96clS7Kc//WlccMEFyXI1sHztf+u3fgu33347rr/+egDA9PQ07r777swyhiCOY3z0ox/FM57xDMRxjJ07dyafiy++GHv37sW3vvWtgym+wzESeNKTnoStW7fiuOOOwwte8AKMj4/j4x//OH7u534u4bL/9t/+WyrOa17zGgBI3DSmp6cBLK/eLC0tmfl8+MMfxtTUFJ785Cen+um5556L8fFxfPGLXwzaKNxqLfdaWC3/P+lJT8LJJ5+c/H7Ywx6GycnJhO96vR6uueYaPPOZz0zNPJ5++um4+OKLh7JpWHz605/GMccck/K3rFaruPzyyzE7O4t/+7d/S4V//vOfj02bNiW/H/e4xwFAYnuz2UStVsOXvvSloZbXHYcOX/ZdBaRTz87OZs695z3vwf79+3HvvfemHiIQVCoVHHvssaljd9xxBx74wAdmyEKm2u+4446DtlUvO0jH2717NyYnJ3HHHXegVCqlyAQATj311IPO08JJJ520pukxGo1G4hco2LRp09DkceKJJ+K9731vsoXEKaecgm3btiXn77jjDnN5n6/PWWedhf/+3/87Pve5z+GCCy7Agx/8YDzlKU/BJZdcgsc85jEAlp8k3rNnD6666ipcddVVpi333XffUDY7HKOId77znXjIQx6CSqWC7du349RTT00eqhMue/CDH5yKc8wxx2B6ejrh0QsvvBDPfvazceWVV+Itb3kLLrroIjzzmc/EJZdckjz8cPPNN2Pv3r0pHmDk9dPJyUkAwP79+4cq02r531pKZr7bsWMHFhYWcMopp2TCnXrqqckgeS1wxx134JRTTsk82Dis7Xw/ApYfPnnjG9+I17zmNdi+fTse+chH4pd+6Zfwm7/5mzjmmGPWzG7HCnzwtwpMTU3hAQ94gPm0lgwSQs6p9Xp94BPAIejNOQV5DzaUy2XzeLwKv7u1QLPZzByLosi0Y7UPaoTKOCzGxsbwpCc96ZDSAJYJ70c/+hE+9alP4TOf+Qw++tGP4l3vehf+1//6X7jyyiuTfQN//dd/HS960YvMNA51WxyHo8i44IILkqd9QwjxJJ//yEc+gq9//ev45Cc/iWuuuQYveclL8Fd/9Vf4+te/jvHxcfT7fWzbtg0f+MAHzDS02GQ8+MEPRqVSwfe///3BBToIHC2cfjAYxvYrrrgCz3jGM/CJT3wC11xzDf7n//yfeMMb3oAvfOELePjDH36kTB0Z+LLvKvH0pz8dt9xyC77xjW8cclonnHACfvrTn2aU4o033picB1ZU0p49e1LhDmVm8IQTTkC/308cpwU/+tGPDjrNYbFp06ZMWYBseQaR+eHGCSecYNaHvj7A8kDy+c9/Pt73vvfhzjvvxNOf/nT86Z/+KdrtdvI0da/Xw5Oe9CTzE5ppcDgc+RAuu/nmm1PH7733XuzZsyez3+ojH/lI/Omf/imuu+46fOADH8APf/hDfOhDHwIAnHzyydi1axce85jHmP307LPPDtrRarXwxCc+EV/+8pdx1113DWX3MPw/LLZu3Ypms5mpB2A4Xl8N355wwgm4+eabMxviH6ztgpNPPhmvec1r8NnPfhY/+MEP0Ol08Fd/9VcHlZYjHz74WyV+//d/H61WCy95yUvMHeZXo8Ke9rSnodfr4R3veEfq+Fve8hZEUYSnPvWpAJaXE7Zs2YIvf/nLqXDvete7DqIEy5C03/a2t6WOv/Wtbz3oNIfFySefjBtvvDHZTgUAvvvd7+JrX/taKpxs3moNFI8Enva0p+Eb3/gG/uM//iM5Njc3h6uuugonnngizjjjDADArl27UvFqtRrOOOMMxHGMpaUllMtlPPvZz8ZHP/pRc9aY68HhcKwOT3va0wBkuevNb34zACQ7GOzevTvDz+eccw4AJFvCPO95z0Ov18PrX//6TD7dbncgF732ta9FHMf4jd/4DdM96Jvf/GayHcqw/D8syuUyLr74YnziE5/AnXfemRy/4YYbcM011wyML3uwDsO3T3va0/Czn/0M//AP/5Ac63a7ePvb347x8XFceOGFq7J9fn4e7XY7dezkk0/GxMREZrsex9rAl31XiVNOOQUf/OAH8cIXvhCnnnpq8oaPOI5x22234YMf/CBKpVLGv8/CM57xDDzhCU/AH/3RH+H222/H2Wefjc9+9rP4x3/8R1xxxRUpf7yXvvSl+PM//3O89KUvxXnnnYcvf/nLuOmmmw66HOeccw5e+MIX4l3vehf27t2LRz/60fj85z+PW2655aDTHBYveclL8OY3vxkXX3wxLrvsMtx3333467/+a5x55pmJ0zSwvGR8xhln4B/+4R/wkIc8BDMzMzjrrLNw1llnHXYbAeAP/uAP8Pd///d46lOfissvvxwzMzO4+uqrcdttt+GjH/1osoz/lKc8Bccccwwe85jHYPv27bjhhhvwjne8A09/+tMTf54///M/xxe/+EU84hGPwH/5L/8FZ5xxBu6//35861vfwuc+9zncf//9R6RMDkfRcPbZZ+NFL3oRrrrqKuzZswcXXnghvvGNb+Dqq6/GM5/5TDzhCU8AAFx99dV417vehWc961k4+eSTsX//frz3ve/F5ORkMoC88MIL8bKXvQxveMMb8J3vfAdPecpTUK1WcfPNN+PDH/4w/vf//t94znOeE7Tl0Y9+NN75znfila98JU477bTUGz6+9KUv4Z/+6Z/wJ3/yJwBWx//D4sorr8RnPvMZPO5xj8MrX/nKZEB25plnDnzt6DnnnINyuYw3vvGN2Lt3L+r1Op74xCeaqxK/9Vu/hfe85z249NJL8c1vfhMnnngiPvKRj+BrX/sa3vrWtw790Ivgpptuwi/8wi/gec97Hs444wxUKhV8/OMfx7333osXvOAFq0rLMSTW5RnjAuCWW26JX/GKV8QPfvCD40ajETebzfi0006LX/7yl8ff+c53UmFf9KIXxWNjY2Y6+/fvj3/nd34nfuADHxhXq9X4lFNOif/yL/8y7vf7qXDz8/PxZZddFk9NTcUTExPx8573vPi+++4LbvWyY8eOVHzZMuG2225Lji0sLMSXX355vHnz5nhsbCx+xjOeEd91111rutWLtkPwd3/3d/GDHvSguFarxeecc058zTXXZLZ6ieM4/vd///f43HPPjWu1WsquUJ1KvoNw4YUXDtyeJY7j+NZbb42f85znxNPT03Gj0YgvuOCC+FOf+lQqzHve85748Y9/fLx58+a4Xq/HJ598cvx7v/d78d69e1Ph7r333vhVr3pVfNxxx8XVajU+5phj4l/4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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/png": "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+1w84hGPwOc+9zk86UlPOuo6PXrmM5+J008/Hf/9v//3IUAbRdu3b8c73vEOvPSlL8XTnvY0/OIv/iKA/Fby8vIyLrvsMjz0oQ/FG97wBnzmM5/BX/zFX+C0007DS1/60qPi9wc/+AHK5TKmp6dx55134hGPeAQWFxdxxRVXYG5uDldddRWe8pSn4EMf+hCe9rSnAQDe9a534YorrsAznvEM/OZv/iba7Ta+9a1v4ctf/jKe+9znAgDuvPNOPOxhD8tAfPv27fiHf/gHXH755Th48CBe+cpXHhW/kSJtRbrpppsAAHNzcwBWsOyqq67CM57xDLzqVa/Cl7/8Zbzuda/Dddddh49+9KMAViYrT3jCE7B9+3b83u/9Hqanp3HLLbfgIx/5SK7sF7/4xXjPe96DF7zgBbjiiitw8803461vfSu+8Y1v4Itf/GJwR2BxcRGf/exn8TM/8zO4//3vv2YbjhT///Vf/xUf+chH8LKXvQwTExN485vfjKc//em47bbbMjl8+9vfztr4mte8Bv1+H69+9auxc+fOkbz8zM/8DK644oqh8B0O42FaWlrCox/9aNx44414xStegVNPPRUf/OAH8fznPx/79+/Hb/7mb+bSv//978ehQ4fw4he/GEmS4M/+7M/wi7/4i/jBD36QyfPpT386vvvd7+I//sf/iFNOOQV33XUX/vEf/xG33Xbb0DZ0pGNAaaR104EDB1IA6VOf+tShe/v27Ut37dqVfRYXF7N7z3ve81IA6e/93u/l8nzsYx9LAaR//Md/nLv+jGc8I02SJL3xxhvTNE3Tm2++OQWQvvvd7x6qF0D66le/Ovv96le/OgWQvvCFL8yle9rTnpbOzc1lv6+55poUQPqyl70sl+65z33uUJlr0Qc/+MEUQPr5z39+iI/nPOc5Q+kvvfTS9NJLLx26/rznPS89+eSTs9+7du0K8mIy/cM//MPc9Yc85CHphRdeuCbPl156aXrmmWdm/XXdddelV1xxRQogffKTn5ymaZq+8pWvTAGk//Iv/5LlO3ToUHrqqaemp5xySrq8vJymaZr+wi/8QnrOOeeMrO/yyy9Pjz/++HT37t25689+9rPTqampnL5EihRphd797nenANLPfOYz6a5du9Lbb789/cAHPpDOzc2ljUYjveOOOzIse9GLXpTL+9u//dspgPRzn/tcmqZp+tGPfjQFkH71q18N1vcv//IvKYD0fe97X+76Jz/5Sfc60ze/+c0UQPqbv/mb62rbevE/TVdwvlar5a5ZfW95y1uya0996lPTsbGx9NZbb82uXXvttWm5XE51uD/55JPT5z3vedlvD8eNFLPf9KY3pQDS9773vdm1brebPvzhD09brVZ68ODBNE1Xx665ubl07969Wdq/+7u/SwGkf//3f5+m6cr4CSD98z//81Eii3QMKW77HgEdPHgQANwjRh796Edj+/bt2edtb3vbUBpdjfrEJz6BcrmMK664Inf9Va96FdI0xT/8wz8cNa8veclLcr8f9ahHYc+ePVkbPvGJTwDAUN3HegVK+TjW5LXzBz/4wbryXn/99Vl/nXXWWXjLW96CJz3pSdlW7Cc+8Qlccskl2XY1sNL3v/Ebv4FbbrkF1157LQBgenoad9xxx9A2hlGapvjwhz+MJz/5yUjTFLt3784+l112GQ4cOICvf/3rR9P8SJG2BD3+8Y/H9u3bcdJJJ+HZz342Wq0WPvrRj+J+97tfhmX/6T/9p1yeV73qVQCQhWlMT08DWNm96fV6bj0f/OAHMTU1hZ/92Z/N2emFF16IVquFz3/+80EeDVu97V6PjhT/H//4x+O0007Lfp933nmYnJzM8G55eRmf+tSn8NSnPjW38njWWWfhsssuWxdP66VPfOITOO6443LxltVqFVdccQXm5+fxT//0T7n0v/RLv4SZmZns96Me9SgAyHhvNBqo1Wr4whe+sK7t9Ugbp7jtewRkRj0/Pz90753vfCcOHTqEO++8M/cQgVGlUsGJJ56Yu3brrbfihBNOGAILW2q/9dZbj5pX3XYww9u3bx8mJydx6623olQq5cAEAM4444yjrtOjU0899ZiWxzQ2NpbFBRrNzMysGzxOOeUUvOtd78qOkDj99NOxY8eO7P6tt97qbu9z/5x77rn4z//5P+Mzn/kMLrnkEjzwgQ/EE57wBDz3uc/FIx/5SAArTxLv378fV155Ja688kqXl7vuumtdPEeKtBXpbW97Gx70oAehUqlg586dOOOMM7KH6gzLHvjAB+byHHfccZiens5w9NJLL8XTn/50vPa1r8Ub3/hGPPrRj8ZTn/pUPPe5z80efrjhhhtw4MCBHA4wjbLTyclJAMChQ4fW1aYjxX9vK5nxbteuXVhaWsLpp58+lO6MM87IJsnHgm699VacfvrpQw82rpd3Ho+AlYdPXv/61+NVr3oVdu7ciYc97GH4+Z//efzar/0ajjvuuGPGd6RVipO/I6CpqSkcf/zx7tNaNkkIBafW6/U1nwAOkR7OaTTqwYZyuexeT48g7u5YUKPRGLqWJInLx5E+qBFq43ppfHwcj3/84zdUBrACeN/73vfw8Y9/HJ/85Cfx4Q9/GG9/+9vx3/7bf8NrX/va7NzAX/mVX8Hznvc8t4yNHosTKdJmpksuuSR72jdEIZzk+x/60IfwpS99CX//93+PT33qU3jhC1+Iv/iLv8CXvvQltFotDAYD7NixA+973/vcMtTZZHrgAx+ISqWCb3/722s36CjovoLpR0Pr4f2Vr3wlnvzkJ+NjH/sYPvWpT+G//tf/ite97nX43Oc+h4c85CH3FKtbhuK27xHSk570JNx44434yle+suGyTj75ZPzoRz8a8hSvv/767D6w6iXt378/l24jK4Mnn3wyBoNBFjht9L3vfe+oy1wvzczMDLUFGG7PWmB+d9PJJ5/sykP7B1iZSP7SL/0S3v3ud+O2227Dk570JPzJn/wJ2u129jT18vIyHv/4x7uf0EpDpEiRRpNh2Q033JC7fuedd2L//v1D560+7GEPw5/8yZ/g6quvxvve9z5897vfxQc+8AEAwGmnnYY9e/bgkY98pGun559/fpCPZrOJxz72sfjnf/5n3H777eviez34v17avn07Go3GkByA9eH6keDtySefjBtuuGHoQPyj5d3otNNOw6te9Sp8+tOfxne+8x10u138xV/8xVGVFWk0xcnfEdLv/u7votls4oUvfKF7wvyReGFPfOITsby8jLe+9a2562984xuRJAl+7ud+DsDKdsK2bdvwz//8z7l0b3/724+iBStkZb/5zW/OXX/Tm9501GWul0477TRcf/312XEqAPDNb34TX/ziF3Pp7PBWb6J4T9ATn/hEfOUrX8G///u/Z9cWFhZw5ZVX4pRTTsHZZ58NANizZ08uX61Ww9lnn400TdHr9VAul/H0pz8dH/7wh91VY5ZDpEiRjoye+MQnAhjGrr/8y78EgOwEg3379g3h8wUXXAAA2ZEwz3rWs7C8vIw/+qM/Gqqn3++viUWvfvWrkaYpfvVXf9UND/ra176WHYeyXvxfL5XLZVx22WX42Mc+httuuy27ft11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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "ctd_loader = dlc_torch.DLCLoader(config, shuffle=CTD_SHUFFLE)\n",
+ "\n",
+ "# We'll edit the model config here directly; In practice, edit the pytorch_config file instead.\n",
+ "# The parameters that can be set here are the parameters of the `dlc_torch.GenSamplingConfig`\n",
+ "ctd_loader.model_cfg[\"data\"][\"gen_sampling\"] = {\n",
+ " # lower the keypoint sigma by a factor of 2 (default: 0.1)\n",
+ " # -> this changes by how much keypoints are jittered; the smaller\n",
+ " # the value, the smaller the jitter\n",
+ " \"keypoint_sigmas\": 0.05,\n",
+ "}\n",
+ "\n",
+ "transform = dlc_torch.build_transforms(ctd_loader.model_cfg[\"data\"][\"train\"])\n",
+ "dataset = ctd_loader.create_dataset(transform, mode=\"train\", task=ctd_loader.pose_task)\n",
+ "\n",
+ "# Fix the seeds for reproducibility; you can change the seed from `0` to another value\n",
+ "# to change the results\n",
+ "dlc_torch.fix_seeds(0)\n",
+ "plot_generative_sampling(dataset)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "055dea55",
+ "metadata": {
+ "id": "055dea55"
+ },
+ "source": [
+ "Next, we'll update the probabilities of make errors. You can edit these values yourself to see how it impacts the generative sampling. Note that these probabilities are **not absolute** - as a single type of error is applied to each keypoint, changing the probability of one type of error happening will change the probability that other types of errors occur."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "fa509d65",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 989
+ },
+ "executionInfo": {
+ "elapsed": 1638,
+ "status": "ok",
+ "timestamp": 1744358513089,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "fa509d65",
+ "outputId": "d20a3ab8-6a7c-4f5f-8212-a47bfea0c3d6"
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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+/D6178+C/PiF78Yf/7nf47nPe95ePjDH45//Md/xFOf+tSTztOTZz/72Tj//PPxX/7LfxkBWpHs3bsXb3/72/Gyl70Mz3jGM/ATP/ETAPJTyYPBAE9+8pPxkIc8BH/4h3+Ij33sY/ijP/ojnHfeeXjZy152Uvp+73vfQ7VaxeLiIm6++WY8/OEPx9raGi677DLs3r0bl19+OX7sx34M73vf+/CMZzwDAPCnf/qnuOyyy/CsZz0Lv/Irv4L19XV89atfxec//3k873nPAwDcfPPNeOhDH5pBfO/evfi///f/4kUvehGOHTuGV77ylSelb5QoO1GuvvpqAMDu3bsBbLDs8ssvx7Oe9Sy86lWvwuc//3m87nWvw7e+9S28//3vB7AxWHnSk56EvXv34jd+4zewuLiI73//+/ibv/mbXNoveclL8O53vxs///M/j8suuwzXXHMN3vKWt+Bf//Vf8dnPfjY4I7C2toaPf/zjePSjH4273e1uY8twovz/zGc+g7/5m7/By1/+cszNzeFNb3oTnvnMZ+K6667L7PC1r30tK+NrXvMa9Pt9vPrVr8b+/fsLdXn0ox+Nyy67bGT5Di/jYWm323jMYx6Dq666Cr/0S7+Ec889F+9973vxghe8AEeOHMGv/Mqv5MK/5z3vwfLyMl7ykpcgSRL81//6X/ETP/ET+N73vpfZ85nPfCa+8Y1v4Jd/+Zdxzjnn4JZbbsE//MM/4LrrrhuZho6yBZJGmViOHj2aAkif/vSnj5w7fPhwevDgwexvbW0tO/f85z8/BZD+xm/8Ri7OBz7wgRRA+ru/+7u548961rPSJEnSq666Kk3TNL3mmmtSAOm73vWukXwBpK9+9auz369+9atTAOkLX/jCXLhnPOMZ6e7du7PfX/nKV1IA6ctf/vJcuOc973kjaY6T9773vSmA9BOf+MSIHs997nNHwl966aXppZdeOnL8+c9/fnr22Wdnvw8ePBjUxWz627/927nj97///dMHPOABY3W+9NJL0wsuuCCrr29961vpZZddlgJIn/a0p6VpmqavfOUrUwDppz/96Sze8vJyeu6556bnnHNOOhgM0jRN0x//8R9P73Of+xTm96IXvSg944wz0kOHDuWO/9RP/VS6sLCQay9RokTZkHe9610pgPRjH/tYevDgwfT6669P/+qv/irdvXt3Oj09nf7g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P+suD/rSl76UbYcyKf8nlWq1iic/+cn4wAc+gOuuuy47/q1vfQsf+chHxsa3PVgn4e1TnvIU3HTTTfhf/+t/Zcf6/T7e/OY3Y3Z2FpdeeukJ6b62tob19fXcsfPOOw9zc3Mj2/VE2RqJ074nKOeffz7e85734LnPfS7uda97ZW/4SNMU11xzDd7znvegUqmMrO/z5GlPexoe+9jH4j/9p/+E73//+7jf/e6Hj370o/jbv/1bvPKVr8ytx3vxi1+M3//938eLX/xiPPCBD8SnPvUpfOc73znpclxyySV47nOfi7e97W04evQoHv7wh+PjH/84rrrqqpNOc1J54QtfiDe84Q148pOfjBe96EW45ZZb8Cd/8ie4z33uky2aBjamjC+88EL8r//1v3DPe94Tu3btwkUXXYSLLrrolOsIAL/xG7+Bv/zLv8SP/MiP4LLLLsOuXbtw+eWX45prrsFf//VfZ9P4T3rSk3DgwAE84hGPwP79+/Gtb30Lb3nLW/DUpz41W8/z+7//+/jEJz6BhzzkIfiFX/gFXHjhhbjtttvw5S9/GR/72Mdw22233SFlihJlu8n97nc/PP/5z8c73/lOHDlyBJdeeim+8IUv4PLLL8fTn/50PPaxjwUAXH755Xjb296GZzzjGTjvvPOwvLyMP/3TP8X8/Hw2gLz00kvxkpe8BK973evwla98BU960pNQr9fx3e9+F+9973vx3/7bf8OznvWsoC4Pf/jD8da3vhUvf/nLccEFF+Te8PHJT34Sf/d3f4ff/d3fBXBi/J9UXvva1+LDH/4wHvWoR+HlL395NiC7z33uM/a1o5dccgmq1Spe//rX4+jRo5iamsLjHvc4d1biF3/xF/GOd7wDL3jBC/ClL30J55xzDt73vvfhs5/9LN74xjdO/NCLyXe+8x08/vGPx3Oe8xxceOGFqNVqeP/734+bb74ZP/VTP3VCaUWZUO6UZ4y3gVx11VXpy172svQe97hH2mw20+np6fSCCy5IX/rSl6Zf+cpXcmGf//znpzMzM246y8vL6a/+6q+mZ555Zlqv19Pzzz8//YM/+IN0OBzmwq2traUvetGL0oWFhXRubi59znOek95yyy3BrV4OHjyYi29bJlxzzTXZsXa7nV522WXp7t2705mZmfRpT3taev3112/pVi+qh8mf//mfp3e/+93TRqORXnLJ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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "image/png": "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38sFF1zAf/kv/+W4QPt8Tj/9dH7913+dH/7hH+bhD3843/M93wMcO5RcSuHiiy/mPve5Dy94wQt461vfyi//8i9z/vnn88M//MMn1d6PfvSjOOfYt28fV111Ffe73/1YLpdceumlHDx4kFe96lV813d9F7/3e7/Hwx/+cABe/vKXc+mll/LIRz6SH/mRH6HrOv7xH/+Rv/mbv+Gxj30sAFdddRX3ve99j4b46aefzh//8R9zySWXsL29zTOf+cyTau9odCr6yEc+AsDBgweBdZa96lWv4pGPfCTPetaz+Ju/+Rue97zn8b73vY/Xv/71wLqz8qAHPYjTTz+dn/zJn2Tfvn1cccUV/P7v//4xj/2UpzyFV77ylTzxiU/k0ksv5fLLL+clL3kJ//f//l/++q//+mZHBJbLJW9729v45m/+Zm5729t+wddwS/P/r/7qr/j93/99nva0p7GxscGLXvQiHvGIR/Cxj33s6Hn4p3/6p6Ov8TnPeQ45Z5797GdzxhlnfN62fPM3fzOXXnrpcdN3Pnsaz2dbrVY84AEP4MMf/jDPeMYzOO+883jta1/LE57wBA4fPsyP/MiPHHP717zmNezs7PCUpzwFEeEXf/EX+Z7v+R4++tGPHj2fj3jEI/jnf/5n/t2/+3ece+65XH311fyv//W/+NjHPnbcMPToi8BGJ+zIkSMG2MMe9rDjfnbo0CG75pprjn4tl8ujP3v84x9vgP3kT/7kMfd5wxveYID93M/93DHHH/nIR5qI2Ic//GEzM7v88ssNsFe84hXHPS9gz372s49+/+xnP9sAe9KTnnTM7R7+8IfbwYMHj37/7ne/2wB72tOedsztHvvYxx73mF/Ia1/7WgPs7W9/+3HteMxjHnPc7S+66CK76KKLjjv++Mc/3s4555yj319zzTU325Ybz+nP/MzPHHP867/+6+0e97jHF2zzRRddZHe4wx2O/r7e97732aWXXmqAPfShDzUzs2c+85kG2F/+5V8evd/Ozo6dd955du6551opxczMvvu7v9vufOc7f97nu+SSS+yss86ya6+99pjjj370o21ra+uYv5fRaLT2ile8wgB761vfatdcc419/OMft9/5nd+xgwcP2mQysU984hNHs+zJT37yMff9sR/7MQPsT//0T83M7PWvf70B9nd/93c3+3x/+Zd/aYC9+tWvPub4m9/85ps8/tn+4R/+wQD7kR/5kRN6bSea/2brnI8xHnPsxud78YtffPTYwx72MGvb1q688sqjx9773veac84+99/9OeecY49//OOPfn9TOX6jz83sF77whQbYb//2bx89NgyDfcM3fIPN53Pb3t42s3/533Xw4EG7/vrrj972D/7gDwywP/zDPzSz9f9PwH7pl37p852y0RfROOx7C2xvbwPcZImRBzzgAZx++ulHv37t137tuNt87qdRb3rTm3DOcemllx5z/FnPehZmxh//8R+fdFuf+tSnHvP9N33TN3HdddcdfQ1vetObAI577i/2J1Cf244vtpt6nR/96EdP6L7vf//7j/6+7njHO/LiF7+YhzzkIUeHYt/0pjdx73vf++hwNax/9z/0Qz/EFVdcwXvf+14A9u3bxyc+8YnjhjFuZGa87nWv46EPfShmxrXXXnv06+KLL+bIkSO8613vOpmXPxqdEh74wAdy+umnc/bZZ/PoRz+a+XzO61//em5961sfzbJ//+///TH3edazngVwdJrGvn37gPXoTUrpJp/nta99LVtbW3z7t3/7Me/Te9zjHsznc97+9rffbBtvzNabGu69Kbc0/x/4wAdy/vnnH/3+rne9K5ubm0fzrpTCW97yFh72sIcd88njHe94Ry6++OITatOJetOb3sSZZ555zHzLEAKXXnopu7u7/Pmf//kxt/++7/s+9u/ff/T7b/qmbwI42vbJZEKMkT/7sz87oeH10b/eOOx7C9z4pt7d3T3uZy972cvY2dnhqquuOmYRwY2899zmNrc55tiVV17J13zN1xwXFjd+1H7llVeedFs/d9jhxjfeoUOH2Nzc5Morr0RVjwkTgAsvvPCkn/OmnHfeeV/Ux/tsbdsenRd4o/37959weJx77rm8/OUvP1pC4oILLuBWt7rV0Z9feeWVNzm8/9m/n7vc5S78h//wH3jrW9/Kve99b253u9vxoAc9iMc+9rHc//73B9YriQ8fPsxll13GZZdddpNtufrqq0+ozaPRqejXfu3XuP3tb4/3njPOOIMLL7zw6KK6G7Psdre73TH3OfPMM9m3b9/RHL3ooot4xCMewXOf+1x+9Vd/lQc84AE87GEP47GPfezRxQ8f+tCHOHLkyDE58Nk+3/t0c3MTgJ2dnRN6Tbc0/29qKPmz8+6aa65htVpxwQUXHHe7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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "ctd_loader = dlc_torch.DLCLoader(config, shuffle=CTD_SHUFFLE)\n",
+ "\n",
+ "# We'll edit the model config here directly; In practice, edit the pytorch_config file instead.\n",
+ "# The parameters that can be set here are the parameters of the `dlc_torch.GenSamplingConfig`\n",
+ "ctd_loader.model_cfg[\"data\"][\"gen_sampling\"] = {\n",
+ " \"jitter_prob\": 0.5,\n",
+ " \"swap_prob\": 0.1,\n",
+ " \"inv_prob\": 0.1,\n",
+ " \"miss_prob\": 0.25,\n",
+ "}\n",
+ "\n",
+ "transform = dlc_torch.build_transforms(ctd_loader.model_cfg[\"data\"][\"train\"])\n",
+ "dataset = ctd_loader.create_dataset(transform, mode=\"train\", task=ctd_loader.pose_task)\n",
+ "\n",
+ "# Fix the seeds for reproducibility; you can change the seed from `0` to another value\n",
+ "# to change the results\n",
+ "dlc_torch.fix_seeds(0)\n",
+ "plot_generative_sampling(dataset)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d46ecdc8",
+ "metadata": {
+ "id": "d46ecdc8"
+ },
+ "source": [
+ "#### Training and Evaluating the CTD Model\n",
+ "\n",
+ "Next, we can simply train the CTD model. It should take **20 to 60 minutes** to train the model to 150 epochs on a GPU, depending on the performance of the machine you're on.\n",
+ "\n",
+ "If you think your model has converged before the end of training, you can always interrupt the execution of the cell using the \"Stop\" button, as I did here after 150 epochs. The best-performing model up to that point should be saved.\n",
+ "\n",
+ "You'll notice that in the logs for the bottom-up model above, it's printed `using 78 images and 34 for testing` while now it's showing `using 234 images and 102 for testing`. This is because CTD models (and top-down models) perform pose estimation on each mouse indenpendently! As their are 3 mice per image, each ground-truth image creates 3 examples the model can use for training. Checkout the [docs](https://deeplabcut.github.io/DeepLabCut/docs/pytorch/architectures.html#information-on-multi-animal-models) for more information on different approaches to pose estimation!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "7427576f",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 1000,
+ "referenced_widgets": [
+ "c95c172c3f4c468c8d3c4ed859405670",
+ "9ce048962ea4456ab5201da2ec611028",
+ "50677fd71424491f9f963efb76526763",
+ "e394660e6bf2425d81dde09c75367caa",
+ "d9204c9f72ce4e729ba5ff2168cc787e",
+ "d8c442f4d8ab4484b2a91accd2fdcd9d",
+ "60c578d6f09e48b3939ac559a5305fde",
+ "f6f2745f152341fbb8f909ff9c159d61",
+ "cd18393881074e36850cb8cb4ee96e56",
+ "ef61bcc8f6124b6c9681bd3c29b1bcac",
+ "ac9e6c7e5a55470b87443a9d1f290e20"
+ ]
+ },
+ "executionInfo": {
+ "elapsed": 3371917,
+ "status": "error",
+ "timestamp": 1744361892651,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "7427576f",
+ "outputId": "2b50e01f-1bbd-48e3-f110-06f1b4f5fd70"
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "Training with configuration:\n",
+ "data:\n",
+ " bbox_margin: 25\n",
+ " colormode: RGB\n",
+ " inference:\n",
+ " normalize_images: True\n",
+ " top_down_crop:\n",
+ " width: 256\n",
+ " height: 256\n",
+ " crop_with_context: False\n",
+ " train:\n",
+ " affine:\n",
+ " p: 0.5\n",
+ " rotation: 30\n",
+ " scaling: [1.0, 1.0]\n",
+ " translation: 0\n",
+ " gaussian_noise: 12.75\n",
+ " motion_blur: True\n",
+ " normalize_images: True\n",
+ " top_down_crop:\n",
+ " width: 256\n",
+ " height: 256\n",
+ " crop_with_context: False\n",
+ " conditions:\n",
+ " shuffle: 1\n",
+ " snapshot_index: -1\n",
+ " gen_sampling:\n",
+ " keypoint_sigmas: 0.1\n",
+ "device: auto\n",
+ "metadata:\n",
+ " project_path: /content/trimice-dlc-2021-06-22\n",
+ " pose_config_path: /content/trimice-dlc-2021-06-22/dlc-models-pytorch/iteration-0/trimiceJun22-trainset70shuffle2/train/pytorch_config.yaml\n",
+ " bodyparts: ['snout', 'leftear', 'rightear', 'shoulder', 'spine1', 'spine2', 'spine3', 'spine4', 'tailbase', 'tail1', 'tail2', 'tailend']\n",
+ " unique_bodyparts: []\n",
+ " individuals: ['mus1', 'mus2', 'mus3']\n",
+ " with_identity: None\n",
+ "method: ctd\n",
+ "model:\n",
+ " backbone:\n",
+ " type: CondPreNet\n",
+ " backbone:\n",
+ " type: CSPNeXt\n",
+ " model_name: cspnext_m\n",
+ " freeze_bn_stats: False\n",
+ " freeze_bn_weights: False\n",
+ " deepen_factor: 0.67\n",
+ " widen_factor: 0.75\n",
+ " kpt_encoder:\n",
+ " type: ColoredKeypointEncoder\n",
+ " num_joints: 12\n",
+ " kernel_size: [15, 15]\n",
+ " img_size: [256, 256]\n",
+ " backbone_output_channels: 768\n",
+ " heads:\n",
+ " bodypart:\n",
+ " type: HeatmapHead\n",
+ " weight_init: normal\n",
+ " predictor:\n",
+ " type: HeatmapPredictor\n",
+ " apply_sigmoid: False\n",
+ " clip_scores: True\n",
+ " location_refinement: True\n",
+ " locref_std: 7.2801\n",
+ " target_generator:\n",
+ " type: HeatmapGaussianGenerator\n",
+ " num_heatmaps: 12\n",
+ " pos_dist_thresh: 17\n",
+ " heatmap_mode: KEYPOINT\n",
+ " generate_locref: True\n",
+ " locref_std: 7.2801\n",
+ " criterion:\n",
+ " heatmap:\n",
+ " type: WeightedMSECriterion\n",
+ " weight: 1.0\n",
+ " locref:\n",
+ " type: WeightedHuberCriterion\n",
+ " weight: 0.05\n",
+ " heatmap_config:\n",
+ " channels: [768, 12]\n",
+ " kernel_size: [3]\n",
+ " strides: [2]\n",
+ " locref_config:\n",
+ " channels: [768, 24]\n",
+ " kernel_size: [3]\n",
+ " strides: [2]\n",
+ "net_type: ctd_prenet_cspnext_m\n",
+ "runner:\n",
+ " type: PoseTrainingRunner\n",
+ " gpus: None\n",
+ " key_metric: test.mAP\n",
+ " key_metric_asc: True\n",
+ " eval_interval: 10\n",
+ " optimizer:\n",
+ " type: AdamW\n",
+ " params:\n",
+ " lr: 1e-05\n",
+ " scheduler:\n",
+ " type: LRListScheduler\n",
+ " params:\n",
+ " lr_list: [[0.0005], [0.0001], [1e-05]]\n",
+ " milestones: [5, 90, 120]\n",
+ " snapshots:\n",
+ " max_snapshots: 5\n",
+ " save_epochs: 25\n",
+ " save_optimizer_state: False\n",
+ "train_settings:\n",
+ " batch_size: 8\n",
+ " dataloader_workers: 0\n",
+ " dataloader_pin_memory: False\n",
+ " display_iters: 500\n",
+ " epochs: 200\n",
+ " seed: 42\n",
+ "Downloading the pre-trained backbone to /usr/local/lib/python3.11/dist-packages/deeplabcut/pose_estimation_pytorch/models/backbones/pretrained_weights/cspnext_m.pt\n"
+ ]
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "c95c172c3f4c468c8d3c4ed859405670",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "cspnext_m.pt: 0%| | 0.00/49.3M [00:00, ?B/s]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "Data Transforms:\n",
+ " Training: Compose([\n",
+ " Affine(always_apply=False, p=0.5, interpolation=1, mask_interpolation=0, cval=0, mode=0, scale={'x': (1.0, 1.0), 'y': (1.0, 1.0)}, translate_percent=None, translate_px={'x': (0, 0), 'y': (0, 0)}, rotate=(-30, 30), fit_output=False, shear={'x': (0.0, 0.0), 'y': (0.0, 0.0)}, cval_mask=0, keep_ratio=True, rotate_method='largest_box'),\n",
+ " MotionBlur(always_apply=False, p=0.5, blur_limit=(3, 7), allow_shifted=True),\n",
+ " GaussNoise(always_apply=False, p=0.5, var_limit=(0, 162.5625), per_channel=True, mean=0),\n",
+ " Normalize(always_apply=False, p=1.0, mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], max_pixel_value=255.0),\n",
+ "], p=1.0, bbox_params={'format': 'coco', 'label_fields': ['bbox_labels'], 'min_area': 0.0, 'min_visibility': 0.0, 'min_width': 0.0, 'min_height': 0.0, 'check_each_transform': True}, keypoint_params={'format': 'xy', 'label_fields': ['class_labels'], 'remove_invisible': False, 'angle_in_degrees': True, 'check_each_transform': True}, additional_targets={}, is_check_shapes=True)\n",
+ " Validation: Compose([\n",
+ " Normalize(always_apply=False, p=1.0, mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], max_pixel_value=255.0),\n",
+ "], p=1.0, bbox_params={'format': 'coco', 'label_fields': ['bbox_labels'], 'min_area': 0.0, 'min_visibility': 0.0, 'min_width': 0.0, 'min_height': 0.0, 'check_each_transform': True}, keypoint_params={'format': 'xy', 'label_fields': ['class_labels'], 'remove_invisible': False, 'angle_in_degrees': True, 'check_each_transform': True}, additional_targets={}, is_check_shapes=True)\n",
+ "Using 234 images and 102 for testing\n",
+ "\n",
+ "Starting pose model training...\n",
+ "--------------------------------------------------\n",
+ "Epoch 1/200 (lr=1e-05), train loss 0.01711\n",
+ "Epoch 2/200 (lr=1e-05), train loss 0.01699\n",
+ "Epoch 3/200 (lr=1e-05), train loss 0.01687\n",
+ "Epoch 4/200 (lr=1e-05), train loss 0.01690\n",
+ "Epoch 5/200 (lr=0.0005), train loss 0.01683\n",
+ "Epoch 6/200 (lr=0.0005), train loss 0.01477\n",
+ "Epoch 7/200 (lr=0.0005), train loss 0.01076\n",
+ "Epoch 8/200 (lr=0.0005), train loss 0.00796\n",
+ "Epoch 9/200 (lr=0.0005), train loss 0.00670\n",
+ "Training for epoch 10 done, starting evaluation\n",
+ "Epoch 10/200 (lr=0.0005), train loss 0.00602, valid loss 0.00515\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 7.56\n",
+ " metrics/test.rmse_pcutoff: 5.56\n",
+ " metrics/test.mAP: 90.48\n",
+ " metrics/test.mAR: 92.35\n",
+ "Epoch 11/200 (lr=0.0005), train loss 0.00472\n",
+ "Epoch 12/200 (lr=0.0005), train loss 0.00448\n",
+ "Epoch 13/200 (lr=0.0005), train loss 0.00435\n",
+ "Epoch 14/200 (lr=0.0005), train loss 0.00387\n",
+ "Epoch 15/200 (lr=0.0005), train loss 0.00341\n",
+ "Epoch 16/200 (lr=0.0005), train loss 0.00344\n",
+ "Epoch 17/200 (lr=0.0005), train loss 0.00309\n",
+ "Epoch 18/200 (lr=0.0005), train loss 0.00310\n",
+ "Epoch 19/200 (lr=0.0005), train loss 0.00308\n",
+ "Training for epoch 20 done, starting evaluation\n",
+ "Epoch 20/200 (lr=0.0005), train loss 0.00305, valid loss 0.00318\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 6.61\n",
+ " metrics/test.rmse_pcutoff: 5.61\n",
+ " metrics/test.mAP: 94.12\n",
+ " metrics/test.mAR: 95.00\n",
+ "Epoch 21/200 (lr=0.0005), train loss 0.00273\n",
+ "Epoch 22/200 (lr=0.0005), train loss 0.00267\n",
+ "Epoch 23/200 (lr=0.0005), train loss 0.00256\n",
+ "Epoch 24/200 (lr=0.0005), train loss 0.00254\n",
+ "Epoch 25/200 (lr=0.0005), train loss 0.00241\n",
+ "Epoch 26/200 (lr=0.0005), train loss 0.00247\n",
+ "Epoch 27/200 (lr=0.0005), train loss 0.00246\n",
+ "Epoch 28/200 (lr=0.0005), train loss 0.00233\n",
+ "Epoch 29/200 (lr=0.0005), train loss 0.00234\n",
+ "Training for epoch 30 done, starting evaluation\n",
+ "Epoch 30/200 (lr=0.0005), train loss 0.00222, valid loss 0.00280\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 5.40\n",
+ " metrics/test.rmse_pcutoff: 4.04\n",
+ " metrics/test.mAP: 95.15\n",
+ " metrics/test.mAR: 96.18\n",
+ "Epoch 31/200 (lr=0.0005), train loss 0.00223\n",
+ "Epoch 32/200 (lr=0.0005), train loss 0.00239\n",
+ "Epoch 33/200 (lr=0.0005), train loss 0.00211\n",
+ "Epoch 34/200 (lr=0.0005), train loss 0.00193\n",
+ "Epoch 35/200 (lr=0.0005), train loss 0.00210\n",
+ "Epoch 36/200 (lr=0.0005), train loss 0.00204\n",
+ "Epoch 37/200 (lr=0.0005), train loss 0.00201\n",
+ "Epoch 38/200 (lr=0.0005), train loss 0.00186\n",
+ "Epoch 39/200 (lr=0.0005), train loss 0.00197\n",
+ "Training for epoch 40 done, starting evaluation\n",
+ "Epoch 40/200 (lr=0.0005), train loss 0.00195, valid loss 0.00255\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 4.46\n",
+ " metrics/test.rmse_pcutoff: 3.74\n",
+ " metrics/test.mAP: 97.18\n",
+ " metrics/test.mAR: 97.84\n",
+ "Epoch 41/200 (lr=0.0005), train loss 0.00188\n",
+ "Epoch 42/200 (lr=0.0005), train loss 0.00198\n",
+ "Epoch 43/200 (lr=0.0005), train loss 0.00192\n",
+ "Epoch 44/200 (lr=0.0005), train loss 0.00186\n",
+ "Epoch 45/200 (lr=0.0005), train loss 0.00188\n",
+ "Epoch 46/200 (lr=0.0005), train loss 0.00178\n",
+ "Epoch 47/200 (lr=0.0005), train loss 0.00180\n",
+ "Epoch 48/200 (lr=0.0005), train loss 0.00186\n",
+ "Epoch 49/200 (lr=0.0005), train loss 0.00171\n",
+ "Training for epoch 50 done, starting evaluation\n",
+ "Epoch 50/200 (lr=0.0005), train loss 0.00183, valid loss 0.00262\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 5.46\n",
+ " metrics/test.rmse_pcutoff: 3.90\n",
+ " metrics/test.mAP: 95.49\n",
+ " metrics/test.mAR: 95.78\n",
+ "Epoch 51/200 (lr=0.0005), train loss 0.00191\n",
+ "Epoch 52/200 (lr=0.0005), train loss 0.00198\n",
+ "Epoch 53/200 (lr=0.0005), train loss 0.00173\n",
+ "Epoch 54/200 (lr=0.0005), train loss 0.00179\n",
+ "Epoch 55/200 (lr=0.0005), train loss 0.00181\n",
+ "Epoch 56/200 (lr=0.0005), train loss 0.00187\n",
+ "Epoch 57/200 (lr=0.0005), train loss 0.00162\n",
+ "Epoch 58/200 (lr=0.0005), train loss 0.00156\n",
+ "Epoch 59/200 (lr=0.0005), train loss 0.00154\n",
+ "Training for epoch 60 done, starting evaluation\n",
+ "Epoch 60/200 (lr=0.0005), train loss 0.00152, valid loss 0.00216\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 4.11\n",
+ " metrics/test.rmse_pcutoff: 3.40\n",
+ " metrics/test.mAP: 97.45\n",
+ " metrics/test.mAR: 97.65\n",
+ "Epoch 61/200 (lr=0.0005), train loss 0.00151\n",
+ "Epoch 62/200 (lr=0.0005), train loss 0.00156\n",
+ "Epoch 63/200 (lr=0.0005), train loss 0.00143\n",
+ "Epoch 64/200 (lr=0.0005), train loss 0.00155\n",
+ "Epoch 65/200 (lr=0.0005), train loss 0.00149\n",
+ "Epoch 66/200 (lr=0.0005), train loss 0.00149\n",
+ "Epoch 67/200 (lr=0.0005), train loss 0.00148\n",
+ "Epoch 68/200 (lr=0.0005), train loss 0.00147\n",
+ "Epoch 69/200 (lr=0.0005), train loss 0.00160\n",
+ "Training for epoch 70 done, starting evaluation\n",
+ "Epoch 70/200 (lr=0.0005), train loss 0.00159, valid loss 0.00240\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 4.21\n",
+ " metrics/test.rmse_pcutoff: 3.46\n",
+ " metrics/test.mAP: 97.49\n",
+ " metrics/test.mAR: 97.65\n",
+ "Epoch 71/200 (lr=0.0005), train loss 0.00186\n",
+ "Epoch 72/200 (lr=0.0005), train loss 0.00187\n",
+ "Epoch 73/200 (lr=0.0005), train loss 0.00158\n",
+ "Epoch 74/200 (lr=0.0005), train loss 0.00161\n",
+ "Epoch 75/200 (lr=0.0005), train loss 0.00146\n",
+ "Epoch 76/200 (lr=0.0005), train loss 0.00145\n",
+ "Epoch 77/200 (lr=0.0005), train loss 0.00145\n",
+ "Epoch 78/200 (lr=0.0005), train loss 0.00144\n",
+ "Epoch 79/200 (lr=0.0005), train loss 0.00154\n",
+ "Training for epoch 80 done, starting evaluation\n",
+ "Epoch 80/200 (lr=0.0005), train loss 0.00154, valid loss 0.00225\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 4.01\n",
+ " metrics/test.rmse_pcutoff: 3.53\n",
+ " metrics/test.mAP: 96.84\n",
+ " metrics/test.mAR: 97.35\n",
+ "Epoch 81/200 (lr=0.0005), train loss 0.00154\n",
+ "Epoch 82/200 (lr=0.0005), train loss 0.00144\n",
+ "Epoch 83/200 (lr=0.0005), train loss 0.00138\n",
+ "Epoch 84/200 (lr=0.0005), train loss 0.00131\n",
+ "Epoch 85/200 (lr=0.0005), train loss 0.00143\n",
+ "Epoch 86/200 (lr=0.0005), train loss 0.00140\n",
+ "Epoch 87/200 (lr=0.0005), train loss 0.00142\n",
+ "Epoch 88/200 (lr=0.0005), train loss 0.00148\n",
+ "Epoch 89/200 (lr=0.0005), train loss 0.00139\n",
+ "Training for epoch 90 done, starting evaluation\n",
+ "Epoch 90/200 (lr=0.0001), train loss 0.00137, valid loss 0.00210\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 3.86\n",
+ " metrics/test.rmse_pcutoff: 3.39\n",
+ " metrics/test.mAP: 98.17\n",
+ " metrics/test.mAR: 98.33\n",
+ "Epoch 91/200 (lr=0.0001), train loss 0.00132\n",
+ "Epoch 92/200 (lr=0.0001), train loss 0.00114\n",
+ "Epoch 93/200 (lr=0.0001), train loss 0.00105\n",
+ "Epoch 94/200 (lr=0.0001), train loss 0.00102\n",
+ "Epoch 95/200 (lr=0.0001), train loss 0.00107\n",
+ "Epoch 96/200 (lr=0.0001), train loss 0.00102\n",
+ "Epoch 97/200 (lr=0.0001), train loss 0.00103\n",
+ "Epoch 98/200 (lr=0.0001), train loss 0.00104\n",
+ "Epoch 99/200 (lr=0.0001), train loss 0.00109\n",
+ "Training for epoch 100 done, starting evaluation\n",
+ "Epoch 100/200 (lr=0.0001), train loss 0.00101, valid loss 0.00182\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 3.91\n",
+ " metrics/test.rmse_pcutoff: 3.06\n",
+ " metrics/test.mAP: 97.78\n",
+ " metrics/test.mAR: 97.94\n",
+ "Epoch 101/200 (lr=0.0001), train loss 0.00105\n",
+ "Epoch 102/200 (lr=0.0001), train loss 0.00098\n",
+ "Epoch 103/200 (lr=0.0001), train loss 0.00101\n",
+ "Epoch 104/200 (lr=0.0001), train loss 0.00093\n",
+ "Epoch 105/200 (lr=0.0001), train loss 0.00102\n",
+ "Epoch 106/200 (lr=0.0001), train loss 0.00093\n",
+ "Epoch 107/200 (lr=0.0001), train loss 0.00104\n",
+ "Epoch 108/200 (lr=0.0001), train loss 0.00094\n",
+ "Epoch 109/200 (lr=0.0001), train loss 0.00094\n",
+ "Training for epoch 110 done, starting evaluation\n",
+ "Epoch 110/200 (lr=0.0001), train loss 0.00096, valid loss 0.00184\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 3.62\n",
+ " metrics/test.rmse_pcutoff: 3.03\n",
+ " metrics/test.mAP: 98.36\n",
+ " metrics/test.mAR: 98.43\n",
+ "Epoch 111/200 (lr=0.0001), train loss 0.00096\n",
+ "Epoch 112/200 (lr=0.0001), train loss 0.00105\n",
+ "Epoch 113/200 (lr=0.0001), train loss 0.00092\n",
+ "Epoch 114/200 (lr=0.0001), train loss 0.00098\n",
+ "Epoch 115/200 (lr=0.0001), train loss 0.00098\n",
+ "Epoch 116/200 (lr=0.0001), train loss 0.00092\n",
+ "Epoch 117/200 (lr=0.0001), train loss 0.00088\n",
+ "Epoch 118/200 (lr=0.0001), train loss 0.00092\n",
+ "Epoch 119/200 (lr=0.0001), train loss 0.00085\n",
+ "Training for epoch 120 done, starting evaluation\n",
+ "Epoch 120/200 (lr=1e-05), train loss 0.00086, valid loss 0.00177\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 3.85\n",
+ " metrics/test.rmse_pcutoff: 3.37\n",
+ " metrics/test.mAP: 97.23\n",
+ " metrics/test.mAR: 97.94\n",
+ "Epoch 121/200 (lr=1e-05), train loss 0.00087\n",
+ "Epoch 122/200 (lr=1e-05), train loss 0.00092\n",
+ "Epoch 123/200 (lr=1e-05), train loss 0.00084\n",
+ "Epoch 124/200 (lr=1e-05), train loss 0.00082\n",
+ "Epoch 125/200 (lr=1e-05), train loss 0.00087\n",
+ "Epoch 126/200 (lr=1e-05), train loss 0.00081\n",
+ "Epoch 127/200 (lr=1e-05), train loss 0.00077\n",
+ "Epoch 128/200 (lr=1e-05), train loss 0.00083\n",
+ "Epoch 129/200 (lr=1e-05), train loss 0.00087\n",
+ "Training for epoch 130 done, starting evaluation\n",
+ "Epoch 130/200 (lr=1e-05), train loss 0.00081, valid loss 0.00165\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 3.36\n",
+ " metrics/test.rmse_pcutoff: 3.01\n",
+ " metrics/test.mAP: 98.75\n",
+ " metrics/test.mAR: 98.82\n",
+ "Epoch 131/200 (lr=1e-05), train loss 0.00078\n",
+ "Epoch 132/200 (lr=1e-05), train loss 0.00083\n",
+ "Epoch 133/200 (lr=1e-05), train loss 0.00079\n",
+ "Epoch 134/200 (lr=1e-05), train loss 0.00088\n",
+ "Epoch 135/200 (lr=1e-05), train loss 0.00087\n",
+ "Epoch 136/200 (lr=1e-05), train loss 0.00084\n",
+ "Epoch 137/200 (lr=1e-05), train loss 0.00085\n",
+ "Epoch 138/200 (lr=1e-05), train loss 0.00083\n",
+ "Epoch 139/200 (lr=1e-05), train loss 0.00088\n",
+ "Training for epoch 140 done, starting evaluation\n",
+ "Epoch 140/200 (lr=1e-05), train loss 0.00081, valid loss 0.00170\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 3.40\n",
+ " metrics/test.rmse_pcutoff: 3.04\n",
+ " metrics/test.mAP: 98.34\n",
+ " metrics/test.mAR: 98.43\n",
+ "Epoch 141/200 (lr=1e-05), train loss 0.00084\n",
+ "Epoch 142/200 (lr=1e-05), train loss 0.00081\n",
+ "Epoch 143/200 (lr=1e-05), train loss 0.00085\n",
+ "Epoch 144/200 (lr=1e-05), train loss 0.00085\n",
+ "Epoch 145/200 (lr=1e-05), train loss 0.00083\n",
+ "Epoch 146/200 (lr=1e-05), train loss 0.00089\n",
+ "Epoch 147/200 (lr=1e-05), train loss 0.00075\n",
+ "Epoch 148/200 (lr=1e-05), train loss 0.00079\n",
+ "Epoch 149/200 (lr=1e-05), train loss 0.00079\n",
+ "Training for epoch 150 done, starting evaluation\n",
+ "Epoch 150/200 (lr=1e-05), train loss 0.00084, valid loss 0.00167\n",
+ "Model performance:\n",
+ " metrics/test.rmse: 3.56\n",
+ " metrics/test.rmse_pcutoff: 2.89\n",
+ " metrics/test.mAP: 98.04\n",
+ " metrics/test.mAR: 98.24\n"
+ ]
+ },
+ {
+ "ename": "KeyboardInterrupt",
+ "evalue": "",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdeeplabcut\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain_network\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshuffle\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mCTD_SHUFFLE\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/deeplabcut/compat.py\u001b[0m in \u001b[0;36mtrain_network\u001b[0;34m(config, shuffle, trainingsetindex, max_snapshots_to_keep, displayiters, saveiters, maxiters, epochs, save_epochs, allow_growth, gputouse, autotune, keepdeconvweights, modelprefix, superanimal_name, superanimal_transfer_learning, engine, device, snapshot_path, detector_path, batch_size, detector_batch_size, detector_epochs, detector_save_epochs, pose_threshold, pytorch_cfg_updates)\u001b[0m\n\u001b[1;32m 285\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mdeeplabcut\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpose_estimation_pytorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mapis\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mtrain_network\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 286\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 287\u001b[0;31m return train_network(\n\u001b[0m\u001b[1;32m 288\u001b[0m \u001b[0mconfig\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 289\u001b[0m \u001b[0mshuffle\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mshuffle\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/deeplabcut/pose_estimation_pytorch/apis/training.py\u001b[0m in \u001b[0;36mtrain_network\u001b[0;34m(config, shuffle, trainingsetindex, modelprefix, device, snapshot_path, detector_path, load_head_weights, batch_size, epochs, save_epochs, detector_batch_size, detector_epochs, detector_save_epochs, display_iters, max_snapshots_to_keep, pose_threshold, pytorch_cfg_updates)\u001b[0m\n\u001b[1;32m 358\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 359\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mloader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel_cfg\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"train_settings\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"epochs\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 360\u001b[0;31m train(\n\u001b[0m\u001b[1;32m 361\u001b[0m \u001b[0mloader\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mloader\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 362\u001b[0m \u001b[0mrun_config\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mloader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel_cfg\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/deeplabcut/pose_estimation_pytorch/apis/training.py\u001b[0m in \u001b[0;36mtrain\u001b[0;34m(loader, run_config, task, device, gpus, logger_config, snapshot_path, transform, inference_transform, max_snapshots_to_keep, load_head_weights)\u001b[0m\n\u001b[1;32m 190\u001b[0m \u001b[0mlogging\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"\\nStarting pose model training...\\n\"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m50\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0;34m\"-\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 191\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 192\u001b[0;31m runner.fit(\n\u001b[0m\u001b[1;32m 193\u001b[0m \u001b[0mtrain_dataloader\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 194\u001b[0m \u001b[0mvalid_dataloader\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/deeplabcut/pose_estimation_pytorch/runners/train.py\u001b[0m in \u001b[0;36mfit\u001b[0;34m(self, train_loader, valid_loader, epochs, display_iters)\u001b[0m\n\u001b[1;32m 212\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcurrent_epoch\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 213\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_metadata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"epoch\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 214\u001b[0;31m train_loss = self._epoch(\n\u001b[0m\u001b[1;32m 215\u001b[0m \u001b[0mtrain_loader\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"train\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdisplay_iters\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdisplay_iters\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 216\u001b[0m )\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/deeplabcut/pose_estimation_pytorch/runners/train.py\u001b[0m in \u001b[0;36m_epoch\u001b[0;34m(self, loader, mode, display_iters)\u001b[0m\n\u001b[1;32m 274\u001b[0m \u001b[0mloss_metrics\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdefaultdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 275\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch\u001b[0m \u001b[0;32min\u001b[0m \u001b[0menumerate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 276\u001b[0;31m \u001b[0mlosses_dict\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbatch\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 277\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m\"total_loss\"\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mlosses_dict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 278\u001b[0m \u001b[0mepoch_loss\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlosses_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"total_loss\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/deeplabcut/pose_estimation_pytorch/runners/train.py\u001b[0m in \u001b[0;36mstep\u001b[0;34m(self, batch, mode)\u001b[0m\n\u001b[1;32m 438\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m'cond_keypoints'\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mbatch\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'context'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 439\u001b[0m \u001b[0mcond_kpts\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbatch\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'context'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'cond_keypoints'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 440\u001b[0;31m \u001b[0moutputs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minputs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcond_kpts\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcond_kpts\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 441\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 442\u001b[0m \u001b[0moutputs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minputs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/torch/nn/modules/module.py\u001b[0m in \u001b[0;36m_wrapped_call_impl\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1737\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_compiled_call_impl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# type: ignore[misc]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1738\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1739\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_call_impl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1740\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1741\u001b[0m \u001b[0;31m# torchrec tests the code consistency with the following code\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/torch/nn/modules/module.py\u001b[0m in \u001b[0;36m_call_impl\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1748\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0m_global_backward_pre_hooks\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0m_global_backward_hooks\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1749\u001b[0m or _global_forward_hooks or _global_forward_pre_hooks):\n\u001b[0;32m-> 1750\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mforward_call\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1751\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1752\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/deeplabcut/pose_estimation_pytorch/models/model.py\u001b[0m in \u001b[0;36mforward\u001b[0;34m(self, x, **backbone_kwargs)\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdim\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 77\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 78\u001b[0;31m \u001b[0mfeatures\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackbone\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mbackbone_kwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 79\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mneck\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0mfeatures\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mneck\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfeatures\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/torch/nn/modules/module.py\u001b[0m in \u001b[0;36m_wrapped_call_impl\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1737\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_compiled_call_impl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# type: ignore[misc]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1738\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1739\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_call_impl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1740\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1741\u001b[0m \u001b[0;31m# torchrec tests the code consistency with the following code\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/torch/nn/modules/module.py\u001b[0m in \u001b[0;36m_call_impl\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1748\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0m_global_backward_pre_hooks\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0m_global_backward_hooks\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1749\u001b[0m or _global_forward_hooks or _global_forward_pre_hooks):\n\u001b[0;32m-> 1750\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mforward_call\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1751\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1752\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/deeplabcut/pose_estimation_pytorch/models/backbones/cond_prenet.py\u001b[0m in \u001b[0;36mforward\u001b[0;34m(self, x, cond_kpts)\u001b[0m\n\u001b[1;32m 98\u001b[0m \u001b[0mcond_kpts\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcond_kpts\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdetach\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnumpy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 99\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 100\u001b[0;31m \u001b[0mcond_hm\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcond_enc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcond_kpts\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msqueeze\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 101\u001b[0m \u001b[0mcond_hm\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfrom_numpy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcond_hm\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0mcond_hm\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcond_hm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpermute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# (B, C, H, W)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/deeplabcut/pose_estimation_pytorch/models/modules/kpt_encoders.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, keypoints, size)\u001b[0m\n\u001b[1;32m 243\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 244\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbatch_size\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 245\u001b[0;31m \u001b[0mcondition_heatmap\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mblur_heatmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcondition\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 246\u001b[0m \u001b[0mcondition\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcondition_heatmap\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 247\u001b[0m \u001b[0;31m# condition = self.blur_heatmap_batch(torch.from_numpy(condition))\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.11/dist-packages/deeplabcut/pose_estimation_pytorch/models/modules/kpt_encoders.py\u001b[0m in \u001b[0;36mblur_heatmap\u001b[0;34m(self, heatmap)\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0mThe\u001b[0m \u001b[0mheatmap\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0ma\u001b[0m \u001b[0mGaussian\u001b[0m \u001b[0mblur\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msuch\u001b[0m \u001b[0mthat\u001b[0m \u001b[0mmax\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheatmap\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m255\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 77\u001b[0m \"\"\"\n\u001b[0;32m---> 78\u001b[0;31m \u001b[0mheatmap\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcv2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mGaussianBlur\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheatmap\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkernel_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msigmaX\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 79\u001b[0m \u001b[0mam\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mamax\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheatmap\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mam\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
+ ]
+ }
+ ],
+ "source": [
+ "deeplabcut.train_network(config, shuffle=CTD_SHUFFLE)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5d4b810f",
+ "metadata": {
+ "id": "5d4b810f"
+ },
+ "source": [
+ "If your CTD model is well trained, it should now outperform the performance of the BU model who's predictions it uses as conditions!\n",
+ "\n",
+ "Note that during training, the model is evaluated using pose conditions that were created with generative sampling. When you evaluate the network with the `evaluate_network` method, the performance will be different as you're using the actual conditions from the bottom-up model we trained first."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "0cb3c2da",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "executionInfo": {
+ "elapsed": 9116,
+ "status": "ok",
+ "timestamp": 1744361908966,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "0cb3c2da",
+ "outputId": "6b446a05-afd1-452f-c3f9-d866caed6bc7"
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 78/78 [00:05<00:00, 14.65it/s]\n",
+ "100%|██████████| 34/34 [00:02<00:00, 14.91it/s]\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Evaluation results for DLC_CtdPrenetCspnextM_trimiceJun22shuffle2_snapshot_130-results.csv (pcutoff: 0.01):\n",
+ "train rmse 2.46\n",
+ "train rmse_pcutoff 2.46\n",
+ "train mAP 98.51\n",
+ "train mAR 98.93\n",
+ "test rmse 4.41\n",
+ "test rmse_pcutoff 4.41\n",
+ "test mAP 96.88\n",
+ "test mAR 97.06\n",
+ "Name: (0.7, 2, 130, -1, 0.01), dtype: float64\n"
+ ]
+ }
+ ],
+ "source": [
+ "deeplabcut.evaluate_network(config, Shuffles=[CTD_SHUFFLE])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5ea24b55",
+ "metadata": {
+ "id": "5ea24b55"
+ },
+ "source": [
+ "## Tracking with CTD\n",
+ "\n",
+ "One of the big advantages of having a CTD model is that it can be used to track individuals directly! Let's say you have the pose for your animals at `frame T`. Then you can use those poses as conditions for `frame T+1`, and let your CTD model simply \"update\" the poses depending on how much your mice moved.\n",
+ "\n",
+ "In the simplest scenario, you only need to run the BU model on the first frame, and then the CTD model takes over for inference and tracking:\n",
+ "\n",
+ "1. Run the BU model to generate conditions for the 1st frame of the video\n",
+ "2. For every frame after that, use the predictions from the previous frame as conditions\n",
+ "\n",
+ "However, this may not fit your scenario perfectly. Maybe all the mice aren't present in the first frame, and if they aren't detected by the BU model they'll never be tracked. Maybe at some point the CTD model makes an error and you lose track of a mouse. There are some options to deal with this:\n",
+ "\n",
+ "- Run the BU model every time at least one mouse is not detected (if you expect N mice to be in the video and you only detect N-1 mice, run the BU model):\n",
+ " - In this case, the predictions from the BU model need to be \"merged in\" to the existing N-1 tracks\n",
+ " - We can merge them in by using a similarity score between poses (OKS) which ranges from 0 to 1\n",
+ " - You likely don't want to run the BU model every frame, as this would slow down inference.\n",
+ "- Run the BU model every K frames in case new mice appear\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "bbc777c8",
+ "metadata": {
+ "id": "bbc777c8"
+ },
+ "source": [
+ "### Downloading a Tri-Mouse video\n",
+ "\n",
+ "First, let's download a video from the Tri-Mouse dataset. Note that this may take some time to run (1 minute or 2). If you have any issues downloading the files through the code, you can simply download the zipfile through [zenodo.org/records/7883589/files/demo-me-2021-07-14.zip](https://zenodo.org/records/7883589/files/demo-me-2021-07-14.zip?download=1), and then drag-and-drop the video in `demo-me-2021-07-14/videos/videocompressed1.mp4` file into COLAB in the right panel to upload it. Make sure the video is fully uploaded before you run analysis."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "d678a5e4",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "executionInfo": {
+ "elapsed": 43,
+ "status": "ok",
+ "timestamp": 1744361934728,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "d678a5e4",
+ "outputId": "4d9dd907-183a-4f34-94a1-2550b24cafc0"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Video will be saved in /content/videocompressed1.mp4\n"
+ ]
+ }
+ ],
+ "source": [
+ "download_path = Path.cwd()\n",
+ "video_name = \"videocompressed1.mp4\"\n",
+ "video_path = str(download_path / video_name)\n",
+ "print(f\"Video will be saved in {video_path}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2247556f",
+ "metadata": {
+ "id": "2247556f"
+ },
+ "outputs": [],
+ "source": [
+ "print(f\"Downloading the tri-mouse video into {download_path}\")\n",
+ "\n",
+ "url_video_record = \"https://zenodo.org/api/records/7883589\"\n",
+ "response = requests.get(url_video_record)\n",
+ "if response.status_code == 200:\n",
+ " file = response.json()[\"files\"][0]\n",
+ " title = file[\"key\"]\n",
+ " print(f\"Downloading {title}...\")\n",
+ " with requests.get(file[\"links\"][\"self\"], stream=True) as r:\n",
+ " with ZipFile(BytesIO(r.content)) as zf:\n",
+ " zf.extractall(path=download_path)\n",
+ "else:\n",
+ " raise ValueError(f\"The URL {url_video_record} could not be reached.\")\n",
+ "\n",
+ "# Check that the video was downloaded\n",
+ "src_video_path = download_path / \"demo-me-2021-07-14\" / \"videos\" / video_name\n",
+ "if not src_video_path.exists():\n",
+ " raise ValueError(\"Failed to download the video\")\n",
+ "\n",
+ "# Move the video to the final path\n",
+ "shutil.move(src_video_path, video_path)\n",
+ "if not Path(video_path).exists():\n",
+ " raise ValueError(\"Failed to move the video\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "721ce122",
+ "metadata": {
+ "id": "721ce122"
+ },
+ "source": [
+ "### Running Video Analysis\n",
+ "\n",
+ "You can track using your CTD model by setting `ctd_tracking=True` when calling `analyze_videos`. Of course, you then won't need to convert detections to tracklets or link tracklets, as the CTD model will directly be tracking the animals. This should run at 15 to 40 FPS depending on your hardware.\n",
+ "\n",
+ "You can create a labeled video containing the predictions made with the CTD tracker by setting `track_method=\"ctd\"` when calling `create_labeled_video`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "50b3787c",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "executionInfo": {
+ "elapsed": 193256,
+ "status": "ok",
+ "timestamp": 1744363303936,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "50b3787c",
+ "outputId": "c9e0c4fb-fab0-4dec-a587-a6c3e003b4ef"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Analyzing videos with /content/trimice-dlc-2021-06-22/dlc-models-pytorch/iteration-0/trimiceJun22-trainset70shuffle2/train/snapshot-best-130.pt\n",
+ "CTD tracking can only be used with batch size 1. Updating it.\n",
+ "Starting to analyze /content/videocompressed1.mp4\n",
+ "Video metadata: \n",
+ " Overall # of frames: 2330\n",
+ " Duration of video [s]: 77.67\n",
+ " fps: 30.0\n",
+ " resolution: w=640, h=480\n",
+ "\n",
+ "Running pose prediction with batch size 1\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 2330/2330 [02:10<00:00, 17.80it/s]\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Saving results in /content/videocompressed1DLC_CtdPrenetCspnextM_trimiceJun22shuffle2_snapshot_130_ctd.h5 and /content/videocompressed1DLC_CtdPrenetCspnextM_trimiceJun22shuffle2_snapshot_130_ctd_full.pickle\n",
+ "The videos are analyzed. Now your research can truly start!\n",
+ "You can create labeled videos with 'create_labeled_video'.\n",
+ "If the tracking is not satisfactory for some videos, consider expanding the training set. You can use the function 'extract_outlier_frames' to extract a few representative outlier frames.\n",
+ "\n",
+ "Starting to process video: /content/videocompressed1.mp4\n",
+ "Loading /content/videocompressed1.mp4 and data.\n",
+ "Duration of video [s]: 77.67, recorded with 30.0 fps!\n",
+ "Overall # of frames: 2330 with cropped frame dimensions: 640 480\n",
+ "Generating frames and creating video.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/usr/local/lib/python3.11/dist-packages/deeplabcut/utils/make_labeled_video.py:146: FutureWarning: DataFrame.groupby with axis=1 is deprecated. Do `frame.T.groupby(...)` without axis instead.\n",
+ " Dataframe.groupby(level=\"individuals\", axis=1).size().values // 3\n",
+ "100%|██████████| 2330/2330 [00:58<00:00, 39.95it/s]\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "[True]"
+ ]
+ },
+ "execution_count": 19,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "deeplabcut.analyze_videos(\n",
+ " config,\n",
+ " [video_path],\n",
+ " shuffle=CTD_SHUFFLE,\n",
+ " ctd_tracking=True,\n",
+ ")\n",
+ "deeplabcut.create_labeled_video(\n",
+ " config,\n",
+ " [video_path],\n",
+ " shuffle=CTD_SHUFFLE,\n",
+ " track_method=\"ctd\",\n",
+ " color_by=\"individual\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2jDlgrJnEw_y",
+ "metadata": {
+ "id": "2jDlgrJnEw_y"
+ },
+ "source": [
+ "We can then visualize the results of tracking with CTD."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "JU8d1zvBEwWq",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 501,
+ "output_embedded_package_id": "15Gie9uW38e2cxxI0IyBAAYBUpaRBia4K"
+ },
+ "executionInfo": {
+ "elapsed": 7557,
+ "status": "ok",
+ "timestamp": 1744363338435,
+ "user": {
+ "displayName": "Niels Poulsen",
+ "userId": "07147513190166716525"
+ },
+ "user_tz": -120
+ },
+ "id": "JU8d1zvBEwWq",
+ "outputId": "ec8e3860-5e21-47b3-a457-7576593f2379"
+ },
+ "outputs": [],
+ "source": [
+ "from base64 import b64encode\n",
+ "\n",
+ "from IPython.display import HTML\n",
+ "\n",
+ "\n",
+ "def show_video(video_path, width=640):\n",
+ " video_file = open(video_path, \"rb\").read()\n",
+ " video_url = f\"data:video/mp4;base64,{b64encode(video_file).decode()}\"\n",
+ " return HTML(f\"\"\"\n",
+ " \n",
+ " \n",
+ " \n",
+ " \"\"\")\n",
+ "\n",
+ "\n",
+ "show_video(download_path / \"videocompressed1DLC_CtdCoamW32_trimiceJun22shuffle2_snapshot_best-80_ctd_id_p1_labeled.mp4\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "56bd9ab7",
+ "metadata": {
+ "id": "56bd9ab7"
+ },
+ "source": [
+ "It can be beneficial to customize the tracking parameters a bit. The tracking parameters you can set are:\n",
+ "\n",
+ "Note: [OKS (object-keypoint similarity)](https://cocodataset.org/#keypoints-eval) is a similarity metric for pose estimation, ranging from 0 to 1 (where 1 means the pose is identical)\n",
+ "\n",
+ "- **`bu_on_lost_idv`**: When True, the BU model is run when there are fewer conditions found than the expected number of individuals in the video.\n",
+ "- **`bu_min_frequency`**: The minimum frequency at which the BU model is run to generate conditions. If None, the BU model is only run to initialize the pose in the first frame, and then is not run again. If a positive number N, the BU model is run every N frames. The BU predictions are then combined with the CTD predictions to continue the tracklets.\n",
+ "- **`bu_max_frequency`**: The maximum frequency at which the BU model can be run. Must be greater than `bu_min_frequency`. When there are fewer conditions than individuals expected in the video and `bu_on_lost_idv` is True, the BU model may be run on every frame. This can happen if individuals can disappear from the video, and each frame may have a variable number of individuals. If `bu_max_frequency` is set to N, then the BU model will be run at most every N-th frame, which improves the inference speed of the model.\n",
+ "- **`threshold_bu_add`**: The OKS threshold below which a BU pose must be (wrt. any existing CTD pose) to be added to the poses.\n",
+ "- **`threshold_ctd`**: The score threshold below which detected keypoints are NOT given to the CTD model to predict pose for the next frame.\n",
+ "- **`threshold_nms`**: The OKS threshold to use for non-maximum suppression. This is used to remove duplicates poses when two CTD model predictions converge to a single animal. If two poses have an OKS above this threshold, one of the poses is removed.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5e667af4",
+ "metadata": {
+ "id": "5e667af4"
+ },
+ "outputs": [],
+ "source": [
+ "dest_folder = str(Path(video_path).parent / \"custom-ctd-tracking\")\n",
+ "\n",
+ "deeplabcut.analyze_videos(\n",
+ " config,\n",
+ " [video_path],\n",
+ " shuffle=CTD_SHUFFLE,\n",
+ " destfolder=dest_folder,\n",
+ " ctd_tracking=dict(\n",
+ " bu_on_lost_idv=True,\n",
+ " bu_max_frequency=10,\n",
+ " threshold_bu_add=0.5,\n",
+ " threshold_ctd=0.01,\n",
+ " threshold_nms=0.8,\n",
+ " ),\n",
+ ")\n",
+ "deeplabcut.create_labeled_video(\n",
+ " config,\n",
+ " [video_path],\n",
+ " shuffle=CTD_SHUFFLE,\n",
+ " destfolder=dest_folder,\n",
+ " track_method=\"ctd\",\n",
+ " color_by=\"individual\",\n",
+ ")"
+ ]
+ }
+ ],
+ "metadata": {
+ "accelerator": "GPU",
+ "colab": {
+ "collapsed_sections": [
+ "b2829415",
+ "d46ecdc8"
+ ],
+ "gpuType": "T4",
+ "provenance": []
+ },
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-10-02",
+ "last_metadata_updated": "2026-03-06"
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.13"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/COLAB/COLAB_DEMO_SuperAnimal.ipynb b/examples/COLAB/COLAB_DEMO_SuperAnimal.ipynb
index 55fb4bf06f..935d7bad59 100644
--- a/examples/COLAB/COLAB_DEMO_SuperAnimal.ipynb
+++ b/examples/COLAB/COLAB_DEMO_SuperAnimal.ipynb
@@ -1,138 +1,251 @@
{
- "cells": [
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- " \n",
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "23v-XAUNQIPY"
- },
- "source": [
- "# First, go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "03ylSyQ4O9Ee"
- },
- "outputs": [],
- "source": [
- "!pip install \"deeplabcut[tf,modelzoo] @ git+https://github.com/DeepLabCut/DeepLabCut@main\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "TguLMTJpQx1_"
- },
- "source": [
- "## PLEASE, click \"restart runtime\" from the output above before proceeding! "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {
- "id": "4BejjXKFO2Zg"
- },
- "outputs": [],
- "source": [
- "import deeplabcut\n",
- "import os"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "xXNMNLe6xEBC"
- },
- "outputs": [],
- "source": [
- "from google.colab import files\n",
- "\n",
- "uploaded = files.upload()\n",
- "for filepath, content in uploaded.items():\n",
- " print(f'User uploaded file \"{filepath}\" with length {len(content)} bytes')\n",
- "video_path = os.path.abspath(filepath)\n",
- "\n",
- "# If this cell fails (e.g., when using Safari in place of Google Chrome),\n",
- "# manually upload your video via the Files menu to the left \n",
- "# and define `video_path` yourself with right click > copy path on the video."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {
- "id": "ge589yC4v9yX"
- },
- "outputs": [],
- "source": [
- "supermodel_name = \"superanimal_topviewmouse\" #@param [\"superanimal_topviewmouse\", \"superanimal_quadruped\"]\n",
- "pcutoff = 0.3 #@param {type:\"slider\", min:0, max:1, step:0.05}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "yqcnEVVSQDC0"
- },
- "outputs": [],
- "source": [
- "videotype = os.path.splitext(video_path)[1]\n",
- "scale_list = []\n",
- "deeplabcut.video_inference_superanimal(\n",
- " [video_path],\n",
- " supermodel_name,\n",
- " videotype=videotype,\n",
- " video_adapt=True,\n",
- " scale_list=scale_list,\n",
- " pcutoff=pcutoff,\n",
- ")"
- ]
- }
- ],
- "metadata": {
- "accelerator": "GPU",
- "colab": {
- "provenance": []
- },
- "gpuClass": "standard",
- "kernelspec": {
- "display_name": "dlc",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13 | packaged by conda-forge | (main, May 27 2022, 17:01:00) \n[Clang 13.0.1 ]"
- },
- "vscode": {
- "interpreter": {
- "hash": "ef00193d8f29a47f592f520086c931b5dd2a83e8a593fa0efe5afff3c413a788"
- }
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "3G1Nx3YLOVaZ"
+ },
+ "source": [
+ "\n",
+ " \n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "23v-XAUNQIPY"
+ },
+ "source": [
+ "# DeepLabCut SuperAnimal models\n",
+ "\n",
+ "\n",
+ "\n",
+ "http://modelzoo.deeplabcut.org\n",
+ "\n",
+ "You can use this notebook to analyze videos with pretrained networks from our model zoo - NO local installation of DeepLabCut is needed!\n",
+ "\n",
+ "- **What you need:** a video of your favorite dog, cat, human, etc: check the list of currently available models here: http://modelzoo.deeplabcut.org\n",
+ "\n",
+ "- **What to do:** (1) in the top right corner, click \"CONNECT\". Then, just hit run (play icon) on each cell below and follow the instructions!\n",
+ "\n",
+ "- **Note, if you performance is less that you would like:** firstly check the labeled_video parameters (i.e. \"pcutoff\" that will set the video plotting) - see the end of this notebook.\n",
+ "- You can also use the model in your own projects locally. Please be sure to cite the papers for the model, i.e., [Ye et al. 2024](https://arxiv.org/abs/2203.07436) 🎉\n",
+ "\n",
+ "\n",
+ "\n",
+ "## **Let's get going: install DeepLabCut into COLAB:**\n",
+ "\n",
+ "*Also, be sure you are connected to a GPU: go to menu, click Runtime > Change Runtime Type > select \"GPU\"*\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "03ylSyQ4O9Ee"
+ },
+ "outputs": [],
+ "source": [
+ "!pip install --pre deeplabcut"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "TguLMTJpQx1_"
+ },
+ "source": [
+ "## PLEASE, click \"restart runtime\" from the output above before proceeding!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "4BejjXKFO2Zg"
+ },
+ "outputs": [],
+ "source": [
+ "from pathlib import Path\n",
+ "\n",
+ "import deeplabcut"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "GXf8N4v28Xqo"
+ },
+ "source": [
+ "## Please select a video you want to run SuperAnimal-X on:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "xXNMNLe6xEBC"
+ },
+ "outputs": [],
+ "source": [
+ "from google.colab import files\n",
+ "\n",
+ "uploaded = files.upload()\n",
+ "for filepath, content in uploaded.items():\n",
+ " print(f'User uploaded file \"{filepath}\" with length {len(content)} bytes')\n",
+ "\n",
+ "video_path = Path(filepath).resolve()\n",
+ "\n",
+ "# If this cell fails (e.g., when using Safari in place of Google Chrome),\n",
+ "# manually upload your video via the Files menu to the left\n",
+ "# and define `video_path` yourself with right click > copy path on the video."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "A8sDYMa08f62"
+ },
+ "source": [
+ "## Next select the model you want to use, Quadruped or TopViewMouse\n",
+ "- See http://modelzoo.deeplabcut.org/ for more details on these models\n",
+ "- The pcutoff is for visualization only, namely only keypoints with a value over what you set are shown. 0 is low confidience, 1 is perfect confidience of the model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "ge589yC4v9yX"
+ },
+ "outputs": [],
+ "source": [
+ "superanimal_name = \"superanimal_quadruped\" # @param [\"superanimal_topviewmouse\", \"superanimal_quadruped\"]\n",
+ "model_name = \"hrnet_w32\" # @param [\"hrnet_w32\", \"resnet_50\"]\n",
+ "detector_name = (\n",
+ " \"fasterrcnn_resnet50_fpn_v2\" # @param [\"fasterrcnn_resnet50_fpn_v2\", \"fasterrcnn_mobilenet_v3_large_fpn\"]\n",
+ ")\n",
+ "pcutoff = 0.15 # @param {type:\"slider\", min:0, max:1, step:0.05}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "zsB0pGtj9Luq"
+ },
+ "source": [
+ "## Okay, let's go! 🐭🦓🐻"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "yqcnEVVSQDC0"
+ },
+ "outputs": [],
+ "source": [
+ "videotype = video_path.suffix\n",
+ "scale_list = []\n",
+ "\n",
+ "deeplabcut.video_inference_superanimal(\n",
+ " [video_path],\n",
+ " superanimal_name,\n",
+ " model_name=model_name,\n",
+ " detector_name=detector_name,\n",
+ " videotype=videotype,\n",
+ " video_adapt=True,\n",
+ " scale_list=scale_list,\n",
+ " pcutoff=pcutoff,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "gPLZSBpD34Mj"
+ },
+ "source": [
+ "## Let's view the video in Colab:\n",
+ "- otherwise, you can download and look at the video from the left side of your screen! It will end with _labeled.mp4\n",
+ "- If your data doesn't work as well as you'd like, consider fine-tuning our model on your data, changing the pcutoff, changing the scale-range\n",
+ "(pick values smaller and larger than your video image input size). See our repo for more details."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "ejFJ1Pbg33i6"
+ },
+ "outputs": [],
+ "source": [
+ "from base64 import b64encode\n",
+ "\n",
+ "from IPython.display import HTML\n",
+ "\n",
+ "# Get the parent directory and stem (filename without extension)\n",
+ "directory = video_path.parent\n",
+ "basename = video_path.stem\n",
+ "\n",
+ "# Build the pattern\n",
+ "# This uses '*' to allow for any characters between the fixed parts\n",
+ "pattern = f\"{basename}*{superanimal_name}*{detector_name}*{model_name}*_labeled_after_adapt.mp4\"\n",
+ "\n",
+ "# Search for matching files\n",
+ "matches = list(directory.glob(pattern))\n",
+ "\n",
+ "# Choose the first match if it exists\n",
+ "labeled_video_path = matches[0] if matches else None\n",
+ "\n",
+ "view_video = open(labeled_video_path, \"rb\").read()\n",
+ "\n",
+ "data_url = \"data:video/mp4;base64,\" + b64encode(view_video).decode()\n",
+ "HTML(\n",
+ " f\"\"\"\n",
+ "\n",
+ " \n",
+ " \n",
+ "\"\"\"\n",
+ ")"
+ ]
+ }
+ ],
+ "metadata": {
+ "accelerator": "GPU",
+ "colab": {
+ "provenance": []
+ },
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-06-30",
+ "last_metadata_updated": "2026-03-06"
+ },
+ "gpuClass": "standard",
+ "kernelspec": {
+ "display_name": "dlc",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.13 | packaged by conda-forge | (main, May 27 2022, 17:01:00) \n[Clang 13.0.1 ]"
+ },
+ "vscode": {
+ "interpreter": {
+ "hash": "ef00193d8f29a47f592f520086c931b5dd2a83e8a593fa0efe5afff3c413a788"
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
}
diff --git a/examples/COLAB/COLAB_DEMO_mouse_openfield.ipynb b/examples/COLAB/COLAB_DEMO_mouse_openfield.ipynb
index 704730271b..1cb74fcb46 100644
--- a/examples/COLAB/COLAB_DEMO_mouse_openfield.ipynb
+++ b/examples/COLAB/COLAB_DEMO_mouse_openfield.ipynb
@@ -1,290 +1,338 @@
{
- "nbformat": 4,
- "nbformat_minor": 0,
- "metadata": {
- "accelerator": "GPU",
- "colab": {
- "name": "Colab_DEMO_mouse_openfield.ipynb",
- "provenance": [],
- "collapsed_sections": []
- },
- "kernelspec": {
- "display_name": "Python [default]",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.6.9"
- }
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "view-in-github"
+ },
+ "source": [
+ " "
+ ]
},
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "view-in-github",
- "colab_type": "text"
- },
- "source": [
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "TGChzLdc-lUJ"
- },
- "source": [
- "# DeepLabCut Toolbox - Colab Demo on Topview Mouse Data\n",
- "https://github.com/DeepLabCut/DeepLabCut\n",
- "\n",
- "\n",
- "\n",
- "Demo supporting: Nath\\*, Mathis\\* et al. *Using DeepLabCut for markerless3D pose estimation during behavior across species. Nature Protocols, 2019 \n",
- "\n",
- "This notebook demonstrates the necessary steps to use DeepLabCut on our demo data. We provide a sub-set of the mouse data from Mathis et al, 2018 Nature Neuroscience.\n",
- "\n",
- "This demo notebook mostly shows the most simple code to train and evaluate your model, but many of the functions have additional features, so please check out the overview & the protocol paper!\n",
- "\n",
- "This notebook illustrates how to use the cloud to:\n",
- "\n",
- "- load demo data\n",
- "- create a training set\n",
- "- train a network\n",
- "- evaluate a network\n",
- "- analyze a novel video"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "txoddlM8hLKm"
- },
- "source": [
- "## First, go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\""
- ]
- },
- {
- "cell_type": "code",
- "metadata": {
- "id": "Ew6r4hotoQjt"
- },
- "source": [
- "# Clone the entire deeplabcut repo so we can use the demo data:\n",
- "!git clone -l -s https://github.com/DeepLabCut/DeepLabCut.git cloned-DLC-repo\n",
- "%cd cloned-DLC-repo\n",
- "!ls"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "code",
- "metadata": {
- "id": "yDaY78dFoxyD"
- },
- "source": [
- "%cd /content/cloned-DLC-repo/examples/openfield-Pranav-2018-10-30\n",
- "!ls"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "code",
- "metadata": {
- "id": "q23BzhA6CXxu"
- },
- "source": [
- "#(this will take a few minutes to install all the dependences!)\n",
- "\n",
- "!pip install deeplabcut"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "XymV_Hnlp1OJ"
- },
- "source": [
- "## PLEASE, click \"restart runtime\" from the output above before proceeding! "
- ]
- },
- {
- "cell_type": "code",
- "metadata": {
- "id": "sXufoX6INe6w"
- },
- "source": [
- "import deeplabcut"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "code",
- "metadata": {
- "id": "Z7ZlDr3wV4D1"
- },
- "source": [
- "#create a path variable that links to the config file:\n",
- "path_config_file = '/content/cloned-DLC-repo/examples/openfield-Pranav-2018-10-30/config.yaml'\n",
- "\n",
- "# Loading example data set:\n",
- "deeplabcut.load_demo_data(path_config_file)"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "c4FczXGDoEJU"
- },
- "source": [
- "## Start training:\n",
- "This function trains the network for a specific shuffle of the training dataset. "
- ]
- },
- {
- "cell_type": "code",
- "metadata": {
- "id": "_pOvDq_2oEJW"
- },
- "source": [
- "#let's also change the display and save_iters just in case Colab takes away the GPU... \n",
- "#if that happens, you can reload from a saved point. Typically, you want to train to 200,000 + iterations.\n",
- "#more info and there are more things you can set: https://github.com/DeepLabCut/DeepLabCut/wiki/DOCSTRINGS#train_network\n",
- "\n",
- "deeplabcut.train_network(path_config_file, shuffle=1, displayiters=100,saveiters=500, maxiters=10000)\n",
- "\n",
- "#this will run until you stop it (CTRL+C), or hit \"STOP\" icon, or when it hits the end (default, 1.03M iterations). \n",
- "#Whichever you chose, you will see what looks like an error message, but it's not an error - don't worry...."
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "RiDwIVf5-3H_"
- },
- "source": [
- "We recommend you run this for ~1,000 iterations, just as a demo. This should take around 20 min. Note, that **when you hit \"STOP\" you will get a KeyInterrupt \"error\"! No worries! :)**"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "xZygsb2DoEJc"
- },
- "source": [
- "## Start evaluating:\n",
- "This function evaluates a trained model for a specific shuffle/shuffles at a particular state or all the states on the data set (images)\n",
- "and stores the results as .csv file in a subdirectory under **evaluation-results**"
- ]
- },
- {
- "cell_type": "code",
- "metadata": {
- "id": "nv4zlbrnoEJg"
- },
- "source": [
- "%matplotlib notebook\n",
- "deeplabcut.evaluate_network(path_config_file,plotting=True)\n",
- "\n",
- "# Here you want to see a low pixel error! Of course, it can only be as good as the labeler, so be sure your labels are good!"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "oxy5JG-kYKF4"
- },
- "source": [
- "**Check the images**:\n",
- "You can go look in the newly created \"evalutaion-results\" folder at the images. At around 3500 iterations, the error is ~3 pixels (but this can vary on how your demo data was split for training)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "OVFLSKKfoEJk"
- },
- "source": [
- "## Start Analyzing videos: \n",
- "This function analyzes the new video. The user can choose the best model from the evaluation results and specify the correct snapshot index for the variable **snapshotindex** in the **config.yaml** file. Otherwise, by default the most recent snapshot is used to analyse the video.\n",
- "\n",
- "The results are stored in hd5 file in the same directory where the video resides. \n",
- "\n",
- "**On the demo data, this should take around ~ 3 min! (The demo frames are 640x480, which should run around 35 FPS on the google-provided GPU)**"
- ]
- },
- {
- "cell_type": "code",
- "metadata": {
- "id": "Y_LZiS_0oEJl"
- },
- "source": [
- "videofile_path = ['/content/cloned-DLC-repo/examples/openfield-Pranav-2018-10-30/videos/m3v1mp4.mp4'] #Enter the list of videos to analyze.\n",
- "deeplabcut.analyze_videos(path_config_file,videofile_path, videotype='.mp4')"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "pCrUvQIvoEKD"
- },
- "source": [
- "## Create labeled video:\n",
- "This function is for visualiztion purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. This should run around 215 FPS on the demo video!"
- ]
- },
- {
- "cell_type": "code",
- "metadata": {
- "id": "6aDF7Q7KoEKE"
- },
- "source": [
- "deeplabcut.create_labeled_video(path_config_file,videofile_path)"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "8GTiuJESoEKH"
- },
- "source": [
- "## Plot the trajectories of the analyzed videos:\n",
- "This function plots the trajectories of all the body parts across the entire video. Each body part is identified by a unique color."
- ]
- },
- {
- "cell_type": "code",
- "metadata": {
- "id": "gX21zZbXoEKJ"
- },
- "source": [
- "deeplabcut.plot_trajectories(path_config_file,videofile_path)"
- ],
- "execution_count": null,
- "outputs": []
- }
- ]
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "TGChzLdc-lUJ"
+ },
+ "source": [
+ "# DeepLabCut on Single Mouse Data Demo\n",
+ "\n",
+ "Some useful links:\n",
+ "\n",
+ "- [DeepLabCut's GitHub: github.com/DeepLabCut/DeepLabCut](https://github.com/DeepLabCut/DeepLabCut)\n",
+ "- [DeepLabCut's Documentation: User Guide for Single Animal projects](https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide.html)\n",
+ "\n",
+ "\n",
+ "\n",
+ "Demo supporting: Nath\\*, Mathis\\* et al. *Using DeepLabCut for markerless3D pose estimation during behavior across species. Nature Protocols, 2019 \n",
+ "\n",
+ "This notebook demonstrates the necessary steps to use DeepLabCut on our demo data. We provide a sub-set of the mouse data from Mathis et al, 2018 Nature Neuroscience.\n",
+ "\n",
+ "This demo notebook mostly shows the most simple code to train and evaluate your model, but many of the functions have additional features, so please check out the overview & the protocol paper!\n",
+ "\n",
+ "This notebook illustrates how to use the cloud to:\n",
+ "\n",
+ "- load demo data\n",
+ "- create a training set\n",
+ "- train a network\n",
+ "- evaluate a network\n",
+ "- analyze a novel video"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "txoddlM8hLKm"
+ },
+ "source": [
+ "## Installation\n",
+ "\n",
+ "### First, go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Ew6r4hotoQjt"
+ },
+ "outputs": [],
+ "source": [
+ "# Clone the entire deeplabcut repo so we can use the demo data:\n",
+ "!git clone -l -s https://github.com/DeepLabCut/DeepLabCut.git cloned-DLC-repo\n",
+ "%cd cloned-DLC-repo\n",
+ "!ls"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "yDaY78dFoxyD"
+ },
+ "outputs": [],
+ "source": [
+ "%cd /content/cloned-DLC-repo/examples/openfield-Pranav-2018-10-30\n",
+ "!ls"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "q23BzhA6CXxu"
+ },
+ "outputs": [],
+ "source": [
+ "# Install the latest DeepLabCut version (this will take a few minutes to install all the dependencies!)\n",
+ "%cd /content/cloned-DLC-repo/\n",
+ "%pip install \".\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "XymV_Hnlp1OJ"
+ },
+ "source": [
+ "### PLEASE, click \"restart runtime\" from the output above before proceeding!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "sXufoX6INe6w"
+ },
+ "outputs": [],
+ "source": [
+ "import deeplabcut"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Z7ZlDr3wV4D1"
+ },
+ "outputs": [],
+ "source": [
+ "# Create a path variable that links to the config file:\n",
+ "path_config_file = \"/content/cloned-DLC-repo/examples/openfield-Pranav-2018-10-30/config.yaml\"\n",
+ "\n",
+ "# Loading example data set:\n",
+ "deeplabcut.load_demo_data(path_config_file)\n",
+ "\n",
+ "# Automatically update some hyperparameters for training,\n",
+ "# here rotations to +/- 180 degrees. This can be helpful for optimizing performance.\n",
+ "# see Primer -- Mathis et al. Neuron 2020\n",
+ "import deeplabcut.pose_estimation_pytorch as dlc_torch\n",
+ "from deeplabcut.core.config import read_config_as_dict\n",
+ "\n",
+ "loader = dlc_torch.DLCLoader(\n",
+ " config=path_config_file,\n",
+ " trainset_index=0,\n",
+ " shuffle=1,\n",
+ ")\n",
+ "\n",
+ "# Get the pytorch config path\n",
+ "pytorch_config_path = loader.model_folder / \"pytorch_config.yaml\"\n",
+ "\n",
+ "model_cfg = read_config_as_dict(pytorch_config_path)\n",
+ "model_cfg[\"data\"][\"train\"][\"affine\"][\"rotation\"] = 180\n",
+ "\n",
+ "# Save the modified config\n",
+ "dlc_torch.config.write_config(pytorch_config_path, model_cfg)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "c4FczXGDoEJU"
+ },
+ "source": [
+ "## Start training:\n",
+ "This function trains the network for a specific shuffle of the training dataset. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "_pOvDq_2oEJW"
+ },
+ "outputs": [],
+ "source": [
+ "# Let's also change the display and save_epochs just in case Colab takes away\n",
+ "# the GPU... If that happens, you can reload from a saved point using the\n",
+ "# `snapshot_path` argument to `deeplabcut.train_network`:\n",
+ "# deeplabcut.train_network(..., snapshot_path=\"/content/.../snapshot-050.pt\")\n",
+ "\n",
+ "# Typically, you want to train to ~200 epochs. We set the batch size to 8 to\n",
+ "# utilize the GPU's capabilities.\n",
+ "\n",
+ "# More info and there are more things you can set:\n",
+ "# https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide.html#g-train-the-network\n",
+ "\n",
+ "deeplabcut.train_network(\n",
+ " path_config_file,\n",
+ " shuffle=1,\n",
+ " save_epochs=5,\n",
+ " epochs=200,\n",
+ " batch_size=8,\n",
+ ")\n",
+ "\n",
+ "# This will run until you stop it (CTRL+C), or hit \"STOP\" icon, or when it hits the end."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "RiDwIVf5-3H_"
+ },
+ "source": [
+ "We recommend you run this for ~100 epochs, just as a demo. This should take around 15 minutes. Note, that **when you hit \"STOP\" you will get a `KeyboardInterrupt` \"error\"! No worries! :)**\n",
+ "\n",
+ "A new snapshot is saved every `save_epochs` epochs. So once you hit 80 epochs, your latest snapshot in `/content/cloned-DLC-repo/examples/openfield-Pranav-2018-10-30/dlc-models-pytorch/iteration-0/openfieldOct30-trainset95shuffle1/train` should be `snapshot-80.pt`. The best snapshot evaluated during training is saved, and is named `snapshot-best-XX.pt`, where `XX` is the number of epochs the model was trained with."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "xZygsb2DoEJc"
+ },
+ "source": [
+ "## Start evaluating:\n",
+ "This function evaluates a trained model for a specific shuffle/shuffles at a particular state or all the states on the data set (images)\n",
+ "and stores the results as .csv file in a subdirectory under **evaluation-results**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "nv4zlbrnoEJg"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.evaluate_network(path_config_file, plotting=True)\n",
+ "\n",
+ "# Here you want to see a low pixel error! Of course, it can only be as\n",
+ "# good as the labeler, so be sure your labels are good!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "oxy5JG-kYKF4"
+ },
+ "source": [
+ "**Check the images**:\n",
+ "\n",
+ "You can go look in the newly created `\"evaluation-results-pytorch\"` folder at the images. At around 100 epochs, the error is ~3 pixels (but this can vary on how your demo data was split for training)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "OVFLSKKfoEJk"
+ },
+ "source": [
+ "## Start Analyzing videos: \n",
+ "This function analyzes the new video. The user can choose the best model from the evaluation results and specify the correct snapshot index for the variable **snapshotindex** in the **config.yaml** file. Otherwise, by default the most recent snapshot is used to analyse the video.\n",
+ "\n",
+ "The results are stored in hd5 file in the same directory where the video resides. \n",
+ "\n",
+ "**On the demo data, this should take around ~ 90 seconds! (The demo frames are 640x480, which should run around 25 FPS on the google-provided T4 GPU)**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Y_LZiS_0oEJl"
+ },
+ "outputs": [],
+ "source": [
+ "# Enter the list of videos to analyze.\n",
+ "videofile_path = [\"/content/cloned-DLC-repo/examples/openfield-Pranav-2018-10-30/videos/m3v1mp4.mp4\"]\n",
+ "deeplabcut.analyze_videos(path_config_file, videofile_path, videotype=\".mp4\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "pCrUvQIvoEKD"
+ },
+ "source": [
+ "## Create labeled video:\n",
+ "This function is for visualization purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. This should run around 215 FPS on the demo video!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "6aDF7Q7KoEKE"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.create_labeled_video(path_config_file, videofile_path)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "8GTiuJESoEKH"
+ },
+ "source": [
+ "## Plot the trajectories of the analyzed videos:\n",
+ "This function plots the trajectories of all the body parts across the entire video. Each body part is identified by a unique color."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "gX21zZbXoEKJ"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.plot_trajectories(path_config_file, videofile_path)"
+ ]
+ }
+ ],
+ "metadata": {
+ "accelerator": "GPU",
+ "colab": {
+ "collapsed_sections": [],
+ "name": "Colab_DEMO_mouse_openfield.ipynb",
+ "provenance": []
+ },
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-09-16",
+ "last_metadata_updated": "2026-03-06"
+ },
+ "kernelspec": {
+ "display_name": "Python [default]",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
}
diff --git a/examples/COLAB/COLAB_DLC_ModelZoo.ipynb b/examples/COLAB/COLAB_DLC_ModelZoo.ipynb
index 15af42c073..3a48250c18 100644
--- a/examples/COLAB/COLAB_DLC_ModelZoo.ipynb
+++ b/examples/COLAB/COLAB_DLC_ModelZoo.ipynb
@@ -1,314 +1,318 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "view-in-github"
- },
- "source": [
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "RK255E7YoEIt"
- },
- "source": [
- "# **DeepLabCut Model Zoo!**\n",
- "\n",
- "\n",
- "\n",
- "http://modelzoo.deeplabcut.org\n",
- "\n",
- "You can use this notebook to analyze videos with pretrained networks from our model zoo - NO local installation of DeepLabCut is needed! \n",
- "\n",
- "- **What you need:** a video of your favorite dog, cat, human, etc: check the list of currently available models here: http://modelzoo.deeplabcut.org\n",
- "\n",
- "- **What to do:** (1) in the top right corner, click \"CONNECT\". Then, just hit run (play icon) on each cell below and follow the instructions!\n",
- "\n",
- "## **Please consider giving back and labeling a little data to help make each network even better!** \n",
- "\n",
- "We have a WebApp, so no need to install anything, just a few clicks! We'd really appreciate your help!\n",
- " \n",
- "https://contrib.deeplabcut.org/\n",
- "\n",
- "\n",
- "- **Note, if you performance is less that you would like:** firstly check the labeled_video parameters (i.e. \"pcutoff\" in the config.yaml file that will set the video plotting) - see the end of this notebook. You can also use the model in your own projects locally. Please be sure to cite the papers for the model, and http://modelzoo.deeplabcut.org (paper forthcoming!)\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "## **Let's get going: install DeepLabCut into COLAB:**\n",
- "\n",
- "*Also, be sure you are connected to a GPU: go to menu, click Runtime > Change Runtime Type > select \"GPU\"*"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "q23BzhA6CXxu"
- },
- "outputs": [],
- "source": [
- "#click the play icon (this will take a few minutes to install all the dependencies!)\n",
- "!pip install deeplabcut[tf,modelzoo]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "zYm6DljQB0Y7"
- },
- "source": [
- "###proTip: be sure to click \"restart runtime button\" if it appears above ^ "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "ZT4PwGSbYQEO"
- },
- "source": [
- "## Now let's set the backend & import the DeepLabCut package \n",
- "#### (if colab is buggy/throws an error, just rerun this cell):"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "bvoiWefrYQEP"
- },
- "outputs": [],
- "source": [
- "import os\n",
- "import deeplabcut"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "syweXs88tyuO"
- },
- "source": [
- "## Next, run the cell below to upload your video file from your computer:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "7eqEZYs_CaLy"
- },
- "outputs": [],
- "source": [
- "from google.colab import files\n",
- "\n",
- "uploaded = files.upload()\n",
- "for filepath, content in uploaded.items():\n",
- " print(f'User uploaded file \"{filepath}\" with length {len(content)} bytes')\n",
- "video_path = os.path.abspath(filepath)\n",
- "\n",
- "# If this cell fails (e.g., when using Safari in place of Google Chrome),\n",
- "# manually upload your video via the Files menu to the left \n",
- "# and define `video_path` yourself with right click > copy path on the video."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "YsaqOTkZtf-w"
- },
- "source": [
- "## Select your model from the dropdown menu, then below (optionally) input the name you want for the project:\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "Ih0t7lUjYQEd"
- },
- "outputs": [],
- "source": [
- "import ipywidgets as widgets\n",
- "from IPython.display import display\n",
- "\n",
- "model_options = deeplabcut.create_project.modelzoo.Modeloptions\n",
- "model_selection = widgets.Dropdown(\n",
- " options=model_options,\n",
- " value=model_options[0],\n",
- " description=\"Choose a DLC ModelZoo model!\",\n",
- " disabled=False\n",
- ")\n",
- "display(model_selection)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "UV0QXswGCFrI"
- },
- "outputs": [],
- "source": [
- "project_name = 'myDLC_modelZoo'\n",
- "your_name = 'teamDLC'\n",
- "model2use = model_selection.value\n",
- "videotype = os.path.splitext(video_path)[-1].lstrip('.') #or MOV, or avi, whatever you uploaded!"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "JQxko-t3uMVO"
- },
- "source": [
- "## Attention on this step !! \n",
- "- Please note that for optimal performance your videos should contain frames that are around ~300-600 pixels (on one edge). If you have a larger video (like from an iPhone, first downsize by running this please! :)\n",
- "\n",
- "- Thus, if you're using an iPhone, or such, you'll need to downsample the video first by running the code below**\n",
- "\n",
- "(no need to edit it unless you want to change the size)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "WpAX3BKY94e0"
- },
- "outputs": [],
- "source": [
- "video_path = deeplabcut.DownSampleVideo(video_path, width=300)\n",
- "print(video_path)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "KJm_Vbx-s5OY"
- },
- "source": [
- "## Lastly, run the cell below to create a pretrained project, analyze your video with your selected pretrained network, plot trajectories, and create a labeled video!:\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "T9MGgAdIFKPY"
- },
- "outputs": [],
- "source": [
- "config_path, train_config_path = deeplabcut.create_pretrained_project(\n",
- " project_name,\n",
- " your_name,\n",
- " [video_path],\n",
- " videotype=videotype,\n",
- " model=model2use,\n",
- " analyzevideo=True,\n",
- " createlabeledvideo=True,\n",
- " copy_videos=True, #must leave copy_videos=True\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "WS-KxhBMvEBj"
- },
- "source": [
- "Now, you can move this project from Colab (i.e. download it to your GoogleDrive), and use it like a normal standard project! \n",
- "\n",
- "You can analyze more videos, extract outliers, refine then, and/or then add new key points + label new frames, and retrain if desired. We hope this gives you a good launching point for your work!\n",
- "\n",
- "###Happy DeepLabCutting! Welcome to the Zoo :)\n",
- "\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "KPOqiLmo6d7t"
- },
- "source": [
- "## More advanced options: \n",
- "\n",
- "- If you would now like to customize the video/plots - i.e., color, dot size, threshold for the point to be plotted (pcutoff), please simply edit the \"config.yaml\" file by updating the values below:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "yGLNVK1q6rIp"
- },
- "outputs": [],
- "source": [
- "# Updating the plotting within the config.yaml file (without opening it ;):\n",
- "edits = {\n",
- " 'dotsize': 7, # size of the dots!\n",
- " 'colormap': 'spring', # any matplotlib colormap!\n",
- " 'pcutoff': 0.5, # the higher the more conservative the plotting!\n",
- "}\n",
- "deeplabcut.auxiliaryfunctions.edit_config(config_path, edits)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "Vlc0wZgB7R5e"
- },
- "outputs": [],
- "source": [
- "# re-create the labeled video (first you will need to delete in the folder to the LEFT!):\n",
- "project_path = os.path.dirname(config_path)\n",
- "full_video_path = os.path.join(\n",
- " project_path,\n",
- " 'videos',\n",
- " os.path.basename(video_path),\n",
- ")\n",
- "\n",
- "#filter predictions (should already be done above ;):\n",
- "deeplabcut.filterpredictions(config_path, [full_video_path], videotype=videotype)\n",
- "\n",
- "#re-create the video with your edits!\n",
- "deeplabcut.create_labeled_video(config_path, [full_video_path], videotype=videotype, filtered=True)"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "include_colab_link": true,
- "name": "Copy of COLAB_DLC_ModelZoo.ipynb",
- "provenance": [],
- "toc_visible": true
- },
- "gpuClass": "standard",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.7.7"
- }
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "view-in-github"
+ },
+ "source": [
+ " "
+ ]
},
- "nbformat": 4,
- "nbformat_minor": 0
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "RK255E7YoEIt"
+ },
+ "source": [
+ "# DeepLabCut Model Zoo user-contributed models\n",
+ "\n",
+ "🚨 **WARNING** -- This is using the old version from 2020-2023 with user-supplied models. Please see the SuperAnimal notebook if you want to use our Foundational Models for Quadrupeds or mice.\n",
+ "\n",
+ "\n",
+ "\n",
+ "http://modelzoo.deeplabcut.org\n",
+ "\n",
+ "You can use this notebook to analyze videos with pretrained networks from our model zoo - NO local installation of DeepLabCut is needed!\n",
+ "\n",
+ "- **What you need:** a video of your favorite dog, cat, human, etc: check the list of currently available models here: http://modelzoo.deeplabcut.org\n",
+ "\n",
+ "- **What to do:** (1) in the top right corner, click \"CONNECT\". Then, just hit run (play icon) on each cell below and follow the instructions!\n",
+ "\n",
+ "## **Please consider giving back and labeling a little data to help make each network even better!**\n",
+ "\n",
+ "We have a WebApp, so no need to install anything, just a few clicks! We'd really appreciate your help!\n",
+ " \n",
+ "https://contrib.deeplabcut.org/\n",
+ "\n",
+ "\n",
+ "- **Note, if you performance is less that you would like:** firstly check the labeled_video parameters (i.e. \"pcutoff\" in the config.yaml file that will set the video plotting) - see the end of this notebook. You can also use the model in your own projects locally. Please be sure to cite the papers for the model, and http://modelzoo.deeplabcut.org (paper forthcoming!)\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "## **Let's get going: install DeepLabCut into COLAB:**\n",
+ "\n",
+ "*Also, be sure you are connected to a GPU: go to menu, click Runtime > Change Runtime Type > select \"GPU\"*\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Install the latest version of DeepLabCut\n",
+ "!pip install --pre \"deeplabcut[tf,modelzoo]\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Important - Restart the Runtime for the updated packages to be imported!\n",
+ "\n",
+ "PLEASE, click \"restart runtime\" from the output above before proceeding!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "ZT4PwGSbYQEO"
+ },
+ "source": [
+ "## Now let's set the backend & import the DeepLabCut package\n",
+ "### (if colab is buggy/throws an error, just rerun this cell):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "bvoiWefrYQEP"
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "import deeplabcut"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "syweXs88tyuO"
+ },
+ "source": [
+ "## Next, run the cell below to upload your video file from your computer:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "7eqEZYs_CaLy"
+ },
+ "outputs": [],
+ "source": [
+ "from google.colab import files\n",
+ "\n",
+ "uploaded = files.upload()\n",
+ "for filepath, content in uploaded.items():\n",
+ " print(f'User uploaded file \"{filepath}\" with length {len(content)} bytes')\n",
+ "video_path = os.path.abspath(filepath)\n",
+ "\n",
+ "# If this cell fails (e.g., when using Safari in place of Google Chrome),\n",
+ "# manually upload your video via the Files menu to the left\n",
+ "# and define `video_path` yourself with right click > copy path on the video."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "YsaqOTkZtf-w"
+ },
+ "source": [
+ "## Select your model from the dropdown menu, then below (optionally) input the name you want for the project:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Ih0t7lUjYQEd"
+ },
+ "outputs": [],
+ "source": [
+ "import ipywidgets as widgets\n",
+ "from IPython.display import display\n",
+ "\n",
+ "model_options = deeplabcut.create_project.modelzoo.Modeloptions\n",
+ "model_selection = widgets.Dropdown(\n",
+ " options=model_options, value=model_options[0], description=\"Choose a DLC ModelZoo model!\", disabled=False\n",
+ ")\n",
+ "display(model_selection)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "UV0QXswGCFrI"
+ },
+ "outputs": [],
+ "source": [
+ "project_name = \"myDLC_modelZoo\"\n",
+ "your_name = \"teamDLC\"\n",
+ "model2use = model_selection.value\n",
+ "videotype = os.path.splitext(video_path)[-1].lstrip(\".\") # or MOV, or avi, whatever you uploaded!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "JQxko-t3uMVO"
+ },
+ "source": [
+ "## Attention on this step !!\n",
+ "- Please note that for optimal performance your videos should contain frames that are around ~300-600 pixels (on one edge). If you have a larger video (like from an iPhone, first downsize by running this please! :)\n",
+ "\n",
+ "- Thus, if you're using an iPhone, or such, you'll need to downsample the video first by running the code below**\n",
+ "\n",
+ "(no need to edit it unless you want to change the size)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "WpAX3BKY94e0"
+ },
+ "outputs": [],
+ "source": [
+ "video_path = deeplabcut.DownSampleVideo(video_path, width=300)\n",
+ "print(video_path)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "KJm_Vbx-s5OY"
+ },
+ "source": [
+ "## Lastly, run the cell below to create a pretrained project, analyze your video with your selected pretrained network, plot trajectories, and create a labeled video!:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "T9MGgAdIFKPY"
+ },
+ "outputs": [],
+ "source": [
+ "config_path, train_config_path = deeplabcut.create_pretrained_project(\n",
+ " project_name,\n",
+ " your_name,\n",
+ " [video_path],\n",
+ " videotype=videotype,\n",
+ " model=model2use,\n",
+ " analyzevideo=True,\n",
+ " createlabeledvideo=True,\n",
+ " copy_videos=True, # must leave copy_videos=True\n",
+ " engine=deeplabcut.Engine.TF,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "WS-KxhBMvEBj"
+ },
+ "source": [
+ "Now, you can move this project from Colab (i.e. download it to your GoogleDrive), and use it like a normal standard project!\n",
+ "\n",
+ "You can analyze more videos, extract outliers, refine then, and/or then add new key points + label new frames, and retrain if desired. We hope this gives you a good launching point for your work!\n",
+ "\n",
+ "###Happy DeepLabCutting! Welcome to the Zoo :)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "KPOqiLmo6d7t"
+ },
+ "source": [
+ "## More advanced options:\n",
+ "\n",
+ "- If you would now like to customize the video/plots - i.e., color, dot size, threshold for the point to be plotted (pcutoff), please simply edit the \"config.yaml\" file by updating the values below:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "yGLNVK1q6rIp"
+ },
+ "outputs": [],
+ "source": [
+ "# Updating the plotting within the config.yaml file (without opening it ;):\n",
+ "edits = {\n",
+ " \"dotsize\": 7, # size of the dots!\n",
+ " \"colormap\": \"spring\", # any matplotlib colormap!\n",
+ " \"pcutoff\": 0.5, # the higher the more conservative the plotting!\n",
+ "}\n",
+ "deeplabcut.auxiliaryfunctions.edit_config(config_path, edits)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Vlc0wZgB7R5e"
+ },
+ "outputs": [],
+ "source": [
+ "# re-create the labeled video (first you will need to delete in the folder to the LEFT!):\n",
+ "project_path = os.path.dirname(config_path)\n",
+ "full_video_path = os.path.join(\n",
+ " project_path,\n",
+ " \"videos\",\n",
+ " os.path.basename(video_path),\n",
+ ")\n",
+ "\n",
+ "# filter predictions (should already be done above ;):\n",
+ "deeplabcut.filterpredictions(config_path, [full_video_path], videotype=videotype)\n",
+ "\n",
+ "# re-create the video with your edits!\n",
+ "deeplabcut.create_labeled_video(config_path, [full_video_path], videotype=videotype, filtered=True)"
+ ]
+ }
+ ],
+ "metadata": {
+ "colab": {
+ "include_colab_link": true,
+ "name": "Copy of COLAB_DLC_ModelZoo.ipynb",
+ "provenance": [],
+ "toc_visible": true
+ },
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-10-02",
+ "last_metadata_updated": "2026-03-06"
+ },
+ "gpuClass": "standard",
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.7.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
}
diff --git a/examples/COLAB/COLAB_HumanPose_with_RTMPose.ipynb b/examples/COLAB/COLAB_HumanPose_with_RTMPose.ipynb
new file mode 100644
index 0000000000..1e0ec0865f
--- /dev/null
+++ b/examples/COLAB/COLAB_HumanPose_with_RTMPose.ipynb
@@ -0,0 +1,1175 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "t3P1R5BTwud1"
+ },
+ "source": [
+ " \n",
+ "\n",
+ "# DeepLabCut RTMPose human pose estimation demo"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "tJm8QpTzyAEe"
+ },
+ "source": [
+ "Some useful links:\n",
+ "\n",
+ "- DeepLabCut's GitHub: [github.com/DeepLabCut/DeepLabCut](https://github.com/DeepLabCut/DeepLabCut/tree/main)\n",
+ "- DeepLabCut's Documentation: [deeplabcut.github.io/DeepLabCut](https://deeplabcut.github.io/DeepLabCut/README.html)\n",
+ "\n",
+ "This notebook illustrates how to use the cloud to run pose estimation on humans using a pre-trained [RTMPose](https://arxiv.org/abs/2303.07399) model. **⚠️Note: It uses DeepLabCut's low-level interface, so may be suited for more experienced users.⚠️**\n",
+ "\n",
+ "RTMPose is a top-down pose estimation model, which means that bounding boxes must be obtained for individuals (which is usually done through an [object detection model](https://en.wikipedia.org/wiki/Object_detection)) before running pose estimation. We obtain bounding boxes using a pre-trained object detector provided by [`torchvision`](https://pytorch.org/vision/main/models.html#object-detection-instance-segmentation-and-person-keypoint-detection).\n",
+ "\n",
+ "## Selecting the Runtime and Installing DeepLabCut\n",
+ "\n",
+ "**First, go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\".**\n",
+ "\n",
+ "Next, we need to install DeepLabCut and its dependencies."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Aj7Fgm0Xx_fS"
+ },
+ "outputs": [],
+ "source": [
+ "# this will take a couple of minutes to install all the dependencies!\n",
+ "!pip install --pre deeplabcut"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "twiCWHbgzbwH"
+ },
+ "source": [
+ "**(Be sure to click \"RESTART RUNTIME\" if it is displayed above before moving on !) You will see this button at the output of the cells above ^.**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "x6DugzWMzGoj"
+ },
+ "source": [
+ "## Importing Packages and Downloading Model Snapshots"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Y7jKbk_mzPJR"
+ },
+ "source": [
+ "Next, we'll need to import `deeplabcut`, `huggingface_hub` and other dependencies needed to run the demo."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "gbXwpGKXzF98",
+ "outputId": "d7cc8390-e76a-4cc6-b945-42f0951c8d01"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Loading DLC 3.0.0rc10...\n",
+ "DLC loaded in light mode; you cannot use any GUI (labeling, relabeling and standalone GUI)\n"
+ ]
+ }
+ ],
+ "source": [
+ "from pathlib import Path\n",
+ "\n",
+ "import huggingface_hub\n",
+ "import matplotlib.collections as collections\n",
+ "import matplotlib.pyplot as plt\n",
+ "import numpy as np\n",
+ "import torch\n",
+ "import torchvision.models.detection as detection\n",
+ "from PIL import Image\n",
+ "from tqdm import tqdm\n",
+ "\n",
+ "import deeplabcut.pose_estimation_pytorch as dlc_torch"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "6KWKmWRxzX5R"
+ },
+ "source": [
+ "We can now download the pre-trained RTMPose model weights with which we'll run pose estimation."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "L_V11iCszw3s",
+ "outputId": "8b010e6c-27f5-46ad-f713-2fd07effa3b1"
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/usr/local/lib/python3.11/dist-packages/huggingface_hub/utils/_auth.py:94: UserWarning: \n",
+ "The secret `HF_TOKEN` does not exist in your Colab secrets.\n",
+ "To authenticate with the Hugging Face Hub, create a token in your settings tab (https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session.\n",
+ "You will be able to reuse this secret in all of your notebooks.\n",
+ "Please note that authentication is recommended but still optional to access public models or datasets.\n",
+ " warnings.warn(\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Folder in COLAB where snapshots will be saved\n",
+ "model_files = Path(\"hf_files\").resolve()\n",
+ "model_files.mkdir(exist_ok=True)\n",
+ "\n",
+ "# Download the snapshot and model configuration file\n",
+ "# This is generic code to download any snapshot from HuggingFace\n",
+ "# To download DeepLabCut SuperAnimal or Model Zoo models, check\n",
+ "# out dlclibrary!\n",
+ "path_model_config = Path(\n",
+ " huggingface_hub.hf_hub_download(\n",
+ " \"DeepLabCut/HumanBody\",\n",
+ " \"rtmpose-x_simcc-body7_pytorch_config.yaml\",\n",
+ " local_dir=model_files,\n",
+ " )\n",
+ ")\n",
+ "path_snapshot = Path(\n",
+ " huggingface_hub.hf_hub_download(\n",
+ " \"DeepLabCut/HumanBody\",\n",
+ " \"rtmpose-x_simcc-body7.pt\",\n",
+ " local_dir=model_files,\n",
+ " )\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "eEqukXXy0coy"
+ },
+ "source": [
+ "We'll now also define some parameters that we'll later use to plot predictions:\n",
+ "\n",
+ "- a colormap for the keypoints to plot\n",
+ "- a colormap for the limbs of the skeleton\n",
+ "- a skeleton for the model\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "id": "Tam4rfJK0c_b"
+ },
+ "outputs": [],
+ "source": [
+ "cmap_keypoints = plt.get_cmap(\"rainbow\")\n",
+ "cmap_skeleton = plt.get_cmap(\"rainbow_r\")\n",
+ "\n",
+ "bodyparts2connect = [\n",
+ " (\"right_ankle\", \"right_knee\"),\n",
+ " (\"right_knee\", \"right_hip\"),\n",
+ " (\"left_ankle\", \"left_knee\"),\n",
+ " (\"left_hip\", \"left_knee\"),\n",
+ " (\"left_hip\", \"right_hip\"),\n",
+ " (\"right_shoulder\", \"right_hip\"),\n",
+ " (\"left_shoulder\", \"left_hip\"),\n",
+ " (\"left_shoulder\", \"right_shoulder\"),\n",
+ " (\"left_shoulder\", \"left_elbow\"),\n",
+ " (\"right_shoulder\", \"right_elbow\"),\n",
+ " (\"left_elbow\", \"left_wrist\"),\n",
+ " (\"right_elbow\", \"right_wrist\"),\n",
+ " (\"right_eye\", \"left_ear\"),\n",
+ " (\"left_eye\", \"right_eye\"),\n",
+ " (\"left_eye\", \"left_ear\"),\n",
+ " (\"right_eye\", \"right_ear\"),\n",
+ " (\"left_ear\", \"left_shoulder\"),\n",
+ " (\"right_ear\", \"right_shoulder\"),\n",
+ " (\"left_shoulder\", \"left_elbow\"),\n",
+ " (\"right_shoulder\", \"right_elbow\"),\n",
+ "]\n",
+ "skeleton = [\n",
+ " [16, 14],\n",
+ " [14, 12],\n",
+ " [17, 15],\n",
+ " [15, 13],\n",
+ " [12, 13],\n",
+ " [6, 12],\n",
+ " [7, 13],\n",
+ " [6, 7],\n",
+ " [6, 8],\n",
+ " [7, 9],\n",
+ " [8, 10],\n",
+ " [9, 11],\n",
+ " [2, 3],\n",
+ " [1, 2],\n",
+ " [1, 3],\n",
+ " [2, 4],\n",
+ " [3, 5],\n",
+ " [4, 6],\n",
+ " [5, 7],\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "cCxkkd-b0EJq"
+ },
+ "source": [
+ "## Running Inference on Images"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "dotn_xN-05gh"
+ },
+ "source": [
+ "First, let's upload some images to run inference on. To do so, you can just run the cell below."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 92
+ },
+ "id": "mZtikE1H0D34",
+ "outputId": "3d47314f-3ed0-40b2-e54d-2677feef9943"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ " \n",
+ " \n",
+ " Upload widget is only available when the cell has been executed in the\n",
+ " current browser session. Please rerun this cell to enable.\n",
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Saving taylor_swift.jpg to taylor_swift.jpg\n",
+ "User uploaded file 'taylor_swift.jpg' with length 46915 bytes\n"
+ ]
+ }
+ ],
+ "source": [
+ "from google.colab import files\n",
+ "\n",
+ "# JPG or PNG is recommended:\n",
+ "uploaded = files.upload()\n",
+ "for filepath, content in uploaded.items():\n",
+ " print(f\"User uploaded file '{filepath}' with length {len(content)} bytes\")\n",
+ "\n",
+ "image_paths = [Path(filepath).resolve() for filepath in uploaded.keys()]\n",
+ "\n",
+ "# If this cell fails (e.g., when using Safari in place of Google Chrome),\n",
+ "# manually upload your image via the Files menu to the left and define\n",
+ "# `image_paths` yourself with right `click` > `copy path` on the image:\n",
+ "#\n",
+ "# image_paths = [\n",
+ "# Path(\"/path/to/my/image_000.png\"),\n",
+ "# Path(\"/path/to/my/image_001.png\"),\n",
+ "# ]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "nj-HtOBSwtdk",
+ "outputId": "eb5f3b18-cc89-4dd1-a58e-6c39c62582af"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Running object detection\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 1/1 [00:00<00:00, 1.95it/s]\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Running pose estimation\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "1it [00:00, 78.27it/s]\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Saving the predictions to a CSV file\n",
+ "Done!\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Define the device on which the models will run\n",
+ "device = \"cuda\" # e.g. cuda, cpu\n",
+ "\n",
+ "# The maximum number of detections to keep in an image\n",
+ "max_detections = 10\n",
+ "\n",
+ "#############################################\n",
+ "# Run a pretrained detector to get bounding boxes\n",
+ "\n",
+ "# Load the detector from torchvision\n",
+ "weights = detection.FasterRCNN_MobileNet_V3_Large_FPN_Weights.DEFAULT\n",
+ "detector = detection.fasterrcnn_mobilenet_v3_large_fpn(\n",
+ " weights=weights,\n",
+ " box_score_thresh=0.6,\n",
+ ")\n",
+ "detector.eval()\n",
+ "detector.to(device)\n",
+ "preprocess = weights.transforms()\n",
+ "\n",
+ "# The context is a list containing the bounding boxes predicted\n",
+ "# for each image; it will be given to the RTMPose model alongside\n",
+ "# the images.\n",
+ "context = []\n",
+ "\n",
+ "print(\"Running object detection\")\n",
+ "with torch.no_grad():\n",
+ " for image_path in tqdm(image_paths):\n",
+ " image = Image.open(image_path).convert(\"RGB\")\n",
+ " batch = [preprocess(image).to(device)]\n",
+ " predictions = detector(batch)[0]\n",
+ " bboxes = predictions[\"boxes\"].cpu().numpy()\n",
+ " labels = predictions[\"labels\"].cpu().numpy()\n",
+ "\n",
+ " # Obtain the bounding boxes predicted for humans\n",
+ " human_bboxes = [bbox for bbox, label in zip(bboxes, labels, strict=False) if label == 1]\n",
+ "\n",
+ " # Convert bounding boxes to xywh format\n",
+ " bboxes = np.zeros((0, 4))\n",
+ " if len(human_bboxes) > 0:\n",
+ " bboxes = np.stack(human_bboxes)\n",
+ " bboxes[:, 2] -= bboxes[:, 0]\n",
+ " bboxes[:, 3] -= bboxes[:, 1]\n",
+ "\n",
+ " # Only keep the best N detections\n",
+ " bboxes = bboxes[:max_detections]\n",
+ "\n",
+ " context.append({\"bboxes\": bboxes})\n",
+ "\n",
+ "\n",
+ "#############################################\n",
+ "# Run inference on the images\n",
+ "pose_cfg = dlc_torch.config.read_config_as_dict(path_model_config)\n",
+ "runner = dlc_torch.get_pose_inference_runner(\n",
+ " pose_cfg,\n",
+ " snapshot_path=path_snapshot,\n",
+ " batch_size=16,\n",
+ " max_individuals=max_detections,\n",
+ ")\n",
+ "\n",
+ "print(\"Running pose estimation\")\n",
+ "predictions = runner.inference(tqdm(zip(image_paths, context, strict=False)))\n",
+ "\n",
+ "\n",
+ "#############################################\n",
+ "# Create a DataFrame with the predictions, and save them to a CSV file.\n",
+ "print(\"Saving the predictions to a CSV file\")\n",
+ "df = dlc_torch.build_predictions_dataframe(\n",
+ " scorer=\"rtmpose-body7\",\n",
+ " predictions={\n",
+ " img_path: img_predictions for img_path, img_predictions in zip(image_paths, predictions, strict=False)\n",
+ " },\n",
+ " parameters=dlc_torch.PoseDatasetParameters(\n",
+ " bodyparts=pose_cfg[\"metadata\"][\"bodyparts\"],\n",
+ " unique_bpts=pose_cfg[\"metadata\"][\"unique_bodyparts\"],\n",
+ " individuals=[f\"idv_{i}\" for i in range(max_detections)],\n",
+ " ),\n",
+ ")\n",
+ "\n",
+ "# Save to CSV\n",
+ "df.to_csv(\"image_predictions.csv\")\n",
+ "\n",
+ "print(\"Done!\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "pWtdL4U52OBJ"
+ },
+ "source": [
+ "Finally, we can plot the predictions!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 447
+ },
+ "id": "3slKu6Lr2MUh",
+ "outputId": "ef7d938c-39fc-473a-9b88-6169cbfbc567"
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "#############################################\n",
+ "# Unpack and plot predictions\n",
+ "plot_skeleton = True\n",
+ "plot_pose_markers = True\n",
+ "plot_bounding_boxes = True\n",
+ "marker_size = 12\n",
+ "\n",
+ "for image_path, image_predictions in zip(image_paths, predictions, strict=False):\n",
+ " image = Image.open(image_path).convert(\"RGB\")\n",
+ "\n",
+ " pose = image_predictions[\"bodyparts\"]\n",
+ " bboxes = image_predictions[\"bboxes\"]\n",
+ " num_individuals, num_bodyparts = pose.shape[:2]\n",
+ "\n",
+ " fig, ax = plt.subplots(figsize=(8, 8))\n",
+ " ax.imshow(image)\n",
+ " ax.set_xlim(0, image.width)\n",
+ " ax.set_ylim(image.height, 0)\n",
+ " ax.axis(\"off\")\n",
+ " for idv_pose in pose:\n",
+ " if plot_skeleton:\n",
+ " bones = []\n",
+ " for bpt_1, bpt_2 in skeleton:\n",
+ " bones.append([idv_pose[bpt_1 - 1, :2], idv_pose[bpt_2 - 1, :2]])\n",
+ "\n",
+ " bone_colors = cmap_skeleton\n",
+ " if not isinstance(cmap_skeleton, str):\n",
+ " bone_colors = cmap_skeleton(np.linspace(0, 1, len(skeleton)))\n",
+ "\n",
+ " ax.add_collection(collections.LineCollection(bones, colors=bone_colors))\n",
+ "\n",
+ " if plot_pose_markers:\n",
+ " ax.scatter(\n",
+ " idv_pose[:, 0],\n",
+ " idv_pose[:, 1],\n",
+ " c=list(range(num_bodyparts)),\n",
+ " cmap=\"rainbow\",\n",
+ " s=marker_size,\n",
+ " )\n",
+ "\n",
+ " if plot_bounding_boxes:\n",
+ " for x, y, w, h in bboxes:\n",
+ " ax.plot(\n",
+ " [x, x + w, x + w, x, x],\n",
+ " [y, y, y + h, y + h, y],\n",
+ " c=\"r\",\n",
+ " )\n",
+ "\n",
+ " plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "wO18A_3m5Spk"
+ },
+ "source": [
+ "## Running Inference on a Video\n",
+ "\n",
+ "Running pose inference on a video is very similar! First, upload a video to Google Drive."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 92
+ },
+ "id": "d9a7gSe15bCa",
+ "outputId": "698b180c-cd8f-4d17-9c71-f8e58f93631b"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ " \n",
+ " \n",
+ " Upload widget is only available when the cell has been executed in the\n",
+ " current browser session. Please rerun this cell to enable.\n",
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Saving taylor-dancing.mov to taylor-dancing.mov\n",
+ "User uploaded file 'taylor-dancing.mov' with length 1415324 bytes\n"
+ ]
+ }
+ ],
+ "source": [
+ "from google.colab import files\n",
+ "\n",
+ "uploaded = files.upload()\n",
+ "for filepath, content in uploaded.items():\n",
+ " print(f\"User uploaded file '{filepath}' with length {len(content)} bytes\")\n",
+ "\n",
+ "\n",
+ "video_path = [Path(filepath).resolve() for filepath in uploaded.keys()][0]\n",
+ "\n",
+ "# If this cell fails (e.g., when using Safari in place of Google Chrome),\n",
+ "# manually upload your video via the Files menu to the left and define\n",
+ "# `video_path` yourself with right `click` > `copy path` on the video:\n",
+ "#\n",
+ "# video_path = Path(\"/path/to/my/video.mp4\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "I885B01359qu",
+ "outputId": "0affdeda-a10b-4849-b3cd-edf1cb202b52"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Running object detection\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ " 81%|████████▏ | 66/81 [00:02<00:00, 25.37it/s]\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Running pose estimation\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ " 81%|████████▏ | 66/81 [00:01<00:00, 53.25it/s]\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Saving the predictions to a CSV file\n",
+ "Done!\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Define the device on which the models will run\n",
+ "device = \"cuda\" # e.g. cuda, cpu\n",
+ "\n",
+ "# The maximum number of individuals to detect in an image\n",
+ "max_detections = 30\n",
+ "\n",
+ "\n",
+ "#############################################\n",
+ "# Create a video iterator\n",
+ "video = dlc_torch.VideoIterator(video_path)\n",
+ "\n",
+ "\n",
+ "#############################################\n",
+ "# Run a pretrained detector to get bounding boxes\n",
+ "\n",
+ "# Load the detector from torchvision\n",
+ "weights = detection.FasterRCNN_MobileNet_V3_Large_FPN_Weights.DEFAULT\n",
+ "detector = detection.fasterrcnn_mobilenet_v3_large_fpn(\n",
+ " weights=weights,\n",
+ " box_score_thresh=0.6,\n",
+ ")\n",
+ "detector.eval()\n",
+ "detector.to(device)\n",
+ "preprocess = weights.transforms()\n",
+ "\n",
+ "# The context is a list containing the bounding boxes predicted for each frame\n",
+ "# in the video.\n",
+ "context = []\n",
+ "\n",
+ "print(\"Running object detection\")\n",
+ "with torch.no_grad():\n",
+ " for frame in tqdm(video):\n",
+ " batch = [preprocess(Image.fromarray(frame)).to(device)]\n",
+ " predictions = detector(batch)[0]\n",
+ " bboxes = predictions[\"boxes\"].cpu().numpy()\n",
+ " labels = predictions[\"labels\"].cpu().numpy()\n",
+ "\n",
+ " # Obtain the bounding boxes predicted for humans\n",
+ " human_bboxes = [bbox for bbox, label in zip(bboxes, labels, strict=False) if label == 1]\n",
+ "\n",
+ " # Convert bounding boxes to xywh format\n",
+ " bboxes = np.zeros((0, 4))\n",
+ " if len(human_bboxes) > 0:\n",
+ " bboxes = np.stack(human_bboxes)\n",
+ " bboxes[:, 2] -= bboxes[:, 0]\n",
+ " bboxes[:, 3] -= bboxes[:, 1]\n",
+ "\n",
+ " # Only keep the top N bounding boxes\n",
+ " bboxes = bboxes[:max_detections]\n",
+ "\n",
+ " context.append({\"bboxes\": bboxes})\n",
+ "\n",
+ "# Set the context for the video\n",
+ "video.set_context(context)\n",
+ "\n",
+ "\n",
+ "#############################################\n",
+ "# Run inference on the images (in this case a single image)\n",
+ "pose_cfg = dlc_torch.config.read_config_as_dict(path_model_config)\n",
+ "runner = dlc_torch.get_pose_inference_runner(\n",
+ " pose_cfg,\n",
+ " snapshot_path=path_snapshot,\n",
+ " batch_size=16,\n",
+ " max_individuals=max_detections,\n",
+ ")\n",
+ "\n",
+ "print(\"Running pose estimation\")\n",
+ "predictions = runner.inference(tqdm(video))\n",
+ "\n",
+ "\n",
+ "print(\"Saving the predictions to a CSV file\")\n",
+ "df = dlc_torch.build_predictions_dataframe(\n",
+ " scorer=\"rtmpose-body7\",\n",
+ " predictions={idx: img_predictions for idx, img_predictions in enumerate(predictions)},\n",
+ " parameters=dlc_torch.PoseDatasetParameters(\n",
+ " bodyparts=pose_cfg[\"metadata\"][\"bodyparts\"],\n",
+ " unique_bpts=pose_cfg[\"metadata\"][\"unique_bodyparts\"],\n",
+ " individuals=[f\"idv_{i}\" for i in range(max_detections)],\n",
+ " ),\n",
+ ")\n",
+ "df.to_csv(\"video_predictions.csv\")\n",
+ "\n",
+ "print(\"Done!\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "altka3NGB_su"
+ },
+ "source": [
+ "Finally, we can plot the predictions on the video! The labeled video output is saved in the `\"video_predictions.mp4\"` file, and can be downloaded to be viewed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "xRWxH0gO6oPg",
+ "outputId": "c2cc9025-7741-4403-d5cc-c62470a4ba74"
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/usr/local/lib/python3.11/dist-packages/deeplabcut/utils/make_labeled_video.py:146: FutureWarning: DataFrame.groupby with axis=1 is deprecated. Do `frame.T.groupby(...)` without axis instead.\n",
+ " Dataframe.groupby(level=\"individuals\", axis=1).size().values // 3\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Duration of video [s]: 1.57, recorded with 51.7 fps!\n",
+ "Overall # of frames: 81 with cropped frame dimensions: 828 768\n",
+ "Generating frames and creating video.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 66/66 [00:01<00:00, 35.27it/s]\n"
+ ]
+ }
+ ],
+ "source": [
+ "from deeplabcut.utils.make_labeled_video import CreateVideo\n",
+ "from deeplabcut.utils.video_processor import VideoProcessorCV\n",
+ "\n",
+ "video_output_path = \"video_predictions.mp4\"\n",
+ "\n",
+ "clip = VideoProcessorCV(str(video_path), sname=video_output_path, codec=\"mp4v\")\n",
+ "CreateVideo(\n",
+ " clip,\n",
+ " df,\n",
+ " pcutoff=0.4,\n",
+ " dotsize=3,\n",
+ " colormap=\"rainbow\",\n",
+ " bodyparts2plot=pose_cfg[\"metadata\"][\"bodyparts\"],\n",
+ " trailpoints=0,\n",
+ " cropping=False,\n",
+ " x1=0,\n",
+ " x2=clip.w,\n",
+ " y1=0,\n",
+ " y2=clip.h,\n",
+ " bodyparts2connect=bodyparts2connect,\n",
+ " skeleton_color=\"w\",\n",
+ " draw_skeleton=True,\n",
+ " displaycropped=True,\n",
+ " color_by=\"bodypart\",\n",
+ ")"
+ ]
+ }
+ ],
+ "metadata": {
+ "accelerator": "GPU",
+ "colab": {
+ "gpuType": "T4",
+ "include_colab_link": true,
+ "provenance": []
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/examples/COLAB/COLAB_YOURDATA_SuperAnimal.ipynb b/examples/COLAB/COLAB_YOURDATA_SuperAnimal.ipynb
new file mode 100644
index 0000000000..7e77e835d2
--- /dev/null
+++ b/examples/COLAB/COLAB_YOURDATA_SuperAnimal.ipynb
@@ -0,0 +1,2228 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "view-in-github"
+ },
+ "source": [
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "5SSZpZUu0Z4S"
+ },
+ "source": [
+ "# DeepLabCut Model Zoo: SuperAnimal models\n",
+ "\n",
+ "\n",
+ "\n",
+ "# 🦄 SuperAnimal in DeepLabCut PyTorch! 🔥\n",
+ "\n",
+ "This notebook demos how to use our SuperAnimal models within DeepLabCut 3.0! Please read more in [Ye et al. Nature Communications 2024](https://www.nature.com/articles/s41467-024-48792-2) about the available SuperAnimal models, and follow along below!\n",
+ "\n",
+ "### **Let's get going: install the latest version of DeepLabCut into COLAB:**\n",
+ "\n",
+ "*Also, be sure you are connected to a GPU: go to menu, click Runtime > Change Runtime Type > select \"GPU\"*\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 1000
+ },
+ "id": "AjET5cJE5UYM",
+ "jupyter": {
+ "outputs_hidden": true
+ },
+ "outputId": "290a589f-a063-4933-d315-e13052ec1024"
+ },
+ "outputs": [],
+ "source": [
+ "!pip install --pre deeplabcut"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "5h0vq6E50Z4W"
+ },
+ "source": [
+ "**PLEASE, click \"restart runtime\" from the output above before proceeding!**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "LvnlIvQm0Z4X",
+ "jupyter": {
+ "outputs_hidden": true
+ },
+ "outputId": "ef4fd2ed-4569-41d4-b78a-8bf5ae9a0e6b"
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "from pathlib import Path\n",
+ "\n",
+ "import matplotlib.pyplot as plt\n",
+ "import pandas as pd\n",
+ "from PIL import Image\n",
+ "\n",
+ "import deeplabcut\n",
+ "import deeplabcut.utils.auxiliaryfunctions as auxiliaryfunctions\n",
+ "from deeplabcut.modelzoo import build_weight_init\n",
+ "from deeplabcut.modelzoo.utils import (\n",
+ " create_conversion_table,\n",
+ " read_conversion_table_from_csv,\n",
+ ")\n",
+ "from deeplabcut.modelzoo.video_inference import video_inference_superanimal\n",
+ "from deeplabcut.pose_estimation_pytorch.apis import (\n",
+ " superanimal_analyze_images,\n",
+ ")\n",
+ "from deeplabcut.utils.pseudo_label import keypoint_matching"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "UeXjmtu40Z4X"
+ },
+ "source": [
+ "## Zero-shot Image & Video Inference\n",
+ "SuperAnimal models are foundation animal pose models. They can be used for zero-shot predictions without further training on the data.\n",
+ "In this section, we show how to use SuperAnimal models to predict pose from images (given an image folder) and output the predicted images (with pose) into another destination folder."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "FvFzntDMxPoL"
+ },
+ "source": [
+ "### Zero-shot image inference\n",
+ "\n",
+ "If you have a single Image you want to test, upload it here!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "NbDsZQfsxPoL"
+ },
+ "source": [
+ "#### Upload the images you want to predict"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "c4yfTj7r0Z4Y",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from google.colab import files\n",
+ "\n",
+ "uploaded = files.upload()\n",
+ "for filepath, content in uploaded.items():\n",
+ " print(f\"User uploaded file '{filepath}' with length {len(content)} bytes\")\n",
+ "image_path = os.path.abspath(filepath)\n",
+ "image_name = os.path.splitext(image_path)[0]\n",
+ "\n",
+ "# If this cell fails (e.g., when using Safari in place of Google Chrome),\n",
+ "# manually upload your video via the Files menu to the left\n",
+ "# and define `image_path` yourself with right click > copy path on the image:\n",
+ "#\n",
+ "# image_path = \"/path/to/my/image.png\"\n",
+ "# image_name = os.path.splitext(image_path)[0]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Jashzdjb0Z4Y"
+ },
+ "source": [
+ "#### Select a SuperAnimal name and corresponding model architecture\n",
+ "\n",
+ "Check Our Docs on [SuperAnimals](https://github.com/DeepLabCut/DeepLabCut/blob/main/docs/ModelZoo.md) to learn more!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "uH9LXig90Z4Y",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# @markdown ---\n",
+ "# @markdown SuperAnimal Configurations\n",
+ "superanimal_name = \"superanimal_topviewmouse\" # @param [\"superanimal_topviewmouse\", \"superanimal_quadruped\"]\n",
+ "model_name = \"hrnet_w32\" # @param [\"hrnet_w32\", \"resnet_50\"]\n",
+ "detector_name = (\n",
+ " \"fasterrcnn_resnet50_fpn_v2\" # @param [\"fasterrcnn_resnet50_fpn_v2\", \"fasterrcnn_mobilenet_v3_large_fpn\"]\n",
+ ")\n",
+ "\n",
+ "# @markdown ---\n",
+ "# @markdown What is the maximum number of animals you expect to have in an image\n",
+ "max_individuals = 3 # @param {type:\"slider\", min:1, max:30, step:1}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "OmJtVmHq0Z4Y",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Note you need to enter max_individuals correctly to get the correct number of predictions in the image.\n",
+ "_ = superanimal_analyze_images(\n",
+ " superanimal_name,\n",
+ " model_name,\n",
+ " detector_name,\n",
+ " image_path,\n",
+ " max_individuals,\n",
+ " out_folder=\"/content/\",\n",
+ " close_figure_after_save=False,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "6VEjHu-00Z4Y"
+ },
+ "source": [
+ "### Zero-shot Video Inference\n",
+ "\n",
+ "This can be done with or without video adaptation (faster, but not self-supervised fine-tuned on your data!)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "qGoAhxZOxPoM"
+ },
+ "source": [
+ "#### Upload a video you want to predict"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "PK3efA0I0Z4Y",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from google.colab import files\n",
+ "\n",
+ "uploaded = files.upload()\n",
+ "for filepath, content in uploaded.items():\n",
+ " print(f\"User uploaded file '{filepath}' with length {len(content)} bytes\")\n",
+ "video_path = os.path.abspath(filepath)\n",
+ "video_name = os.path.splitext(video_path)[0]\n",
+ "\n",
+ "# If this cell fails (e.g., when using Safari in place of Google Chrome),\n",
+ "# manually upload your video via the Files menu to the left\n",
+ "# and define `video_path` yourself with right click > copy path on the video."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "JoA-RATSICj_"
+ },
+ "source": [
+ "#### Choose the superanimal and the model name"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "OiRAP9XD0Z4Z",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# @markdown ---\n",
+ "# @markdown SuperAnimal Configurations\n",
+ "superanimal_name = \"superanimal_topviewmouse\" # @param [\"superanimal_topviewmouse\", \"superanimal_quadruped\"]\n",
+ "model_name = \"hrnet_w32\" # @param [\"hrnet_w32\", \"resnet_50\"]\n",
+ "detector_name = (\n",
+ " \"fasterrcnn_resnet50_fpn_v2\" # @param [\"fasterrcnn_resnet50_fpn_v2\", \"fasterrcnn_mobilenet_v3_large_fpn\"]\n",
+ ")\n",
+ "\n",
+ "# @markdown ---\n",
+ "# @markdown What is the maximum number of animals you expect to have in an image\n",
+ "max_individuals = 3 # @param {type:\"slider\", min:1, max:30, step:1}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Zv3v0QgSJNOg"
+ },
+ "source": [
+ "#### Zero-shot Video Inference without video adaptation\n",
+ "\n",
+ "The labeled video (and pose predictions for the video) are saved in `\"/content/\"`, with the labeled video name being `{your_video_name}_superanimal_{superanimal_name}_hrnetw32_labeled.mp4`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "poqynL0UJTBp",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "_ = video_inference_superanimal(\n",
+ " videos=video_path,\n",
+ " superanimal_name=superanimal_name,\n",
+ " model_name=model_name,\n",
+ " detector_name=detector_name,\n",
+ " video_adapt=False,\n",
+ " max_individuals=max_individuals,\n",
+ " dest_folder=\"/content/\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Z8Z5GSti0Z4Z"
+ },
+ "source": [
+ "#### Zero-shot Video Inference with video adaptation (unsupervised)\n",
+ "\n",
+ "The labeled video (and pose predictions for the video) are saved in `\"/content/\"`, with the labeled video name being `{your_video_name}_superanimal_{superanimal_name}_hrnetw32_labeled_after_adapt.mp4`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "5mhOmtzw0Z4Z",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "_ = video_inference_superanimal(\n",
+ " videos=[video_path],\n",
+ " superanimal_name=superanimal_name,\n",
+ " model_name=model_name,\n",
+ " detector_name=detector_name,\n",
+ " video_adapt=True,\n",
+ " max_individuals=max_individuals,\n",
+ " pseudo_threshold=0.1,\n",
+ " bbox_threshold=0.9,\n",
+ " detector_epochs=1,\n",
+ " pose_epochs=1,\n",
+ " dest_folder=\"/content/\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "br3pwGf40Z4a"
+ },
+ "source": [
+ "## Training with SuperAnimal\n",
+ "\n",
+ "In this section, we compare different ways to train models in DeepLabCut 3.0, with or without using SuperAnimal-pretrained models.\n",
+ "You can compare the evaluation results and get a sense of each baseline. We have following baselines:\n",
+ "\n",
+ "- ImageNet transfer learning (training without superanimal)\n",
+ "- SuperAnimal transfer learning (baseline 1)\n",
+ "- SuperAnimal naive fine-tuning (baseline 2)\n",
+ "- SuperAnimal memory-replay fine-tuning (baseline3)\n",
+ "\n",
+ "This is done on one of your DeepLabCut projects! If you don't have a DeepLabCut project that you can use SuperAnimal models with, you can always using the example openfield dataset [available in the DeepLabCut repository](https://github.com/DeepLabCut/DeepLabCut/tree/main/examples/openfield-Pranav-2018-10-30) or the Tri-Mouse dataset available on [Zenodo](https://zenodo.org/records/5851157)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "yPy5VgDDhD6o"
+ },
+ "source": [
+ "### Preparing the DeepLabCut Project\n",
+ "\n",
+ "First, place your DeepLabCut project folder into you google drive! \"i.e. move the folder named \"Project-YourName-TheDate\" into Google Drive."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "SXzBBV8ehDR9",
+ "outputId": "90d61c19-400b-4e5d-8ac9-63680d72cdb5"
+ },
+ "outputs": [],
+ "source": [
+ "# Now, let's link to your GoogleDrive. Run this cell and follow the\n",
+ "# authorization instructions:\n",
+ "\n",
+ "from google.colab import drive\n",
+ "\n",
+ "drive.mount(\"/content/drive\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "-QmTftBMo4h6"
+ },
+ "source": [
+ "You will need to edit the project path in the config.yaml file to be set to your Google Drive link!\n",
+ "\n",
+ "Typically, this will be in the format: `/content/drive/MyDrive/yourProjectFolderName`. You can obtain this path by going to the file navigator in the left pane, finding your DeepLabCut project folder, clicking on the vertical `...` next to the folder name and selecting \"Copy path\".\n",
+ "\n",
+ "If the `drive` folder is not immediately visible after mounting the drive, refresh the available files!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "_iFFEYAB7Uum"
+ },
+ "outputs": [],
+ "source": [
+ "# TODO: Update the `project_path` to be the path of your DeepLabCut project!\n",
+ "project_path = Path(\"/content/drive/MyDrive/my-project-2024-07-17\")\n",
+ "config_path = str(project_path / \"config.yaml\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "HZTG3Eo475w0"
+ },
+ "source": [
+ "Then, use the panel below to select the appropriate SuperAnimal model for your project (don't forget to run the cell)!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "t8NtCy1Jo0bu"
+ },
+ "outputs": [],
+ "source": [
+ "# @markdown ---\n",
+ "# @markdown SuperAnimal Configurations\n",
+ "superanimal_name = \"superanimal_topviewmouse\" # @param [\"superanimal_topviewmouse\", \"superanimal_quadruped\"]\n",
+ "model_name = \"hrnet_w32\" # @param [\"hrnet_w32\", \"resnet_50\"]\n",
+ "detector_name = (\n",
+ " \"fasterrcnn_resnet50_fpn_v2\" # @param [\"fasterrcnn_resnet50_fpn_v2\", \"fasterrcnn_mobilenet_v3_large_fpn\"]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "BPvoL9uZ0Z4a"
+ },
+ "source": [
+ "### Comparison between different training baselines\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "eVmpaLdB0Z4a"
+ },
+ "source": [
+ "Definition of data split: the unique combination of training images and testing images.\n",
+ "We create a data split named split 0. All baselines will share the data split to make fair comparisons.\n",
+ "- split 0 -> shared by all baselines\n",
+ "- shuffle 0 (split0) -> imagenet transfer learning\n",
+ "- shuffle 1 (split0) -> superanimal transfer learning\n",
+ "- shuffle 2 (split0) -> superanimal naive fine-tuning\n",
+ "- shuffle 3 (split0) -> superanimal memory-replay fine-tuning"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "WofR2jytxPoR"
+ },
+ "source": [
+ "### What is the difference between baselines?\n",
+ "\n",
+ "**Transfer learning** For canonical task-agnostic transfer learning,\n",
+ "the encoder learns universal visual features from a large pre-training dataset, and a randomly\n",
+ "initialized decoder is used to learn the pose from the downstream dataset.\n",
+ "\n",
+ "**Fine-tuning** For task aware\n",
+ "fine-tuning, both encoder and decoder learn task-related visual-pose features\n",
+ "in the pre-training datasets, and the decoder is fine-tuned to update pose\n",
+ "priors in downstream datasets. Crucially, the network has pose-estimation-specific\n",
+ "weights\n",
+ "\n",
+ "**ImageNet transfer-learning** The encoder was pre-trained from ImageNet. The decoder is trained from scratch in the downstream tasks\n",
+ "\n",
+ "**SuperAnimal transfer-learning** The encoder was pre-trained first from ImageNet, then in pose datasets we colleceted. Then decoder is trained from scratch in downstream tasks.\n",
+ "\n",
+ "**SuperAnimal naive fine-tuning** Both the encoder and the decoder were pre-trained in pose datasets we collected. In downstream datasets, we only finetune convolutional channels that correspond to the annotated keypoints in the downstream datasets. This introduces catastrophic forgetting in keypoints that are not annotated in the downstream datasets.\n",
+ "\n",
+ "**SuperAnimal memory-replay fine-tuning** If we apply fine-tuning with SuperAnimal without further cares, the models will forget about keypoints that are not annotated in the downstream datasets. To mitigate this, we mix the annotations and zero-shot predictions of SuperAnimal models to create a dataset that 'replays' the memory of the SuperAnimal keypoints.\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "AgIsUu6v0Z4a",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "imagenet_transfer_learning_shuffle = 0\n",
+ "superanimal_transfer_learning_shuffle = 1\n",
+ "superanimal_naive_finetune_shuffle = 2\n",
+ "superanimal_memory_replay_shuffle = 3"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "kuKcxM8F0Z4a",
+ "jupyter": {
+ "outputs_hidden": true
+ },
+ "outputId": "c7df2943-1e2c-4b85-c20d-8b94a8aabd75"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.create_training_dataset(\n",
+ " config_path,\n",
+ " Shuffles=[imagenet_transfer_learning_shuffle],\n",
+ " net_type=f\"top_down_{model_name}\",\n",
+ " detector_type=detector_name,\n",
+ " engine=deeplabcut.Engine.PYTORCH,\n",
+ " userfeedback=False,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "_6RncQbr0Z4a"
+ },
+ "source": [
+ "### ImageNet transfer learning\n",
+ "\n",
+ "Historically, the transfer learning using ImageNet weights strategies assumed no “animal pose task priors” in the pretrained\n",
+ "model, a paradigm adopted from previous task-agnostic transfer learning.\n",
+ "\n",
+ "You can change the number of epochs you want to train for. How long training will take depends on many parameters, including the number of images in your dataset, the resolution of the images, and the number of epochs you train for."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 1000,
+ "referenced_widgets": [
+ "7ed11ae2a4be462da84ff716e0725af0",
+ "0f0ed94a863f49b9b85d0a18fa8ce2a5",
+ "343f2670d37c4bf18859238c3d81d419",
+ "d104ae21091e4f10a7de18e191b9f04d",
+ "5dcbd8f3fb6148cca6cfc72b20ce49bd",
+ "e1675e53ca9a4da8acf6c16fba7a2578",
+ "3d2996e10f96404baf24d2c4215b75a1",
+ "b988f87e676840ee98daa3d996c9ddbc",
+ "1779b84e748b4989a8ed53434c30016f",
+ "d37cf6fe7c444bc2a2568c3407389ea8",
+ "2cef5e028d2e40a6bba7400be922d0c2"
+ ]
+ },
+ "id": "H2z8kM340Z4a",
+ "jupyter": {
+ "outputs_hidden": true
+ },
+ "outputId": "75cc2c95-2ac7-4354-9134-4847937e15ce"
+ },
+ "outputs": [],
+ "source": [
+ "# Note we skip the detector training to save time.\n",
+ "# For Top-Down models, the evaluation is by default using ground-truth bounding\n",
+ "# boxes. But to train a model that can be used to inference videos and images,\n",
+ "# you have to set detector_epochs > 0.\n",
+ "\n",
+ "deeplabcut.train_network(\n",
+ " config_path,\n",
+ " detector_epochs=0,\n",
+ " epochs=50,\n",
+ " save_epochs=10,\n",
+ " batch_size=64, # if you get a CUDA OOM error when training on a GPU, reduce to 32, 16, ...!\n",
+ " displayiters=10,\n",
+ " shuffle=imagenet_transfer_learning_shuffle,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "J-udMck7nDbG"
+ },
+ "source": [
+ "Now let's evaluate the performance of our trained models."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "TDHMdKz4m_16",
+ "jupyter": {
+ "outputs_hidden": true
+ },
+ "outputId": "1d38fb84-7f4c-45d1-dbcd-fd7117ca4dad"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.evaluate_network(config_path, Shuffles=[imagenet_transfer_learning_shuffle])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "0GIFWU-MxPoR"
+ },
+ "source": [
+ "### Transfer learning with SuperAnimal weights\n",
+ "\n",
+ "First, we prepare training shuffle for transfer-learning with SuperAnimal weights. As we've already create a shuffle with a train/test split that we want to reuse, we use `deeplabcut.create_training_dataset_from_existing_split` to keep the same train/test indices as in the ImageNet transfer learning shuffle.\n",
+ "\n",
+ "We specify that we want to initialize the model weights with the selected SuperAnimal model, but without keeping the decoding layers (this is called transfer learning)!\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "wOSdZQtOp8qa",
+ "jupyter": {
+ "outputs_hidden": true
+ },
+ "outputId": "ea721606-ea9f-444b-cdae-f62cf0ad30be"
+ },
+ "outputs": [],
+ "source": [
+ "weight_init = build_weight_init(\n",
+ " cfg=auxiliaryfunctions.read_config(config_path),\n",
+ " super_animal=superanimal_name,\n",
+ " model_name=model_name,\n",
+ " detector_name=detector_name,\n",
+ " with_decoder=False,\n",
+ ")\n",
+ "\n",
+ "deeplabcut.create_training_dataset_from_existing_split(\n",
+ " config_path,\n",
+ " from_shuffle=imagenet_transfer_learning_shuffle,\n",
+ " shuffles=[superanimal_transfer_learning_shuffle],\n",
+ " engine=deeplabcut.Engine.PYTORCH,\n",
+ " net_type=f\"top_down_{model_name}\",\n",
+ " detector_type=detector_name,\n",
+ " weight_init=weight_init,\n",
+ " userfeedback=False,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "3qFxlRHixPoR"
+ },
+ "source": [
+ "Then, we launch the training for transfer-learning with SuperAnimal weights."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 1000,
+ "referenced_widgets": [
+ "9a996c8dc3b34bc5b8805b3687e22b27",
+ "d012b421c189412dabeac84cba4164a7",
+ "1abff22a7c9a416d9166e6b150612171",
+ "7271412c1f0141649a7300dbce2b003c",
+ "3c011813d7cb48588a8d236785d9c24f",
+ "3ea385fe815f4e50a0b81ec299040314",
+ "fe59f6c5ed7b4e2cb87bb60224acdaba",
+ "04370d8302c04c5ca6a351383126193f",
+ "d67c4871543e405fbb576a55f8c9048a",
+ "a6cb25fa67ef4733a720960b3fc8213c",
+ "b73b1b64620d492dbc4eaf4bd83ca23a",
+ "dccbe277cc084ed6aa0b329067b5c69c",
+ "c8b57833d3f946abae69b84075345a54",
+ "bee292213d8645618536fcdf6a491d83",
+ "fbbc8c5b20c7423fb21b74296e0eeb28",
+ "ff0c737c49624b1ea27588611951fc84",
+ "42874cdab4be4dc38b0c33775b27d98c",
+ "e3a185abf8a04edabf32d58bdee10dd1",
+ "7cdcbbf9cb694dbf949e8b7eea8e7836",
+ "2ec06260b237411cabd3de7c37e03b1b",
+ "9f8009429aa34b40a65c998230f20c99",
+ "2a3abfe7867641db9fbfe3ee76854bf4"
+ ]
+ },
+ "id": "W60UgRQWqghn",
+ "jupyter": {
+ "outputs_hidden": true
+ },
+ "outputId": "18b931b8-98f4-4539-bf82-1910ff5b7f70"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.train_network(\n",
+ " config_path,\n",
+ " detector_epochs=0,\n",
+ " epochs=50,\n",
+ " save_epochs=10,\n",
+ " batch_size=64, # if you get a CUDA OOM error when training on a GPU, reduce to 32, 16, ...!\n",
+ " displayiters=10,\n",
+ " shuffle=superanimal_transfer_learning_shuffle,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "XzOWKiOixPoR"
+ },
+ "source": [
+ "Finally, we evaluate the model obtained by transfer-learning with SuperAnimal weights."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "jpO3aIAIsWbz",
+ "jupyter": {
+ "outputs_hidden": true
+ },
+ "outputId": "30415e5b-8011-4651-af77-a781ea2b5af7"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.evaluate_network(config_path, Shuffles=[superanimal_transfer_learning_shuffle])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "_Es6RR-_0Z4b"
+ },
+ "source": [
+ "### Fine-tuning with SuperAnimal (without keeping full SuperAnimal keypoints)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "6oo9oJ8XyZrn"
+ },
+ "source": [
+ "#### Setup the weight init and dataset\n",
+ "\n",
+ "First we do keypoint matching. This steps make it possible to understand the correspondence between the existing annotations and SuperAnimal annotations. This step produces 3 outputs\n",
+ "- The confusion matrix\n",
+ "- The conversion table\n",
+ "- Pseudo predictions over the whole dataset"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "fRm62Ji_xPoS"
+ },
+ "source": [
+ "#### What is keypoint matching?\n",
+ "\n",
+ "Because SuperAnimal models have their pre-defined keypoints that are potentially different from your annotations, we proposed this algorithm to minimize the gap between the model and the dataset. We use our model to perform zero-shot inference on the whole dataset. This gives pairs of predictions and ground truth for every image. Then, we cast the matching between models’ predictions (2D coordinates)\n",
+ "and ground truth as bipartitematching using the Euclidean distance as the cost between paired of keypoints. We then solve the matching using the Hungarian algorithm. Thus for every image, we end up getting a matching matrix where 1 counts formatch and 0 counts for non-matching. Because the models’ predictions can be noisy from image to image, we average the aforementioned matching matrix across all the images and perform another bipartite matching, resulting in the final keypoint conversion table between the model and the dataset. Note that the quality of thematching will impact the performance\n",
+ "of the model, especially for zero-shot. In the case where, e.g., the annotation nose is mistakenly converted to keypoint tail and vice versa, the model will have to unlearn the channel that corresponds to nose and tail (see also case study in Mathis et al.)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 1000
+ },
+ "id": "vEHeuKSKyjA6",
+ "jupyter": {
+ "outputs_hidden": true
+ },
+ "outputId": "5863a81e-e0b9-48c7-f2f9-de14d38e805e"
+ },
+ "outputs": [],
+ "source": [
+ "keypoint_matching(\n",
+ " config_path,\n",
+ " superanimal_name,\n",
+ " model_name,\n",
+ " detector_name,\n",
+ " copy_images=True,\n",
+ ")\n",
+ "\n",
+ "conversion_table_path = project_path / \"memory_replay\" / \"conversion_table.csv\"\n",
+ "confusion_matrix_path = project_path / \"memory_replay\" / \"confusion_matrix.png\"\n",
+ "\n",
+ "# You can visualize the pseudo predictions, or do pose embedding clustering etc.\n",
+ "pseudo_prediction_path = project_path / \"memory_replay\" / \"pseudo_predictions.json\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "sA8yyLgs0zoO"
+ },
+ "source": [
+ "#### Display the confusion matrix\n",
+ "\n",
+ "The x axis lists the keypoints in the existing annotations. The y axis lists the keypoints in SuperAnimal keypoint space. Darker color encodes stronger correspondence between the human annotation and SuperAnimal annotations."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "luDxpD9H0zYZ",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "confusion_matrix_image = Image.open(confusion_matrix_path)\n",
+ "\n",
+ "plt.imshow(confusion_matrix_image)\n",
+ "plt.axis(\"off\") # Hide the axes for better view\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "i0QWikYmy_Mj"
+ },
+ "source": [
+ "#### Display the conversion table\n",
+ "The gt columns represents the keypoint names in the existing dataset. The MasterName represents the corresponding keypoints in SuperAnimal keypoint space."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "CeA-NzDMynYV",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "df = pd.read_csv(conversion_table_path)\n",
+ "df = df.dropna()\n",
+ "\n",
+ "df"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Adding the Conversion Table to your project's `config.yaml` file\n",
+ "\n",
+ "Once you've run keypoint matching, you can add the conversion table to your project's `config.yaml` file, and edit it if there are some matches you think are wrong. As an example, for a top-view mouse dataset with 4 bodyparts labeled (`'snout', 'leftear', 'rightear', 'tailbase'`), the conversion table mapping project bodyparts to SuperAnimal bodyparts would be added as:\n",
+ "\n",
+ "```yaml\n",
+ "# Conversion tables to fine-tune SuperAnimal weights\n",
+ "SuperAnimalConversionTables:\n",
+ " superanimal_topviewmouse:\n",
+ " snout: nose\n",
+ " leftear: left_ear\n",
+ " rightear: right_ear\n",
+ " tailbase: tail_base\n",
+ "```\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "create_conversion_table(\n",
+ " config=config_path,\n",
+ " super_animal=superanimal_name,\n",
+ " project_to_super_animal=read_conversion_table_from_csv(conversion_table_path),\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "GkfIo8zTxPoS"
+ },
+ "source": [
+ "#### Prepare the training shuffle and weight initialization for (naive) fine-tuning with SuperAnimal weights\n",
+ "\n",
+ "Then, when you call `build_weight_init` with `with_decoder=True`, the conversion table in your project's `config.yaml` is used to get predictions for the correct bodyparts."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "xEeM_hrOu6k8",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "weight_init = build_weight_init(\n",
+ " cfg=auxiliaryfunctions.read_config(config_path),\n",
+ " super_animal=superanimal_name,\n",
+ " model_name=model_name,\n",
+ " detector_name=detector_name,\n",
+ " with_decoder=True,\n",
+ ")\n",
+ "\n",
+ "deeplabcut.create_training_dataset_from_existing_split(\n",
+ " config_path,\n",
+ " from_shuffle=imagenet_transfer_learning_shuffle,\n",
+ " shuffles=[superanimal_naive_finetune_shuffle],\n",
+ " engine=deeplabcut.Engine.PYTORCH,\n",
+ " net_type=f\"top_down_{model_name}\",\n",
+ " detector_type=detector_name,\n",
+ " weight_init=weight_init,\n",
+ " userfeedback=False,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "gZx6nr-ExPoS"
+ },
+ "source": [
+ "#### Launch the training for (naive) fine-tuning with SuperAnimal"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "c3XAr6uRyXOD",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.train_network(\n",
+ " config_path,\n",
+ " detector_epochs=0,\n",
+ " epochs=50,\n",
+ " save_epochs=10,\n",
+ " batch_size=64, # if you get a CUDA OOM error when training on a GPU, reduce to 32, 16, ...!\n",
+ " displayiters=10,\n",
+ " shuffle=superanimal_naive_finetune_shuffle,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "oXuRshzhxPoS"
+ },
+ "source": [
+ "#### Evaluate the model obtained by (naive) fine-tuning with SuperAnimal"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "VXfdKS-H2yqw",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.evaluate_network(\n",
+ " config_path,\n",
+ " Shuffles=[superanimal_naive_finetune_shuffle],\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "_nUAMlbZ0Z4b"
+ },
+ "source": [
+ "### Memory-replay fine-tuning with SuperAnimal (keeping full SuperAnimal keypoints)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "n6HPu6RaxPoS"
+ },
+ "source": [
+ "**Catastrophic forgetting** describes a\n",
+ "classic problemin continual learning. Indeed, amodel gradually loses\n",
+ "its ability to solve previous tasks after it learns to solve new ones.\n",
+ "Fine-tuning a SuperAnimal models falls into the category of continual\n",
+ "learning: the downstream dataset defines potentially different\n",
+ "keypoints than those learned by the models. Thus, the models might\n",
+ "forget the keypoints they learned and only pick up those defined in the\n",
+ "target dataset. Here, retraining with the original dataset and the new\n",
+ "one, is not a feasible option as datasets cannot be easily shared and\n",
+ "more computational resources would be required.\n",
+ "To counter that, we treat zero-shot inference of the model as a\n",
+ "memory buffer that stores knowledge from the original model. When\n",
+ "we fine-tune a SuperAnimal model, we replace the model predicted\n",
+ "keypoints with the ground-truth annotations, resulting in hybrid\n",
+ "learning of old and new knowledge. The quality of the zero-shot predictions\n",
+ "can vary and we use the confidence of prediction (0.7) as a\n",
+ "threshold to filter out low-confidence predictions. With the threshold\n",
+ "set to 1, memory replay fine-tuning becomes naive-fine-tuning."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "CSLmjlCIxPoS"
+ },
+ "source": [
+ "#### Prepare training shuffle and weight initialization for memory-replay finetuning with SuperAnimal"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "BKEF76AI0Z4c",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "weight_init = build_weight_init(\n",
+ " cfg=auxiliaryfunctions.read_config(config_path),\n",
+ " super_animal=superanimal_name,\n",
+ " model_name=model_name,\n",
+ " detector_name=detector_name,\n",
+ " with_decoder=True,\n",
+ " memory_replay=True,\n",
+ ")\n",
+ "\n",
+ "deeplabcut.create_training_dataset_from_existing_split(\n",
+ " config_path,\n",
+ " from_shuffle=imagenet_transfer_learning_shuffle,\n",
+ " shuffles=[superanimal_memory_replay_shuffle],\n",
+ " engine=deeplabcut.Engine.PYTORCH,\n",
+ " net_type=f\"top_down_{model_name}\",\n",
+ " detector_type=detector_name,\n",
+ " weight_init=weight_init,\n",
+ " userfeedback=False,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "MKwJiIyKxPoT"
+ },
+ "source": [
+ "#### Launch the training for memory-replay fine-tuning with SuperAnimal"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Ru8tIFmD2Mkv",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.train_network(\n",
+ " config_path,\n",
+ " detector_epochs=0,\n",
+ " epochs=50,\n",
+ " save_epochs=10,\n",
+ " batch_size=64, # if you get a CUDA OOM error when training on a GPU, reduce to 32, 16, ...!\n",
+ " displayiters=10,\n",
+ " shuffle=superanimal_memory_replay_shuffle,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "i-2MBRDjxPoT"
+ },
+ "source": [
+ "#### Evaluate the model obtained by memory-replay finetuning with SuperAnimal"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "sfMcK3gq8WxZ",
+ "jupyter": {
+ "outputs_hidden": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.evaluate_network(config_path, Shuffles=[superanimal_memory_replay_shuffle])"
+ ]
+ }
+ ],
+ "metadata": {
+ "accelerator": "GPU",
+ "colab": {
+ "collapsed_sections": [
+ "UeXjmtu40Z4X",
+ "FvFzntDMxPoL",
+ "6VEjHu-00Z4Y"
+ ],
+ "gpuType": "T4",
+ "provenance": []
+ },
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-09-10",
+ "last_metadata_updated": "2026-03-06"
+ },
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.13"
+ },
+ "widgets": {
+ "application/vnd.jupyter.widget-state+json": {
+ "04370d8302c04c5ca6a351383126193f": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ },
+ "0f0ed94a863f49b9b85d0a18fa8ce2a5": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "HTMLModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "HTMLModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "HTMLView",
+ "description": "",
+ "description_tooltip": null,
+ "layout": "IPY_MODEL_e1675e53ca9a4da8acf6c16fba7a2578",
+ "placeholder": "",
+ "style": "IPY_MODEL_3d2996e10f96404baf24d2c4215b75a1",
+ "value": "model.safetensors: 100%"
+ }
+ },
+ "1779b84e748b4989a8ed53434c30016f": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "ProgressStyleModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "ProgressStyleModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "StyleView",
+ "bar_color": null,
+ "description_width": ""
+ }
+ },
+ "1abff22a7c9a416d9166e6b150612171": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "FloatProgressModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "FloatProgressModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "ProgressView",
+ "bar_style": "success",
+ "description": "",
+ "description_tooltip": null,
+ "layout": "IPY_MODEL_04370d8302c04c5ca6a351383126193f",
+ "max": 159594859,
+ "min": 0,
+ "orientation": "horizontal",
+ "style": "IPY_MODEL_d67c4871543e405fbb576a55f8c9048a",
+ "value": 159594859
+ }
+ },
+ "2a3abfe7867641db9fbfe3ee76854bf4": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "DescriptionStyleModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "DescriptionStyleModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "StyleView",
+ "description_width": ""
+ }
+ },
+ "2cef5e028d2e40a6bba7400be922d0c2": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "DescriptionStyleModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "DescriptionStyleModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "StyleView",
+ "description_width": ""
+ }
+ },
+ "2ec06260b237411cabd3de7c37e03b1b": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "ProgressStyleModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "ProgressStyleModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "StyleView",
+ "bar_color": null,
+ "description_width": ""
+ }
+ },
+ "343f2670d37c4bf18859238c3d81d419": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "FloatProgressModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "FloatProgressModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "ProgressView",
+ "bar_style": "success",
+ "description": "",
+ "description_tooltip": null,
+ "layout": "IPY_MODEL_b988f87e676840ee98daa3d996c9ddbc",
+ "max": 165432914,
+ "min": 0,
+ "orientation": "horizontal",
+ "style": "IPY_MODEL_1779b84e748b4989a8ed53434c30016f",
+ "value": 165432914
+ }
+ },
+ "3c011813d7cb48588a8d236785d9c24f": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ },
+ "3d2996e10f96404baf24d2c4215b75a1": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "DescriptionStyleModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "DescriptionStyleModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "StyleView",
+ "description_width": ""
+ }
+ },
+ "3ea385fe815f4e50a0b81ec299040314": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ },
+ "42874cdab4be4dc38b0c33775b27d98c": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ },
+ "5dcbd8f3fb6148cca6cfc72b20ce49bd": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ },
+ "7271412c1f0141649a7300dbce2b003c": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "HTMLModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "HTMLModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "HTMLView",
+ "description": "",
+ "description_tooltip": null,
+ "layout": "IPY_MODEL_a6cb25fa67ef4733a720960b3fc8213c",
+ "placeholder": "",
+ "style": "IPY_MODEL_b73b1b64620d492dbc4eaf4bd83ca23a",
+ "value": " 160M/160M [00:00<00:00, 201MB/s]"
+ }
+ },
+ "7cdcbbf9cb694dbf949e8b7eea8e7836": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ },
+ "7ed11ae2a4be462da84ff716e0725af0": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "HBoxModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "HBoxModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "HBoxView",
+ "box_style": "",
+ "children": [
+ "IPY_MODEL_0f0ed94a863f49b9b85d0a18fa8ce2a5",
+ "IPY_MODEL_343f2670d37c4bf18859238c3d81d419",
+ "IPY_MODEL_d104ae21091e4f10a7de18e191b9f04d"
+ ],
+ "layout": "IPY_MODEL_5dcbd8f3fb6148cca6cfc72b20ce49bd"
+ }
+ },
+ "9a996c8dc3b34bc5b8805b3687e22b27": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "HBoxModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "HBoxModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "HBoxView",
+ "box_style": "",
+ "children": [
+ "IPY_MODEL_d012b421c189412dabeac84cba4164a7",
+ "IPY_MODEL_1abff22a7c9a416d9166e6b150612171",
+ "IPY_MODEL_7271412c1f0141649a7300dbce2b003c"
+ ],
+ "layout": "IPY_MODEL_3c011813d7cb48588a8d236785d9c24f"
+ }
+ },
+ "9f8009429aa34b40a65c998230f20c99": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ },
+ "a6cb25fa67ef4733a720960b3fc8213c": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ },
+ "b73b1b64620d492dbc4eaf4bd83ca23a": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "DescriptionStyleModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "DescriptionStyleModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "StyleView",
+ "description_width": ""
+ }
+ },
+ "b988f87e676840ee98daa3d996c9ddbc": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ },
+ "bee292213d8645618536fcdf6a491d83": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "FloatProgressModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "FloatProgressModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "ProgressView",
+ "bar_style": "success",
+ "description": "",
+ "description_tooltip": null,
+ "layout": "IPY_MODEL_7cdcbbf9cb694dbf949e8b7eea8e7836",
+ "max": 517816013,
+ "min": 0,
+ "orientation": "horizontal",
+ "style": "IPY_MODEL_2ec06260b237411cabd3de7c37e03b1b",
+ "value": 517816013
+ }
+ },
+ "c8b57833d3f946abae69b84075345a54": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "HTMLModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "HTMLModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "HTMLView",
+ "description": "",
+ "description_tooltip": null,
+ "layout": "IPY_MODEL_42874cdab4be4dc38b0c33775b27d98c",
+ "placeholder": "",
+ "style": "IPY_MODEL_e3a185abf8a04edabf32d58bdee10dd1",
+ "value": "detector.pt: 100%"
+ }
+ },
+ "d012b421c189412dabeac84cba4164a7": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "HTMLModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "HTMLModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "HTMLView",
+ "description": "",
+ "description_tooltip": null,
+ "layout": "IPY_MODEL_3ea385fe815f4e50a0b81ec299040314",
+ "placeholder": "",
+ "style": "IPY_MODEL_fe59f6c5ed7b4e2cb87bb60224acdaba",
+ "value": "pose_model.pth: 100%"
+ }
+ },
+ "d104ae21091e4f10a7de18e191b9f04d": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "HTMLModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "HTMLModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "HTMLView",
+ "description": "",
+ "description_tooltip": null,
+ "layout": "IPY_MODEL_d37cf6fe7c444bc2a2568c3407389ea8",
+ "placeholder": "",
+ "style": "IPY_MODEL_2cef5e028d2e40a6bba7400be922d0c2",
+ "value": " 165M/165M [00:04<00:00, 41.1MB/s]"
+ }
+ },
+ "d37cf6fe7c444bc2a2568c3407389ea8": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ },
+ "d67c4871543e405fbb576a55f8c9048a": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "ProgressStyleModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "ProgressStyleModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "StyleView",
+ "bar_color": null,
+ "description_width": ""
+ }
+ },
+ "dccbe277cc084ed6aa0b329067b5c69c": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "HBoxModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "HBoxModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "HBoxView",
+ "box_style": "",
+ "children": [
+ "IPY_MODEL_c8b57833d3f946abae69b84075345a54",
+ "IPY_MODEL_bee292213d8645618536fcdf6a491d83",
+ "IPY_MODEL_fbbc8c5b20c7423fb21b74296e0eeb28"
+ ],
+ "layout": "IPY_MODEL_ff0c737c49624b1ea27588611951fc84"
+ }
+ },
+ "e1675e53ca9a4da8acf6c16fba7a2578": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ },
+ "e3a185abf8a04edabf32d58bdee10dd1": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "DescriptionStyleModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "DescriptionStyleModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "StyleView",
+ "description_width": ""
+ }
+ },
+ "fbbc8c5b20c7423fb21b74296e0eeb28": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "HTMLModel",
+ "state": {
+ "_dom_classes": [],
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "HTMLModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/controls",
+ "_view_module_version": "1.5.0",
+ "_view_name": "HTMLView",
+ "description": "",
+ "description_tooltip": null,
+ "layout": "IPY_MODEL_9f8009429aa34b40a65c998230f20c99",
+ "placeholder": "",
+ "style": "IPY_MODEL_2a3abfe7867641db9fbfe3ee76854bf4",
+ "value": " 518M/518M [00:05<00:00, 101MB/s]"
+ }
+ },
+ "fe59f6c5ed7b4e2cb87bb60224acdaba": {
+ "model_module": "@jupyter-widgets/controls",
+ "model_module_version": "1.5.0",
+ "model_name": "DescriptionStyleModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/controls",
+ "_model_module_version": "1.5.0",
+ "_model_name": "DescriptionStyleModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "StyleView",
+ "description_width": ""
+ }
+ },
+ "ff0c737c49624b1ea27588611951fc84": {
+ "model_module": "@jupyter-widgets/base",
+ "model_module_version": "1.2.0",
+ "model_name": "LayoutModel",
+ "state": {
+ "_model_module": "@jupyter-widgets/base",
+ "_model_module_version": "1.2.0",
+ "_model_name": "LayoutModel",
+ "_view_count": null,
+ "_view_module": "@jupyter-widgets/base",
+ "_view_module_version": "1.2.0",
+ "_view_name": "LayoutView",
+ "align_content": null,
+ "align_items": null,
+ "align_self": null,
+ "border": null,
+ "bottom": null,
+ "display": null,
+ "flex": null,
+ "flex_flow": null,
+ "grid_area": null,
+ "grid_auto_columns": null,
+ "grid_auto_flow": null,
+ "grid_auto_rows": null,
+ "grid_column": null,
+ "grid_gap": null,
+ "grid_row": null,
+ "grid_template_areas": null,
+ "grid_template_columns": null,
+ "grid_template_rows": null,
+ "height": null,
+ "justify_content": null,
+ "justify_items": null,
+ "left": null,
+ "margin": null,
+ "max_height": null,
+ "max_width": null,
+ "min_height": null,
+ "min_width": null,
+ "object_fit": null,
+ "object_position": null,
+ "order": null,
+ "overflow": null,
+ "overflow_x": null,
+ "overflow_y": null,
+ "padding": null,
+ "right": null,
+ "top": null,
+ "visibility": null,
+ "width": null
+ }
+ }
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/examples/COLAB/COLAB_YOURDATA_TrainNetwork_VideoAnalysis.ipynb b/examples/COLAB/COLAB_YOURDATA_TrainNetwork_VideoAnalysis.ipynb
index db15622199..00049863f8 100644
--- a/examples/COLAB/COLAB_YOURDATA_TrainNetwork_VideoAnalysis.ipynb
+++ b/examples/COLAB/COLAB_YOURDATA_TrainNetwork_VideoAnalysis.ipynb
@@ -1,409 +1,437 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "view-in-github"
- },
- "source": [
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "RK255E7YoEIt"
- },
- "source": [
- "# DeepLabCut Toolbox - Colab for standard (single animal) projects!\n",
- "https://github.com/DeepLabCut/DeepLabCut\n",
- "\n",
- "This notebook illustrates how to use the cloud to:\n",
- "- create a training set\n",
- "- train a network\n",
- "- evaluate a network\n",
- "- create simple quality check plots\n",
- "- analyze novel videos!\n",
- "\n",
- "###This notebook assumes you already have a project folder with labeled data! \n",
- "\n",
- "This notebook demonstrates the necessary steps to use DeepLabCut for your own project.\n",
- "\n",
- "This shows the most simple code to do so, but many of the functions have additional features, so please check out the overview & the protocol paper!\n",
- "\n",
- "Nath\\*, Mathis\\* et al.: Using DeepLabCut for markerless pose estimation during behavior across species. Nature Protocols, 2019.\n",
- "\n",
- "\n",
- "Paper: https://www.nature.com/articles/s41596-019-0176-0\n",
- "\n",
- "Pre-print: https://www.biorxiv.org/content/biorxiv/early/2018/11/24/476531.full.pdf\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "txoddlM8hLKm"
- },
- "source": [
- "## First, go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\"\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "q23BzhA6CXxu"
- },
- "outputs": [],
- "source": [
- "#(this will take a few minutes to install all the dependences!)\n",
- "!pip install deeplabcut"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "25wSj6TlVclR"
- },
- "source": [
- "**(Be sure to click \"RESTART RUNTIME\" if it is displayed above before moving on !)**"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "cQ-nlTkri4HZ"
- },
- "source": [
- "## Link your Google Drive (with your labeled data, or the demo data):\n",
- "\n",
- "### First, place your project folder into you google drive! \"i.e. move the folder named \"Project-YourName-TheDate\" into google drive."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "KS4Q4UkR9rgG"
- },
- "outputs": [],
- "source": [
- "#Now, let's link to your GoogleDrive. Run this cell and follow the authorization instructions:\n",
- "#(We recommend putting a copy of the github repo in your google drive if you are using the demo \"examples\")\n",
- "\n",
- "from google.colab import drive\n",
- "drive.mount('/content/drive')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "Frnj1RVDyEqs"
- },
- "source": [
- "YOU WILL NEED TO EDIT THE PROJECT PATH **in the config.yaml file** TO BE SET TO YOUR GOOGLE DRIVE LINK!\n",
- "\n",
- "Typically, this will be: /content/drive/My Drive/yourProjectFolderName\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "vhENAlQnFENJ"
- },
- "outputs": [],
- "source": [
- "#Setup your project variables:\n",
- "# PLEASE EDIT THESE:\n",
- " \n",
- "ProjectFolderName = 'myproject-teamDLC-2020-03-29'\n",
- "VideoType = 'mp4' \n",
- "\n",
- "#don't edit these:\n",
- "videofile_path = ['/content/drive/My Drive/'+ProjectFolderName+'/videos/'] #Enter the list of videos or folder to analyze.\n",
- "videofile_path"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "3K9Ndy1beyfG"
- },
- "outputs": [],
- "source": [
- "import deeplabcut"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "o4orkg9QTHKK"
- },
- "outputs": [],
- "source": [
- "deeplabcut.__version__"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "Z7ZlDr3wV4D1"
- },
- "outputs": [],
- "source": [
- "#This creates a path variable that links to your google drive copy\n",
- "#No need to edit this, as you set it up before: \n",
- "path_config_file = '/content/drive/My Drive/'+ProjectFolderName+'/config.yaml'\n",
- "path_config_file"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "xNi9s1dboEJN"
- },
- "source": [
- "## Create a training dataset:\n",
- "### You must do this step inside of Colab:\n",
- "After running this script the training dataset is created and saved in the project directory under the subdirectory **'training-datasets'**\n",
- "\n",
- "This function also creates new subdirectories under **dlc-models** and appends the project config.yaml file with the correct path to the training and testing pose configuration file. These files hold the parameters for training the network. Such an example file is provided with the toolbox and named as **pose_cfg.yaml**.\n",
- "\n",
- "Now it is the time to start training the network!"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "eMeUwgxPoEJP",
- "scrolled": true
- },
- "outputs": [],
- "source": [
- "# Note: if you are using the demo data (i.e. examples/Reaching-Mackenzie-2018-08-30/), first delete the folder called dlc-models! \n",
- "#Then, run this cell. There are many more functions you can set here, including which netowkr to use!\n",
- "#check the docstring for full options you can do!\n",
- "deeplabcut.create_training_dataset(path_config_file, net_type='resnet_50', augmenter_type='imgaug')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "c4FczXGDoEJU"
- },
- "source": [
- "## Start training:\n",
- "This function trains the network for a specific shuffle of the training dataset. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "_pOvDq_2oEJW"
- },
- "outputs": [],
- "source": [
- "#let's also change the display and save_iters just in case Colab takes away the GPU... \n",
- "#if that happens, you can reload from a saved point. Typically, you want to train to 200,000 + iterations.\n",
- "#more info and there are more things you can set: https://github.com/DeepLabCut/DeepLabCut/wiki/DOCSTRINGS#train_network\n",
- "\n",
- "deeplabcut.train_network(path_config_file, shuffle=1, displayiters=10,saveiters=500)\n",
- "\n",
- "#this will run until you stop it (CTRL+C), or hit \"STOP\" icon, or when it hits the end (default, 1.03M iterations). \n",
- "#Whichever you chose, you will see what looks like an error message, but it's not an error - don't worry...."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "RiDwIVf5-3H_"
- },
- "source": [
- "**When you hit \"STOP\" you will get a KeyInterrupt \"error\"! No worries! :)**"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "xZygsb2DoEJc"
- },
- "source": [
- "## Start evaluating:\n",
- "This function evaluates a trained model for a specific shuffle/shuffles at a particular state or all the states on the data set (images)\n",
- "and stores the results as .csv file in a subdirectory under **evaluation-results**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "nv4zlbrnoEJg"
- },
- "outputs": [],
- "source": [
- "%matplotlib notebook\n",
- "deeplabcut.evaluate_network(path_config_file,plotting=True)\n",
- "\n",
- "# Here you want to see a low pixel error! Of course, it can only be as good as the labeler, \n",
- "#so be sure your labels are good! (And you have trained enough ;)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "BaLBl3TQtrfB"
- },
- "source": [
- "## There is an optional refinement step you can do outside of Colab:\n",
- "- if your pixel errors are not low enough, please check out the protocol guide on how to refine your network!\n",
- "- You will need to adjust the labels **outside of Colab!** We recommend coming back to train and analyze videos... \n",
- "- Please see the repo and protocol instructions on how to refine your data!"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "OVFLSKKfoEJk"
- },
- "source": [
- "## Start Analyzing videos: \n",
- "This function analyzes the new video. The user can choose the best model from the evaluation results and specify the correct snapshot index for the variable **snapshotindex** in the **config.yaml** file. Otherwise, by default the most recent snapshot is used to analyse the video.\n",
- "\n",
- "The results are stored in hd5 file in the same directory where the video resides. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "Y_LZiS_0oEJl"
- },
- "outputs": [],
- "source": [
- "deeplabcut.analyze_videos(path_config_file,videofile_path, videotype=VideoType)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "8GTiuJESoEKH"
- },
- "source": [
- "## Plot the trajectories of the analyzed videos:\n",
- "This function plots the trajectories of all the body parts across the entire video. Each body part is identified by a unique color."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "gX21zZbXoEKJ"
- },
- "outputs": [],
- "source": [
- "deeplabcut.plot_trajectories(path_config_file,videofile_path, videotype=VideoType)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "pqaCw15v8EmB"
- },
- "source": [
- "Now you can look at the plot-poses file and check the \"plot-likelihood.png\" might want to change the \"p-cutoff\" in the config.yaml file so that you have only high confidnece points plotted in the video. i.e. ~0.8 or 0.9. The current default is 0.4. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "pCrUvQIvoEKD"
- },
- "source": [
- "## Create labeled video:\n",
- "This function is for visualiztion purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "6aDF7Q7KoEKE"
- },
- "outputs": [],
- "source": [
- "deeplabcut.create_labeled_video(path_config_file,videofile_path, videotype=VideoType)"
- ]
- }
- ],
- "metadata": {
- "accelerator": "GPU",
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "view-in-github"
+ },
+ "source": [
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "RK255E7YoEIt"
+ },
+ "source": [
+ "# DeepLabCut for your standard (single animal) projects!\n",
+ "\n",
+ "Some useful links:\n",
+ "\n",
+ "- [DeepLabCut's GitHub: github.com/DeepLabCut/DeepLabCut](https://github.com/DeepLabCut/DeepLabCut)\n",
+ "- [DeepLabCut's Documentation: User Guide for Single Animal projects](https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide.html)\n",
+ "\n",
+ "\n",
+ "This notebook illustrates how to use the cloud to:\n",
+ "- create a training set\n",
+ "- train a network\n",
+ "- evaluate a network\n",
+ "- create simple quality check plots\n",
+ "- analyze novel videos!\n",
+ "\n",
+ "### This notebook assumes you already have a project folder with labeled data! \n",
+ "\n",
+ "This notebook demonstrates the necessary steps to use DeepLabCut for your own project.\n",
+ "\n",
+ "This shows the most simple code to do so, but many of the functions have additional features, so please check out the overview & the protocol paper!\n",
+ "\n",
+ "Nath\\*, Mathis\\* et al.: Using DeepLabCut for markerless pose estimation during behavior across species. Nature Protocols, 2019.\n",
+ "\n",
+ "\n",
+ "Paper: https://www.nature.com/articles/s41596-019-0176-0\n",
+ "\n",
+ "Pre-print: https://www.biorxiv.org/content/biorxiv/early/2018/11/24/476531.full.pdf\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "txoddlM8hLKm"
+ },
+ "source": [
+ "## First, go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\"\n",
+ "\n",
+ "As the COLAB environments were updated to CUDA 12.X and Python 3.11, we need to install DeepLabCut and TensorFlow in a distinct way to get TensorFlow to connect to the GPU."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# this will take a couple of minutes to install all the dependencies!\n",
+ "!pip install --pre deeplabcut"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "25wSj6TlVclR"
+ },
+ "source": [
+ "**(Be sure to click \"RESTART RUNTIME\" if it is displayed above before moving on !)** You will see this button at the output of the cells above ^."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
"colab": {
- "collapsed_sections": [],
- "include_colab_link": true,
- "name": "Copy of latest_Colab_TrainNetwork_VideoAnalysis.ipynb",
- "provenance": [],
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3.8.12 ('dlc')",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "name": "python",
- "version": "3.8.12"
+ "base_uri": "https://localhost:8080/"
},
- "vscode": {
- "interpreter": {
- "hash": "70cad038f2bddb56e8a0ba66c48b76ebce20579892bf83e71733a81977e3ceea"
- }
+ "id": "oTwAcbq2-FZz",
+ "outputId": "9cfd8dcf-a0a8-4801-ed1d-fbcd5ec056af"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "DLC loaded in light mode; you cannot use any GUI (labeling, relabeling and standalone GUI)\n"
+ ]
}
+ ],
+ "source": [
+ "import deeplabcut"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "cQ-nlTkri4HZ"
+ },
+ "source": [
+ "## Link your Google Drive (with your labeled data, or the demo data):\n",
+ "\n",
+ "### First, place your project folder into you google drive! \"i.e. move the folder named \"Project-YourName-TheDate\" into google drive."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {},
+ "colab_type": "code",
+ "id": "KS4Q4UkR9rgG"
+ },
+ "outputs": [],
+ "source": [
+ "# Now, let's link to your GoogleDrive. Run this cell and follow the authorization instructions:\n",
+ "# (We recommend putting a copy of the github repo in your google drive if you are using the demo \"examples\")\n",
+ "\n",
+ "from google.colab import drive\n",
+ "\n",
+ "drive.mount(\"/content/drive\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "Frnj1RVDyEqs"
+ },
+ "source": [
+ "YOU WILL NEED TO EDIT THE PROJECT PATH **in the config.yaml file** TO BE SET TO YOUR GOOGLE DRIVE LINK!\n",
+ "\n",
+ "Typically, this will be: `/content/drive/My Drive/yourProjectFolderName`\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {},
+ "colab_type": "code",
+ "id": "vhENAlQnFENJ"
+ },
+ "outputs": [],
+ "source": [
+ "# PLEASE EDIT THIS:\n",
+ "project_folder_name = \"MontBlanc-Daniel-2019-12-16\"\n",
+ "video_type = \"mp4\" # , mp4, MOV, or avi, whatever you uploaded!\n",
+ "\n",
+ "# No need to edit this, we are going to assume you put videos you want to analyze\n",
+ "# in the \"videos\" folder, but if this is NOT true, edit below:\n",
+ "videofile_path = [f\"/content/drive/My Drive/{project_folder_name}/videos/\"]\n",
+ "print(videofile_path)\n",
+ "\n",
+ "# The prediction files and labeled videos will be saved in this `labeled-videos` folder\n",
+ "# in your project folder; if you want them elsewhere, you can edit this;\n",
+ "# if you want the output files in the same folder as the videos, set this to an empty string.\n",
+ "destfolder = f\"/content/drive/My Drive/{project_folder_name}/labeled-videos\"\n",
+ "\n",
+ "# No need to edit this, as you set it when you passed the ProjectFolderName (above):\n",
+ "path_config_file = f\"/content/drive/My Drive/{project_folder_name}/config.yaml\"\n",
+ "print(path_config_file)\n",
+ "\n",
+ "# This creates a path variable that links to your Google Drive project"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "xNi9s1dboEJN"
+ },
+ "source": [
+ "## Create a training dataset:\n",
+ "\n",
+ "### You must do this step inside of Colab\n",
+ "\n",
+ "After running this script the training dataset is created and saved in the project directory under the subdirectory **'training-datasets'**\n",
+ "\n",
+ "This function also creates new subdirectories under **dlc-models-pytorch** and appends the project config.yaml file with the correct path to the training and testing pose configuration file. These files hold the parameters for training the network. Such an example file is provided with the toolbox and named as **pytorch_config.yaml**.\n",
+ "\n",
+ "Now it is the time to start training the network!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {},
+ "colab_type": "code",
+ "id": "eMeUwgxPoEJP",
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "# There are many more functions you can set here, including which network to use!\n",
+ "# Check the docstring for `create_training_dataset` for all options you can use!\n",
+ "\n",
+ "deeplabcut.create_training_dataset(path_config_file, net_type=\"resnet_50\", engine=deeplabcut.Engine.PYTORCH)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "c4FczXGDoEJU"
+ },
+ "source": [
+ "## Start training:\n",
+ "This function trains the network for a specific shuffle of the training dataset. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {},
+ "colab_type": "code",
+ "id": "_pOvDq_2oEJW"
+ },
+ "outputs": [],
+ "source": [
+ "# Let's also change the display and save_epochs just in case Colab takes away\n",
+ "# the GPU... If that happens, you can reload from a saved point using the\n",
+ "# `snapshot_path` argument to `deeplabcut.train_network`:\n",
+ "# deeplabcut.train_network(..., snapshot_path=\"/content/.../snapshot-050.pt\")\n",
+ "\n",
+ "# Typically, you want to train to ~200 epochs. We set the batch size to 8 to\n",
+ "# utilize the GPU's capabilities.\n",
+ "\n",
+ "# More info and there are more things you can set:\n",
+ "# https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide.html#g-train-the-network\n",
+ "\n",
+ "deeplabcut.train_network(\n",
+ " path_config_file,\n",
+ " shuffle=1,\n",
+ " save_epochs=5,\n",
+ " epochs=200,\n",
+ " batch_size=8,\n",
+ ")\n",
+ "\n",
+ "# This will run until you stop it (CTRL+C), or hit \"STOP\" icon, or when it hits the end."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "RiDwIVf5-3H_"
+ },
+ "source": [
+ "Note, that **when you hit \"STOP\" you will get a `KeyboardInterrupt` \"error\"! No worries! :)**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "xZygsb2DoEJc"
+ },
+ "source": [
+ "## Start evaluating:\n",
+ "This function evaluates a trained model for a specific shuffle/shuffles at a particular state or all the states on the data set (images)\n",
+ "and stores the results as .csv file in a subdirectory under **evaluation-results-pytorch**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {},
+ "colab_type": "code",
+ "id": "nv4zlbrnoEJg"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.evaluate_network(path_config_file, plotting=True)\n",
+ "\n",
+ "# Here you want to see a low pixel error! Of course, it can only be as\n",
+ "# good as the labeler, so be sure your labels are good!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "BaLBl3TQtrfB"
+ },
+ "source": [
+ "## There is an optional refinement step you can do outside of Colab:\n",
+ "- if your pixel errors are not low enough, please check out the protocol guide on how to refine your network!\n",
+ "- You will need to adjust the labels **outside of Colab!** We recommend coming back to train and analyze videos... \n",
+ "- Please see the repo and protocol instructions on how to refine your data!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "OVFLSKKfoEJk"
+ },
+ "source": [
+ "## Start Analyzing videos: \n",
+ "This function analyzes the new video. The user can choose the best model from the evaluation results and specify the correct snapshot index for the variable **snapshotindex** in the **config.yaml** file. Otherwise, by default the most recent snapshot is used to analyse the video.\n",
+ "\n",
+ "The results are stored in hd5 file in the same directory where the video resides. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {},
+ "colab_type": "code",
+ "id": "Y_LZiS_0oEJl"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.analyze_videos(\n",
+ " path_config_file,\n",
+ " videofile_path,\n",
+ " videotype=video_type,\n",
+ " destfolder=destfolder,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "8GTiuJESoEKH"
+ },
+ "source": [
+ "## Plot the trajectories of the analyzed videos:\n",
+ "This function plots the trajectories of all the body parts across the entire video. Each body part is identified by a unique color."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {},
+ "colab_type": "code",
+ "id": "gX21zZbXoEKJ"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.plot_trajectories(\n",
+ " path_config_file,\n",
+ " videofile_path,\n",
+ " videotype=video_type,\n",
+ " destfolder=destfolder,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "pqaCw15v8EmB"
+ },
+ "source": [
+ "Now you can look at the plot-poses file and check the \"plot-likelihood.png\" might want to change the \"p-cutoff\" in the config.yaml file so that you have only high confidnece points plotted in the video. i.e. ~0.8 or 0.9. The current default is 0.4. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "pCrUvQIvoEKD"
+ },
+ "source": [
+ "## Create labeled video:\n",
+ "This function is for visualization purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {},
+ "colab_type": "code",
+ "id": "6aDF7Q7KoEKE"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.create_labeled_video(\n",
+ " path_config_file,\n",
+ " videofile_path,\n",
+ " videotype=video_type,\n",
+ " destfolder=destfolder,\n",
+ ")"
+ ]
+ }
+ ],
+ "metadata": {
+ "accelerator": "GPU",
+ "colab": {
+ "collapsed_sections": [],
+ "include_colab_link": true,
+ "name": "Copy of latest_Colab_TrainNetwork_VideoAnalysis.ipynb",
+ "provenance": [],
+ "toc_visible": true
+ },
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-09-16",
+ "last_metadata_updated": "2026-03-06"
+ },
+ "kernelspec": {
+ "display_name": "Python 3.8.12 ('dlc')",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python",
+ "version": "3.8.12"
},
- "nbformat": 4,
- "nbformat_minor": 0
+ "vscode": {
+ "interpreter": {
+ "hash": "70cad038f2bddb56e8a0ba66c48b76ebce20579892bf83e71733a81977e3ceea"
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
}
diff --git a/examples/COLAB/COLAB_YOURDATA_maDLC_TrainNetwork_VideoAnalysis.ipynb b/examples/COLAB/COLAB_YOURDATA_maDLC_TrainNetwork_VideoAnalysis.ipynb
new file mode 100644
index 0000000000..08633839fb
--- /dev/null
+++ b/examples/COLAB/COLAB_YOURDATA_maDLC_TrainNetwork_VideoAnalysis.ipynb
@@ -0,0 +1,544 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "view-in-github"
+ },
+ "source": [
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "RK255E7YoEIt"
+ },
+ "source": [
+ "# DeepLabCut for your multi-animal projects!\n",
+ "\n",
+ "Some useful links:\n",
+ "\n",
+ "- [DeepLabCut's GitHub: github.com/DeepLabCut/DeepLabCut](https://github.com/DeepLabCut/DeepLabCut)\n",
+ "- [DeepLabCut's Documentation: User Guide for Multi-Animal projects](https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html)\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "This notebook illustrates how to, for multi-animal projects, use the cloud-based GPU to:\n",
+ "- create a multi-animal training set\n",
+ "- train a network\n",
+ "- evaluate a network\n",
+ "- analyze novel videos\n",
+ "- assemble animals and tracklets\n",
+ "- create quality check plots!\n",
+ "\n",
+ "### This notebook assumes you already have a DLC project folder with labeled data and you uploaded it to your own Google Drive.\n",
+ "\n",
+ "This notebook demonstrates the necessary steps to use DeepLabCut for your own project.\n",
+ "\n",
+ "This shows the most simple code to do so, but many of the functions have additional features, so please check out the docs on GitHub. We also recommend checking out our preprint, which covers the science of maDLC\n",
+ "\n",
+ "**Lauer et al 2021:** https://www.biorxiv.org/content/10.1101/2021.04.30.442096v1\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "txoddlM8hLKm"
+ },
+ "source": [
+ "## First, go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\"\n",
+ "\n",
+ "As the COLAB environments were updated to CUDA 12.X and Python 3.11, we need to install DeepLabCut and TensorFlow in a distinct way to get TensorFlow to connect to the GPU."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# this will take a couple of minutes to install all the dependencies!\n",
+ "!pip install --pre deeplabcut"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**(Be sure to click \"RESTART RUNTIME\" if it is displayed above before moving on !)** You will see this button at the output of the cells above ^."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "oTwAcbq2-FZz",
+ "outputId": "9cfd8dcf-a0a8-4801-ed1d-fbcd5ec056af"
+ },
+ "outputs": [],
+ "source": [
+ "import deeplabcut"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "cQ-nlTkri4HZ"
+ },
+ "source": [
+ "## Link your Google Drive (with your labeled data):\n",
+ "\n",
+ "- This code assumes you locally installed DeepLabCut, created a project, extracted and labeled frames. Be sure to \"check Labels\" to confirm you are happy with your data. As, these frames are the only thing that is used to train your network. 💪 You can find all the docs to do this here: [deeplabcut.github.io/DeepLabCut](https://deeplabcut.github.io/DeepLabCut/README.html)\n",
+ "- Next, place your DLC project folder into you Google Drive- i.e., copy the folder named \"Project-YourName-TheDate\" into Google Drive.\n",
+ "- Then, click run on the cell below to link this notebook to your Google Drive:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "KS4Q4UkR9rgG"
+ },
+ "outputs": [],
+ "source": [
+ "# Now, let's link to your GoogleDrive. Run this cell and follow the authorization instructions:\n",
+ "# (We recommend putting a copy of the github repo in your google drive if you are using the demo \"examples\")\n",
+ "\n",
+ "from google.colab import drive\n",
+ "\n",
+ "drive.mount(\"/content/drive\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Frnj1RVDyEqs"
+ },
+ "source": [
+ "## Next, edit the few items below, and click run:\n",
+ "\n",
+ "YOU WILL NEED TO EDIT THE PROJECT PATH **in the `config.yaml` file** TO BE SET TO YOUR GOOGLE DRIVE LINK! Typically, this will be: `/content/drive/My Drive/yourProjectFolderName`\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "vhENAlQnFENJ"
+ },
+ "outputs": [],
+ "source": [
+ "# PLEASE EDIT THIS:\n",
+ "project_folder_name = \"MontBlanc-Daniel-2019-12-16\"\n",
+ "video_type = \"mp4\" #, mp4, MOV, or avi, whatever you uploaded!\n",
+ "\n",
+ "# No need to edit this, we are going to assume you put videos you want to analyze\n",
+ "# in the \"videos\" folder, but if this is NOT true, edit below:\n",
+ "videofile_path = [f\"/content/drive/My Drive/{project_folder_name}/videos/\"]\n",
+ "print(videofile_path)\n",
+ "\n",
+ "# The prediction files and labeled videos will be saved in this `labeled-videos` folder\n",
+ "# in your project folder; if you want them elsewhere, you can edit this;\n",
+ "# if you want the output files in the same folder as the videos, set this to an empty string.\n",
+ "destfolder = f\"/content/drive/My Drive/{project_folder_name}/labeled-videos\"\n",
+ "\n",
+ "#No need to edit this, as you set it when you passed the ProjectFolderName (above):\n",
+ "path_config_file = f\"/content/drive/My Drive/{project_folder_name}/config.yaml\"\n",
+ "print(path_config_file)\n",
+ "\n",
+ "# This creates a path variable that links to your Google Drive project"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "xNi9s1dboEJN"
+ },
+ "source": [
+ "## Create a multi-animal training dataset:\n",
+ "\n",
+ "- more info can be [found in the docs](https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html#create-training-dataset)\n",
+ "- please check the text below, edit if needed, and then click run (this can take some time):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "-AAYSoW313me"
+ },
+ "outputs": [],
+ "source": [
+ "# OPTIONAL LEARNING: did you know you can check what each function does by running with a ?\n",
+ "deeplabcut.create_multianimaltraining_dataset?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "eMeUwgxPoEJP",
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "# ATTENTION:\n",
+ "# Which shuffle do you want to create and train?\n",
+ "shuffle = 1 # Edit if needed; 1 is the default.\n",
+ "\n",
+ "deeplabcut.create_multianimaltraining_dataset(\n",
+ " path_config_file,\n",
+ " Shuffles=[shuffle],\n",
+ " net_type=\"dlcrnet_ms5\",\n",
+ " engine=deeplabcut.Engine.PYTORCH,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "c4FczXGDoEJU"
+ },
+ "source": [
+ "## Start training:\n",
+ "This function trains the network for a specific shuffle of the training dataset. More info can be found [in the docs](https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html#train-the-network)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "_pOvDq_2oEJW"
+ },
+ "outputs": [],
+ "source": [
+ "# Let's also change the display and save_epochs just in case Colab takes away\n",
+ "# the GPU... If that happens, you can reload from a saved point using the\n",
+ "# `snapshot_path` argument to `deeplabcut.train_network`:\n",
+ "# deeplabcut.train_network(..., snapshot_path=\"/content/.../snapshot-050.pt\")\n",
+ "\n",
+ "# Typically, you want to train to ~200 epochs. We set the batch size to 8 to\n",
+ "# utilize the GPU's capabilities.\n",
+ "\n",
+ "# More info and there are more things you can set:\n",
+ "# https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide.html#g-train-the-network\n",
+ "\n",
+ "deeplabcut.train_network(\n",
+ " path_config_file,\n",
+ " shuffle=shuffle,\n",
+ " save_epochs=5,\n",
+ " epochs=200,\n",
+ " batch_size=8,\n",
+ ")\n",
+ "\n",
+ "# This will run until you stop it (CTRL+C), or hit \"STOP\" icon, or when it hits the end."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "RiDwIVf5-3H_"
+ },
+ "source": [
+ "Note, that **when you hit \"STOP\" you will get a `KeyboardInterrupt` \"error\"! No worries! :)**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "xZygsb2DoEJc"
+ },
+ "source": [
+ "## Start evaluating: \n",
+ "\n",
+ "- First, we evaluate the pose estimation performance.\n",
+ "- This function evaluates a trained model for a specific shuffle/shuffles at a particular state or all the states on the data set (images) and stores the results as .5 and .csv file in a subdirectory under **evaluation-results-pytorch**\n",
+ "- If the scoremaps do not look accurate, don't proceed to tracklet assembly; please consider (1) adding more data, (2) adding more bodyparts!\n",
+ "- More info can be [found in the docs](https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html#evaluate-the-trained-network)\n",
+ "\n",
+ "Here is an example of what you'd aim to see before proceeding:\n",
+ "\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "nv4zlbrnoEJg"
+ },
+ "outputs": [],
+ "source": [
+ "# Let's evaluate first:\n",
+ "deeplabcut.evaluate_network(path_config_file, Shuffles=[shuffle], plotting=True)\n",
+ "\n",
+ "# plot a few scoremaps:\n",
+ "deeplabcut.extract_save_all_maps(path_config_file, shuffle=shuffle, Indices=[0, 1, 2, 3])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "fYlGbloolDU2"
+ },
+ "source": [
+ "IF these images, numbers, and maps do not look good, do not proceed. You should increase the diversity and number of frames you label, and re-create a training dataset and re-train! "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "OVFLSKKfoEJk"
+ },
+ "source": [
+ "## Start Analyzing videos: \n",
+ "This function analyzes the new video. The user can choose the best model from the evaluation results and specify the correct snapshot index for the variable **snapshotindex** in the **config.yaml** file. Otherwise, by default the most recent snapshot is used to analyse the video.\n",
+ "\n",
+ "The results are stored in a pickle file in the same directory where the video resides. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Y_LZiS_0oEJl"
+ },
+ "outputs": [],
+ "source": [
+ "print(\"Start Analyzing my video(s)!\")\n",
+ "#EDIT OPTION: which video(s) do you want to analyze? You can pass a path or a folder:\n",
+ "# currently, if you run \"as is\" it assumes you have a video in the DLC project video folder!\n",
+ "\n",
+ "deeplabcut.analyze_videos(\n",
+ " path_config_file,\n",
+ " videofile_path,\n",
+ " shuffle=shuffle,\n",
+ " videotype=video_type,\n",
+ " auto_track=False,\n",
+ " destfolder=destfolder,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "91xBLOcBzGxo"
+ },
+ "source": [
+ "Optional: Now you have the option to check the raw detections before animals are tracked. To do so, pass a video path:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "65mWwX5bTc5C"
+ },
+ "outputs": [],
+ "source": [
+ "##### PROTIP: #####\n",
+ "## look at the output video; if the pose estimation (i.e. key points)\n",
+ "## don't look good, don't proceed with tracking - add more data to your training set and re-train!\n",
+ "\n",
+ "# EDIT: let's check a specific video (PLEASE EDIT VIDEO PATH):\n",
+ "specific_videofile = \"/content/drive/MyDrive/DeepLabCut_maDLC_DemoData/MontBlanc-Daniel-2019-12-16/videos/short.mov\"\n",
+ "\n",
+ "# Don't edit:\n",
+ "deeplabcut.create_video_with_all_detections(\n",
+ " path_config_file, [specific_videofile], shuffle=shuffle, destfolder=destfolder,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "3-OgTJ0Lz20e"
+ },
+ "source": [
+ "If the resulting video (ends in full.mp4) is not good, we highly recommend adding more data and training again. See [here, in the docs](https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html#decision-break-point)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "PxRLS2_-r55K"
+ },
+ "source": [
+ "## Next, we will assemble animals using our data-driven optimal graph method:\n",
+ "\n",
+ "During video analysis, animals are assembled using the optimal graph, which matches the \"data-driven\" method from our paper (Figure adapted from Lauer et al. 2021)\n",
+ "\n",
+ "\n",
+ "\n",
+ "The optimal graph is computed when `evaluate_network` - so make sure you don't skip that step!\n",
+ "\n",
+ "**Note**: you can set the number of animals you expect to see, so check, edit, then click run:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "zIvXM7TXIs-U"
+ },
+ "outputs": [],
+ "source": [
+ "#Check and edit:\n",
+ "num_animals = 4 # How many animals do you expect to find?\n",
+ "track_type= \"box\" # box, skeleton, ellipse\n",
+ "#-- ellipse is recommended, unless you have a single-point MA project, then use BOX!\n",
+ "\n",
+ "# Optional:\n",
+ "# imagine you tracked a point that is not useful for assembly,\n",
+ "# like a tail tip that is far from the body, consider dropping it for this step (it's still used later)!\n",
+ "# To drop it, uncomment the next line TWO lines and add your parts(s):\n",
+ "\n",
+ "# bodypart= 'Tail_end'\n",
+ "# deeplabcut.convert_detections2tracklets(path_config_file, videofile_path, videotype=VideoType, shuffle=shuffle, overwrite=True, ignore_bodyparts=[bodypart])\n",
+ "\n",
+ "# OR don't drop, just click RUN:\n",
+ "deeplabcut.convert_detections2tracklets(\n",
+ " path_config_file,\n",
+ " videofile_path,\n",
+ " videotype=video_type,\n",
+ " shuffle=shuffle,\n",
+ " track_method=track_type,\n",
+ " destfolder=destfolder,\n",
+ " overwrite=True,\n",
+ ")\n",
+ "\n",
+ "deeplabcut.stitch_tracklets(\n",
+ " path_config_file,\n",
+ " videofile_path,\n",
+ " shuffle=shuffle,\n",
+ " track_method=track_type,\n",
+ " n_tracks=num_animals,\n",
+ " destfolder=destfolder,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "TqbAnyfL0Q7h"
+ },
+ "source": [
+ "Now let's filter the data to remove any small jitter:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "a6izVWX8sdzL"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.filterpredictions(\n",
+ " path_config_file,\n",
+ " videofile_path,\n",
+ " shuffle=shuffle,\n",
+ " videotype=video_type,\n",
+ " track_method=track_type,\n",
+ " destfolder=destfolder,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Zk4xGb8Ftf3B"
+ },
+ "source": [
+ "## Create plots of your trajectories:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "gX21zZbXoEKJ"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.plot_trajectories(\n",
+ " path_config_file,\n",
+ " videofile_path,\n",
+ " videotype=video_type,\n",
+ " shuffle=shuffle,\n",
+ " track_method=track_type,\n",
+ " destfolder=destfolder,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "pqaCw15v8EmB"
+ },
+ "source": [
+ "Now you can look at the plot-poses file and check the \"plot-likelihood.png\" might want to change the \"p-cutoff\" in the config.yaml file so that you have only high confidnece points plotted in the video. i.e. ~0.8 or 0.9. The current default is 0.4. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "pCrUvQIvoEKD"
+ },
+ "source": [
+ "## Create labeled video:\n",
+ "This function is for visualization purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "6aDF7Q7KoEKE"
+ },
+ "outputs": [],
+ "source": [
+ "deeplabcut.create_labeled_video(\n",
+ " path_config_file,\n",
+ " videofile_path,\n",
+ " shuffle=shuffle,\n",
+ " color_by=\"individual\",\n",
+ " videotype=video_type,\n",
+ " save_frames=False,\n",
+ " filtered=True,\n",
+ " track_method=track_type,\n",
+ " destfolder=destfolder,\n",
+ ")"
+ ]
+ }
+ ],
+ "metadata": {
+ "accelerator": "GPU",
+ "colab": {
+ "collapsed_sections": [],
+ "include_colab_link": true,
+ "name": "COLAB_maDLC_TrainNetwork_VideoAnalysis.ipynb",
+ "provenance": []
+ },
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2026-02-10",
+ "last_metadata_updated": "2026-03-06"
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "name": "python3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/examples/COLAB/COLAB_maDLC_TrainNetwork_VideoAnalysis.ipynb b/examples/COLAB/COLAB_maDLC_TrainNetwork_VideoAnalysis.ipynb
deleted file mode 100644
index 53125ab13e..0000000000
--- a/examples/COLAB/COLAB_maDLC_TrainNetwork_VideoAnalysis.ipynb
+++ /dev/null
@@ -1,533 +0,0 @@
-{
- "nbformat": 4,
- "nbformat_minor": 0,
- "metadata": {
- "colab": {
- "name": "COLAB_maDLC_TrainNetwork_VideoAnalysis.ipynb",
- "provenance": [],
- "collapsed_sections": [],
- "machine_shape": "hm",
- "include_colab_link": true
- },
- "kernelspec": {
- "name": "python3",
- "display_name": "Python 3"
- },
- "accelerator": "GPU"
- },
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "view-in-github"
- },
- "source": [
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "RK255E7YoEIt"
- },
- "source": [
- "# DeepLabCut 2.2+ Toolbox - COLAB\n",
- "\n",
- "\n",
- "https://github.com/DeepLabCut/DeepLabCut\n",
- "\n",
- "This notebook illustrates how to, for multi-animal projects, use the cloud-based GPU to:\n",
- "- create a multi-animal training set\n",
- "- train a network\n",
- "- evaluate a network\n",
- "- analyze novel videos\n",
- "- assemble animals and tracklets\n",
- "- create quality check plots!\n",
- "\n",
- "###This notebook assumes you already have a DLC project folder with labeled data and you uploaded it to your own Google Drive.\n",
- "\n",
- "This notebook demonstrates the necessary steps to use DeepLabCut for your own project.\n",
- "\n",
- "This shows the most simple code to do so, but many of the functions have additional features, so please check out the docs on GitHub. We also recommend checking out our preprint, which covers the science of maDLC\n",
- "\n",
- "**Lauer et al 2021:** https://www.biorxiv.org/content/10.1101/2021.04.30.442096v1\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "txoddlM8hLKm"
- },
- "source": [
- "## First, go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\"\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "q23BzhA6CXxu"
- },
- "outputs": [],
- "source": [
- "#(this will take a few minutes to install all the dependences!)\n",
- "!pip install deeplabcut\n",
- "%reload_ext numpy\n",
- "%reload_ext scipy\n",
- "%reload_ext matplotlib\n",
- "%reload_ext mpl_toolkits"
- ]
- },
- {
- "cell_type": "code",
- "metadata": {
- "id": "-MVvZ13_FMvP"
- },
- "source": [
- "#a few colab specific things needed:\n",
- "!pip install --upgrade scikit-image\n",
- "!pip3 install pickle5"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "bqUEb8TBdpWb"
- },
- "source": [
- "After the package is installed, please click \"restart runtime\" if it appears for DLC changes to take effect in your COLAB environment. You will see this button at the output of the cells above ^."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "oTwAcbq2-FZz",
- "colab": {
- "base_uri": "https://localhost:8080/"
- },
- "outputId": "9cfd8dcf-a0a8-4801-ed1d-fbcd5ec056af"
- },
- "outputs": [],
- "source": [
- "import deeplabcut\n",
- "import pickle5 as pickle"
- ],
- "execution_count": 2,
- "outputs": [
- {
- "output_type": "stream",
- "name": "stdout",
- "text": [
- "DLC loaded in light mode; you cannot use any GUI (labeling, relabeling and standalone GUI)\n"
- ]
- }
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "cQ-nlTkri4HZ"
- },
- "source": [
- "## Link your Google Drive (with your labeled data):\n",
- "\n",
- "- This code assumes you locally installed DeepLabCut, created a project, extracted and labeled frames. Be sure to \"check Labels\" to confirm you are happy with your data. As, these frames are the only thing that is used to train your network. 💪 You can find all the docs to do this here: https://deeplabcut.github.io/DeepLabCut\n",
- "\n",
- "- Next, place your DLC project folder into you Google Drive- i.e., copy the folder named \"Project-YourName-TheDate\" into Google Drive.\n",
- "\n",
- "- Then, click run on the cell below to link this notebook to your Google Drive:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "KS4Q4UkR9rgG"
- },
- "outputs": [],
- "source": [
- "#Now, let's link to your Google Drive. Run this cell and follow the authorization instructions:\n",
- "#(We recommend putting a copy of the github repo in your google drive if you are using the demo \"examples\")\n",
- "\n",
- "from google.colab import drive\n",
- "drive.mount('/content/drive')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "Frnj1RVDyEqs"
- },
- "source": [
- "## Next, edit the few items below, and click run:\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "vhENAlQnFENJ"
- },
- "outputs": [],
- "source": [
- "# PLEASE EDIT THIS:\n",
- "ProjectFolderName = 'MontBlanc-Daniel-2019-12-16'\n",
- "VideoType = 'mp4' #, mp4, MOV, or avi, whatever you uploaded!\n",
- "\n",
- "\n",
- "# No need to edit this, we are going to assume you put videos you want to analyze in the \"videos\" folder, but if this is NOT true, edit below:\n",
- "videofile_path = ['/content/drive/My Drive/'+ProjectFolderName+'/videos/'] #Enter the list of videos or folder to analyze.\n",
- "videofile_path\n",
- "\n",
- "#No need to edit this, as you set it when you passed the ProjectFolderName (above): \n",
- "path_config_file = '/content/drive/My Drive/'+ProjectFolderName+'/config.yaml'\n",
- "path_config_file\n",
- "#This creates a path variable that links to your google drive project"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "xNi9s1dboEJN"
- },
- "source": [
- "## Create a multi-animal training dataset:\n",
- "\n",
- "- more info: https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html#create-training-dataset\n",
- "\n",
- "- please check the text below, edit if needed, and then click run (this can take some time):"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "-AAYSoW313me"
- },
- "outputs": [],
- "source": [
- "#OPTIONAL LEARNING: did you know you can check what each function does by running with a ?\n",
- "deeplabcut.create_multianimaltraining_dataset?"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "eMeUwgxPoEJP",
- "scrolled": true
- },
- "outputs": [],
- "source": [
- "# ATTENTION:\n",
- "#which shuffle do you want to create and train?\n",
- "shuffle = 1 #edit if needed; 1 is the default.\n",
- "\n",
- "#if you labeled on Windows, please set the windows2linux=True:\n",
- "deeplabcut.create_multianimaltraining_dataset(path_config_file, Shuffles=[shuffle], net_type=\"dlcrnet_ms5\",windows2linux=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "c4FczXGDoEJU"
- },
- "source": [
- "## Start training:\n",
- "This function trains the network for a specific shuffle of the training dataset. \n",
- " - more info: https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html#train-the-network"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "_pOvDq_2oEJW"
- },
- "outputs": [],
- "source": [
- "#let's also change the display and save_iters just in case Colab takes away the GPU... \n",
- "#Typically, you want to train to 50,000 - 200K iterations.\n",
- "#more info and there are more things you can set: https://github.com/DeepLabCut/DeepLabCut/blob/master/docs/functionDetails.md#g-train-the-network\n",
- "\n",
- "deeplabcut.train_network(path_config_file, shuffle=shuffle, displayiters=100,saveiters=1000, maxiters=75000, allow_growth=True)\n",
- "\n",
- "#this will run until you stop it (CTRL+C), or hit \"STOP\" icon, or when it hits the end (default, 50K iterations). \n",
- "#Whichever you chose, you will see what looks like an error message, but it's not an error - don't worry...."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "RiDwIVf5-3H_"
- },
- "source": [
- "**When you hit \"STOP\" you will get a KeyInterrupt \"error\"! No worries! :)**"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "xZygsb2DoEJc"
- },
- "source": [
- "## Start evaluating: \n",
- "\n",
- " - First, we evaluate the pose estimation performance.\n",
- "\n",
- "- This function evaluates a trained model for a specific shuffle/shuffles at a particular state or all the states on the data set (images) and stores the results as .5 and .csv file in a subdirectory under **evaluation-results**\n",
- "\n",
- "- If the scoremaps do not look accurate, don't proceed to tracklet assembly; please consider (1) adding more data, (2) adding more bodyparts!\n",
- "\n",
- "- more info: https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html#evaluate-the-trained-network\n",
- "\n",
- "Here is an example of what you'd aim to see before proceeding:\n",
- "\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "nv4zlbrnoEJg"
- },
- "outputs": [],
- "source": [
- "#let's evaluate first:\n",
- "deeplabcut.evaluate_network(path_config_file,Shuffles=[shuffle], plotting=True)\n",
- "#plot a few scoremaps:\n",
- "deeplabcut.extract_save_all_maps(path_config_file, shuffle=shuffle, Indices=[0])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "fYlGbloolDU2"
- },
- "source": [
- "IF these images, numbers, and maps do not look good, do not proceed. You should increase the diversity and number of frames you label, and re-create a training dataset and re-train! "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "OVFLSKKfoEJk"
- },
- "source": [
- "## Start Analyzing videos: \n",
- "This function analyzes the new video. The user can choose the best model from the evaluation results and specify the correct snapshot index for the variable **snapshotindex** in the **config.yaml** file. Otherwise, by default the most recent snapshot is used to analyse the video.\n",
- "\n",
- "The results are stored in a pickle file in the same directory where the video resides. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "Y_LZiS_0oEJl"
- },
- "outputs": [],
- "source": [
- "print(\"Start Analyzing my video(s)!\")\n",
- "#EDIT OPTION: which video(s) do you want to analyze? You can pass a path or a folder:\n",
- "# currently, if you run \"as is\" it assumes you have a video in the DLC project video folder!\n",
- "\n",
- "deeplabcut.analyze_videos(path_config_file,videofile_path, shuffle=shuffle, videotype=VideoType)"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "91xBLOcBzGxo"
- },
- "source": [
- "Optional: Now you have the option to check the raw dections before animals are assembled. To do so, pass a video path:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "65mWwX5bTc5C"
- },
- "outputs": [],
- "source": [
- "##### PROTIP: #####\n",
- "## look at the output video; if the pose estimation (i.e. key points)\n",
- "## don't look good, don't proceed with tracking - add more data to your training set and re-train!\n",
- "\n",
- "#EDIT: let's check a specific video (PLEASE EDIT VIDEO PATH):\n",
- "Specific_videofile = '/content/drive/MyDrive/DeepLabCut_maDLC_DemoData/MontBlanc-Daniel-2019-12-16/videos/short.mov'\n",
- "\n",
- "#don't edit:\n",
- "deeplabcut.create_video_with_all_detections(path_config_file, [Specific_videofile], shuffle=shuffle)"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3-OgTJ0Lz20e"
- },
- "source": [
- "If the resutling video (ends in full.mp4) is not good, we highly recommend adding more data and training again. See here: https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html#decision-break-point"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "PxRLS2_-r55K"
- },
- "source": [
- "# Next, we will assemble animals using our data-driven optimal graph method:\n",
- "\n",
- "- Here, we will find the optimal graph, which matches the \"data-driven\" method from our paper (Figure adapted from Lauer et al. 2021):\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "- note, you can set the number of animals you expect to see, so check, edit, then click run:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "zIvXM7TXIs-U"
- },
- "outputs": [],
- "source": [
- "#Check and edit:\n",
- "numAnimals = 4 #how many animals do you expect to find?\n",
- "tracktype= 'box' #box, skeleton, ellipse:\n",
- "#-- ellipse is recommended, unless you have a single-point ma project, then use BOX!\n",
- "\n",
- "#Optional: \n",
- "#imagine you tracked a point that is not useful for assembly, \n",
- "#like a tail tip that is far from the body, consider dropping it for this step (it's still used later)!\n",
- "#To drop it, uncomment the next line TWO lines and add your parts(s):\n",
- "\n",
- "#bodypart= 'Tail_end'\n",
- "#deeplabcut.convert_detections2tracklets(path_config_file, videofile_path, videotype=VideoType, shuffle=shuffle, overwrite=True, ignore_bodyparts=[bodypart])\n",
- "\n",
- "#OR don't drop, just click RUN:\n",
- "deeplabcut.convert_detections2tracklets(path_config_file, videofile_path, videotype=VideoType, \n",
- " shuffle=shuffle, overwrite=True)\n",
- "\n",
- "deeplabcut.stitch_tracklets(path_config_file, videofile_path, shuffle=shuffle, track_method=tracktype, n_tracks=numAnimals)"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "TqbAnyfL0Q7h"
- },
- "source": [
- "Now let's filter the data to remove any small jitter:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "a6izVWX8sdzL"
- },
- "outputs": [],
- "source": [
- "deeplabcut.filterpredictions(path_config_file, \n",
- " videofile_path, \n",
- " shuffle=shuffle,\n",
- " videotype=VideoType, \n",
- " track_method = tracktype)"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "Zk4xGb8Ftf3B"
- },
- "source": [
- "## Create plots of your trajectories:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "gX21zZbXoEKJ"
- },
- "outputs": [],
- "source": [
- "deeplabcut.plot_trajectories(path_config_file, videofile_path, videotype=VideoType, shuffle=shuffle, track_method=tracktype)"
- ],
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "pqaCw15v8EmB"
- },
- "source": [
- "Now you can look at the plot-poses file and check the \"plot-likelihood.png\" might want to change the \"p-cutoff\" in the config.yaml file so that you have only high confidnece points plotted in the video. i.e. ~0.8 or 0.9. The current default is 0.4. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "pCrUvQIvoEKD"
- },
- "source": [
- "## Create labeled video:\n",
- "This function is for visualiztion purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "6aDF7Q7KoEKE"
- },
- "outputs": [],
- "source": [
- "deeplabcut.create_labeled_video(path_config_file,\n",
- " videofile_path, \n",
- " shuffle=shuffle, \n",
- " color_by=\"individual\",\n",
- " videotype=VideoType, \n",
- " save_frames=False,\n",
- " filtered=True)"
- ]
- }
- ],
- "metadata": {
- "accelerator": "GPU",
- "colab": {
- "collapsed_sections": [],
- "include_colab_link": true,
- "name": "COLAB_maDLC_TrainNetwork_VideoAnalysis.ipynb",
- "provenance": []
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/examples/COLAB/COLAB_transformer_reID.ipynb b/examples/COLAB/COLAB_transformer_reID.ipynb
index 89d10638f7..12eb4f89ea 100644
--- a/examples/COLAB/COLAB_transformer_reID.ipynb
+++ b/examples/COLAB/COLAB_transformer_reID.ipynb
@@ -7,7 +7,7 @@
"id": "view-in-github"
},
"source": [
- " "
+ " "
]
},
{
@@ -16,35 +16,57 @@
"id": "TGChzLdc-lUJ"
},
"source": [
- "# DeepLabCut 2.2 Toolbox Demo on how to use our Pose Transformer for unsupervised identity tracking of animals\n",
+ "# Demo: How to use our Pose Transformer for unsupervised identity tracking of animals\n",
"\n",
"\n",
"https://github.com/DeepLabCut/DeepLabCut\n",
"\n",
- "### This notebook illustrates how to use the transformer for a multi-animal DeepLabCut (maDLC) Demo 3 mouse project:\n",
+ "### This notebook illustrates how to use the transformer for a multi-animal DeepLabCut (maDLC) Demo tri-mouse project:\n",
"- load our mini-demo data that includes a pretrained model and unlabeled video.\n",
"- analyze a novel video.\n",
"- use the transformer to do unsupervised ID tracking.\n",
"- create quality check plots and video.\n",
"\n",
- "### To create a full maDLC pipeline please see our full docs: https://deeplabcut.github.io/DeepLabCut/docs/intro.html \n",
+ "### To create a full maDLC pipeline please see our full docs: https://deeplabcut.github.io/DeepLabCut/README.html\n",
"- Of interest is a full how-to for maDLC: https://deeplabcut.github.io/DeepLabCut/docs/maDLC_UserGuide.html\n",
- "- a quick guide to maDLC: https://deeplabcut.github.io/DeepLabCut/docs/tutorial.html\n",
- "- a demo COLAB for how to use maDLC on your own data: https://github.com/DeepLabCut/DeepLabCut/blob/master/examples/COLAB_maDLC_TrainNetwork_VideoAnalysis.ipynb\n",
+ "- a quick guide to maDLC: https://deeplabcut.github.io/DeepLabCut/docs/quick-start/tutorial_maDLC.html\n",
+ "- a demo COLAB for how to use maDLC on your own data: https://github.com/DeepLabCut/DeepLabCut/blob/main/examples/COLAB/COLAB_YOURDATA_maDLC_TrainNetwork_VideoAnalysis.ipynb\n",
"\n",
"### To get started, please go to \"Runtime\" ->\"change runtime type\"->select \"Python3\", and then select \"GPU\"\n"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "xOe2hvy85EVP"
+ },
+ "source": [
+ "‼️ **Attention: this demo is for maDLC, which is version 2.2**\n"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
"metadata": {
- "id": "HoNN2_0Z9rr_"
+ "id": "NXmLeZBX45Oe"
+ },
+ "outputs": [],
+ "source": [
+ "# Install DLC version 2.2-2.3 (pre DLC3):\n",
+ "!pip install \"deeplabcut[tf]\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "id": "TlhrVFKN8euh"
},
"outputs": [],
"source": [
- "# Install the latest DeepLabCut version:\n",
- "!pip install https://github.com/DeepLabCut/DeepLabCut/archive/master.zip"
+ "import os\n",
+ "\n",
+ "import deeplabcut"
]
},
{
@@ -53,6 +75,10 @@
"id": "Wid0GTGMAEnZ"
},
"source": [
+ "## Important - Restart the Runtime for the updated packages to be imported!\n",
+ "\n",
+ "PLEASE, click \"restart runtime\" from the output above before proceeding!\n",
+ "\n",
"No information needs edited in the cells below, you can simply click run on each:\n",
"\n",
"### Download our Demo Project from our server:"
@@ -60,26 +86,41 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 5,
"metadata": {
- "id": "PusLdqbqJi60"
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "PusLdqbqJi60",
+ "outputId": "dbe30821-d3a7-443f-de74-6cb0bee49aac"
},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Downloading demo-me-2021-07-14.zip...\n"
+ ]
+ }
+ ],
"source": [
"# Download our demo project:\n",
- "import urllib.request\n",
"from io import BytesIO\n",
"from zipfile import ZipFile\n",
"\n",
- "def unzip_from_url(url, dest_folder=''):\n",
- " # Directly extract files without writing the archive to disk\n",
- " resp = urllib.request.urlopen(url)\n",
- " with ZipFile(BytesIO(resp.read())) as zf:\n",
- " zf.extractall(path=dest_folder)\n",
- "\n",
+ "import requests\n",
"\n",
- "project_url = \"http://deeplabcut.rowland.harvard.edu/datasets/demo-me-2021-07-14.zip\"\n",
- "unzip_from_url(project_url, \"/content\")"
+ "url_record = \"https://zenodo.org/api/records/7883589\"\n",
+ "response = requests.get(url_record)\n",
+ "if response.status_code == 200:\n",
+ " file = response.json()[\"files\"][0]\n",
+ " title = file[\"key\"]\n",
+ " print(f\"Downloading {title}...\")\n",
+ " with requests.get(file[\"links\"][\"self\"], stream=True) as r:\n",
+ " with ZipFile(BytesIO(r.content)) as zf:\n",
+ " zf.extractall(path=\"/content\")\n",
+ "else:\n",
+ " raise ValueError(f\"The URL {url_record} could not be reached.\")"
]
},
{
@@ -88,27 +129,144 @@
"id": "8iXtySnQB0BE"
},
"source": [
- "## Analyze a novel 3 mouse video with our maDLC DLCRNet, pretrained on 3 mice data \n",
+ "## Analyze a novel 3 mouse video with our maDLC DLCRNet, pretrained on 3 mice data\n",
"\n",
- "###in one step, since auto_track=True you extract detections and association costs, create tracklets, & stitch them. We can use this to compare to the transformer-guided tracking below.\n"
+ "In one step, since `auto_track=True` you extract detections and association costs, create tracklets, & stitch them. We can use this to compare to the transformer-guided tracking below.\n"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 6,
"metadata": {
"id": "odYrU3o8BSAr"
},
"outputs": [],
"source": [
- "import deeplabcut as dlc\n",
- "import os\n",
- "\n",
"project_path = \"/content/demo-me-2021-07-14\"\n",
"config_path = os.path.join(project_path, \"config.yaml\")\n",
- "video = os.path.join(project_path, \"videos\", \"videocompressed1.mp4\")\n",
- "\n",
- "dlc.analyze_videos(config_path,[video], shuffle=0, videotype=\"mp4\",auto_track=True)"
+ "video = os.path.join(project_path, \"videos\", \"videocompressed1.mp4\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 520
+ },
+ "id": "U_351Hkv81X-",
+ "outputId": "f7c30461-101f-47b6-c04f-15809aa5a4bb"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Using snapshot-20000 for model /content/demo-me-2021-07-14/dlc-models/iteration-0/demoJul14-trainset95shuffle0\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/usr/local/lib/python3.11/dist-packages/tensorflow/python/keras/engine/base_layer_v1.py:1694: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.\n",
+ " warnings.warn('`layer.apply` is deprecated and '\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Activating extracting of PAFs\n",
+ "Starting to analyze % /content/demo-me-2021-07-14/videos/videocompressed1.mp4\n",
+ "Loading /content/demo-me-2021-07-14/videos/videocompressed1.mp4\n",
+ "Duration of video [s]: 77.67 , recorded with 30.0 fps!\n",
+ "Overall # of frames: 2330 found with (before cropping) frame dimensions: 640 480\n",
+ "Starting to extract posture from the video(s) with batchsize: 8\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 2330/2330 [00:39<00:00, 58.83it/s]\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Video Analyzed. Saving results in /content/demo-me-2021-07-14/videos...\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/usr/local/lib/python3.11/dist-packages/deeplabcut/utils/auxfun_multianimal.py:83: UserWarning: default_track_method` is undefined in the config.yaml file and will be set to `ellipse`.\n",
+ " warnings.warn(\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Using snapshot-20000 for model /content/demo-me-2021-07-14/dlc-models/iteration-0/demoJul14-trainset95shuffle0\n",
+ "Processing... /content/demo-me-2021-07-14/videos/videocompressed1.mp4\n",
+ "Analyzing /content/demo-me-2021-07-14/videos/videocompressed1DLC_dlcrnetms5_demoJul14shuffle0_20000.h5\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 2330/2330 [00:02<00:00, 1088.72it/s]\n",
+ "2330it [00:06, 342.29it/s] \n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "The tracklets were created (i.e., under the hood deeplabcut.convert_detections2tracklets was run). Now you can 'refine_tracklets' in the GUI, or run 'deeplabcut.stitch_tracklets'.\n",
+ "Processing... /content/demo-me-2021-07-14/videos/videocompressed1.mp4\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 4/4 [00:00<00:00, 1488.53it/s]\n",
+ "/usr/local/lib/python3.11/dist-packages/deeplabcut/refine_training_dataset/stitch.py:934: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " df.to_hdf(output_name, \"tracks\", format=\"table\", mode=\"w\")\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "The videos are analyzed. Time to assemble animals and track 'em... \n",
+ " Call 'create_video_with_all_detections' to check multi-animal detection quality before tracking.\n",
+ "If the tracking is not satisfactory for some videos, consider expanding the training set. You can use the function 'extract_outlier_frames' to extract a few representative outlier frames.\n"
+ ]
+ },
+ {
+ "data": {
+ "application/vnd.google.colaboratory.intrinsic+json": {
+ "type": "string"
+ },
+ "text/plain": [
+ "'DLC_dlcrnetms5_demoJul14shuffle0_20000'"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "deeplabcut.analyze_videos(config_path, [video], shuffle=0, videotype=\"mp4\", auto_track=True)"
]
},
{
@@ -131,23 +289,69 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 8,
"metadata": {
- "id": "aTRbuUQ1FBO0"
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "aTRbuUQ1FBO0",
+ "outputId": "0d182f64-512d-463d-a997-226c7199b724"
},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Filtering with median model /content/demo-me-2021-07-14/videos/videocompressed1.mp4\n",
+ "Saving filtered csv poses!\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/usr/local/lib/python3.11/dist-packages/deeplabcut/post_processing/filtering.py:298: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " data.to_hdf(outdataname, \"df_with_missing\", format=\"table\", mode=\"w\")\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Starting to process video: /content/demo-me-2021-07-14/videos/videocompressed1.mp4\n",
+ "Loading /content/demo-me-2021-07-14/videos/videocompressed1.mp4 and data.\n",
+ "Duration of video [s]: 77.67, recorded with 30.0 fps!\n",
+ "Overall # of frames: 2330 with cropped frame dimensions: 640 480\n",
+ "Generating frames and creating video.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/usr/local/lib/python3.11/dist-packages/deeplabcut/utils/make_labeled_video.py:140: FutureWarning: DataFrame.groupby with axis=1 is deprecated. Do `frame.T.groupby(...)` without axis instead.\n",
+ " Dataframe.groupby(level=\"individuals\", axis=1).size().values // 3\n",
+ "100%|██████████| 2330/2330 [00:31<00:00, 73.04it/s]\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "[True]"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
- "#Filter the predictions to remove small jitter, if desired:\n",
- "dlc.filterpredictions(config_path, \n",
- " [video], \n",
- " shuffle=0,\n",
- " videotype='mp4', \n",
- " )\n",
- "\n",
- "dlc.create_labeled_video(\n",
+ "# Filter the predictions to remove small jitter, if desired:\n",
+ "deeplabcut.filterpredictions(config_path, [video], shuffle=0, videotype=\"mp4\")\n",
+ "deeplabcut.create_labeled_video(\n",
" config_path,\n",
" [video],\n",
- " videotype='mp4',\n",
+ " videotype=\"mp4\",\n",
" shuffle=0,\n",
" color_by=\"individual\",\n",
" keypoints_only=False,\n",
@@ -179,13 +383,26 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 9,
"metadata": {
- "id": "7w9BDIA7BB_i"
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "7w9BDIA7BB_i",
+ "outputId": "a163087d-cbcb-4e4d-f461-2e24ed19a80b"
},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Loading /content/demo-me-2021-07-14/videos/videocompressed1.mp4 and data.\n",
+ "Plots created! Please check the directory \"plot-poses\" within the video directory\n"
+ ]
+ }
+ ],
"source": [
- "dlc.plot_trajectories(config_path, [video], shuffle=0,videotype='mp4')"
+ "deeplabcut.plot_trajectories(config_path, [video], shuffle=0, videotype=\"mp4\")"
]
},
{
@@ -196,21 +413,108 @@
"source": [
"# Transformer for reID\n",
"\n",
- "while the tracking here is very good without using the transformer, we want to demo the workflow for you! "
+ "while the tracking here is very good without using the transformer, we want to demo the workflow for you!"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 10,
"metadata": {
- "id": "5xlO6TVYxQWc"
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "5xlO6TVYxQWc",
+ "outputId": "a433221f-0390-4028-fe68-be0b90adad48"
},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Using snapshot-20000 for model /content/demo-me-2021-07-14/dlc-models/iteration-0/demoJul14-trainset95shuffle0\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/usr/local/lib/python3.11/dist-packages/tensorflow/python/keras/engine/base_layer_v1.py:1694: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.\n",
+ " warnings.warn('`layer.apply` is deprecated and '\n",
+ "/usr/local/lib/python3.11/dist-packages/tensorflow/python/keras/engine/base_layer_v1.py:1694: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.\n",
+ " warnings.warn('`layer.apply` is deprecated and '\n",
+ "/usr/local/lib/python3.11/dist-packages/tensorflow/python/keras/engine/base_layer_v1.py:1694: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.\n",
+ " warnings.warn('`layer.apply` is deprecated and '\n",
+ "/usr/local/lib/python3.11/dist-packages/tensorflow/python/keras/engine/base_layer_v1.py:1694: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.\n",
+ " warnings.warn('`layer.apply` is deprecated and '\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Activating extracting of PAFs\n",
+ "Starting to analyze % /content/demo-me-2021-07-14/videos/videocompressed1.mp4\n",
+ "Loading /content/demo-me-2021-07-14/videos/videocompressed1.mp4\n",
+ "Duration of video [s]: 77.67 , recorded with 30.0 fps!\n",
+ "Overall # of frames: 2330 found with (before cropping) frame dimensions: 640 480\n",
+ "Starting to extract posture\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 2330/2330 [01:18<00:00, 29.78it/s]\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "If the tracking is not satisfactory for some videos, consider expanding the training set. You can use the function 'extract_outlier_frames' to extract a few representative outlier frames.\n",
+ "Epoch 10, train acc: 0.61\n",
+ "Epoch 10, test acc 0.45\n",
+ "Epoch 20, train acc: 0.74\n",
+ "Epoch 20, test acc 0.65\n",
+ "Epoch 30, train acc: 0.78\n",
+ "Epoch 30, test acc 0.55\n",
+ "Epoch 40, train acc: 0.76\n",
+ "Epoch 40, test acc 0.50\n",
+ "Epoch 50, train acc: 0.85\n",
+ "Epoch 50, test acc 0.55\n",
+ "Epoch 60, train acc: 0.84\n",
+ "Epoch 60, test acc 0.60\n",
+ "Epoch 70, train acc: 0.85\n",
+ "Epoch 70, test acc 0.55\n",
+ "Epoch 80, train acc: 0.79\n",
+ "Epoch 80, test acc 0.55\n",
+ "Epoch 90, train acc: 0.88\n",
+ "Epoch 90, test acc 0.55\n",
+ "Epoch 100, train acc: 0.84\n",
+ "Epoch 100, test acc 0.55\n",
+ "loading params\n",
+ "Processing... /content/demo-me-2021-07-14/videos/videocompressed1.mp4\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████| 4/4 [00:00<00:00, 483.21it/s]\n",
+ "/usr/local/lib/python3.11/dist-packages/deeplabcut/refine_training_dataset/stitch.py:934: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " df.to_hdf(output_name, \"tracks\", format=\"table\", mode=\"w\")\n"
+ ]
+ }
+ ],
"source": [
- "dlc.transformer_reID(config_path, [video],\n",
- " shuffle=0, videotype='mp4',\n",
- " track_method='ellipse',n_triplets=100\n",
- " )"
+ "deeplabcut.transformer_reID(\n",
+ " config_path,\n",
+ " [video],\n",
+ " shuffle=0,\n",
+ " videotype=\"mp4\",\n",
+ " track_method=\"ellipse\",\n",
+ " n_triplets=100,\n",
+ ")"
]
},
{
@@ -224,35 +528,86 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 11,
"metadata": {
- "id": "MBMbRFEMxmi4"
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "MBMbRFEMxmi4",
+ "outputId": "5ca4357a-c8e1-46c6-ecad-141bfce48cc5"
},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Loading /content/demo-me-2021-07-14/videos/videocompressed1.mp4 and data.\n",
+ "Plots created! Please check the directory \"plot-poses\" within the video directory\n"
+ ]
+ }
+ ],
"source": [
- "dlc.plot_trajectories(config_path, [video], \n",
- " shuffle=0,videotype='mp4', \n",
- " track_method=\"transformer\"\n",
- " )"
+ "deeplabcut.plot_trajectories(\n",
+ " config_path,\n",
+ " [video],\n",
+ " shuffle=0,\n",
+ " videotype=\"mp4\",\n",
+ " track_method=\"transformer\",\n",
+ ")"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 12,
"metadata": {
- "id": "vx3e-r1CoXaX"
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "vx3e-r1CoXaX",
+ "outputId": "46cdbd39-d1f6-4b78-abba-7e979740f2a2"
},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Starting to process video: /content/demo-me-2021-07-14/videos/videocompressed1.mp4\n",
+ "Loading /content/demo-me-2021-07-14/videos/videocompressed1.mp4 and data.\n",
+ "Duration of video [s]: 77.67, recorded with 30.0 fps!\n",
+ "Overall # of frames: 2330 with cropped frame dimensions: 640 480\n",
+ "Generating frames and creating video.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/usr/local/lib/python3.11/dist-packages/deeplabcut/utils/make_labeled_video.py:140: FutureWarning: DataFrame.groupby with axis=1 is deprecated. Do `frame.T.groupby(...)` without axis instead.\n",
+ " Dataframe.groupby(level=\"individuals\", axis=1).size().values // 3\n",
+ "100%|██████████| 2330/2330 [00:31<00:00, 73.75it/s]\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "[True]"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
- "dlc.create_labeled_video(\n",
+ "deeplabcut.create_labeled_video(\n",
" config_path,\n",
" [video],\n",
- " videotype='mp4',\n",
+ " videotype=\"mp4\",\n",
" shuffle=0,\n",
" color_by=\"individual\",\n",
" keypoints_only=False,\n",
" draw_skeleton=True,\n",
- " track_method=\"transformer\"\n",
+ " track_method=\"transformer\",\n",
")"
]
}
@@ -260,8 +615,9 @@
"metadata": {
"accelerator": "GPU",
"colab": {
- "collapsed_sections": [],
+ "gpuType": "A100",
"include_colab_link": true,
+ "machine_shape": "hm",
"name": "COLAB_transformer_reID.ipynb",
"provenance": []
},
diff --git a/examples/JUPYTER/Demo_3D_DeepLabCut.ipynb b/examples/JUPYTER/Demo_3D_DeepLabCut.ipynb
index 92d7eb9ff9..578a69fe01 100644
--- a/examples/JUPYTER/Demo_3D_DeepLabCut.ipynb
+++ b/examples/JUPYTER/Demo_3D_DeepLabCut.ipynb
@@ -50,9 +50,9 @@
"metadata": {},
"outputs": [],
"source": [
- "#Setup your project variables:\n",
- "YourName = 'teamDLC'\n",
- "YourExperimentName = 'testing'"
+ "# Setup your project variables:\n",
+ "YourName = \"teamDLC\"\n",
+ "YourExperimentName = \"testing\""
]
},
{
@@ -75,7 +75,7 @@
}
],
"source": [
- "config_path = deeplabcut.create_new_project_3d(YourExperimentName,YourName,num_cameras=2)"
+ "config_path = deeplabcut.create_new_project_3d(YourExperimentName, YourName, num_cameras=2)"
]
},
{
@@ -93,11 +93,11 @@
"metadata": {},
"outputs": [],
"source": [
- "#If you're loading an already created project, just set the 3D Project config_path variable:\n",
- "#import os\n",
- "#from pathlib import Path\n",
- "#config_path3d = os.path.join(os.getcwd(),'testing3D-DeepLabCutTeam-2019-06-05-3d/config.yaml')\n",
- "#print(config_path3d)"
+ "# If you're loading an already created project, just set the 3D Project config_path variable:\n",
+ "# import os\n",
+ "# from pathlib import Path\n",
+ "# config_path3d = os.path.join(os.getcwd(),'testing3D-DeepLabCutTeam-2019-06-05-3d/config.yaml')\n",
+ "# print(config_path3d)"
]
},
{
@@ -151,7 +151,7 @@
"metadata": {},
"outputs": [],
"source": [
- "deeplabcut.calibrate_cameras(config_path3d, cbrow =9,cbcol =6,calibrate=False,alpha=0.9)"
+ "deeplabcut.calibrate_cameras(config_path, cbrow=9, cbcol=6, calibrate=False, alpha=0.9)"
]
},
{
@@ -179,7 +179,7 @@
"metadata": {},
"outputs": [],
"source": [
- "deeplabcut.calibrate_cameras(config_path3d, cbrow = 9,cbcol = 6, calibrate=True, alpha=0.9)"
+ "deeplabcut.calibrate_cameras(config_path, cbrow=9, cbcol=6, calibrate=True, alpha=0.9)"
]
},
{
@@ -197,10 +197,9 @@
"metadata": {},
"outputs": [],
"source": [
- "import matplotlib\n",
"%matplotlib inline\n",
"\n",
- "deeplabcut.check_undistortion(config_path3d)"
+ "deeplabcut.check_undistortion(config_path)"
]
},
{
@@ -239,12 +238,12 @@
"metadata": {},
"outputs": [],
"source": [
- "# Of course, this does not work on the demo calibration images, \n",
+ "# Of course, this does not work on the demo calibration images,\n",
"# but when you are ready for your own dataset, edit and then run the following!\n",
"\n",
- "video_path = '/home/yourname/videoFolder'\n",
+ "video_path = \"/home/yourname/videoFolder\"\n",
"\n",
- "deeplabcut.triangulate(config_path3d,video_path, videotype='mp4')"
+ "deeplabcut.triangulate(config_path, video_path, videotype=\"mp4\")"
]
},
{
@@ -269,15 +268,20 @@
"metadata": {},
"outputs": [],
"source": [
- "deeplabcut.create_labeled_video_3d(config_path,['triangulated_file_folder'],start=50,end=250, trailpoints=3)"
+ "deeplabcut.create_labeled_video_3d(config_path, [\"triangulated_file_folder\"], start=50, end=250, trailpoints=3)"
]
}
],
"metadata": {
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-02-28",
+ "last_metadata_updated": "2026-03-06"
+ },
"kernelspec": {
- "display_name": "Python [conda env:DEEPLABCUT_newGUI] *",
+ "display_name": "Python 3 (ipykernel)",
"language": "python",
- "name": "conda-env-DEEPLABCUT_newGUI-py"
+ "name": "python3"
},
"language_info": {
"codemirror_mode": {
@@ -289,7 +293,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.11.11"
}
},
"nbformat": 4,
diff --git a/examples/JUPYTER/Demo_labeledexample_MouseReaching.ipynb b/examples/JUPYTER/Demo_labeledexample_MouseReaching.ipynb
index cc45d9a2a7..549ce4260a 100644
--- a/examples/JUPYTER/Demo_labeledexample_MouseReaching.ipynb
+++ b/examples/JUPYTER/Demo_labeledexample_MouseReaching.ipynb
@@ -8,7 +8,11 @@
},
"source": [
"# DeepLabCut Toolbox - DEMO (mouse reaching)\n",
- "https://github.com/DeepLabCut/DeepLabCut\n",
+ "\n",
+ "Some resources that can be useful:\n",
+ "\n",
+ "- [github.com/DeepLabCut/DeepLabCut](https://github.com/DeepLabCut/DeepLabCut)\n",
+ "- [DeepLabCut's Documentation: User Guide for Single Animal projects](https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide.html)\n",
"\n",
"#### The notebook accompanies the following user-guide:\n",
"\n",
@@ -35,7 +39,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Import the toolbox:"
+ "## Import the Toolbox and Required Libraries"
]
},
{
@@ -48,6 +52,8 @@
},
"outputs": [],
"source": [
+ "from pathlib import Path\n",
+ "\n",
"import deeplabcut"
]
},
@@ -64,12 +70,17 @@
"metadata": {},
"outputs": [],
"source": [
- "import os\n",
- "# Note that parameters of this project can be seen at: *Reaching-Mackenzie-2018-08-30/config.yaml*\n",
- "from pathlib import Path\n",
+ "# Create a variable to set the config.yaml file path:\n",
+ "# If this path does not point to the project from the URL below,\n",
+ "# edit it to make sure it does:\n",
+ "# https://github.com/DeepLabCut/DeepLabCut/tree/main/examples/Reaching-Mackenzie-2018-08-30\n",
+ "#\n",
+ "# Example - Linux/OSX\n",
+ "# path_config_file = \"/Users/john/DeepLabCut/examples/Reaching-Mackenzie-2018-08-30/config.yaml\"\n",
+ "# Example - Windows\n",
+ "# path_config_file = r\"C:\\DeepLabCut\\examples\\Reaching-Mackenzie-2018-08-30\\config.yaml\"\n",
"\n",
- "#create a variable to set the config.yaml file path:\n",
- "path_config_file = os.path.join(os.getcwd(),'Reaching-Mackenzie-2018-08-30/config.yaml')\n",
+ "path_config_file = str(Path.cwd() / \"Reaching-Mackenzie-2018-08-30\" / \"config.yaml\")\n",
"print(path_config_file)"
]
},
@@ -99,8 +110,8 @@
},
"outputs": [],
"source": [
- "#let's load some demo data, and create a training set \n",
- "#(note, this function is not used when you create your own project):\n",
+ "# Let's load some demo data, and create a training set\n",
+ "# (note, this function is not used when you create your own project):\n",
"\n",
"deeplabcut.load_demo_data(path_config_file)"
]
@@ -115,7 +126,7 @@
},
"outputs": [],
"source": [
- "#Perhaps plot the labels to see how the frames were annotated:\n",
+ "# Perhaps plot the labels to see how the frames were annotated:\n",
"\n",
"deeplabcut.check_labels(path_config_file)"
]
@@ -128,11 +139,12 @@
},
"source": [
"## Start training of Feature Detectors\n",
- "This function trains the network for a specific shuffle of the training dataset. **The user can set various parameters in /Reaching-Mackenzie-2018-08-30/dlc-models/ReachingAug30-trainset95shuffle1/iteration-0/train/pose_cfg.yaml.**\n",
"\n",
- "Training can be stopped at any time. Note that the weights are only stored every 'save_iters' steps. For this demo the it is advisable to store & display the progress very often (i.e. display every 20, save every 100). In practice this is inefficient (in reality, you will train until ~200K, so we save every 50K).\n",
+ "This function trains the network for a specific shuffle of the training dataset. **The user can set various parameters in `.../Reaching-Mackenzie-2018-08-30/dlc-models-pytorch/iteration-0/ReachingAug30-trainset95shuffle1/train/pytorch_config.yaml`**. For more information about the variables that can be set, check out the [docs](https://deeplabcut.github.io/DeepLabCut/docs/pytorch/pytorch_config.html)!\n",
"\n",
- "**We recommend just training for 10-20 min, as you aren't running this demo to use DLC, just to work through the steps. In total, this demo should take you LESS THAN 1 HOUR!**"
+ "Training can be stopped at any time. Note that the weights are only stored every 'save_epochs' steps. For this demo the it is advisable to store & display the progress very often (i.e. display every 20, save every 2). In practice this is inefficient (in reality, you will train until ~200, so we save every 10).\n",
+ "\n",
+ "**We recommend just training for 15-20 min, as you aren't running this demo to use DLC, just to work through the steps. In total, this demo should take you LESS THAN 1 HOUR!**"
]
},
{
@@ -142,24 +154,26 @@
"colab": {},
"colab_type": "code",
"id": "jg96O2acywnW",
- "scrolled": false
+ "scrolled": true
},
"outputs": [],
"source": [
- "deeplabcut.train_network(path_config_file, shuffle=1, saveiters=300, displayiters=10)\n",
- "#notice the variables \"saveiters\" and \"dsiplayiters\" that can be set in the function\n",
+ "# notice the variables \"save_epochs\" and \"displayiters\" that can be set in the function\n",
+ "deeplabcut.train_network(path_config_file, shuffle=1, save_epochs=2, displayiters=10)\n",
+ "\n",
+ "# you just need to run this until you get at least 1 snapshot, which is set by: \"save_epochs\"\n",
+ "# (so in this case you could stop after 2 epochs!) How do I stop? Click the STOP button!\n",
"\n",
- "#you just need to run this until you get at least 1 snapshot, which is set by: \"save_iters\" \n",
- "#(so in this case you could stop after 500!) How do I stop? Click the STOP button!\n",
- "# To train until ~2,000 iterations on a CPU should be ~30 min"
+ "# To train until ~50 epochs on a CPU should be ~15 min\n",
+ "# Every 10 epochs, your model will be evaluated. You can keep an eye on model performance\n",
+ "# while the model is being trained."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "*Note, that if it reaches the end (default 1M) or you stop it (by \"stop\" or by CTRL+C), \n",
- "you will see an keyboard interrupt \"error\", but it is not a real error, i.e. you can ignore this.*"
+ "*Note, that if you stop it (by \"stop\" or by CTRL+C), you will see an keyboard interrupt \"error\", but it is not a real error, i.e. you can ignore this.*"
]
},
{
@@ -171,7 +185,7 @@
"source": [
"## Evaluate the trained network\n",
"\n",
- "This function evaluates a trained model for a specific shuffle/shuffles at a particular training state (snapshot) or on all the states. The network is evaluated on the data set (images) and stores the results as .csv file in a subdirectory under **evaluation-results**.\n",
+ "This function evaluates a trained model for a specific shuffle/shuffles at a particular training state (snapshot) or on all the states. The network is evaluated on the data set (images) and stores the results as .csv file in a subdirectory under **evaluation-results-pytorch**.\n",
"\n",
"You can change various parameters in the ```config.yaml``` file of this project. For the evaluation one can change pcutoff. This cutoff also influences how likely estimated positions need to be so that they are shown in the plots."
]
@@ -187,14 +201,14 @@
},
"outputs": [],
"source": [
- "deeplabcut.evaluate_network(path_config_file,plotting=True)"
+ "deeplabcut.evaluate_network(path_config_file, plotting=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "**NOTE: depending on your set up sometimes you get some \"matplotlib errors, but these are not important**\n",
+ "**NOTE: depending on your setup sometimes you get some \"matplotlib errors, but these are not important**\n",
"\n",
"Now you can go check out the images. Given the limited data input and it took ~20 mins to test this out, it is not meant to track well, so don't be alarmed. This is just to get you familiar with the workflow... "
]
@@ -223,12 +237,12 @@
"outputs": [],
"source": [
"# Set the video path:\n",
- "#The video can be the one you trained with and new videos that look similar, i.e. same experiments, etc.\n",
+ "# The video can be the one you trained with and new videos that look similar, i.e. same experiments, etc.\n",
"# You can add individual videos, OR just a folder - it will skip videos that are already analyzed once.\n",
"\n",
- "#i.e you can run 'reachingvideo1' and/or 'MovieS2_Perturbation_noLaser_compressed'\n",
+ "# i.e. you can run 'reachingvideo1' and/or 'MovieS2_Perturbation_noLaser_compressed'\n",
"\n",
- "videofile_path = os.path.join(os.getcwd(),'Reaching-Mackenzie-2018-08-30/videos/reachingvideo1.avi') "
+ "videofile_path = str(Path(path_config_file).parent / \"videos\" / \"reachingvideo1.avi\")"
]
},
{
@@ -243,8 +257,9 @@
"outputs": [],
"source": [
"print(\"Start Analyzing the video!\")\n",
- "deeplabcut.analyze_videos(path_config_file,[videofile_path])\n",
- "# this video takes ~ 8 min to analyze with a CPU"
+ "\n",
+ "deeplabcut.analyze_videos(path_config_file, [videofile_path])\n",
+ "# this video takes ~ 1 min to analyze with a CPU"
]
},
{
@@ -279,7 +294,7 @@
},
"outputs": [],
"source": [
- "deeplabcut.create_labeled_video(path_config_file,[videofile_path], draw_skeleton=True)"
+ "deeplabcut.create_labeled_video(path_config_file, [videofile_path], draw_skeleton=True)"
]
},
{
@@ -305,9 +320,9 @@
"outputs": [],
"source": [
"%matplotlib notebook\n",
- "deeplabcut.plot_trajectories(path_config_file,[videofile_path],showfigures=True)\n",
+ "deeplabcut.plot_trajectories(path_config_file, [videofile_path], showfigures=True)\n",
"\n",
- "#These plots can are interactive and can be customized (see https://matplotlib.org/)"
+ "# These plots are interactive and can be customized (see https://matplotlib.org/)"
]
},
{
@@ -339,11 +354,16 @@
"colab": {},
"colab_type": "code",
"id": "RJGiDKuUywoC",
- "scrolled": false
+ "scrolled": true
},
"outputs": [],
"source": [
- "deeplabcut.extract_outlier_frames(path_config_file,videofile_path,outlieralgorithm='uncertain',p_bound=.2)"
+ "deeplabcut.extract_outlier_frames(\n",
+ " path_config_file,\n",
+ " videofile_path,\n",
+ " outlieralgorithm=\"uncertain\",\n",
+ " p_bound=0.2,\n",
+ ")"
]
},
{
@@ -365,7 +385,9 @@
"source": [
"## Manually correct labels\n",
"\n",
- "This step allows the user to correct the labels in the extracted frames. Navigate to the folder with the videos and use the GUI as described in the protocol to update the labels."
+ "This step allows the user to correct the labels in the extracted frames. Navigate to the folder with the videos and use the GUI as described in the protocol to update the labels.\n",
+ "\n",
+ "For documentation regarding the GUI, [look at the docs for `napari-deeplabcut`](https://github.com/DeepLabCut/napari-deeplabcut/tree/main) - and specifically _\"3. Refining labels – the image folder contains a machinelabels-iter<#>.h5 file.\"_!"
]
},
{
@@ -379,9 +401,6 @@
},
"outputs": [],
"source": [
- "#GUI pops up! \n",
- "#sometimes you need to restart the kernel for the GUI to launch.\n",
- "%gui wx\n",
"deeplabcut.refine_labels(path_config_file)"
]
},
@@ -421,7 +440,7 @@
},
"outputs": [],
"source": [
- "#Perhaps plot the labels to see how how all the frames are annotated (including the refined ones)\n",
+ "# Perhaps plot the labels to see how how all the frames are annotated (including the refined ones)\n",
"deeplabcut.check_labels(path_config_file)\n",
"# if they are off, you can load them in the labeling_gui to adjust!"
]
@@ -436,7 +455,7 @@
},
"outputs": [],
"source": [
- "deeplabcut.create_training_dataset(path_config_file)"
+ "deeplabcut.create_training_dataset(path_config_file, engine=deeplabcut.Engine.PYTORCH)"
]
},
{
@@ -446,7 +465,7 @@
"id": "8fhL6nG2ywoW"
},
"source": [
- "Now one can train the network again... (with the expanded data set)"
+ "Now one can train the network again... (with the expanded data set). We can continue training from the snapshot we already have by using the `snapshot_path` argument - instead of training the model from scratch, it will load the weights we already have and fine-tune them!"
]
},
{
@@ -459,8 +478,31 @@
},
"outputs": [],
"source": [
- "deeplabcut.train_network(path_config_file)"
+ "snapshot_path = ( # Edit me if needed! Select the path to the snapshot to continue training from!\n",
+ " Path(path_config_file).parent\n",
+ " / \"dlc-models-pytorch\"\n",
+ " / \"iteration-0\"\n",
+ " / \"ReachingAug30-trainset95shuffle1\"\n",
+ " / \"train\"\n",
+ " / \"snapshot-best-080.pt\"\n",
+ ")\n",
+ "\n",
+ "deeplabcut.train_network(\n",
+ " path_config_file,\n",
+ " shuffle=1,\n",
+ " save_epochs=2,\n",
+ " displayiters=10,\n",
+ " batch_size=8,\n",
+ " snapshot_path=snapshot_path,\n",
+ ")"
]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
}
],
"metadata": {
@@ -470,10 +512,15 @@
"provenance": [],
"version": "0.3.2"
},
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-02-28",
+ "last_metadata_updated": "2026-03-06"
+ },
"kernelspec": {
- "display_name": "Python [conda env:DLC2]",
+ "display_name": "Python 3 (ipykernel)",
"language": "python",
- "name": "conda-env-DLC2-py"
+ "name": "python3"
},
"language_info": {
"codemirror_mode": {
@@ -485,7 +532,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.6.9"
+ "version": "3.11.11"
},
"varInspector": {
"cols": {
diff --git a/examples/JUPYTER/Demo_labeledexample_Openfield.ipynb b/examples/JUPYTER/Demo_labeledexample_Openfield.ipynb
index 9af5d97b6c..0d26f986fe 100644
--- a/examples/JUPYTER/Demo_labeledexample_Openfield.ipynb
+++ b/examples/JUPYTER/Demo_labeledexample_Openfield.ipynb
@@ -8,7 +8,11 @@
},
"source": [
"# DeepLabCut Toolbox - Open-Field DEMO\n",
- "https://github.com/DeepLabCut/DeepLabCut\n",
+ "\n",
+ "Some resources that can be useful:\n",
+ "\n",
+ "- [github.com/DeepLabCut/DeepLabCut](https://github.com/DeepLabCut/DeepLabCut)\n",
+ "- [DeepLabCut's Documentation: User Guide for Single Animal projects](https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide.html)\n",
"\n",
"#### The notebook accompanies the following user-guide:\n",
"\n",
@@ -34,7 +38,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -43,65 +47,50 @@
"outputs": [],
"source": [
"# Importing the toolbox (takes several seconds)\n",
+ "from pathlib import Path\n",
+ "\n",
"import deeplabcut"
]
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "WOEHc0MeywnJ"
},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Loaded, now creating training data...\n",
- "/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/training-datasets/iteration-0/UnaugmentedDataSet_openfieldOct30 already exists!\n",
- "/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/labeled-data/short_mp3/CollectedData_Pranav.h5 not found (perhaps not annotated)\n",
- "/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/dlc-models/iteration-0/openfieldOct30-trainset95shuffle1 already exists!\n",
- "/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/dlc-models/iteration-0/openfieldOct30-trainset95shuffle1//train already exists!\n",
- "/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/dlc-models/iteration-0/openfieldOct30-trainset95shuffle1//test already exists!\n",
- "The training dataset is successfully created. Use the function 'train_network' to start training. Happy training!\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
- "# Loading example data set:\n",
- "import os\n",
+ "# Create a variable to set the config.yaml file path:\n",
+ "# If this path does not point to the project from the URL below,\n",
+ "# edit it to make sure it does:\n",
+ "# https://github.com/DeepLabCut/DeepLabCut/tree/main/examples/openfield-Pranav-2018-10-30\n",
+ "#\n",
+ "# Example - Linux/OSX\n",
+ "# path_config_file = \"/Users/john/DeepLabCut/examples/openfield-Pranav-2018-10-30/config.yaml\"\n",
+ "# Example - Windows\n",
+ "# path_config_file = r\"C:\\DeepLabCut\\examples\\openfield-Pranav-2018-10-30\\config.yaml\"\n",
+ "#\n",
"# Note that parameters of this project can be seen at: *openfield-Pranav-2018-10-30/config.yaml*\n",
- "from pathlib import Path\n",
- "path_config_file = os.path.join(os.getcwd(),'openfield-Pranav-2018-10-30/config.yaml')\n",
+ "\n",
+ "path_config_file = str(Path.cwd() / \"openfield-Pranav-2018-10-30\" / \"config.yaml\")\n",
"deeplabcut.load_demo_data(path_config_file)"
]
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "ROlflqQLywnP"
},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Creating images with labels by Pranav.\n",
- "/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/labeled-data/m4s1_labeled already exists!\n",
- "They are stored in the following folder: /home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/labeled-data/m4s1_labeled.\n",
- "Attention: /home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/labeled-data/short_mp3 does not appear to have labeled data!\n",
- "If all the labels are ok, then use the function 'create_training_dataset' to create the training dataset!\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
- "#[OPTIONAL] Perhaps plot the labels to see how the frames were annotated:\n",
- "#(note, this project was created in Linux, so you might have an error in Windows, but this is an optional step)\n",
+ "# [OPTIONAL] Perhaps plot the labels to see how the frames were annotated:\n",
+ "# (note, this project was created in Linux, so you might have an error in Windows, but this is an optional step)\n",
+ "\n",
"deeplabcut.check_labels(path_config_file)"
]
},
@@ -113,141 +102,30 @@
},
"source": [
"## Start training of Feature Detectors\n",
- "This function trains the network for a specific shuffle of the training dataset. The user can set various parameters in */openfield-Pranav-2018-10-30/dlc-models/.../pose_cfg.yaml*. \n",
"\n",
- "Training can be stopped at any time. Note that the weights are only stored every 'save_iters' steps. For this demo the state it is advisable to store & display the progress very often. In practice this is inefficient. "
+ "This function trains the network for a specific shuffle of the training dataset. The user can set various parameters in `/openfield-Pranav-2018-10-30/dlc-models-pytorch/.../pytorch_config.yaml`. For more information about the variables that can be set, check out the [docs](https://deeplabcut.github.io/DeepLabCut/docs/pytorch/pytorch_config.html)!\n",
+ "\n",
+ "Training can be stopped at any time. Note that the weights are only stored every 'save_epochs' epochs. For this demo the state it is advisable to store & display the progress very often. In practice this is inefficient. You should see the model start converging around 50 to 60 epochs; you can continue training it longer to improve performance."
]
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "jg96O2acywnW",
- "scrolled": false
+ "scrolled": true
},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Config:\n",
- "{'all_joints': [[0], [1], [2], [3]],\n",
- " 'all_joints_names': ['snout', 'leftear', 'rightear', 'tailbase'],\n",
- " 'batch_size': 1,\n",
- " 'bottomheight': 400,\n",
- " 'crop': True,\n",
- " 'crop_pad': 0,\n",
- " 'cropratio': 0.4,\n",
- " 'dataset': 'training-datasets/iteration-0/UnaugmentedDataSet_openfieldOct30/openfield_Pranav95shuffle1.mat',\n",
- " 'dataset_type': 'default',\n",
- " 'display_iters': 1000,\n",
- " 'fg_fraction': 0.25,\n",
- " 'global_scale': 0.8,\n",
- " 'init_weights': '/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/deeplabcut/pose_estimation_tensorflow/models/pretrained/resnet_v1_50.ckpt',\n",
- " 'intermediate_supervision': False,\n",
- " 'intermediate_supervision_layer': 12,\n",
- " 'leftwidth': 400,\n",
- " 'location_refinement': True,\n",
- " 'locref_huber_loss': True,\n",
- " 'locref_loss_weight': 0.05,\n",
- " 'locref_stdev': 7.2801,\n",
- " 'log_dir': 'log',\n",
- " 'max_input_size': 1500,\n",
- " 'mean_pixel': [123.68, 116.779, 103.939],\n",
- " 'metadataset': 'training-datasets/iteration-0/UnaugmentedDataSet_openfieldOct30/Documentation_data-openfield_95shuffle1.pickle',\n",
- " 'min_input_size': 64,\n",
- " 'minsize': 100,\n",
- " 'mirror': False,\n",
- " 'multi_step': [[0.005, 10000],\n",
- " [0.02, 430000],\n",
- " [0.002, 730000],\n",
- " [0.001, 1030000]],\n",
- " 'net_type': 'resnet_50',\n",
- " 'num_joints': 4,\n",
- " 'optimizer': 'sgd',\n",
- " 'pos_dist_thresh': 17,\n",
- " 'project_path': '/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30',\n",
- " 'regularize': False,\n",
- " 'rightwidth': 400,\n",
- " 'save_iters': 50000,\n",
- " 'scale_jitter_lo': 0.5,\n",
- " 'scale_jitter_up': 1.25,\n",
- " 'scoremap_dir': 'test',\n",
- " 'shuffle': True,\n",
- " 'snapshot_prefix': '/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/dlc-models/iteration-0/openfieldOct30-trainset95shuffle1/train/snapshot',\n",
- " 'stride': 8.0,\n",
- " 'topheight': 400,\n",
- " 'use_gt_segm': False,\n",
- " 'video': False,\n",
- " 'video_batch': False,\n",
- " 'weigh_negatives': False,\n",
- " 'weigh_only_present_joints': False,\n",
- " 'weigh_part_predictions': False,\n",
- " 'weight_decay': 0.0001}\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "INFO:tensorflow:Restoring parameters from /home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/deeplabcut/pose_estimation_tensorflow/models/pretrained/resnet_v1_50.ckpt\n",
- "Display_iters overwritten as 10\n",
- "Save_iters overwritten as 100\n",
- "Training parameter:\n",
- "{'stride': 8.0, 'weigh_part_predictions': False, 'weigh_negatives': False, 'fg_fraction': 0.25, 'weigh_only_present_joints': False, 'mean_pixel': [123.68, 116.779, 103.939], 'shuffle': True, 'snapshot_prefix': '/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/dlc-models/iteration-0/openfieldOct30-trainset95shuffle1/train/snapshot', 'log_dir': 'log', 'global_scale': 0.8, 'location_refinement': True, 'locref_stdev': 7.2801, 'locref_loss_weight': 0.05, 'locref_huber_loss': True, 'optimizer': 'sgd', 'intermediate_supervision': False, 'intermediate_supervision_layer': 12, 'regularize': False, 'weight_decay': 0.0001, 'mirror': False, 'crop_pad': 0, 'scoremap_dir': 'test', 'dataset_type': 'default', 'use_gt_segm': False, 'batch_size': 1, 'video': False, 'video_batch': False, 'crop': True, 'cropratio': 0.4, 'minsize': 100, 'leftwidth': 400, 'rightwidth': 400, 'topheight': 400, 'bottomheight': 400, 'all_joints': [[0], [1], [2], [3]], 'all_joints_names': ['snout', 'leftear', 'rightear', 'tailbase'], 'dataset': 'training-datasets/iteration-0/UnaugmentedDataSet_openfieldOct30/openfield_Pranav95shuffle1.mat', 'display_iters': 1000, 'init_weights': '/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/deeplabcut/pose_estimation_tensorflow/models/pretrained/resnet_v1_50.ckpt', 'max_input_size': 1500, 'metadataset': 'training-datasets/iteration-0/UnaugmentedDataSet_openfieldOct30/Documentation_data-openfield_95shuffle1.pickle', 'min_input_size': 64, 'multi_step': [[0.005, 10000], [0.02, 430000], [0.002, 730000], [0.001, 1030000]], 'net_type': 'resnet_50', 'num_joints': 4, 'pos_dist_thresh': 17, 'project_path': '/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30', 'save_iters': 50000, 'scale_jitter_lo': 0.5, 'scale_jitter_up': 1.25}\n",
- "Starting training....\n"
- ]
- },
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "iteration: 10 loss: 0.3308 lr: 0.005\n",
- "iteration: 20 loss: 0.0563 lr: 0.005\n",
- "iteration: 30 loss: 0.0417 lr: 0.005\n",
- "iteration: 40 loss: 0.0362 lr: 0.005\n",
- "iteration: 50 loss: 0.0407 lr: 0.005\n",
- "iteration: 60 loss: 0.0461 lr: 0.005\n",
- "iteration: 70 loss: 0.0385 lr: 0.005\n",
- "iteration: 80 loss: 0.0345 lr: 0.005\n",
- "iteration: 90 loss: 0.0314 lr: 0.005\n",
- "iteration: 100 loss: 0.0428 lr: 0.005\n",
- "iteration: 110 loss: 0.0262 lr: 0.005\n",
- "iteration: 120 loss: 0.0255 lr: 0.005\n",
- "iteration: 130 loss: 0.0275 lr: 0.005\n",
- "iteration: 140 loss: 0.0251 lr: 0.005\n",
- "iteration: 150 loss: 0.0221 lr: 0.005\n",
- "iteration: 160 loss: 0.0209 lr: 0.005\n",
- "iteration: 170 loss: 0.0297 lr: 0.005\n",
- "iteration: 180 loss: 0.0325 lr: 0.005\n",
- "iteration: 190 loss: 0.0242 lr: 0.005\n"
- ]
- },
- {
- "ename": "KeyboardInterrupt",
- "evalue": "",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdeeplabcut\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain_network\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpath_config_file\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshuffle\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdisplayiters\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msaveiters\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;32m/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/deeplabcut/pose_estimation_tensorflow/training.py\u001b[0m in \u001b[0;36mtrain_network\u001b[0;34m(config, shuffle, trainingsetindex, gputouse, max_snapshots_to_keep, autotune, displayiters, saveiters, maxiters)\u001b[0m\n\u001b[1;32m 87\u001b[0m \u001b[0mtrain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mposeconfigfile\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mdisplayiters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0msaveiters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mmaxiters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mmax_to_keep\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmax_snapshots_to_keep\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m#pass on path and file name for pose_cfg.yaml!\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 88\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mBaseException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 89\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 90\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 91\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchdir\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstart_path\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/deeplabcut/pose_estimation_tensorflow/training.py\u001b[0m in \u001b[0;36mtrain_network\u001b[0;34m(config, shuffle, trainingsetindex, gputouse, max_snapshots_to_keep, autotune, displayiters, saveiters, maxiters)\u001b[0m\n\u001b[1;32m 85\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 86\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 87\u001b[0;31m \u001b[0mtrain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mposeconfigfile\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mdisplayiters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0msaveiters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mmaxiters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mmax_to_keep\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmax_snapshots_to_keep\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m#pass on path and file name for pose_cfg.yaml!\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 88\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mBaseException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 89\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/deeplabcut/pose_estimation_tensorflow/train.py\u001b[0m in \u001b[0;36mtrain\u001b[0;34m(config_yaml, displayiters, saveiters, maxiters, max_to_keep)\u001b[0m\n\u001b[1;32m 140\u001b[0m \u001b[0mcurrent_lr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlr_gen\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_lr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 141\u001b[0m [_, loss_val, summary] = sess.run([train_op, total_loss, merged_summaries],\n\u001b[0;32m--> 142\u001b[0;31m feed_dict={learning_rate: current_lr})\n\u001b[0m\u001b[1;32m 143\u001b[0m \u001b[0mcum_loss\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0mloss_val\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 144\u001b[0m \u001b[0mtrain_writer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_summary\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msummary\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/tensorflow/python/client/session.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, fetches, feed_dict, options, run_metadata)\u001b[0m\n\u001b[1;32m 898\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 899\u001b[0m result = self._run(None, fetches, feed_dict, options_ptr,\n\u001b[0;32m--> 900\u001b[0;31m run_metadata_ptr)\n\u001b[0m\u001b[1;32m 901\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mrun_metadata\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 902\u001b[0m \u001b[0mproto_data\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtf_session\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTF_GetBuffer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrun_metadata_ptr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/tensorflow/python/client/session.py\u001b[0m in \u001b[0;36m_run\u001b[0;34m(self, handle, fetches, feed_dict, options, run_metadata)\u001b[0m\n\u001b[1;32m 1133\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mfinal_fetches\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mfinal_targets\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mhandle\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mfeed_dict_tensor\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1134\u001b[0m results = self._do_run(handle, final_targets, final_fetches,\n\u001b[0;32m-> 1135\u001b[0;31m feed_dict_tensor, options, run_metadata)\n\u001b[0m\u001b[1;32m 1136\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1137\u001b[0m \u001b[0mresults\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/tensorflow/python/client/session.py\u001b[0m in \u001b[0;36m_do_run\u001b[0;34m(self, handle, target_list, fetch_list, feed_dict, options, run_metadata)\u001b[0m\n\u001b[1;32m 1314\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mhandle\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1315\u001b[0m return self._do_call(_run_fn, feeds, fetches, targets, options,\n\u001b[0;32m-> 1316\u001b[0;31m run_metadata)\n\u001b[0m\u001b[1;32m 1317\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1318\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_do_call\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_prun_fn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhandle\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfeeds\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfetches\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/tensorflow/python/client/session.py\u001b[0m in \u001b[0;36m_do_call\u001b[0;34m(self, fn, *args)\u001b[0m\n\u001b[1;32m 1320\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_do_call\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1321\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1322\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1323\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0merrors\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mOpError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1324\u001b[0m \u001b[0mmessage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcompat\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_text\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0me\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmessage\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/tensorflow/python/client/session.py\u001b[0m in \u001b[0;36m_run_fn\u001b[0;34m(feed_dict, fetch_list, target_list, options, run_metadata)\u001b[0m\n\u001b[1;32m 1305\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_extend_graph\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1306\u001b[0m return self._call_tf_sessionrun(\n\u001b[0;32m-> 1307\u001b[0;31m options, feed_dict, fetch_list, target_list, run_metadata)\n\u001b[0m\u001b[1;32m 1308\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1309\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_prun_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhandle\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfeed_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfetch_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/tensorflow/python/client/session.py\u001b[0m in \u001b[0;36m_call_tf_sessionrun\u001b[0;34m(self, options, feed_dict, fetch_list, target_list, run_metadata)\u001b[0m\n\u001b[1;32m 1407\u001b[0m return tf_session.TF_SessionRun_wrapper(\n\u001b[1;32m 1408\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_session\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moptions\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfeed_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfetch_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtarget_list\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1409\u001b[0;31m run_metadata)\n\u001b[0m\u001b[1;32m 1410\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1411\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0merrors\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mraise_exception_on_not_ok_status\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mstatus\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
- ]
- }
- ],
+ "outputs": [],
"source": [
- "deeplabcut.train_network(path_config_file, shuffle=1, displayiters=10, saveiters=100)"
+ "# notice the variables \"save_epochs\" and \"displayiters\" that can be set in the function\n",
+ "deeplabcut.train_network(\n",
+ " path_config_file,\n",
+ " shuffle=1,\n",
+ " save_epochs=2,\n",
+ " displayiters=5,\n",
+ ")"
]
},
{
@@ -267,98 +145,23 @@
"source": [
"## Evaluate a trained network\n",
"\n",
- "This function evaluates a trained model for a specific shuffle/shuffles at a particular training state (snapshot) or on all the states. The network is evaluated on the data set (images) and stores the results as .csv file in a subdirectory under **evaluation-results**.\n",
+ "This function evaluates a trained model for a specific shuffle/shuffles at a particular training state (snapshot) or on all the states. The network is evaluated on the data set (images) and stores the results as .csv file in a subdirectory under **evaluation-results-pytorch**.\n",
"\n",
"You can change various parameters in the ```config.yaml``` file of this project. For evaluation all the model descriptors (Task, TrainingFraction, Date etc.) are important. For the evaluation one can change pcutoff. This cutoff also influences how likely estimated positions need to be so that they are shown in the plots. One can furthermore, change the colormap and dotsize for those graphs."
]
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "kuprPKDdywne",
"scrolled": false
},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Config:\n",
- "{'all_joints': [[0], [1], [2], [3]],\n",
- " 'all_joints_names': ['snout', 'leftear', 'rightear', 'tailbase'],\n",
- " 'batch_size': 1,\n",
- " 'bottomheight': 400,\n",
- " 'crop': True,\n",
- " 'crop_pad': 0,\n",
- " 'cropratio': 0.4,\n",
- " 'dataset': 'training-datasets/iteration-0/UnaugmentedDataSet_openfieldOct30/openfield_Pranav95shuffle1.mat',\n",
- " 'dataset_type': 'default',\n",
- " 'display_iters': 1000,\n",
- " 'fg_fraction': 0.25,\n",
- " 'global_scale': 0.8,\n",
- " 'init_weights': '/home/mackenzie/anaconda3/envs/DLC2/lib/python3.6/site-packages/deeplabcut/pose_estimation_tensorflow/models/pretrained/resnet_v1_50.ckpt',\n",
- " 'intermediate_supervision': False,\n",
- " 'intermediate_supervision_layer': 12,\n",
- " 'leftwidth': 400,\n",
- " 'location_refinement': True,\n",
- " 'locref_huber_loss': True,\n",
- " 'locref_loss_weight': 0.05,\n",
- " 'locref_stdev': 7.2801,\n",
- " 'log_dir': 'log',\n",
- " 'max_input_size': 1500,\n",
- " 'mean_pixel': [123.68, 116.779, 103.939],\n",
- " 'metadataset': 'training-datasets/iteration-0/UnaugmentedDataSet_openfieldOct30/Documentation_data-openfield_95shuffle1.pickle',\n",
- " 'min_input_size': 64,\n",
- " 'minsize': 100,\n",
- " 'mirror': False,\n",
- " 'multi_step': [[0.005, 10000],\n",
- " [0.02, 430000],\n",
- " [0.002, 730000],\n",
- " [0.001, 1030000]],\n",
- " 'net_type': 'resnet_50',\n",
- " 'num_joints': 4,\n",
- " 'optimizer': 'sgd',\n",
- " 'pos_dist_thresh': 17,\n",
- " 'project_path': '/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30',\n",
- " 'regularize': False,\n",
- " 'rightwidth': 400,\n",
- " 'save_iters': 50000,\n",
- " 'scale_jitter_lo': 0.5,\n",
- " 'scale_jitter_up': 1.25,\n",
- " 'scoremap_dir': 'test',\n",
- " 'shuffle': True,\n",
- " 'snapshot_prefix': '/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/dlc-models/iteration-0/openfieldOct30-trainset95shuffle1/test/snapshot',\n",
- " 'stride': 8.0,\n",
- " 'topheight': 400,\n",
- " 'use_gt_segm': False,\n",
- " 'video': False,\n",
- " 'video_batch': False,\n",
- " 'weigh_negatives': False,\n",
- " 'weigh_only_present_joints': False,\n",
- " 'weigh_part_predictions': False,\n",
- " 'weight_decay': 0.0001}\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/evaluation-results/ already exists!\n",
- "/home/mackenzie/DEEPLABCUT/3D/DeepLabCut2.0-master/examples/openfield-Pranav-2018-10-30/evaluation-results/iteration-0/openfieldOct30-trainset95shuffle1 already exists!\n",
- "Running DeepCut_resnet50_openfieldOct30shuffle1_2400 with # of trainingiterations: 2400\n",
- "This net has already been evaluated!\n",
- "The network is evaluated and the results are stored in the subdirectory 'evaluation_results'.\n",
- "If it generalizes well, choose the best model for prediction and update the config file with the appropriate index for the 'snapshotindex'.\n",
- "Use the function 'analyze_video' to make predictions on new videos.\n",
- "Otherwise consider retraining the network (see DeepLabCut workflow Fig 2)\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
- "deeplabcut.evaluate_network(path_config_file,plotting=False)"
+ "deeplabcut.evaluate_network(path_config_file, plotting=False)"
]
},
{
@@ -393,9 +196,7 @@
},
"outputs": [],
"source": [
- "# Creating video path:\n",
- "import os\n",
- "videofile_path = os.path.join(os.getcwd(),'openfield-Pranav-2018-10-30/videos/m3v1mp4.mp4')"
+ "videofile_path = str(Path(path_config_file).parent / \"videos\" / \"m3v1mp4.mp4\")"
]
},
{
@@ -410,8 +211,8 @@
"outputs": [],
"source": [
"print(\"Start analyzing the video!\")\n",
- "#our demo video on a CPU with take ~30 min to analze! GPU is much faster!\n",
- "deeplabcut.analyze_videos(path_config_file,[videofile_path])"
+ "# our demo video on a CPU with take ~5 min to analze! GPU is much faster!\n",
+ "deeplabcut.analyze_videos(path_config_file, [videofile_path])"
]
},
{
@@ -439,7 +240,7 @@
},
"outputs": [],
"source": [
- "deeplabcut.create_labeled_video(path_config_file,[videofile_path])"
+ "deeplabcut.create_labeled_video(path_config_file, [videofile_path])"
]
},
{
@@ -465,9 +266,13 @@
"outputs": [],
"source": [
"%matplotlib notebook\n",
- "deeplabcut.plot_trajectories(path_config_file,[videofile_path],showfigures=True)\n",
+ "deeplabcut.plot_trajectories(\n",
+ " path_config_file,\n",
+ " [videofile_path],\n",
+ " showfigures=True,\n",
+ ")\n",
"\n",
- "#These plots can are interactive and can be customized (see https://matplotlib.org/)"
+ "# These plots are interactive and can be customized (see https://matplotlib.org/)"
]
},
{
@@ -493,7 +298,7 @@
},
"outputs": [],
"source": [
- "deeplabcut.extract_outlier_frames(path_config_file,[videofile_path])"
+ "deeplabcut.extract_outlier_frames(path_config_file, [videofile_path])"
]
},
{
@@ -515,7 +320,9 @@
"source": [
"## Manually correct labels\n",
"\n",
- "This step allows the user to correct the labels in the extracted frames. Navigate to the folder corresponding to the video 'm3v1mp4' and use the GUI as described in the protocol to update the labels."
+ "This step allows the user to correct the labels in the extracted frames. Navigate to the folder corresponding to the video 'm3v1mp4' and use the GUI as described in the protocol to update the labels.\n",
+ "\n",
+ "For documentation regarding the GUI, [look at the docs for `napari-deeplabcut`](https://github.com/DeepLabCut/napari-deeplabcut/tree/main) - and specifically _\"3. Refining labels – the image folder contains a machinelabels-iter<#>.h5 file.\"_!"
]
},
{
@@ -528,7 +335,6 @@
},
"outputs": [],
"source": [
- "%gui wx\n",
"deeplabcut.refine_labels(path_config_file)"
]
},
@@ -542,7 +348,7 @@
},
"outputs": [],
"source": [
- "#Perhaps plot the labels to see how how all the frames are annotated (including the refined ones)\n",
+ "# Perhaps plot the labels to see how how all the frames are annotated (including the refined ones)\n",
"deeplabcut.check_labels(path_config_file)"
]
},
@@ -592,7 +398,7 @@
"id": "8fhL6nG2ywoW"
},
"source": [
- "Now one can train the network again... (with the expanded data set)"
+ "Now one can train the network again... (with the expanded data set). We can continue training from the snapshot we already have by using the `snapshot_path` argument - instead of training the model from scratch, it will load the weights we already have and fine-tune them!"
]
},
{
@@ -605,8 +411,31 @@
},
"outputs": [],
"source": [
- "deeplabcut.train_network(path_config_file, shuffle=1)"
+ "snapshot_path = ( # Edit me if needed! Select the path to the snapshot to continue training from!\n",
+ " Path(path_config_file).parent\n",
+ " / \"dlc-models-pytorch\"\n",
+ " / \"iteration-0\"\n",
+ " / \"openfieldOct30-trainset95shuffle1\"\n",
+ " / \"train\"\n",
+ " / \"snapshot-best-080.pt\"\n",
+ ")\n",
+ "\n",
+ "deeplabcut.train_network(\n",
+ " path_config_file,\n",
+ " shuffle=1,\n",
+ " save_epochs=2,\n",
+ " displayiters=10,\n",
+ " batch_size=8,\n",
+ " snapshot_path=snapshot_path,\n",
+ ")"
]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
}
],
"metadata": {
@@ -616,10 +445,15 @@
"provenance": [],
"version": "0.3.2"
},
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-02-28",
+ "last_metadata_updated": "2026-03-06"
+ },
"kernelspec": {
- "display_name": "Python [conda env:DLC2]",
+ "display_name": "Python 3 (ipykernel)",
"language": "python",
- "name": "conda-env-DLC2-py"
+ "name": "python3"
},
"language_info": {
"codemirror_mode": {
@@ -631,7 +465,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.6.9"
+ "version": "3.11.11"
},
"varInspector": {
"cols": {
diff --git a/examples/JUPYTER/Demo_napari.ipynb b/examples/JUPYTER/Demo_napari.ipynb
index 2d906b05f7..764390965d 100644
--- a/examples/JUPYTER/Demo_napari.ipynb
+++ b/examples/JUPYTER/Demo_napari.ipynb
@@ -54,22 +54,13 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "jqLZhp7EoEI0"
},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "/Users/mwmathis/opt/anaconda3/envs/DLC2K/lib/python3.8/site-packages/statsmodels/compat/pandas.py:65: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.\n",
- " from pandas import Int64Index as NumericIndex\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"import deeplabcut"
]
@@ -84,15 +75,20 @@
},
"outputs": [],
"source": [
- "task='Reaching' # Enter the name of your experiment Task\n",
- "experimenter='Mackenzie' # Enter the name of the experimenter\n",
- "video=['/Users/mwmathis/Documents/DeepLabCut/examples/Reaching-Mackenzie-2018-08-30/videos/reachingvideo1.avi'] # Enter the paths of your videos OR FOLDER you want to grab frames from.\n",
+ "task = \"Reaching\" # Enter the name of your experiment Task\n",
+ "experimenter = \"Mackenzie\" # Enter the name of the experimenter\n",
+ "video = [\n",
+ " \"/Users/mwmathis/Documents/DeepLabCut/examples/Reaching-Mackenzie-2018-08-30/videos/reachingvideo1.avi\"\n",
+ "] # Enter the paths of your videos OR FOLDER you want to grab frames from.\n",
"\n",
- "path_config_file=deeplabcut.create_new_project(task,experimenter,video,copy_videos=True) \n",
+ "path_config_file = deeplabcut.create_new_project(task, experimenter, video, copy_videos=True)\n",
"\n",
- "# NOTE: The function returns the path, where your project is. \n",
- "# You could also enter this manually (e.g. if the project is already created and you want to pick up, where you stopped...)\n",
- "#path_config_file = '/home/Mackenzie/Reaching/config.yaml' # Enter the path of the config file that was just created from the above step (check the folder)"
+ "# NOTE: The function returns the path, where your project is.\n",
+ "\n",
+ "# You could also enter this manually (e.g. if the project is already created and you\n",
+ "# want to pick up, where you stopped...): Enter the path of the config file that was\n",
+ "# just created from the above step (check the folder)\n",
+ "# path_config_file = \"/home/Mackenzie/Reaching/config.yaml\""
]
},
{
@@ -148,10 +144,10 @@
},
"outputs": [],
"source": [
- "#there are other ways to grab frames, such as uniformly; please see the paper:\n",
+ "# there are other ways to grab frames, such as uniformly; please see the paper:\n",
"\n",
- "#AUTOMATIC:\n",
- "deeplabcut.extract_frames(path_config_file) "
+ "# AUTOMATIC:\n",
+ "deeplabcut.extract_frames(path_config_file)"
]
},
{
@@ -177,8 +173,8 @@
"# Attention: If you have not installed the napari-dlc plugin, do so now by running this cell:\n",
"!pip install napari-deeplabcut\n",
"\n",
- "#if the plugin does not appear upon launch, consider running in the ternimal the above command \n",
- "#within the same conda env and then re-starting kernel in your notebook (Kernel > restart)."
+ "# if the plugin does not appear upon launch, consider running in the terminal the above command\n",
+ "# within the same conda env and then re-starting kernel in your notebook (Kernel > restart)."
]
},
{
@@ -192,8 +188,9 @@
"outputs": [],
"source": [
"# napari will pop up! Please go to plugin > deeplabcut to start:\n",
- "%gui qt5\n",
+ "%gui qt6\n",
"import napari\n",
+ "\n",
"napari.Viewer()"
]
},
@@ -219,7 +216,7 @@
},
"outputs": [],
"source": [
- "deeplabcut.check_labels(path_config_file) #this creates a subdirectory with the frames + your labels"
+ "deeplabcut.check_labels(path_config_file) # this creates a subdirectory with the frames + your labels"
]
},
{
@@ -245,7 +242,7 @@
"\n",
"After running this script the training dataset is created and saved in the project directory under the subdirectory **'training-datasets'**\n",
"\n",
- "This function also creates new subdirectories under **dlc-models** and appends the project config.yaml file with the correct path to the training and testing pose configuration file. These files hold the parameters for training the network. Such an example file is provided with the toolbox and named as **pose_cfg.yaml**. For most all use cases we have seen, the defaults are perfectly fine.\n",
+ "This function also creates new subdirectories under **dlc-models-pytorch** and appends the project config.yaml file with the correct path to the training and testing pose configuration file. These files hold the parameters for training the network. Such an example file is provided with the toolbox and named as **pytorch_config.yaml**. For most all use cases we have seen, the defaults are perfectly fine.\n",
"\n",
"Now it is the time to start training the network!"
]
@@ -262,7 +259,7 @@
"outputs": [],
"source": [
"deeplabcut.create_training_dataset(path_config_file)\n",
- "#remember, there are several networks you can pick, the default is resnet-50!"
+ "# remember, there are several networks you can pick, the default is resnet-50!"
]
},
{
@@ -299,7 +296,7 @@
"source": [
"## Start evaluating\n",
"This function evaluates a trained model for a specific shuffle/shuffles at a particular state or all the states on the data set (images)\n",
- "and stores the results as .csv file in a subdirectory under **evaluation-results**"
+ "and stores the results as .csv file in a subdirectory under **evaluation-results-pytorch**"
]
},
{
@@ -338,9 +335,9 @@
},
"outputs": [],
"source": [
- "videofile_path = ['videos/video3.avi','videos/video4.avi'] #Enter a folder OR a list of videos to analyze.\n",
+ "videofile_path = [\"videos/video3.avi\", \"videos/video4.avi\"] # Enter a folder OR a list of videos to analyze.\n",
"\n",
- "deeplabcut.analyze_videos(path_config_file,videofile_path, videotype='.avi')"
+ "deeplabcut.analyze_videos(path_config_file, videofile_path, videotype=\".avi\")"
]
},
{
@@ -374,7 +371,7 @@
},
"outputs": [],
"source": [
- "deeplabcut.extract_outlier_frames(path_config_file,['/videos/video3.avi']) #pass a specific video"
+ "deeplabcut.extract_outlier_frames(path_config_file, [\"/videos/video3.avi\"]) # pass a specific video"
]
},
{
@@ -398,10 +395,11 @@
},
"outputs": [],
"source": [
- "#now you can edit the \"machine-labeled file\" within napari; \n",
- "#just again drop the file and images into the workspace after you load the plugin\n",
- "%gui qt5\n",
+ "# now you can edit the \"machine-labeled file\" within napari;\n",
+ "# just again drop the file and images into the workspace after you load the plugin\n",
+ "%gui qt6\n",
"import napari\n",
+ "\n",
"napari.Viewer()"
]
},
@@ -432,7 +430,7 @@
},
"outputs": [],
"source": [
- "#NOW, merge this with your original data:\n",
+ "# NOW, merge this with your original data:\n",
"\n",
"deeplabcut.merge_datasets(path_config_file)"
]
@@ -469,7 +467,7 @@
},
"source": [
"## Create labeled video\n",
- "This function is for visualiztion purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. \n",
+ "This function is for visualization purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. \n",
"\n",
"THIS HAS MANY FUN OPTIONS! \n",
"\n",
@@ -497,7 +495,7 @@
},
"outputs": [],
"source": [
- "deeplabcut.create_labeled_video(path_config_file,videofile_path)"
+ "deeplabcut.create_labeled_video(path_config_file, videofile_path)"
]
},
{
@@ -522,7 +520,7 @@
"outputs": [],
"source": [
"%matplotlib notebook #for making interactive plots.\n",
- "deeplabcut.plot_trajectories(path_config_file,videofile_path)"
+ "deeplabcut.plot_trajectories(path_config_file, videofile_path)"
]
}
],
@@ -533,10 +531,15 @@
"provenance": [],
"version": "0.3.2"
},
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-09-16",
+ "last_metadata_updated": "2026-03-06"
+ },
"kernelspec": {
- "display_name": "Python [conda env:DLC2K]",
+ "display_name": "Python 3 (ipykernel)",
"language": "python",
- "name": "conda-env-DLC2K-py"
+ "name": "python3"
},
"language_info": {
"codemirror_mode": {
@@ -548,7 +551,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.8.12"
+ "version": "3.11.11"
},
"varInspector": {
"cols": {
diff --git a/examples/JUPYTER/Demo_yourowndata.ipynb b/examples/JUPYTER/Demo_yourowndata.ipynb
index 066f08e165..8a62f53afe 100644
--- a/examples/JUPYTER/Demo_yourowndata.ipynb
+++ b/examples/JUPYTER/Demo_yourowndata.ipynb
@@ -8,7 +8,12 @@
},
"source": [
"# DeepLabCut Toolbox\n",
- "https://github.com/DeepLabCut/DeepLabCut\n",
+ "\n",
+ "\n",
+ "Some resources that can be useful:\n",
+ "\n",
+ "- [github.com/DeepLabCut/DeepLabCut](https://github.com/DeepLabCut/DeepLabCut)\n",
+ "- [DeepLabCut's Documentation: User Guide for Single Animal projects](https://deeplabcut.github.io/DeepLabCut/docs/standardDeepLabCut_UserGuide.html)\n",
"\n",
"This notebook demonstrates the necessary steps to use DeepLabCut for your own project.\n",
"This shows the most simple code to do so, but many of the functions have additional features, so please check out the overview & the protocol paper!\n",
@@ -52,7 +57,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -65,7 +70,7 @@
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -73,15 +78,25 @@
},
"outputs": [],
"source": [
- "task='Reaching' # Enter the name of your experiment Task\n",
- "experimenter='Mackenzie' # Enter the name of the experimenter\n",
- "video=['videos/video1.avi','videos/video2.avi'] # Enter the paths of your videos OR FOLDER you want to grab frames from.\n",
+ "task = \"Reaching\" # Enter the name of your experiment Task\n",
+ "experimenter = \"Mackenzie\" # Enter the name of the experimenter\n",
+ "video = [\n",
+ " \"videos/video1.avi\",\n",
+ " \"videos/video2.avi\",\n",
+ "] # Enter the paths of your videos OR FOLDER you want to grab frames from.\n",
"\n",
- "path_config_file=deeplabcut.create_new_project(task,experimenter,video,copy_videos=True) \n",
+ "path_config_file = deeplabcut.create_new_project(\n",
+ " task,\n",
+ " experimenter,\n",
+ " video,\n",
+ " copy_videos=True,\n",
+ ")\n",
"\n",
- "# NOTE: The function returns the path, where your project is. \n",
- "# You could also enter this manually (e.g. if the project is already created and you want to pick up, where you stopped...)\n",
- "#path_config_file = '/home/Mackenzie/Reaching/config.yaml' # Enter the path of the config file that was just created from the above step (check the folder)"
+ "# NOTE: The function returns the path, where your project is.\n",
+ "# You could also enter this manually (e.g. if the project is already created\n",
+ "# and you want to pick up where you stopped...)\n",
+ "# Enter the path of the config file that was just created from the above step (check the folder):\n",
+ "# path_config_file = \"/home/Mackenzie/Reaching/config.yaml\""
]
},
{
@@ -101,7 +116,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -129,7 +144,7 @@
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -138,22 +153,10 @@
"outputs": [],
"source": [
"%matplotlib inline\n",
- "#there are other ways to grab frames, such as uniformly; please see the paper:\n",
+ "# there are other ways to grab frames, such as uniformly; please see the paper:\n",
"\n",
- "#AUTOMATIC:\n",
- "deeplabcut.extract_frames(path_config_file) "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "#AND/OR:\n",
- "#SELECT RARE EVENTS MANUALLY:\n",
- "%gui wx\n",
- "deeplabcut.extract_frames(path_config_file,'manual')"
+ "# AUTOMATIC:\n",
+ "deeplabcut.extract_frames(path_config_file)"
]
},
{
@@ -165,12 +168,14 @@
"source": [
"## Label the extracted frames\n",
"\n",
- "Only videos in the config file can be used to extract the frames. Extracted labels for each video are stored in the project directory under the subdirectory **'labeled-data'**. Each subdirectory is named after the name of the video. The toolbox has a labeling toolbox which could be used for labeling. "
+ "Only videos in the config file can be used to extract the frames. Extracted labels for each video are stored in the project directory under the subdirectory **'labeled-data'**. Each subdirectory is named after the name of the video. The toolbox has a labeling toolbox which could be used for labeling. \n",
+ "\n",
+ "Check out [our `napari-deeplabcut` docs](https://github.com/DeepLabCut/napari-deeplabcut/tree/main) for more information about labelling!"
]
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -178,8 +183,15 @@
},
"outputs": [],
"source": [
- "%gui wx\n",
- "deeplabcut.label_frames(path_config_file)"
+ "# napari will pop up!\n",
+ "# Please go to plugin > deeplabcut to start\n",
+ "# then, drag-and-drop the project configuration file into the viewer (the value of path_config_file)\n",
+ "# finally, drop the folder containing the images (in 'labeled-data') in the viewer\n",
+ "\n",
+ "%gui qt6\n",
+ "import napari\n",
+ "\n",
+ "napari.Viewer()"
]
},
{
@@ -196,7 +208,7 @@
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -204,7 +216,7 @@
},
"outputs": [],
"source": [
- "deeplabcut.check_labels(path_config_file) #this creates a subdirectory with the frames + your labels"
+ "deeplabcut.check_labels(path_config_file) # this creates a subdirectory with the frames + your labels"
]
},
{
@@ -230,14 +242,14 @@
"\n",
"After running this script the training dataset is created and saved in the project directory under the subdirectory **'training-datasets'**\n",
"\n",
- "This function also creates new subdirectories under **dlc-models** and appends the project config.yaml file with the correct path to the training and testing pose configuration file. These files hold the parameters for training the network. Such an example file is provided with the toolbox and named as **pose_cfg.yaml**. For most all use cases we have seen, the defaults are perfectly fine.\n",
+ "This function also creates new subdirectories under **dlc-models-pytorch** and creates a `pytorch_config.yaml` file, defining the model architecture and containing various parameters used for training the network. For most all use cases we have seen, the defaults are perfectly fine. For more information about the variables that can be set, check out the [docs](https://deeplabcut.github.io/DeepLabCut/docs/pytorch/pytorch_config.html)!\n",
"\n",
"Now it is the time to start training the network!"
]
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -247,7 +259,8 @@
"outputs": [],
"source": [
"deeplabcut.create_training_dataset(path_config_file)\n",
- "#remember, there are several networks you can pick, the default is resnet-50!"
+ "\n",
+ "# remember, there are several networks you can pick, the default is resnet-50!"
]
},
{
@@ -259,12 +272,14 @@
"source": [
"## Start training:\n",
"\n",
+ "The user can set various parameters in `.../project-name/dlc-models-pytorch/.../pytorch_config.yaml`. For more information about the variables that can be set, check out the [docs](https://deeplabcut.github.io/DeepLabCut/docs/pytorch/pytorch_config.html)!\n",
+ "\n",
"This function trains the network for a specific shuffle of the training dataset. "
]
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -284,12 +299,12 @@
"source": [
"## Start evaluating\n",
"This function evaluates a trained model for a specific shuffle/shuffles at a particular state or all the states on the data set (images)\n",
- "and stores the results as .csv file in a subdirectory under **evaluation-results**"
+ "and stores the results as .csv file in a subdirectory under **evaluation-results-pytorch**"
]
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -315,7 +330,7 @@
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -323,9 +338,9 @@
},
"outputs": [],
"source": [
- "videofile_path = ['videos/video3.avi','videos/video4.avi'] #Enter a folder OR a list of videos to analyze.\n",
+ "videofile_path = [\"videos/video3.avi\", \"videos/video4.avi\"] # Enter a folder OR a list of videos to analyze.\n",
"\n",
- "deeplabcut.analyze_videos(path_config_file,videofile_path, videotype='.avi')"
+ "deeplabcut.analyze_videos(path_config_file, videofile_path, videotype=\".avi\")"
]
},
{
@@ -342,7 +357,7 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -351,7 +366,7 @@
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -359,7 +374,7 @@
},
"outputs": [],
"source": [
- "deeplabcut.extract_outlier_frames(path_config_file,['/videos/video3.avi']) #pass a specific video"
+ "deeplabcut.extract_outlier_frames(path_config_file, [\"/videos/video3.avi\"]) # pass a specific video"
]
},
{
@@ -375,7 +390,7 @@
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -383,7 +398,7 @@
},
"outputs": [],
"source": [
- "%gui wx\n",
+ "%gui qt6\n",
"deeplabcut.refine_labels(path_config_file)"
]
},
@@ -406,7 +421,7 @@
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -414,7 +429,7 @@
},
"outputs": [],
"source": [
- "#NOW, merge this with your original data:\n",
+ "# NOW, merge this with your original data:\n",
"\n",
"deeplabcut.merge_datasets(path_config_file)"
]
@@ -432,7 +447,7 @@
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -451,18 +466,38 @@
},
"source": [
"## Create labeled video\n",
- "This function is for visualiztion purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. \n",
+ "\n",
+ "This function is for visualization purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. \n",
"\n",
"THIS HAS MANY FUN OPTIONS! \n",
"\n",
- "``deeplabcut.create_labeled_video(config, videos, videotype='avi', shuffle=1, trainingsetindex=0, filtered=False, save_frames=False, Frames2plot=None, delete=False, displayedbodyparts='all', codec='mp4v', outputframerate=None, destfolder=None, draw_skeleton=False, trailpoints=0, displaycropped=False)``\n",
+ "```python\n",
+ "deeplabcut.create_labeled_video(\n",
+ " config,\n",
+ " videos,\n",
+ " videotype='avi',\n",
+ " shuffle=1,\n",
+ " trainingsetindex=0,\n",
+ " filtered=False,\n",
+ " save_frames=False,\n",
+ " Frames2plot=None,\n",
+ " delete=False,\n",
+ " displayedbodyparts='all',\n",
+ " codec='mp4v',\n",
+ " outputframerate=None,\n",
+ " destfolder=None,\n",
+ " draw_skeleton=False,\n",
+ " trailpoints=0,\n",
+ " displaycropped=False,\n",
+ ")\n",
+ "```\n",
"\n",
"So please check:"
]
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -471,7 +506,7 @@
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -479,7 +514,7 @@
},
"outputs": [],
"source": [
- "deeplabcut.create_labeled_video(path_config_file,videofile_path)"
+ "deeplabcut.create_labeled_video(path_config_file, videofile_path)"
]
},
{
@@ -495,7 +530,7 @@
},
{
"cell_type": "code",
- "execution_count": 0,
+ "execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
@@ -504,7 +539,7 @@
"outputs": [],
"source": [
"%matplotlib notebook #for making interactive plots.\n",
- "deeplabcut.plot_trajectories(path_config_file,videofile_path)"
+ "deeplabcut.plot_trajectories(path_config_file, videofile_path)"
]
}
],
@@ -515,10 +550,15 @@
"provenance": [],
"version": "0.3.2"
},
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-09-16",
+ "last_metadata_updated": "2026-03-06"
+ },
"kernelspec": {
- "display_name": "Python [conda env:DLC2]",
+ "display_name": "Python 3 (ipykernel)",
"language": "python",
- "name": "conda-env-DLC2-py"
+ "name": "python3"
},
"language_info": {
"codemirror_mode": {
@@ -530,7 +570,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.6.9"
+ "version": "3.11.11"
},
"varInspector": {
"cols": {
diff --git a/examples/JUPYTER/Docker_TrainNetwork_VideoAnalysis.ipynb b/examples/JUPYTER/Docker_TrainNetwork_VideoAnalysis.ipynb
index b562493a97..c9808a0f35 100644
--- a/examples/JUPYTER/Docker_TrainNetwork_VideoAnalysis.ipynb
+++ b/examples/JUPYTER/Docker_TrainNetwork_VideoAnalysis.ipynb
@@ -56,25 +56,11 @@
},
"outputs": [],
"source": [
- "import tensorflow as tf\n",
- "tf.__version__"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "Pm_PC1Q8lRrH"
- },
- "outputs": [],
- "source": [
- "#let's make sure we see a GPU:\n",
- "#tf.test.gpu_device_name()\n",
- "#or\n",
- "from tensorflow.python.client import device_lib\n",
- "device_lib.list_local_devices()"
+ "import torch\n",
+ "\n",
+ "# Let's make sure we see a GPU:\n",
+ "print(torch.__version__)\n",
+ "print(torch.cuda.is_available())"
]
},
{
@@ -94,10 +80,11 @@
},
"outputs": [],
"source": [
- "#GUIs don't work on in Docker (or the cloud), so label your data locally on your computer! \n",
- "#This notebook is for you to train and run video analysis!\n",
+ "# GUIs don't work on in Docker (or the cloud), so label your data locally on your computer!\n",
+ "# This notebook is for you to train and run video analysis!\n",
"import os\n",
- "os.environ[\"DLClight\"]=\"True\""
+ "\n",
+ "os.environ[\"DLClight\"] = \"True\""
]
},
{
@@ -112,8 +99,7 @@
"outputs": [],
"source": [
"# now we are ready to train!\n",
- "import deeplabcut\n",
- "deeplabcut.__version__"
+ "import deeplabcut"
]
},
{
@@ -133,7 +119,8 @@
},
"outputs": [],
"source": [
- "path_config_file = '/home/mackenzie/DEEPLABCUT/DeepLabCut2.0/examples/Reaching-Mackenzie-2018-08-30/config.yaml' #change to yours!"
+ "# change to yours!\n",
+ "path_config_file = \"/home/mackenzie/DEEPLABCUT/DeepLabCut/examples/Reaching-Mackenzie-2018-08-30/config.yaml\""
]
},
{
@@ -155,11 +142,12 @@
},
"source": [
"## Create a training dataset\n",
- "This function generates the training data information for DeepCut (which requires a mat file) based on the pandas dataframes that hold label information. The user can set the fraction of the training set size (from all labeled image in the hd5 file) in the config.yaml file. While creating the dataset, the user can create multiple shuffles. \n",
+ "\n",
+ "This function generates the training data required for DeepLabCut. The user can set the fraction of the training set size (from all labeled images in the hd5 file) in the `config.yaml` file. While creating the dataset, the user can create multiple shuffles. \n",
"\n",
"After running this script the training dataset is created and saved in the project directory under the subdirectory **'training-datasets'**\n",
"\n",
- "This function also creates new subdirectories under **dlc-models** and appends the project config.yaml file with the correct path to the training and testing pose configuration file. These files hold the parameters for training the network. Such an example file is provided with the toolbox and named as **pose_cfg.yaml**."
+ "This function also creates new subdirectories under **dlc-models-pytorch** and creates a `pytorch_config.yaml` file, defining the model architecture and containing various parameters used for training the network. For most all use cases we have seen, the defaults are perfectly fine. For more information about the variables that can be set, check out the [docs](https://deeplabcut.github.io/DeepLabCut/docs/pytorch/pytorch_config.html)!\n"
]
},
{
@@ -168,16 +156,7 @@
"metadata": {},
"outputs": [],
"source": [
- "deeplabcut.create_training_dataset(path_config_file,Shuffles=[1])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### now go edit the pose_cfg.yaml to make display_iters: low (i.e. 10), and save_iters: 500 (for demo's)\n",
- "\n",
- "Now it is the time to start training the network!"
+ "deeplabcut.create_training_dataset(path_config_file, Shuffles=[1])"
]
},
{
@@ -202,54 +181,18 @@
},
"outputs": [],
"source": [
- "#reset in case you started a session before...\n",
- "#tf.reset_default_graph()\n",
+ "deeplabcut.train_network(\n",
+ " path_config_file,\n",
+ " shuffle=1,\n",
+ " save_epochs=2,\n",
+ " displayiters=5,\n",
+ ")\n",
"\n",
- "deeplabcut.train_network(path_config_file, shuffle=1, saveiters=1000, displayiters=10)\n",
+ "# This will run until you stop it (CTRL+C), or hit \"STOP\" icon, or when it\n",
+ "# hits the end (default, 200 epochs).\n",
"\n",
- "#this will run until you stop it (CTRL+C), or hit \"STOP\" icon, or when it hits the end (default, 1.3M iterations). \n",
- "#Whichever you chose, you will see what looks like an error message, but it's not an error - don't worry....\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Firstly, if the above cell ran, you can stop it with \"stop\" or cntrl-C; you will get a Keyboard Interrupt error (this is fine!)\n",
- "\n",
- "### A couple tips for possible troubleshooting (1): \n",
- "\n",
- "if you get **permission errors** when you run this step (above), first check if the weights downloaded. As some docker containers might not have privileges for this (it can be user specific). They should be under 'init_weights' (see path in the pose_cfg.yaml file). You can enter the DOCKER in the terminal:"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "see more here: https://github.com/MMathisLab/Docker4DeepLabCut2.0#using-the-docker-for-training-and-video-analysis"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "You can \"cd\" in the terminal to this location! i.e. copy and paste this in: **\"cd usr/local/lib/python3.6/dist-packages/deeplabcut/pose_estimation_tensorflow/models/pretrained/\n",
- "\"** \n",
- "\n",
- "And if you type \"ls\" to see the list of files, you should see the resnet:\n",
- "**resnet_v1_50.ckpt**\n",
- "\n",
- "If it is not there, run **\"sudo download.sh\"**\n",
- "then change the permissions: **\"sudo chown yourusername:yourusername resnet_v1_50.ckpt\"**\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Troubleshooting (2): \n",
- "if it appears the training does not start (i.e. \"Starting training...\" does not print immediately),\n",
- "then you have another session running on your GPU. Go check \"nvidia-smi\" and look at the process names. You can only have 1 per GPU!)"
+ "# If you end training before it hits the end, you will see what looks like\n",
+ "# an error message, but it's not an error - don't worry...."
]
},
{
@@ -261,7 +204,7 @@
"source": [
"## Start evaluating\n",
"This function evaluates a trained model for a specific shuffle/shuffles at a particular state or all the states on the data set (images)\n",
- "and stores the results as .csv file in a subdirectory under **evaluation-results**"
+ "and stores the results as .csv file in a subdirectory under **evaluation-results-pytorch**"
]
},
{
@@ -277,7 +220,8 @@
"source": [
"deeplabcut.evaluate_network(path_config_file)\n",
"\n",
- "# Here you want to see a low pixel error! Of course, it can only be as good as the labeler, so be sure your labels are good!"
+ "# Here you want to see a low pixel error! Of course, it can only\n",
+ "# be as good as the labeler, so be sure your labels are good!"
]
},
{
@@ -317,8 +261,10 @@
},
"outputs": [],
"source": [
- "videofile_path = ['/home/mackenzie/DEEPLABCUT/DeepLabCut2.0/examples/Reaching-Mackenzie-2018-08-30/videos/MovieS2_Perturbation_noLaser_compressed.avi'] #Enter the list of videos to analyze.\n",
- "deeplabcut.analyze_videos(path_config_file,videofile_path)"
+ "videofile_path = [\n",
+ " \"/home/mackenzie/DEEPLABCUT/DeepLabCut/examples/Reaching-Mackenzie-2018-08-30/videos/MovieS2_Perturbation_noLaser_compressed.avi\"\n",
+ "] # Enter the list of videos to analyze.\n",
+ "deeplabcut.analyze_videos(path_config_file, videofile_path)"
]
},
{
@@ -329,7 +275,7 @@
},
"source": [
"## Create labeled video\n",
- "This function is for visualiztion purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. "
+ "This function is for visualization purpose and can be used to create a video in .mp4 format with labels predicted by the network. This video is saved in the same directory where the original video resides. "
]
},
{
@@ -343,7 +289,7 @@
},
"outputs": [],
"source": [
- "deeplabcut.create_labeled_video(path_config_file,videofile_path)"
+ "deeplabcut.create_labeled_video(path_config_file, videofile_path)"
]
},
{
@@ -369,10 +315,9 @@
"outputs": [],
"source": [
"%matplotlib notebook \n",
- "#for making interactive plots.\n",
- "#deeplabcut.plot_trajectories(path_config_file,videofile_path, plotting=True)\n",
- "\n",
- "deeplabcut.plot_trajectories(path_config_file,videofile_path,showfigures=True)"
+ "# for making interactive plots.\n",
+ "# deeplabcut.plot_trajectories(path_config_file, videofile_path, plotting=True)\n",
+ "deeplabcut.plot_trajectories(path_config_file, videofile_path, showfigures=True)"
]
}
],
@@ -386,8 +331,13 @@
"toc_visible": true,
"version": "0.3.2"
},
+ "deeplabcut": {
+ "ignore": false,
+ "last_content_updated": "2025-09-16",
+ "last_metadata_updated": "2026-03-06"
+ },
"kernelspec": {
- "display_name": "Python [default]",
+ "display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
@@ -401,7 +351,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.6.6"
+ "version": "3.11.11"
},
"varInspector": {
"cols": {
diff --git a/examples/openfield-Pranav-2018-10-30/config.yaml b/examples/openfield-Pranav-2018-10-30/config.yaml
index 64c2ce17b2..ed8c31fdf3 100644
--- a/examples/openfield-Pranav-2018-10-30/config.yaml
+++ b/examples/openfield-Pranav-2018-10-30/config.yaml
@@ -2,10 +2,17 @@
Task: openfield
scorer: Pranav
date: Oct30
+multianimalproject:
+identity:
+
# Project path (change when moving around)
project_path: WILL BE AUTOMATICALLY UPDATED BY DEMO CODE
+# Default DeepLabCut engine to use for shuffle creation (either pytorch or tensorflow)
+engine: pytorch
+
+
# Annotation data set configuration (and individual video cropping parameters)
video_sets:
WILL BE AUTOMATICALLY UPDATED BY DEMO CODE:
@@ -16,23 +23,33 @@ bodyparts:
- rightear
- tailbase
+
+# Fraction of video to start/stop when extracting frames for labeling/refinement
start: 0
stop: 1
numframes2pick: 20
+
# Plotting configuration
+skeleton: []
+skeleton_color: black
pcutoff: 0.4
dotsize: 8
alphavalue: 0.7
colormap: jet
+
# Training,Evaluation and Analysis configuration
TrainingFraction:
- 0.95
iteration: 0
default_net_type: resnet_50
+default_augmenter: imgaug
snapshotindex: -1
+detector_snapshotindex: -1
batch_size: 4
+detector_batch_size: 1
+
# Cropping Parameters (for analysis and outlier frame detection)
cropping: false
@@ -42,8 +59,18 @@ x2: 640
y1: 277
y2: 624
+
# Refinement configuration (parameters from annotation dataset configuration also relevant in this stage)
corner2move2:
- 50
- 50
move2corner: true
+
+
+# Conversion tables to fine-tune SuperAnimal weights
+SuperAnimalConversionTables:
+ superanimal_topviewmouse:
+ snout: nose
+ leftear: left_ear
+ rightear: right_ear
+ tailbase: tail_base
diff --git a/examples/stereo_example.zip b/examples/stereo_example.zip
new file mode 100644
index 0000000000..85b591cbe0
Binary files /dev/null and b/examples/stereo_example.zip differ
diff --git a/examples/test.sh b/examples/test.sh
index 3a1c4b4968..b9be0a511d 100755
--- a/examples/test.sh
+++ b/examples/test.sh
@@ -6,14 +6,14 @@ rm -r OUT
cd ..
pip uninstall deeplabcut
python3 setup.py sdist bdist_wheel
-pip install dist/deeplabcut-2.3.5-py3-none-any.whl
+pip install dist/deeplabcut-3.0.0-none-any.whl
cd examples
-python3 testscript.py
+python3 testscript_tensorflow_single_animal.py
python3 testscript_3d.py #does not work in container
#python3 testscript_mobilenets.py
-python3 testscript_multianimal.py
+python3 testscript_tensorflow_multi_animal.py
#python3 testscript_openfielddata_netcomparison.py
#python3 testscript_openfielddata_augmentationcomparison.py
diff --git a/examples/testscript_3d.py b/examples/testscript_3d.py
index 67b5f8acf9..a31c034a9f 100644
--- a/examples/testscript_3d.py
+++ b/examples/testscript_3d.py
@@ -20,13 +20,15 @@
This script tests various functionalities in an automatic way.
It produces nothing of interest scientifically.
"""
-import os, deeplabcut
-import zipfile, urllib.request, shutil
-from datetime import datetime as dt
+
import glob
-from pathlib import Path
+import os
+import shutil
import subprocess
+import zipfile
+from pathlib import Path
+import deeplabcut
if __name__ == "__main__":
print("Imported DLC!")
@@ -34,12 +36,11 @@
scorer = "Alex" # Enter the name of the experimenter/labeler
num_cameras = 2 # Enter the number of cameras
- basepath = str(Path(os.path.realpath(__file__)).parents[1])
+ basepath = str(Path(os.path.realpath(__file__)).parents[0])
videoname = "reachingvideo1"
video = [
os.path.join(
basepath,
- "examples",
"Reaching-Mackenzie-2018-08-30",
"videos",
videoname + ".avi",
@@ -47,7 +48,7 @@
]
folder = os.path.join(basepath, "3Dtestviews_videos")
- deeplabcut.auxiliaryfunctions.attempttomakefolder(folder)
+ deeplabcut.auxiliaryfunctions.attempt_to_make_folder(folder)
# copying demo video from reaching data set and create two "views":
dst_videoname1 = "vid1_camera-1"
@@ -88,7 +89,7 @@
output2,
]
)
- except:
+ except Exception:
pass
"""
@@ -103,8 +104,8 @@
# checking if 2d test project is available
try:
config = glob.glob(os.path.join(basepath, "TEST*", "config.yaml"))[-1]
- except:
- raise RuntimeError("Please run the testscript.py first before testing for 3d")
+ except Exception as e:
+ raise RuntimeError("Please run the testscript_tensorflow_single_animal.py first before testing for 3d") from e
dfolder = None
@@ -121,10 +122,8 @@
cfg["skeleton"] = [["bodypart1", "bodypart2"], ["objectA", "bodypart3"]]
deeplabcut.auxiliaryfunctions.write_config_3d(path_config_file, cfg)
- except:
- raise (
- "Please delete the project and re-try."
- ) # otherwise the cfg is an empty array!
+ except Exception as e:
+ raise RuntimeError("Please delete the project and re-try.") from e # otherwise the cfg is an empty array!
"""
# Creating the name of the project
@@ -138,11 +137,8 @@
project_name = path_config_file.split(os.sep)[-2]
os.chdir(os.path.join(project_name, "calibration_images"))
- # Downloading the calibration images
- url = "http://www.vision.caltech.edu/bouguetj/calib_doc/htmls/stereo_example.zip"
- file_name = "stereo_example.zip"
- with urllib.request.urlopen(url) as response, open(file_name, "wb") as out_file:
- shutil.copyfileobj(response, out_file)
+
+ file_name = os.path.join(basepath, "stereo_example.zip")
with zipfile.ZipFile(file_name) as zf:
zf.extractall()
@@ -150,7 +146,8 @@
cwd = os.getcwd()
[os.remove(file) for file in os.listdir(cwd) if not file.endswith(".jpg")]
- # change the file names for calibration images to match the name of cameras in config.yaml file.i.e. camera-1 and camera-2
+ # change the file names for calibration images to match the name of
+ # cameras in config.yaml file.i.e. camera-1 and camera-2
cam1_images = glob.glob(os.path.join(cwd, "left*.jpg"))
cam2_images = glob.glob(os.path.join(cwd, "right*.jpg"))
# Sorting images
@@ -159,13 +156,13 @@
for idx, name in enumerate(cam1_images):
os.rename(
name,
- os.path.join(cwd, str("camera-1_" + "{0:0=2d}".format(idx + 1) + ".jpg")),
+ os.path.join(cwd, str("camera-1_" + f"{idx + 1:0=2d}" + ".jpg")),
)
for idx, name in enumerate(cam2_images):
os.rename(
name,
- os.path.join(cwd, str("camera-2_" + "{0:0=2d}".format(idx + 1) + ".jpg")),
+ os.path.join(cwd, str("camera-2_" + f"{idx + 1:0=2d}" + ".jpg")),
)
# Removing some of the images where the corner was not detected
@@ -180,13 +177,11 @@
print("TRIANGULATING")
video_dir = os.path.join(os.path.dirname(basepath), folder)
- deeplabcut.auxiliaryfunctions.edit_config(
- path_config_file, edits={"pcutoff": 0.1}
- ) # otherwise get all-nan slices
+ deeplabcut.auxiliaryfunctions.edit_config(path_config_file, edits={"pcutoff": 0.1}) # otherwise get all-nan slices
deeplabcut.triangulate(path_config_file, video_dir, save_as_csv=True)
print("CREATING LABELED VIDEO 3-D")
- deeplabcut.create_labeled_video_3d(path_config_file, [video_dir], start=5, end=10)
+ deeplabcut.create_labeled_video_3d(path_config_file, [video_dir], start=5, end=10, video_extensions=".avi")
# output_path = [os.path.join(basepath,folder)]
# deeplabcut.create_labeled_video_3d(path_config_file,output_path,start=5,end=10)
diff --git a/examples/testscript_deterministicwithResNet152.py b/examples/testscript_deterministicwithResNet152.py
index dbaf8c67b8..01e033ee65 100644
--- a/examples/testscript_deterministicwithResNet152.py
+++ b/examples/testscript_deterministicwithResNet152.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
#
# DeepLabCut Toolbox (deeplabcut.org)
# © A. & M.W. Mathis Labs
@@ -38,23 +37,19 @@
It produces nothing of interest scientifically.
"""
-task = "TEST-deterministic" # Enter the name of your experiment Task
-scorer = "Alex" # Enter the name of the experimenter/labeler
-
+import os
-import os, subprocess, deeplabcut
-from pathlib import Path
-import pandas as pd
import numpy as np
+import pandas as pd
+import deeplabcut
+
+task = "TEST-deterministic" # Enter the name of your experiment Task
+scorer = "Alex" # Enter the name of the experimenter/labeler
print("Imported DLC!")
basepath = os.path.dirname(os.path.abspath("testscript.py"))
videoname = "reachingvideo1"
-video = [
- os.path.join(
- basepath, "Reaching-Mackenzie-2018-08-30", "videos", videoname + ".avi"
- )
-]
+video = [os.path.join(basepath, "Reaching-Mackenzie-2018-08-30", "videos", videoname + ".avi")]
# to test destination folder:
# dfolder=basepath
@@ -106,7 +101,7 @@
videoname,
"CollectedData_" + scorer + ".h5",
),
- "df_with_missing",
+ key="df_with_missing",
format="table",
mode="w",
)
@@ -118,7 +113,8 @@
print("CREATING TRAININGSET")
deeplabcut.create_training_dataset(path_config_file)
-# posefile=os.path.join(cfg['project_path'],'dlc-models/iteration-'+str(cfg['iteration'])+'/'+ cfg['Task'] + cfg['date'] + '-trainset' + str(int(cfg['TrainingFraction'][0] * 100)) + 'shuffle' + str(1),'train/pose_cfg.yaml')
+# posefile=os.path.join(cfg['project_path'],'dlc-models/iteration-'+str(cfg['iteration'])+'/'+ cfg['Task'] + cfg['date']
+# + '-trainset' + str(int(cfg['TrainingFraction'][0] * 100)) + 'shuffle' + str(1),'train/pose_cfg.yaml')
shuffle = 1
posefile, _, _ = deeplabcut.return_train_network_path(path_config_file, shuffle=shuffle)
diff --git a/examples/testscript_mobilenets.py b/examples/testscript_mobilenets.py
index 2929a8ddf6..18342cb347 100644
--- a/examples/testscript_mobilenets.py
+++ b/examples/testscript_mobilenets.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
#
# DeepLabCut Toolbox (deeplabcut.org)
# © A. & M.W. Mathis Labs
@@ -15,25 +14,27 @@
@author: alex
DEVELOPERS:
-This script tests various functionalities (creating project ,training, evaluating, outlierextraction, retraining...) in an automatic way.
+This script tests various functionalities (creating project ,training, evaluating, outlierextraction, retraining...) in
+an automatic way.
For that purpose, it trains ResNet and MobileNet briefly on a "fake" dataset.
It should take about 4:15 minutes to run this in a CPU. (incl. downloading the ResNet + MobileNet weights)
It produces nothing of interest scientifically.
"""
+
import os
os.environ["DLClight"] = "True"
-import deeplabcut
from pathlib import Path
-import pandas as pd
+
import numpy as np
+import pandas as pd
+
+import deeplabcut
-def Cuttrainingschedule(
- path_config_file, shuffle, trainingsetindex=0, initweights="imagenet", lastvalue=10
-):
+def Cuttrainingschedule(path_config_file, shuffle, trainingsetindex=0, initweights="imagenet", lastvalue=10):
cfg = deeplabcut.auxiliaryfunctions.read_config(path_config_file)
posefile = os.path.join(
cfg["project_path"],
@@ -72,7 +73,7 @@ def Cuttrainingschedule(
)
print("CHANGING training parameters to end quickly!")
- DLC_config = deeplabcut.auxiliaryfunctions.edit_config(posefile, edits)
+ deeplabcut.auxiliaryfunctions.edit_config(posefile, edits)
return
@@ -82,23 +83,17 @@ def Cuttrainingschedule(
print("Imported DLC!")
basepath = os.path.dirname(os.path.realpath(__file__))
videoname = "reachingvideo1"
- video = [
- os.path.join(
- basepath, "Reaching-Mackenzie-2018-08-30", "videos", videoname + ".avi"
- )
- ]
+ video = [os.path.join(basepath, "Reaching-Mackenzie-2018-08-30", "videos", videoname + ".avi")]
# to test destination folder:
dfolder = os.path.join(basepath, "OUT")
- deeplabcut.auxiliaryfunctions.attempttomakefolder(dfolder)
+ deeplabcut.auxiliaryfunctions.attempt_to_make_folder(dfolder)
# dfolder=None
augmenter_type = "tensorpack" # imgaug'
print("CREATING PROJECT")
- path_config_file = deeplabcut.create_new_project(
- task, scorer, video, copy_videos=True
- )
+ path_config_file = deeplabcut.create_new_project(task, scorer, video, copy_videos=True)
cfg = deeplabcut.auxiliaryfunctions.read_config(path_config_file)
cfg["numframes2pick"] = 5
@@ -143,7 +138,7 @@ def Cuttrainingschedule(
videoname,
"CollectedData_" + scorer + ".h5",
),
- "df_with_missing",
+ key="df_with_missing",
format="table",
mode="w",
)
@@ -153,9 +148,7 @@ def Cuttrainingschedule(
print("Plot labels...")
deeplabcut.check_labels(path_config_file)
- for shuffle, net_type in enumerate(
- ["mobilenet_v2_0.35", "resnet_50"]
- ): #'mobilenet_v2_1.0']): # 'resnet_50']):
+ for shuffle, net_type in enumerate(["mobilenet_v2_0.35", "resnet_50"]): #'mobilenet_v2_1.0']): # 'resnet_50']):
"""
if shuffle==0:
keepdeconvweights=True
@@ -164,9 +157,7 @@ def Cuttrainingschedule(
"""
print("CREATING TRAININGSET", net_type)
if "resnet_50" == net_type: # this tests the default condition...
- deeplabcut.create_training_dataset(
- path_config_file, Shuffles=[shuffle], augmenter_type=augmenter_type
- )
+ deeplabcut.create_training_dataset(path_config_file, Shuffles=[shuffle], augmenter_type=augmenter_type)
else:
deeplabcut.create_training_dataset(
path_config_file,
@@ -200,7 +191,7 @@ def Cuttrainingschedule(
shuffle=shuffle,
save_as_csv=True,
destfolder=dfolder,
- videotype="avi",
+ video_extensions="avi",
)
print("CREATE VIDEO")
@@ -209,7 +200,7 @@ def Cuttrainingschedule(
[newvideo],
shuffle=shuffle,
destfolder=dfolder,
- videotype="avi",
+ video_extensions="avi",
)
print("Making plots")
@@ -218,7 +209,7 @@ def Cuttrainingschedule(
[newvideo],
shuffle=shuffle,
destfolder=dfolder,
- videotype="avi",
+ video_extensions="avi",
)
print("EXTRACT OUTLIERS")
@@ -230,7 +221,7 @@ def Cuttrainingschedule(
epsilon=0,
automatic=True,
destfolder=dfolder,
- videotype="avi",
+ video_extensions="avi",
)
file = os.path.join(
cfg["project_path"],
@@ -242,9 +233,7 @@ def Cuttrainingschedule(
print("RELABELING")
DF = pd.read_hdf(file, "df_with_missing")
DLCscorer = np.unique(DF.columns.get_level_values(0))[0]
- DF.columns.set_levels(
- [scorer.replace(DLCscorer, scorer)], level=0, inplace=True
- )
+ DF.columns.set_levels([scorer.replace(DLCscorer, scorer)], level=0, inplace=True)
DF = DF.drop("likelihood", axis=1, level=2)
DF.to_csv(
os.path.join(
@@ -261,7 +250,7 @@ def Cuttrainingschedule(
vname,
"CollectedData_" + scorer + ".h5",
),
- "df_with_missing",
+ key="df_with_missing",
format="table",
mode="w",
)
@@ -270,17 +259,11 @@ def Cuttrainingschedule(
deeplabcut.merge_datasets(path_config_file)
print("CREATING TRAININGSET")
- deeplabcut.create_training_dataset(
- path_config_file, Shuffles=[shuffle], net_type=net_type
- )
- Cuttrainingschedule(
- path_config_file, shuffle, lastvalue=stoptrain, initweights="previteration"
- )
+ deeplabcut.create_training_dataset(path_config_file, Shuffles=[shuffle], net_type=net_type)
+ Cuttrainingschedule(path_config_file, shuffle, lastvalue=stoptrain, initweights="previteration")
print("TRAINING from previous snapshot!!!!!")
- deeplabcut.train_network(
- path_config_file, shuffle=shuffle, keepdeconvweights=keepdeconvweights
- )
+ deeplabcut.train_network(path_config_file, shuffle=shuffle, keepdeconvweights=keepdeconvweights)
print("ANALYZING some individual frames")
deeplabcut.analyze_time_lapse_frames(
diff --git a/examples/testscript_openfielddata.py b/examples/testscript_openfielddata.py
index 760464a24a..8faf1c3a5d 100644
--- a/examples/testscript_openfielddata.py
+++ b/examples/testscript_openfielddata.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
#
# DeepLabCut Toolbox (deeplabcut.org)
# © A. & M.W. Mathis Labs
@@ -10,8 +9,7 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
-"""
-Created on Mon Nov 5 18:06:13 2018
+"""Created on Mon Nov 5 18:06:13 2018.
@author: alex
@@ -27,17 +25,17 @@
Results for 15001 training iterations: 95 1 train error: 2.89 pixels. Test error: 2.81 pixels.
With pcutoff of 0.1 train error: 2.89 pixels. Test error: 2.81 pixels
-The analysis of the video takes 41 seconds (batch size 32) and creating the frames 8 seconds (+ a few seconds for ffmpeg) to create the video.
+The analysis of the video takes 41 seconds (batch size 32) and creating the frames 8 seconds (+ a few seconds for
+ffmpeg) to create the video.
"""
-import deeplabcut
+
import os
+import deeplabcut
if __name__ == "__main__":
# Loading example data set
- path_config_file = os.path.join(
- os.getcwd(), "openfield-Pranav-2018-10-30/config.yaml"
- )
+ path_config_file = os.path.join(os.getcwd(), "openfield-Pranav-2018-10-30/config.yaml")
deeplabcut.load_demo_data(path_config_file)
shuffle = 13
@@ -45,9 +43,7 @@
cfg = deeplabcut.auxiliaryfunctions.read_config(path_config_file)
# example how to set pose config variables:
- posefile, _, _ = deeplabcut.return_train_network_path(
- path_config_file, shuffle=shuffle
- )
+ posefile, _, _ = deeplabcut.return_train_network_path(path_config_file, shuffle=shuffle)
edits = {"save_iters": 15000, "display_iters": 1000, "multi_step": [[0.005, 15001]]}
DLC_config = deeplabcut.auxiliaryfunctions.edit_config(posefile, edits)
@@ -58,12 +54,8 @@
deeplabcut.evaluate_network(path_config_file, Shuffles=[shuffle], plotting=True)
print("Analyze Video")
- videofile_path = os.path.join(
- os.getcwd(), "openfield-Pranav-2018-10-30", "videos", "m3v1mp4.mp4"
- )
- deeplabcut.analyze_videos(
- path_config_file, [videofile_path], shuffle=shuffle
- ) # ,videotype='.mp4')
+ videofile_path = os.path.join(os.getcwd(), "openfield-Pranav-2018-10-30", "videos", "m3v1mp4.mp4")
+ deeplabcut.analyze_videos(path_config_file, [videofile_path], shuffle=shuffle) # ,videotype='.mp4')
print("Create Labeled Video")
deeplabcut.create_labeled_video(
diff --git a/examples/testscript_openfielddata_augmentationcomparison.py b/examples/testscript_openfielddata_augmentationcomparison.py
index c98b1944e4..6e88425d40 100644
--- a/examples/testscript_openfielddata_augmentationcomparison.py
+++ b/examples/testscript_openfielddata_augmentationcomparison.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
#
# DeepLabCut Toolbox (deeplabcut.org)
# © A. & M.W. Mathis Labs
@@ -10,9 +9,7 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
-"""
-
-This is a test script to compare the loaders and models.
+"""This is a test script to compare the loaders and models.
This script creates one identical splits for the openfield test dataset and trains it with imgaug (default), scalecrop
and the tensorpack loader. We also compare 3 backbones (mobilenet, resnet, efficientnet)
@@ -52,15 +49,13 @@
Notice: despite the higher RMSE for imgaug due to the augmentation,
the network performs much better on the testvideo (see Neuron Primer: https://www.cell.com/neuron/pdf/S0896-6273(20)30717-0.pdf)
-
"""
-
import os
os.environ["CUDA_VISIBLE_DEVICES"] = str(0)
+
import deeplabcut
-import numpy as np
# Loading example data set
path_config_file = os.path.join(os.getcwd(), "openfield-Pranav-2018-10-30/config.yaml")
@@ -82,9 +77,7 @@
)
for idx, shuffle in enumerate(Shuffles):
- posefile, _, _ = deeplabcut.return_train_network_path(
- path_config_file, shuffle=shuffle
- )
+ posefile, _, _ = deeplabcut.return_train_network_path(path_config_file, shuffle=shuffle)
# Setting specific parameters for training
if idx % 3 == 0: # imgaug
@@ -115,9 +108,7 @@
print("Analyze Video")
- videofile_path = os.path.join(
- os.getcwd(), "openfield-Pranav-2018-10-30", "videos", "m3v1mp4.mp4"
- )
+ videofile_path = os.path.join(os.getcwd(), "openfield-Pranav-2018-10-30", "videos", "m3v1mp4.mp4")
deeplabcut.analyze_videos(path_config_file, [videofile_path], shuffle=shuffle)
diff --git a/examples/testscript_pretrained_models.py b/examples/testscript_pretrained_models.py
index 3b09d0bc26..3a668c5caa 100644
--- a/examples/testscript_pretrained_models.py
+++ b/examples/testscript_pretrained_models.py
@@ -8,32 +8,26 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
-"""
-Testscript human network
+"""Testscript human network."""
-"""
-import os, subprocess, deeplabcut
-from pathlib import Path
-import pandas as pd
-import numpy as np
+import os
+
+import deeplabcut
Task = "human_dancing"
YourName = "teamDLC"
MODEL_NAME = "horse_sideview" # full_human"
-basepath = os.path.dirname(os.path.abspath("testscript.py"))
+basepath = os.path.dirname(os.path.abspath("testscript_tensorflow_single_animal.py"))
videoname = "reachingvideo1"
-video = [
- os.path.join(
- basepath, "Reaching-Mackenzie-2018-08-30", "videos", videoname + ".avi"
- )
-]
+video = [os.path.join(basepath, "Reaching-Mackenzie-2018-08-30", "videos", videoname + ".avi")]
# legacy mode:
"""
configfile, path_train_config=deeplabcut.create_pretrained_human_project(Task, YourName,video,
videotype='avi', analyzevideo=True,
- createlabeledvideo=True, copy_videos=False) #must leave copy_videos=True
+ createlabeledvideo=True, copy_videos=False)
+ #must leave copy_videos=True
"""
# new way:
configfile, path_train_config = deeplabcut.create_pretrained_project(
@@ -41,10 +35,11 @@
YourName,
video,
model=MODEL_NAME,
- videotype="avi",
+ video_extensions="avi",
analyzevideo=True,
createlabeledvideo=True,
copy_videos=False,
+ engine=deeplabcut.Engine.TF,
) # must leave copy_videos=True
@@ -90,7 +85,7 @@
videoname,
"CollectedData_" + cfg["scorer"] + ".h5",
),
- "df_with_missing",
+ key="df_with_missing",
format="table",
mode="w",
)
@@ -166,7 +161,7 @@
videoname,
"CollectedData_" + cfg["scorer"] + ".h5",
),
- "df_with_missing",
+ key="df_with_missing",
format="table",
mode="w",
)
diff --git a/examples/testscript_pytorch_multi_animal.py b/examples/testscript_pytorch_multi_animal.py
new file mode 100644
index 0000000000..4a4de3c55a
--- /dev/null
+++ b/examples/testscript_pytorch_multi_animal.py
@@ -0,0 +1,139 @@
+#
+# 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
+#
+"""Testscript for single animal PyTorch projects."""
+
+from __future__ import annotations
+
+from pathlib import Path
+
+from utils import (
+ SyntheticProjectParameters,
+ cleanup,
+ create_fake_project,
+ log_step,
+ run,
+)
+
+import deeplabcut.utils.auxiliaryfunctions as af
+from deeplabcut.compat import Engine
+from deeplabcut.pose_estimation_pytorch.config.utils import (
+ is_model_cond_top_down,
+ is_model_top_down,
+)
+
+
+def main(
+ net_types: list[str],
+ params: SyntheticProjectParameters,
+ epochs: int = 1,
+ top_down_epochs: int = 1,
+ detector_epochs: int = 1,
+ save_epochs: int = 1,
+ batch_size: int = 1,
+ detector_batch_size: int = 1,
+ max_snapshots_to_keep: int = 5,
+ device: str = "cpu",
+ logger: dict | None = None,
+ conditions_shuffle: int = 0,
+ create_labeled_videos: bool = False,
+ delete_after_test_run: bool = False,
+) -> None:
+ project_path = Path("synthetic-data-niels-multi-animal").resolve()
+ config_path = project_path / "config.yaml"
+ create_fake_project(path=project_path, params=params)
+
+ engine = Engine.PYTORCH
+ cfg = af.read_config(config_path)
+ trainset_index = 0
+ train_frac = cfg["TrainingFraction"][trainset_index]
+ try:
+ for net_type in net_types:
+ epochs_ = epochs
+ if is_model_top_down(net_type):
+ epochs_ = top_down_epochs
+ try:
+ pytorch_cfg_updates = {
+ "train_settings.display_iters": 50,
+ "train_settings.epochs": epochs_,
+ "train_settings.batch_size": batch_size,
+ "train_settings.dataloader_workers": 0,
+ "runner.device": device,
+ "runner.snapshots.save_epochs": save_epochs,
+ "runner.snapshots.max_snapshots": max_snapshots_to_keep,
+ "detector.train_settings.display_iters": 1,
+ "detector.train_settings.epochs": detector_epochs,
+ "detector.train_settings.batch_size": detector_batch_size,
+ "detector.train_settings.dataloader_workers": 0,
+ "detector.runner.snapshots.save_epochs": save_epochs,
+ "detector.runner.snapshots.max_snapshots": max_snapshots_to_keep,
+ "logger": logger,
+ }
+ if is_model_cond_top_down(net_type):
+ pytorch_cfg_updates["inference.conditions.shuffle"] = conditions_shuffle
+ pytorch_cfg_updates["inference.conditions.snapshot_index"] = -1
+ run(
+ config_path=config_path,
+ train_fraction=train_frac,
+ trainset_index=trainset_index,
+ net_type=net_type,
+ videos=[str(project_path / "videos" / "video.mp4")],
+ device=device,
+ engine=engine,
+ pytorch_cfg_updates=pytorch_cfg_updates,
+ create_labeled_videos=create_labeled_videos,
+ )
+ except Exception as err:
+ log_step(f"FAILED TO RUN {net_type}")
+ log_step(str(err))
+ log_step("Continuing to next model")
+ raise err
+
+ finally:
+ if delete_after_test_run:
+ cleanup(project_path)
+
+
+if __name__ == "__main__":
+ wandb_logger = {
+ "type": "WandbLogger",
+ "project_name": "testscript-dev",
+ "run_name": "test-logging",
+ }
+ net_types = [
+ "top_down_resnet_50",
+ "resnet_50",
+ "dekr_w32",
+ "rtmpose_m",
+ "ctd_coam_w32",
+ ]
+ main(
+ net_types=net_types,
+ params=SyntheticProjectParameters(
+ multianimal=True,
+ num_bodyparts=4,
+ num_individuals=3,
+ num_unique=0,
+ num_frames=25,
+ frame_shape=(256, 256),
+ ),
+ batch_size=2,
+ detector_batch_size=2,
+ epochs=8,
+ top_down_epochs=2,
+ detector_epochs=10,
+ save_epochs=4,
+ max_snapshots_to_keep=2,
+ device="cpu", # "cpu", "cuda:0", "mps"
+ logger=None,
+ conditions_shuffle=net_types.index("resnet_50") + 1, # shuffles start at index 1
+ create_labeled_videos=True,
+ delete_after_test_run=True,
+ )
diff --git a/examples/testscript_pytorch_single_animal.py b/examples/testscript_pytorch_single_animal.py
new file mode 100644
index 0000000000..8536f2be12
--- /dev/null
+++ b/examples/testscript_pytorch_single_animal.py
@@ -0,0 +1,110 @@
+"""Testscript for single animal PyTorch projects."""
+
+from __future__ import annotations
+
+from pathlib import Path
+
+from utils import (
+ SyntheticProjectParameters,
+ cleanup,
+ copy_project_for_test,
+ create_fake_project,
+ log_step,
+ run,
+)
+
+import deeplabcut.utils.auxiliaryfunctions as af
+from deeplabcut.compat import Engine
+
+
+def main(
+ synthetic_data: bool,
+ net_types: list[str],
+ epochs: int = 1,
+ save_epochs: int = 1,
+ max_snapshots_to_keep: int = 5,
+ batch_size: int = 1,
+ device: str = "cpu",
+ logger: dict | None = None,
+ synthetic_data_params: SyntheticProjectParameters = None,
+ create_labeled_videos: bool = False,
+ delete_after_test_run: bool = False,
+) -> None:
+ if synthetic_data_params is None:
+ synthetic_data_params = SyntheticProjectParameters(
+ multianimal=False,
+ num_bodyparts=6,
+ )
+ engine = Engine.PYTORCH
+ if synthetic_data:
+ project_path = Path("synthetic-data-niels-single-animal").resolve()
+ videos = [str(project_path / "videos" / "video.mp4")]
+ create_fake_project(path=project_path, params=synthetic_data_params)
+
+ else:
+ project_path = copy_project_for_test()
+ videos = [str(project_path / "videos" / "m3v1mp4.mp4")]
+
+ config_path = project_path / "config.yaml"
+ cfg = af.read_config(config_path)
+ trainset_index = 0
+ train_frac = cfg["TrainingFraction"][trainset_index]
+ try:
+ for net_type in net_types:
+ try:
+ run(
+ config_path=config_path,
+ train_fraction=train_frac,
+ trainset_index=trainset_index,
+ net_type=net_type,
+ videos=videos,
+ device=device,
+ engine=engine,
+ pytorch_cfg_updates={
+ "train_settings.display_iters": 50,
+ "train_settings.epochs": epochs,
+ "train_settings.batch_size": batch_size,
+ "runner.device": device,
+ "runner.snapshots.save_epochs": save_epochs,
+ "runner.snapshots.max_snapshots": max_snapshots_to_keep,
+ "logger": logger,
+ },
+ create_labeled_videos=create_labeled_videos,
+ )
+
+ except Exception as err:
+ log_step(f"FAILED TO RUN {net_type}")
+ log_step(str(err))
+ log_step("Continuing to next model")
+ raise err
+ finally:
+ if delete_after_test_run:
+ cleanup(project_path)
+
+
+if __name__ == "__main__":
+ wandb_logger = {
+ "type": "WandbLogger",
+ "project_name": "testscript-dev",
+ "run_name": "test-logging",
+ }
+ main(
+ synthetic_data=True,
+ net_types=["cspnext_m", "resnet_50", "hrnet_w32"],
+ batch_size=4,
+ epochs=8,
+ save_epochs=2,
+ max_snapshots_to_keep=2,
+ device="cpu", # "cpu", "cuda:0", "mps"
+ logger=None,
+ synthetic_data_params=SyntheticProjectParameters(
+ multianimal=False,
+ num_bodyparts=4,
+ num_individuals=1,
+ num_unique=0,
+ num_frames=12,
+ frame_shape=(128, 128),
+ ),
+ create_labeled_videos=True,
+ delete_after_test_run=True,
+ )
diff --git a/examples/testscript_superanimal_adaptation.py b/examples/testscript_superanimal_adaptation.py
index 02a660313d..42d6e2fba9 100644
--- a/examples/testscript_superanimal_adaptation.py
+++ b/examples/testscript_superanimal_adaptation.py
@@ -8,19 +8,16 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
-"""
-Test script for super animal adaptation
-"""
-import deeplabcut
+"""Test script for super animal adaptation."""
+
import os
+import deeplabcut
if __name__ == "__main__":
basepath = os.path.dirname(os.path.realpath(__file__))
videoname = "m3v1mp4"
- video = os.path.join(
- basepath, "openfield-Pranav-2018-10-30", "videos", videoname + ".mp4"
- )
+ video = os.path.join(basepath, "openfield-Pranav-2018-10-30", "videos", videoname + ".mp4")
video = deeplabcut.ShortenVideo(
video,
start="00:00:00",
@@ -31,12 +28,14 @@
print("adaptation training for superanimal_topviewmouse")
superanimal_name = "superanimal_topviewmouse"
- videotype = ".mp4"
+ video_extensions = ".mp4"
scale_list = [200, 300, 400]
deeplabcut.video_inference_superanimal(
[video],
superanimal_name,
- videotype=".mp4",
+ model_name="hrnet_w32",
+ detector_name="fasterrcnn_resnet50_fpn_v2",
+ video_extensions=".mp4",
video_adapt=True,
scale_list=scale_list,
pcutoff=0.1,
diff --git a/examples/testscript_superanimal_create_pretrained_project.py b/examples/testscript_superanimal_create_pretrained_project.py
new file mode 100644
index 0000000000..b697a3e285
--- /dev/null
+++ b/examples/testscript_superanimal_create_pretrained_project.py
@@ -0,0 +1,38 @@
+#
+# 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/master/AUTHORS
+#
+# Licensed under GNU Lesser General Public License v3.0
+#
+"""Testscript for creating a pretrained project from a super animal model."""
+
+import glob
+import shutil
+from pathlib import Path
+
+import deeplabcut
+
+if __name__ == "__main__":
+ superanimal_name = "superanimal_quadruped"
+ working_dir = Path(__file__).resolve().parent
+ video_dir = working_dir / "openfield-Pranav-2018-10-30/videos/m3v1mp4.mp4"
+ project_name = "pretrained"
+
+ deeplabcut.create_pretrained_project(
+ project_name,
+ "max",
+ [str(video_dir)],
+ engine=deeplabcut.Engine.PYTORCH,
+ )
+
+ dirs_to_delete = glob.glob(f"{working_dir}/{project_name}*")
+
+ # Delete directories
+ for directory in dirs_to_delete:
+ shutil.rmtree(directory)
+
+ print("Test passed!")
diff --git a/examples/testscript_superanimal_inference.py b/examples/testscript_superanimal_inference.py
index b4b49c42b6..4db84b2b91 100644
--- a/examples/testscript_superanimal_inference.py
+++ b/examples/testscript_superanimal_inference.py
@@ -8,22 +8,16 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
-"""
-Testscript for super animal inference
+"""Testscript for super animal inference."""
-"""
-import deeplabcut
import os
+import deeplabcut
if __name__ == "__main__":
basepath = os.path.dirname(os.path.realpath(__file__))
videoname = "reachingvideo1"
- video = [
- os.path.join(
- basepath, "Reaching-Mackenzie-2018-08-30", "videos", videoname + ".avi"
- )
- ]
+ video = [os.path.join(basepath, "Reaching-Mackenzie-2018-08-30", "videos", videoname + ".avi")]
print("testing superanimal_topviewmouse")
superanimal_name = "superanimal_topviewmouse"
@@ -31,7 +25,9 @@
deeplabcut.video_inference_superanimal(
video,
superanimal_name,
- videotype=".avi",
+ model_name="hrnet_w32",
+ detector_name="fasterrcnn_resnet50_fpn_v2",
+ video_extensions=".avi",
scale_list=scale_list,
)
@@ -40,6 +36,8 @@
deeplabcut.video_inference_superanimal(
video,
superanimal_name,
- videotype=".avi",
+ model_name="hrnet_w32",
+ detector_name="fasterrcnn_resnet50_fpn_v2",
+ video_extensions=".avi",
scale_list=scale_list,
)
diff --git a/examples/testscript_superanimal_transfer_learning.py b/examples/testscript_superanimal_transfer_learning.py
new file mode 100644
index 0000000000..6d9c347e4f
--- /dev/null
+++ b/examples/testscript_superanimal_transfer_learning.py
@@ -0,0 +1,40 @@
+#
+# 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/master/AUTHORS
+#
+# Licensed under GNU Lesser General Public License v3.0
+#
+"""Test script for super animal adaptation."""
+
+import os
+
+import deeplabcut
+from deeplabcut.modelzoo.weight_initialization import build_weight_init
+
+print(deeplabcut.__file__)
+if __name__ == "__main__":
+ superanimal_name = "superanimal_topviewmouse"
+ basepath = os.path.dirname(os.path.realpath(__file__))
+ config_path = os.path.join(basepath, "openfield-Pranav-2018-10-30", "config.yaml")
+ model_name = "hrnet_w32"
+ detector_name = "fasterrcnn_resnet50_fpn_v2"
+
+ weight_init = build_weight_init(
+ cfg=config_path,
+ super_animal=superanimal_name,
+ model_name=model_name,
+ detector_name=detector_name,
+ with_decoder=False,
+ )
+ deeplabcut.create_training_dataset(config_path, weight_init=weight_init)
+
+ deeplabcut.train_network(
+ config_path,
+ epochs=1,
+ superanimal_name=superanimal_name,
+ superanimal_transfer_learning=True,
+ )
diff --git a/examples/testscript_multianimal.py b/examples/testscript_tensorflow_multi_animal.py
similarity index 81%
rename from examples/testscript_multianimal.py
rename to examples/testscript_tensorflow_multi_animal.py
index 12d0b142c6..f5a69cef33 100644
--- a/examples/testscript_multianimal.py
+++ b/examples/testscript_tensorflow_multi_animal.py
@@ -9,16 +9,22 @@
# Licensed under GNU Lesser General Public License v3.0
#
import os
-import deeplabcut
+import pickle
+import random
+from pathlib import Path
+
+import matplotlib
import numpy as np
import pandas as pd
-import pickle
+
+matplotlib.use("Agg") # Non-interactive backend, for CI/CD on Windows
+
+import deeplabcut
+from deeplabcut.core.engine import Engine
from deeplabcut.utils import auxfun_multianimal, auxiliaryfunctions
from deeplabcut.utils.auxfun_videos import VideoReader
-import random
-from pathlib import Path
-MODELS = ["dlcrnet_ms5", "dlcr101_ms5", "efficientnet-b0", "mobilenet_v2_0.35"]
+MODELS = ["dlcrnet_ms5", "dlcr101_ms5", "efficientnet-b0"]
N_ITER = 5
@@ -31,6 +37,7 @@
SCORER = "dlc_team"
NUM_FRAMES = 5
TRAIN_SIZE = 0.8
+ ENGINE = Engine.TF
# NET = "dlcr101_ms5"
NET = "dlcrnet_ms5"
@@ -42,14 +49,10 @@
DESTFOLDER = basepath
video = "m3v1mp4"
- video_path = os.path.join(
- basepath, "openfield-Pranav-2018-10-30", "videos", video + ".mp4"
- )
+ video_path = os.path.join(basepath, "openfield-Pranav-2018-10-30", "videos", video + ".mp4")
print("Creating project...")
- config_path = deeplabcut.create_new_project(
- TASK, SCORER, [video_path], copy_videos=True, multianimal=True
- )
+ config_path = deeplabcut.create_new_project(TASK, SCORER, [video_path], copy_videos=True, multianimal=True)
print("Project created.")
@@ -78,33 +81,27 @@
bodyparts_single,
bodyparts_multi,
) = auxfun_multianimal.extractindividualsandbodyparts(cfg)
- animals_id = [i for i in range(n_animals) for _ in bodyparts_multi] + [
- n_animals
- ] * len(bodyparts_single)
- map_ = dict(zip(range(len(animals)), animals))
+ animals_id = [i for i in range(n_animals) for _ in bodyparts_multi] + [n_animals] * len(bodyparts_single)
+ map_ = dict(zip(range(len(animals)), animals, strict=False))
individuals = [map_[ind] for ind in animals_id for _ in range(2)]
scorer = [SCORER] * len(individuals)
coords = ["x", "y"] * len(animals_id)
- bodyparts = [
- bp for _ in range(n_animals) for bp in bodyparts_multi for _ in range(2)
- ]
+ bodyparts = [bp for _ in range(n_animals) for bp in bodyparts_multi for _ in range(2)]
bodyparts += [bp for bp in bodyparts_single for _ in range(2)]
columns = pd.MultiIndex.from_arrays(
[scorer, individuals, bodyparts, coords],
names=["scorer", "individuals", "bodyparts", "coords"],
)
- index = [
- os.path.join(rel_folder, image)
- for image in auxiliaryfunctions.grab_files_in_folder(image_folder, "png")
- ]
- fake_data = np.tile(
- np.repeat(50 * np.arange(len(animals_id)) + 50, 2), (len(index), 1)
- )
+ index = [os.path.join(rel_folder, image) for image in auxiliaryfunctions.grab_files_in_folder(image_folder, "png")]
+ fake_data = np.tile(np.repeat(50 * np.arange(len(animals_id)) + 50, 2), (len(index), 1))
df = pd.DataFrame(fake_data, index=index, columns=columns)
output_path = os.path.join(image_folder, f"CollectedData_{SCORER}.csv")
df.to_csv(output_path)
df.to_hdf(
- output_path.replace("csv", "h5"), "df_with_missing", format="table", mode="w"
+ output_path.replace("csv", "h5"),
+ key="df_with_missing",
+ format="table",
+ mode="w",
)
print("Artificial data created.")
@@ -114,7 +111,10 @@
print("Creating train dataset...")
deeplabcut.create_multianimaltraining_dataset(
- config_path, net_type=NET, crop_size=(200, 200)
+ config_path,
+ net_type=NET,
+ crop_size=(200, 200),
+ engine=ENGINE,
)
print("Train dataset created.")
@@ -134,7 +134,7 @@
print("Editing pose config...")
model_folder = auxiliaryfunctions.get_model_folder(
- TRAIN_SIZE, 1, cfg, cfg["project_path"]
+ TRAIN_SIZE, 1, cfg, engine=ENGINE, modelprefix=cfg["project_path"]
)
pose_config_path = os.path.join(model_folder, "train", "pose_cfg.yaml")
edits = {
@@ -153,7 +153,7 @@
print("Network trained.")
print("Evaluating network...")
- deeplabcut.evaluate_network(config_path, plotting=True)
+ deeplabcut.evaluate_network(config_path, plotting=True, per_keypoint_evaluation=True)
print("Network evaluated....")
@@ -192,9 +192,7 @@
print("Video created.")
print("Convert detections to tracklets...")
- deeplabcut.convert_detections2tracklets(
- config_path, [new_video_path], "mp4", track_method=TESTTRACKER
- )
+ deeplabcut.convert_detections2tracklets(config_path, [new_video_path], "mp4", track_method=TESTTRACKER)
print("Tracklets created...")
h5path = os.path.splitext(new_video_path)[0] + scorer + "_el.h5"
try:
@@ -211,9 +209,7 @@
individuals = [map_[ind] for ind in animals_id for _ in range(3)]
scorer = [SCORER] * len(individuals)
coords = ["x", "y", "likelihood"] * len(animals_id)
- bodyparts = [
- bp for _ in range(n_animals) for bp in bodyparts_multi for _ in range(3)
- ]
+ bodyparts = [bp for _ in range(n_animals) for bp in bodyparts_multi for _ in range(3)]
bodyparts += [bp for bp in bodyparts_single for _ in range(3)]
columns = pd.MultiIndex.from_arrays(
[scorer, individuals, bodyparts, coords],
@@ -225,9 +221,7 @@
df.to_hdf(h5path, key="data")
print("Plotting trajectories...")
- deeplabcut.plot_trajectories(
- config_path, [new_video_path], "mp4", track_method=TESTTRACKER
- )
+ deeplabcut.plot_trajectories(config_path, [new_video_path], "mp4", track_method=TESTTRACKER)
print("Trajectory plotted.")
print("Creating labeled video...")
@@ -242,15 +236,11 @@
print("Labeled video created.")
print("Filtering predictions...")
- deeplabcut.filterpredictions(
- config_path, [new_video_path], "mp4", track_method=TESTTRACKER
- )
+ deeplabcut.filterpredictions(config_path, [new_video_path], "mp4", track_method=TESTTRACKER)
print("Predictions filtered.")
print("Extracting outlier frames...")
- deeplabcut.extract_outlier_frames(
- config_path, [new_video_path], "mp4", automatic=True, track_method=TESTTRACKER
- )
+ deeplabcut.extract_outlier_frames(config_path, [new_video_path], "mp4", automatic=True, track_method=TESTTRACKER)
print("Outlier frames extracted.")
vname = Path(new_video_path).stem
@@ -282,21 +272,21 @@
vname,
"CollectedData_" + SCORER + ".h5",
),
- "df_with_missing",
+ key="df_with_missing",
)
print("MERGING")
deeplabcut.merge_datasets(config_path) # iteration + 1
print("CREATING TRAININGSET updated training set")
- deeplabcut.create_training_dataset(config_path, net_type=NET)
+ deeplabcut.create_training_dataset(config_path, net_type=NET, engine=ENGINE)
print("Training network...")
deeplabcut.train_network(config_path, maxiters=N_ITER)
print("Network trained.")
print("Evaluating network...")
- deeplabcut.evaluate_network(config_path, plotting=True)
+ deeplabcut.evaluate_network(config_path, plotting=True, per_keypoint_evaluation=True)
print("Network evaluated....")
@@ -316,16 +306,16 @@
deeplabcut.export_model(config_path, shuffle=1, make_tar=False)
print("Merging datasets...")
- trainIndices, testIndices = deeplabcut.mergeandsplit(
- config_path, trainindex=0, uniform=True
- )
+ trainIndices, testIndices = deeplabcut.mergeandsplit(config_path, trainindex=0, uniform=True)
print("Creating two identical splits...")
deeplabcut.create_multianimaltraining_dataset(
config_path,
Shuffles=[4, 5],
+ net_type=NET,
trainIndices=[trainIndices, trainIndices],
testIndices=[testIndices, testIndices],
+ engine=ENGINE,
)
print("ALL DONE!!! - default multianimal cases are functional.")
diff --git a/examples/testscript.py b/examples/testscript_tensorflow_single_animal.py
similarity index 81%
rename from examples/testscript.py
rename to examples/testscript_tensorflow_single_animal.py
index e5a231d5b5..65869d3b38 100644
--- a/examples/testscript.py
+++ b/examples/testscript_tensorflow_single_animal.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
#
# DeepLabCut Toolbox (deeplabcut.org)
# © A. & M.W. Mathis Labs
@@ -22,36 +21,36 @@
It produces nothing of interest scientifically.
"""
+
import os
-import deeplabcut
import platform
-import scipy.io as sio
-import subprocess
+import random
from pathlib import Path
+import matplotlib
import numpy as np
import pandas as pd
+import scipy.io as sio
+import deeplabcut
+from deeplabcut.core.engine import Engine
from deeplabcut.utils import auxiliaryfunctions
-import random
+matplotlib.use("Agg") # Non-interactive backend, for CI/CD on Windows
USE_SHELVE = random.choice([True, False])
-MODELS = ["resnet_50", "efficientnet-b0", "mobilenet_v2_0.35"]
+MODELS = ["resnet_50", "efficientnet-b0"]
if __name__ == "__main__":
task = "TEST" # Enter the name of your experiment Task
scorer = "Alex" # Enter the name of the experimenter/labeler
+ engine = Engine.TF
print("Imported DLC!")
basepath = os.path.dirname(os.path.realpath(__file__))
videoname = "reachingvideo1"
- video = [
- os.path.join(
- basepath, "Reaching-Mackenzie-2018-08-30", "videos", videoname + ".avi"
- )
- ]
+ video = [os.path.join(basepath, "Reaching-Mackenzie-2018-08-30", "videos", videoname + ".avi")]
# For testing a color video:
# videoname='baby4hin2min'
@@ -71,12 +70,11 @@
else:
augmenter_type3 = "tensorpack" # Does not work on WINDOWS
- N_ITER = 5
+ N_ITER = 6
+ SAVE_ITER = 3
print("CREATING PROJECT")
- path_config_file = deeplabcut.create_new_project(
- task, scorer, video, copy_videos=True
- )
+ path_config_file = deeplabcut.create_new_project(task, scorer, video, copy_videos=True)
cfg = deeplabcut.auxiliaryfunctions.read_config(path_config_file)
cfg["numframes2pick"] = 5
@@ -91,6 +89,7 @@
print("CREATING-SOME LABELS FOR THE FRAMES")
frames = os.listdir(os.path.join(cfg["project_path"], "labeled-data", videoname))
+ frames = [fn for fn in frames if fn.endswith(".png")]
# As this next step is manual, we update the labels by putting them on the diagonal (fixed for all frames)
for index, bodypart in enumerate(cfg["bodyparts"]):
columnindex = pd.MultiIndex.from_product(
@@ -122,7 +121,7 @@
videoname,
"CollectedData_" + scorer + ".h5",
),
- "df_with_missing",
+ key="df_with_missing",
format="table",
mode="w",
)
@@ -133,7 +132,10 @@
print("CREATING TRAININGSET")
deeplabcut.create_training_dataset(
- path_config_file, net_type=NET, augmenter_type=augmenter_type
+ path_config_file,
+ net_type=NET,
+ augmenter_type=augmenter_type,
+ engine=engine,
)
# Check the training image paths are correctly stored as arrays of strings
@@ -166,18 +168,26 @@
)
DLC_config = deeplabcut.auxiliaryfunctions.read_plainconfig(posefile)
- DLC_config["save_iters"] = N_ITER
+ DLC_config["save_iters"] = SAVE_ITER
DLC_config["display_iters"] = 2
- DLC_config["multi_step"] = [[0.001, N_ITER]]
print("CHANGING training parameters to end quickly!")
deeplabcut.auxiliaryfunctions.write_plainconfig(posefile, DLC_config)
print("TRAIN")
- deeplabcut.train_network(path_config_file)
+ deeplabcut.train_network(path_config_file, maxiters=N_ITER)
print("EVALUATE")
- deeplabcut.evaluate_network(path_config_file, plotting=True)
+ deeplabcut.evaluate_network(
+ path_config_file,
+ plotting=True,
+ per_keypoint_evaluation=True,
+ snapshots_to_evaluate=[
+ "snapshot-3",
+ "snapshot-5",
+ "snapshot-6",
+ ], # snapshot-5 intentionally missing :)
+ )
# deeplabcut.evaluate_network(path_config_file,plotting=True,trainingsetindex=33)
print("CUT SHORT VIDEO AND ANALYZE (with dynamic cropping!)")
@@ -192,10 +202,10 @@
outsuffix="short",
outpath=os.path.join(cfg["project_path"], "videos"),
)
- except: # if ffmpeg is broken/missing
+ except Exception: # if ffmpeg is broken/missing
print("using alternative method")
newvideo = os.path.join(cfg["project_path"], "videos", videoname + "short.mp4")
- from moviepy.editor import VideoFileClip, VideoClip
+ from moviepy.editor import VideoClip, VideoFileClip
clip = VideoFileClip(video[0])
clip.reader.initialize()
@@ -217,14 +227,11 @@ def make_frame(t):
)
print("analyze again...")
- deeplabcut.analyze_videos(
- path_config_file, [newvideo], save_as_csv=True, destfolder=DESTFOLDER
- )
+ deeplabcut.analyze_videos(path_config_file, [newvideo], save_as_csv=True, destfolder=DESTFOLDER)
print("CREATE VIDEO")
- deeplabcut.create_labeled_video(
- path_config_file, [newvideo], destfolder=DESTFOLDER, save_frames=True
- )
+ successful = deeplabcut.create_labeled_video(path_config_file, [newvideo], destfolder=DESTFOLDER, save_frames=True)
+ assert all(successful), "Failed to create a labeled video!"
print("Making plots")
deeplabcut.plot_trajectories(path_config_file, [newvideo], destfolder=DESTFOLDER)
@@ -274,16 +281,14 @@ def make_frame(t):
vname,
"CollectedData_" + scorer + ".h5",
),
- "df_with_missing",
+ key="df_with_missing",
)
print("MERGING")
deeplabcut.merge_datasets(path_config_file) # iteration + 1
print("CREATING TRAININGSET")
- deeplabcut.create_training_dataset(
- path_config_file, net_type=NET, augmenter_type=augmenter_type2
- )
+ deeplabcut.create_training_dataset(path_config_file, net_type=NET, augmenter_type=augmenter_type2, engine=engine)
cfg = deeplabcut.auxiliaryfunctions.read_config(path_config_file)
posefile = os.path.join(
@@ -300,15 +305,14 @@ def make_frame(t):
"train/pose_cfg.yaml",
)
DLC_config = deeplabcut.auxiliaryfunctions.read_plainconfig(posefile)
- DLC_config["save_iters"] = N_ITER
+ DLC_config["save_iters"] = SAVE_ITER
DLC_config["display_iters"] = 1
- DLC_config["multi_step"] = [[0.001, N_ITER]]
print("CHANGING training parameters to end quickly!")
deeplabcut.auxiliaryfunctions.write_config(posefile, DLC_config)
print("TRAIN")
- deeplabcut.train_network(path_config_file)
+ deeplabcut.train_network(path_config_file, maxiters=N_ITER)
try: # you need ffmpeg command line interface
# subprocess.call(['ffmpeg','-i',video[0],'-ss','00:00:00','-to','00:00:00.4','-c','copy',newvideo])
@@ -320,11 +324,9 @@ def make_frame(t):
outpath=os.path.join(cfg["project_path"], "videos"),
)
- except: # if ffmpeg is broken
- newvideo2 = os.path.join(
- cfg["project_path"], "videos", videoname + "short2.mp4"
- )
- from moviepy.editor import VideoFileClip, VideoClip
+ except Exception: # if ffmpeg is broken
+ newvideo2 = os.path.join(cfg["project_path"], "videos", videoname + "short2.mp4")
+ from moviepy.editor import VideoClip, VideoFileClip
clip = VideoFileClip(video[0])
clip.reader.initialize()
@@ -349,27 +351,25 @@ def make_frame(t):
)
print("Extracting skeleton distances, filter and plot filtered output")
- deeplabcut.analyzeskeleton(
- path_config_file, [newvideo2], save_as_csv=True, destfolder=DESTFOLDER
- )
+ deeplabcut.analyzeskeleton(path_config_file, [newvideo2], save_as_csv=True, destfolder=DESTFOLDER)
deeplabcut.filterpredictions(path_config_file, [newvideo2])
- deeplabcut.create_labeled_video(
+ successful = deeplabcut.create_labeled_video(
path_config_file,
[newvideo2],
destfolder=DESTFOLDER,
displaycropped=True,
filtered=True,
)
+ assert all(successful), "Failed to create a labeled video!"
print("Creating a Johansson video!")
- deeplabcut.create_labeled_video(
+ successful = deeplabcut.create_labeled_video(
path_config_file, [newvideo2], destfolder=DESTFOLDER, keypoints_only=True
)
+ assert all(successful), "Failed to create a labeled video!"
- deeplabcut.plot_trajectories(
- path_config_file, [newvideo2], destfolder=DESTFOLDER, filtered=True
- )
+ deeplabcut.plot_trajectories(path_config_file, [newvideo2], destfolder=DESTFOLDER, filtered=True)
print("ALL DONE!!! - default cases without Tensorpack loader are functional.")
@@ -381,6 +381,7 @@ def make_frame(t):
Shuffles=[2],
net_type=NET,
augmenter_type=augmenter_type3,
+ engine=engine,
)
posefile = os.path.join(
@@ -398,15 +399,15 @@ def make_frame(t):
)
DLC_config = deeplabcut.auxiliaryfunctions.read_plainconfig(posefile)
- DLC_config["save_iters"] = 10
+ updated_max_iters = 10
+ DLC_config["save_iters"] = updated_max_iters
DLC_config["display_iters"] = 2
- DLC_config["multi_step"] = [[0.001, 10]]
print("CHANGING training parameters to end quickly!")
deeplabcut.auxiliaryfunctions.write_plainconfig(posefile, DLC_config)
print("TRAINING shuffle 2, with smaller allocated memory")
- deeplabcut.train_network(path_config_file, shuffle=2, allow_growth=True)
+ deeplabcut.train_network(path_config_file, shuffle=2, allow_growth=True, maxiters=updated_max_iters)
print("ANALYZING some individual frames")
deeplabcut.analyze_time_lapse_frames(
@@ -418,9 +419,7 @@ def make_frame(t):
deeplabcut.export_model(path_config_file, shuffle=2, make_tar=False)
print("Merging datasets...")
- trainIndices, testIndices = deeplabcut.mergeandsplit(
- path_config_file, trainindex=0, uniform=True
- )
+ trainIndices, testIndices = deeplabcut.mergeandsplit(path_config_file, trainindex=0, uniform=True)
print("Creating two identical splits...")
deeplabcut.create_training_dataset(
@@ -428,6 +427,7 @@ def make_frame(t):
Shuffles=[4, 5],
trainIndices=[trainIndices, trainIndices],
testIndices=[testIndices, testIndices],
+ engine=engine,
)
print("ALL DONE!!! - default cases are functional.")
diff --git a/examples/testscript_transreid.py b/examples/testscript_transreid.py
index b3467832ef..cb10de7f26 100644
--- a/examples/testscript_transreid.py
+++ b/examples/testscript_transreid.py
@@ -9,14 +9,16 @@
# Licensed under GNU Lesser General Public License v3.0
#
import os
-import deeplabcut
-import numpy as np
-import pandas as pd
import pickle
-from deeplabcut.utils import auxfun_multianimal, auxiliaryfunctions
import random
from pathlib import Path
+import numpy as np
+import pandas as pd
+
+import deeplabcut
+from deeplabcut.utils import auxfun_multianimal, auxiliaryfunctions
+
# MODELS = ["dlcrnet_ms5", "dlcr101_ms5", "efficientnet-b0", "mobilenet_v2_0.35"]
MODELS = [
"dlcrnet_ms5",
@@ -43,14 +45,10 @@
DESTFOLDER = basepath
video = "m3v1mp4"
- video_path = os.path.join(
- basepath, "openfield-Pranav-2018-10-30", "videos", video + ".mp4"
- )
+ video_path = os.path.join(basepath, "openfield-Pranav-2018-10-30", "videos", video + ".mp4")
print("Creating project...")
- config_path = deeplabcut.create_new_project(
- TASK, SCORER, [video_path], copy_videos=True, multianimal=True
- )
+ config_path = deeplabcut.create_new_project(TASK, SCORER, [video_path], copy_videos=True, multianimal=True)
print("Project created.")
@@ -79,34 +77,23 @@
bodyparts_single,
bodyparts_multi,
) = auxfun_multianimal.extractindividualsandbodyparts(cfg)
- animals_id = [i for i in range(n_animals) for _ in bodyparts_multi] + [
- n_animals
- ] * len(bodyparts_single)
- map_ = dict(zip(range(len(animals)), animals))
+ animals_id = [i for i in range(n_animals) for _ in bodyparts_multi] + [n_animals] * len(bodyparts_single)
+ map_ = dict(zip(range(len(animals)), animals, strict=False))
individuals = [map_[ind] for ind in animals_id for _ in range(2)]
scorer = [SCORER] * len(individuals)
coords = ["x", "y"] * len(animals_id)
- bodyparts = [
- bp for _ in range(n_animals) for bp in bodyparts_multi for _ in range(2)
- ]
+ bodyparts = [bp for _ in range(n_animals) for bp in bodyparts_multi for _ in range(2)]
bodyparts += [bp for bp in bodyparts_single for _ in range(2)]
columns = pd.MultiIndex.from_arrays(
[scorer, individuals, bodyparts, coords],
names=["scorer", "individuals", "bodyparts", "coords"],
)
- index = [
- os.path.join(rel_folder, image)
- for image in auxiliaryfunctions.grab_files_in_folder(image_folder, "png")
- ]
- fake_data = np.tile(
- np.repeat(50 * np.arange(len(animals_id)) + 50, 2), (len(index), 1)
- )
+ index = [os.path.join(rel_folder, image) for image in auxiliaryfunctions.grab_files_in_folder(image_folder, "png")]
+ fake_data = np.tile(np.repeat(50 * np.arange(len(animals_id)) + 50, 2), (len(index), 1))
df = pd.DataFrame(fake_data, index=index, columns=columns)
output_path = os.path.join(image_folder, f"CollectedData_{SCORER}.csv")
df.to_csv(output_path)
- df.to_hdf(
- output_path.replace("csv", "h5"), "df_with_missing", format="table", mode="w"
- )
+ df.to_hdf(output_path.replace("csv", "h5"), key="df_with_missing", format="table", mode="w")
print("Artificial data created.")
print("Checking labels...")
@@ -114,9 +101,7 @@
print("Labels checked.")
print("Creating train dataset...")
- deeplabcut.create_multianimaltraining_dataset(
- config_path, net_type=NET, crop_size=(200, 200)
- )
+ deeplabcut.create_multianimaltraining_dataset(config_path, net_type=NET, crop_size=(200, 200))
print("Train dataset created.")
# Check the training image paths are correctly stored as arrays of strings
@@ -134,9 +119,7 @@
assert all(len(pickledata[i]["joints"]) == 3 for i in range(num_images))
print("Editing pose config...")
- model_folder = auxiliaryfunctions.get_model_folder(
- TRAIN_SIZE, 1, cfg, cfg["project_path"]
- )
+ model_folder = auxiliaryfunctions.get_model_folder(TRAIN_SIZE, 1, cfg, cfg["project_path"])
pose_config_path = os.path.join(model_folder, "train", "pose_cfg.yaml")
edits = {
"global_scale": 0.5,
@@ -191,9 +174,7 @@
print("Video created.")
print("Convert detections to tracklets...")
- deeplabcut.convert_detections2tracklets(
- config_path, [new_video_path], "mp4", track_method=TESTTRACKER
- )
+ deeplabcut.convert_detections2tracklets(config_path, [new_video_path], "mp4", track_method=TESTTRACKER)
print("Tracklets created...")
### adding it here
@@ -202,9 +183,7 @@
trainposeconfigfile,
testposeconfigfile,
snapshotfolder,
- ) = deeplabcut.return_train_network_path(
- config_path, shuffle=1, modelprefix=modelprefix, trainingsetindex=0
- )
+ ) = deeplabcut.return_train_network_path(config_path, shuffle=1, modelprefix=modelprefix, trainingsetindex=0)
print("Creating triplet dataset")
@@ -212,7 +191,7 @@
config_path,
[new_video_path],
TESTTRACKER,
- videotype="mp4",
+ video_extensions="mp4",
)
train_epochs = 10
@@ -230,9 +209,7 @@
ckpt_folder=snapshotfolder,
)
- transformer_checkpoint = os.path.join(
- snapshotfolder, f"dlc_transreid_{train_epochs}.pth"
- )
+ transformer_checkpoint = os.path.join(snapshotfolder, f"dlc_transreid_{train_epochs}.pth")
print("Stitching tracklets based on transformer")
@@ -245,9 +222,7 @@
)
print("Plotting trajectories...")
- deeplabcut.plot_trajectories(
- config_path, [new_video_path], "mp4", track_method=TESTTRACKER
- )
+ deeplabcut.plot_trajectories(config_path, [new_video_path], "mp4", track_method=TESTTRACKER)
print("Trajectory plotted.")
print("Creating labeled video...")
@@ -262,15 +237,11 @@
print("Labeled video created.")
print("Filtering predictions...")
- deeplabcut.filterpredictions(
- config_path, [new_video_path], "mp4", track_method=TESTTRACKER
- )
+ deeplabcut.filterpredictions(config_path, [new_video_path], "mp4", track_method=TESTTRACKER)
print("Predictions filtered.")
print("Extracting outlier frames...")
- deeplabcut.extract_outlier_frames(
- config_path, [new_video_path], "mp4", automatic=True, track_method=TESTTRACKER
- )
+ deeplabcut.extract_outlier_frames(config_path, [new_video_path], "mp4", automatic=True, track_method=TESTTRACKER)
print("Outlier frames extracted.")
vname = Path(new_video_path).stem
@@ -303,7 +274,7 @@
vname,
"CollectedData_" + scorer + ".h5",
),
- "df_with_missing",
+ key="df_with_missing",
format="table",
mode="w",
)
@@ -329,7 +300,7 @@
config_path,
[new_video_path],
shuffle=3,
- videotype="mp4",
+ video_extensions="mp4",
save_as_csv=True,
destfolder=DESTFOLDER,
cropping=[0, 50, 0, 50],
@@ -345,7 +316,7 @@
deeplabcut.transformer_reID(
config_path,
[new_video_path],
- videotype="mp4",
+ video_extensions="mp4",
shuffle=3,
n_tracks=n_tracks,
track_method=TESTTRACKER,
@@ -359,7 +330,7 @@
deeplabcut.create_labeled_video(
config_path,
[new_video_path],
- videotype="mp4",
+ video_extensions="mp4",
shuffle=3,
track_method="ellipse",
destfolder=DESTFOLDER,
@@ -368,7 +339,7 @@
deeplabcut.create_labeled_video(
config_path,
[new_video_path],
- videotype="mp4",
+ video_extensions="mp4",
shuffle=3,
track_method="transformer",
destfolder=DESTFOLDER,
diff --git a/examples/utils.py b/examples/utils.py
new file mode 100644
index 0000000000..9390cea392
--- /dev/null
+++ b/examples/utils.py
@@ -0,0 +1,438 @@
+#
+# 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
+#
+from __future__ import annotations
+
+import shutil
+import string
+import time
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Any
+
+import matplotlib
+
+matplotlib.use("Agg") # Non-interactive backend, for CI/CD on Windows
+
+import cv2
+import numpy as np
+import pandas as pd
+from PIL import Image
+
+import deeplabcut
+import deeplabcut.utils.auxiliaryfunctions as af
+from deeplabcut.compat import Engine
+from deeplabcut.generate_training_dataset import get_existing_shuffle_indices
+
+
+def log_step(message: Any) -> None:
+ print(100 * "-")
+ print(str(message))
+ print(100 * "-")
+
+
+def cleanup(test_path: Path) -> None:
+ if test_path.exists():
+ shutil.rmtree(test_path)
+
+
+@dataclass(frozen=True)
+class SyntheticProjectParameters:
+ multianimal: bool
+ num_bodyparts: int
+ num_frames: int = 10
+ num_individuals: int = 1
+ num_unique: int = 0
+ identity: bool = False
+ frame_shape: tuple[int, int] = (480, 640)
+
+ def bodyparts(self) -> list[str]:
+ return [i for i in string.ascii_lowercase[: self.num_bodyparts]]
+
+ def unique(self) -> list[str]:
+ return [f"unique_{i}" for i in string.ascii_lowercase[: self.num_unique]]
+
+ def individuals(self) -> list[str]:
+ return [f"animal_{i}" for i in range(self.num_individuals)]
+
+
+def sample_pose_random(
+ gen: np.random.Generator,
+ num_individuals: int,
+ num_bodyparts: int,
+ num_unique: int,
+ img_h: int,
+ img_w: int,
+) -> np.ndarray:
+ """Fully random pose sampling."""
+ xs = gen.choice(img_w, size=(num_individuals, num_bodyparts), replace=False)
+ ys = gen.choice(img_h, size=(num_individuals, num_bodyparts), replace=False)
+ pose = np.stack([xs, ys], axis=-1)
+
+ image_data = pose.reshape(-1)
+ if num_unique > 0:
+ unique_pose = np.stack(
+ [
+ gen.choice(img_w, size=(1, num_unique), replace=False),
+ gen.choice(img_h, size=(1, num_unique), replace=False),
+ ],
+ axis=-1,
+ )
+ image_data = np.concatenate([image_data, unique_pose.reshape(-1)])
+ return image_data
+
+
+def sample_pose_from_center(
+ center_xs: np.ndarray,
+ center_ys: np.ndarray,
+ num_individuals: int,
+ num_bodyparts: int,
+ num_unique: int,
+ radius: int = 25,
+) -> np.ndarray:
+ """Sample keypoints from the center of each individual."""
+ pose = np.zeros((num_individuals, num_bodyparts, 2))
+ for i, (xc, yc) in enumerate(zip(center_xs, center_ys, strict=False)):
+ if i < num_individuals:
+ x_start, x_end = xc - radius + 1, xc + radius - 1
+ y_start, y_end = yc - radius + 1, yc + radius - 1
+ pose[i, :, 0] = np.linspace(start=x_start, stop=x_end, num=num_bodyparts)
+ pose[i, :, 1] = np.linspace(start=y_start, stop=y_end, num=num_bodyparts)
+
+ image_data = pose.reshape(-1)
+ if num_unique > 0:
+ xc, yc = center_xs[-1], center_ys[-1]
+ x_start, x_end = xc - radius + 1, xc + radius - 1
+ y_start, y_end = yc - radius + 1, yc + radius - 1
+ unique_pose = np.zeros((1, num_unique, 2))
+ unique_pose[0, :, 0] = np.linspace(start=x_start, stop=x_end, num=num_unique)
+ unique_pose[0, :, 1] = np.linspace(start=y_start, stop=y_end, num=num_unique)
+ image_data = np.concatenate([image_data, unique_pose.reshape(-1)])
+ return image_data
+
+
+def gen_fake_data(
+ scorer: str,
+ video_name: str,
+ params: SyntheticProjectParameters,
+) -> pd.DataFrame:
+ kpt_entries = ["x", "y"]
+ col_names = ["scorer", "individuals", "bodyparts", "coords"]
+ col_values = []
+ for i in params.individuals():
+ for b in params.bodyparts():
+ col_values += [(scorer, i, b, entry) for entry in kpt_entries]
+
+ for unique_bpt in params.unique():
+ col_values += [(scorer, "single", unique_bpt, entry) for entry in kpt_entries]
+
+ index_data = []
+ pose_data = []
+ gen = np.random.default_rng(seed=0)
+
+ # sample starting points for each individual
+ img_h, img_w = params.frame_shape[:2]
+ radius = 8
+ center_xs = gen.choice(
+ np.arange(radius, img_w - radius),
+ size=params.num_individuals + 1, # in case unique bodyparts
+ replace=False,
+ )
+ center_ys = gen.choice(
+ np.arange(radius, img_h - radius),
+ size=params.num_individuals + 1, # in case unique bodyparts
+ replace=False,
+ )
+
+ for frame_index in range(params.num_frames):
+ index_data.append(("labeled-data", video_name, f"img{frame_index:04}.png"))
+ pose_data.append(
+ sample_pose_from_center(
+ center_xs,
+ center_ys,
+ num_individuals=params.num_individuals,
+ num_bodyparts=params.num_bodyparts,
+ num_unique=params.num_unique,
+ radius=radius,
+ )
+ )
+ mvt_x = gen.integers(low=-1, high=4, size=center_xs.size)
+ mvt_y = gen.integers(low=-1, high=4, size=center_ys.size)
+ center_xs = np.clip(center_xs + mvt_x, radius, img_w - radius)
+ center_ys = np.clip(center_ys + mvt_y, radius, img_h - radius)
+
+ pose = np.stack(pose_data)
+ pose[params.num_frames // 2, :] = np.nan # add missing row in a frame
+ for idv in range(params.num_individuals):
+ idv_start = 2 * params.num_bodyparts * idv
+ idv_end = 2 * params.num_bodyparts * (idv + 1)
+ if params.num_frames > idv + 1:
+ pose[idv + 1, idv_start:idv_end] = np.nan
+
+ for bpt in range(params.num_bodyparts):
+ frame_idx = 1 + params.num_individuals + bpt
+ idv_idx = bpt % params.num_individuals
+ offset = 2 * params.num_bodyparts * idv_idx
+ bpt_start, bpt_end = 2 * bpt + offset, 2 * (bpt + 1) + offset
+ if params.num_frames + 1 > frame_idx:
+ pose[frame_idx, bpt_start:bpt_end] = np.nan
+
+ return pd.DataFrame(
+ pose,
+ index=pd.MultiIndex.from_tuples(index_data),
+ columns=pd.MultiIndex.from_tuples(col_values, names=col_names),
+ )
+
+
+def gen_fake_image(
+ project_root: Path,
+ row: pd.Series,
+ params: SyntheticProjectParameters,
+ radius: int = 5,
+):
+ img_h, img_w = params.frame_shape
+ image_array = np.zeros((*params.frame_shape, 3), dtype=np.uint8)
+ for i, idv in enumerate(params.individuals()):
+ r = int(255 * (i + 1) / params.num_individuals)
+ if "individuals" in row.index.names:
+ idv_data = row.droplevel("scorer").loc[idv]
+ else:
+ idv_data = row.droplevel("scorer")
+
+ keypoints = idv_data.to_numpy().reshape((-1, 2))
+ if not np.all(np.isnan(keypoints)):
+ idv_center = np.nanmean(keypoints, axis=0)
+ x, y = int(idv_center[0]), int(idv_center[1])
+ xmin, xmax = max(0, x - radius), min(img_w - 1, x + radius)
+ ymin, ymax = max(0, y - radius), min(img_h - 1, y + radius)
+ image_array[ymin:ymax, xmin:xmax, 0] = r
+
+ for j, bpt in enumerate(params.bodyparts()):
+ g = int(255 * (j + 1) / params.num_bodyparts)
+
+ bpt_data = idv_data.loc[bpt]
+ if np.all(~pd.isnull(bpt_data)):
+ x, y = int(bpt_data.x), int(bpt_data.y)
+ xmin, xmax = max(0, x - radius), min(img_w - 1, x + radius)
+ ymin, ymax = max(0, y - radius), min(img_h - 1, y + radius)
+ image_array[ymin:ymax, xmin:xmax, 0] = r
+ image_array[ymin:ymax, xmin:xmax, 1] = g
+
+ if params.num_unique > 0:
+ unique_data = row.droplevel("scorer").loc["single"]
+ for i, unique_bpt in enumerate(params.unique()):
+ bpt_data = unique_data.loc[unique_bpt]
+ if np.all(~pd.isnull(bpt_data)):
+ x, y = int(bpt_data.x), int(bpt_data.y)
+ xmin, xmax = max(0, x - radius), min(img_w - 1, x + radius)
+ ymin, ymax = max(0, y - radius), min(img_h - 1, y + radius)
+ image_array[ymin:ymax, xmin:xmax, 2] = int(255 * (i + 1) / params.num_unique)
+
+ img = Image.fromarray(image_array)
+ img.save(project_root / Path(*row.name))
+
+
+def generate_video_from_images(image_dir: Path, output_video: Path) -> None:
+ images = [p for p in image_dir.iterdir() if p.is_file() and p.suffix == ".png"]
+ images = sorted(images, key=lambda f: f.stem)
+ if len(images) == 0:
+ return
+
+ height, width, channels = cv2.imread(str(images[0])).shape
+ fourcc = cv2.VideoWriter_fourcc(*"MJPG")
+ out = cv2.VideoWriter(str(output_video), fourcc, 10, (width, height))
+ for img_path in images:
+ img = cv2.imread(str(img_path))
+ out.write(img)
+ out.release()
+
+
+def create_fake_project(path: Path, params: SyntheticProjectParameters) -> None:
+ if path.exists():
+ raise ValueError("Cannot create a fake project at an existing path")
+
+ scorer = "synthetic"
+ video_name = "cat"
+ path.mkdir(parents=True, exist_ok=False)
+ config = {
+ "Task": "synthetic",
+ "scorer": scorer,
+ "date": "Nov11",
+ "multianimalproject": params.multianimal,
+ "identity": params.identity,
+ "project_path": str(path / "config.yaml"),
+ "TrainingFraction": [0.8],
+ "iteration": 0,
+ "default_net_type": "resnet_50",
+ "default_augmenter": "default",
+ "default_track_method": "ellipse",
+ "snapshotindex": "all",
+ "batch_size": 8,
+ "pcutoff": 0.6,
+ "video_sets": {
+ str(path / "videos" / video_name): {
+ "crop": (0, params.frame_shape[1], 0, params.frame_shape[0]),
+ },
+ },
+ "start": 0,
+ "stop": 1,
+ "numframes2pick": 10,
+ "dotsize": 4,
+ "alphavalue": 1.0,
+ "colormap": "rainbow",
+ }
+ if not params.multianimal:
+ config["bodyparts"] = params.bodyparts()
+ assert params.num_individuals == 1
+ assert params.num_unique == 0
+ else:
+ config["bodyparts"] = "MULTI!"
+ config["multianimalbodyparts"] = params.bodyparts()
+ config["uniquebodyparts"] = params.unique()
+ config["individuals"] = params.individuals()
+
+ af.write_config(str(path / "config.yaml"), config)
+ image_dir = path / "labeled-data" / video_name
+ image_dir.mkdir(parents=True, exist_ok=False)
+
+ df = gen_fake_data(
+ scorer=scorer,
+ video_name=video_name,
+ params=params,
+ )
+ print("SYNTHETIC DATA:")
+ print(df)
+ print("\n")
+ if not params.multianimal:
+ df.columns = df.columns.droplevel("individuals")
+
+ df.to_hdf(image_dir / f"CollectedData_{scorer}.h5", key="df_with_missing")
+ df.to_csv(image_dir / f"CollectedData_{scorer}.csv")
+
+ for idx in range(params.num_frames):
+ gen_fake_image(path, df.iloc[idx], params=params, radius=5)
+
+ output_video = path / "videos" / "video.mp4"
+ output_video.parent.mkdir(exist_ok=True)
+ generate_video_from_images(image_dir, output_video)
+
+
+def copy_project_for_test() -> Path:
+ data_path = Path.cwd() / "openfield-Pranav-2018-10-30"
+ test_path = Path.cwd() / "pytorch-testscript1234-openfield-Pranav-2018-10-30"
+ if not test_path.exists():
+ shutil.copytree(data_path, test_path)
+
+ project_config = af.read_config(str(test_path / "config.yaml"))
+ videos = list(project_config["video_sets"].keys())
+ video = videos[0]
+ crop = project_config["video_sets"][video]
+ project_config["video_sets"] = {str(test_path / "videos" / "m3v1mp4.mp4"): crop}
+ af.write_config(str(test_path / "config.yaml"), project_config)
+ return test_path
+
+
+def run(
+ config_path: Path,
+ train_fraction: float,
+ trainset_index: int,
+ net_type: str,
+ videos: list[str],
+ device: str,
+ engine: Engine = Engine.PYTORCH,
+ pytorch_cfg_updates: dict | None = None,
+ create_labeled_videos: bool = False,
+) -> None:
+ times = [time.time()]
+ log_step(f"Testing with net type {net_type}")
+ log_step("Creating the training dataset")
+ deeplabcut.create_training_dataset(str(config_path), net_type=net_type, engine=engine)
+ existing_shuffles = get_existing_shuffle_indices(config_path, train_fraction=train_fraction, engine=engine)
+ shuffle_index = existing_shuffles[-1]
+
+ log_step(f"Starting training for train_frac {train_fraction}, shuffle {shuffle_index}")
+ deeplabcut.train_network(
+ config=str(config_path),
+ shuffle=shuffle_index,
+ trainingsetindex=trainset_index,
+ device=device,
+ pytorch_cfg_updates=pytorch_cfg_updates,
+ )
+ times.append(time.time())
+ log_step(f"Train time: {times[-1] - times[-2]} seconds")
+
+ log_step(f"Starting evaluation for train_frac {train_fraction}, shuffle {shuffle_index}")
+ deeplabcut.evaluate_network(
+ config=str(config_path),
+ Shuffles=[shuffle_index],
+ trainingsetindex=trainset_index,
+ device=device,
+ plotting=True,
+ per_keypoint_evaluation=True,
+ )
+ times.append(time.time())
+ log_step(f"Evaluation time: {times[-1] - times[-2]} seconds")
+
+ if len(videos) > 0:
+ log_step(f"Analyzing videos for {train_fraction}, shuffle {shuffle_index}")
+ video_kwargs = dict(videos=videos, shuffle=shuffle_index, trainingsetindex=trainset_index)
+ deeplabcut.analyze_videos(str(config_path), **video_kwargs, device=device, auto_track=False)
+ times.append(time.time())
+ log_step(f"Video analysis time: {times[-1] - times[-2]} seconds")
+ log_step(f"Total test time: {times[-1] - times[0]} seconds")
+
+ cfg = af.read_config(config_path)
+ if cfg.get("multianimalproject"):
+ if create_labeled_videos:
+ deeplabcut.create_video_with_all_detections(str(config_path), **video_kwargs)
+
+ # relaxed tracking parameters
+ deeplabcut.convert_detections2tracklets(
+ str(config_path),
+ **video_kwargs,
+ inferencecfg=dict(
+ boundingboxslack=10,
+ iou_threshold=0.2,
+ max_age=5,
+ method="m1",
+ min_hits=1,
+ minimalnumberofconnections=2,
+ pafthreshold=0.1,
+ pcutoff=0.1,
+ topktoretain=3,
+ variant=0,
+ withid=False,
+ ),
+ )
+ deeplabcut.stitch_tracklets(str(config_path), **video_kwargs, min_length=3)
+
+ if create_labeled_videos:
+ log_step(f"Making labeled video, {train_fraction}, shuffle={shuffle_index}")
+ results = deeplabcut.create_labeled_video(
+ config=str(config_path),
+ videos=videos,
+ shuffle=shuffle_index,
+ trainingsetindex=trainset_index,
+ )
+ assert all(results), f"Failed to create some labeled video for {videos}"
+
+
+if __name__ == "__main__":
+ create_fake_project(
+ path=Path("synthetic-data-niels"),
+ params=SyntheticProjectParameters(
+ multianimal=True,
+ num_bodyparts=4,
+ num_individuals=3,
+ num_unique=1,
+ num_frames=50,
+ frame_shape=(128, 256),
+ ),
+ )
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000..a578f17dd9
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,218 @@
+[build-system]
+build-backend = "setuptools.build_meta"
+requires = [ "setuptools>=61" ]
+
+[project]
+name = "deeplabcut"
+version = "3.0.0"
+description = "Markerless pose-estimation of user-defined features with deep learning"
+readme = { file = "README.md", content-type = "text/markdown" }
+keywords = [
+ "animal behavior",
+ "markerless tracking",
+ "neuroscience",
+ "pose estimation",
+]
+license = { text = "LGPL-3.0-or-later" }
+requires-python = ">=3.10"
+classifiers = [
+ "Intended Audience :: Science/Research",
+ "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
+ "Natural Language :: English",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python :: 3 :: Only",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Topic :: Scientific/Engineering",
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
+ "Topic :: Scientific/Engineering :: Image Processing",
+]
+dependencies = [
+ "albumentations<=1.4.3",
+ "dlclibrary>=0.0.12",
+ "einops",
+ "filelock>=3.12,<3.16",
+ "filterpy>=1.4.4",
+ "h5py>=3.15.1; platform_system=='Darwin'",
+ "huggingface-hub>=0.23",
+ "imageio-ffmpeg",
+ "imgaug>=0.4",
+ "matplotlib>=3.3,<3.9,!=3.7,!=3.7.1",
+ "networkx>=2.6",
+ "numba>=0.54",
+ "numpy>=1.18.5,<2",
+ "packaging>=26",
+ "pandas[hdf5,performance]>=2.2,<3",
+ "pillow>=7.1",
+ "pycocotools",
+ "pydantic>=2,<3",
+ "pyyaml",
+ "ruamel-yaml>=0.15",
+ "scikit-image>=0.17",
+ "scikit-learn>=1",
+ "scipy>=1.9",
+ "statsmodels>=0.11",
+ "tables>3.8",
+ "timm",
+ "torch>=2",
+ "torchvision",
+ "tqdm",
+]
+[[project.authors]]
+name = "M-Lab of Adaptive Intelligence"
+email = "mackenzie@deeplabcut.org"
+[[project.authors]]
+name = "Mathis Group for Computational Neuroscience and AI"
+email = "alexander@deeplabcut.org"
+[project.optional-dependencies]
+gui = [
+ "napari-deeplabcut>=0.3.1",
+ "pyside6; platform_system!='Linux' or platform_machine!='x86_64'",
+ # Avoid 6.10.0 only on Linux x86_64 (fails for older glib versions)
+ "pyside6<6.10; platform_system=='Linux' and platform_machine=='x86_64'",
+ "qdarkstyle==3.1",
+]
+openvino = [ "openvino-dev==2022.1" ]
+docs = [
+ "jupyter-book==1.0.4.post1",
+ "numpydoc",
+ "sphinxcontrib-mermaid",
+]
+fmpose3d = [ "fmpose3d>=0.0.8" ]
+# Use only one of [tf, tf-cu11, tf-cu12, tf-latest]. Do not combine extras.
+tf = [
+ "protobuf<7",
+ "tensorflow>=2.12,<2.16; python_version<'3.12'",
+ "tensorflow>=2.16.1,<2.18; python_version>='3.12'",
+ "tensorflow-io-gcs-filesystem==0.31; platform_system=='Windows' and python_version<'3.12'",
+ "tensorflow-metal==1.2; platform_system=='Darwin' and python_version<'3.12'",
+ "tensorflow-metal>=1.2; platform_system=='Darwin' and python_version>='3.12'",
+ "tensorpack>=0.11",
+ "tf-keras<2.15; python_version<'3.12'",
+ "tf-keras>=2.15,<2.18; python_version>='3.12'",
+ "tf-slim>=1.1",
+]
+tf-cu11 = [
+ "protobuf<7",
+ "tensorflow==2.14",
+ "tensorflow-io-gcs-filesystem==0.31; platform_system=='Windows'",
+ "tensorflow-metal==1.2; platform_system=='Darwin'",
+ "tensorpack==0.11",
+ "tf-keras==2.14.1",
+ "tf-slim==1.1",
+ "torch<2.1",
+ "torchvision<0.16",
+]
+tf-cu12 = [
+ "protobuf<7",
+ "tensorflow==2.18",
+ "tensorflow-metal==1.2; platform_system=='Darwin'",
+ "tensorpack==0.11",
+ "tf-keras==2.18",
+ "tf-slim==1.1",
+ "torch<2.11",
+ "torchvision<0.26",
+]
+tf-latest = [
+ "protobuf<7",
+ "tensorflow>=2.18",
+ "tensorflow-metal>=1.2; platform_system=='Darwin'",
+ "tensorpack>=0.11",
+ "tf-keras",
+ "tf-slim>=1.1",
+]
+# apple_mchips is kept for older systems, prefer [tf] in new projects.
+apple_mchips = [
+ "protobuf<7; platform_system=='Darwin'",
+ "tensorflow>=2.12,<2.15; platform_system=='Darwin' and python_version<'3.12'",
+ "tensorflow>=2.15,<2.18; platform_system=='Darwin' and python_version>='3.12'",
+ "tensorflow-metal==1.2; platform_system=='Darwin' and python_version<'3.12'",
+ "tensorflow-metal>=1.2; platform_system=='Darwin' and python_version>='3.12'",
+ "tensorpack>=0.11; platform_system=='Darwin'",
+ "tf-keras; platform_system=='Darwin'",
+ "tf-slim>=1.1; platform_system=='Darwin'",
+]
+modelzoo = [ "huggingface-hub" ]
+wandb = [ "wandb" ]
+[project.scripts]
+dlc = "deeplabcut.__main__:main"
+[project.urls]
+Homepage = "https://www.deeplabcut.org"
+Repository = "https://github.com/DeepLabCut/DeepLabCut"
+Documentation = "https://deeplabcut.github.io/DeepLabCut/README.html"
+
+[dependency-groups]
+dev = [
+ "coverage",
+ "nbformat>5",
+ "pre-commit",
+ "pytest",
+ "pytest-cov",
+ "ruff",
+]
+
+[tool.setuptools]
+include-package-data = false
+[tool.setuptools.package-data]
+"*" = [ "*.yaml", "*.yml", "*.json", "*.qss", "*.png", "*.md", "*.sh" ]
+[tool.setuptools.packages.find]
+include = [ "deeplabcut*" ]
+exclude = [ "tests*", "docs*", "examples*" ]
+
+[tool.uv]
+# One of tf / tf-cu12 / tf-latest. apple_mchips matches [tf] on macOS but conflicts with
+# [tf-cu12] and [tf-latest] (overlapping tensorflow pins cannot be unified with uv's lock).
+conflicts = [
+ [
+ { extra = "tf" },
+ { extra = "tf-cu11" },
+ { extra = "tf-cu12" },
+ { extra = "tf-latest" },
+ { extra = "apple_mchips" },
+ ],
+ [
+ { extra = "tf-cu11" },
+ { extra = "tf-cu12" },
+ { extra = "fmpose3d" },
+ ],
+]
+[[tool.uv.dependency-metadata]]
+name = "openvino-dev"
+version = "2022.1.0"
+requires-dist = []
+[tool.uv.pip]
+torch-backend = "auto"
+
+[tool.ruff]
+target-version = "py310"
+line-length = 120
+fix = true
+[tool.ruff.lint]
+select = [ "E", "F", "B", "I", "UP" ]
+ignore = [ "E741", "B007" ]
+[tool.ruff.lint.per-file-ignores]
+"__init__.py" = [ "F401", "E402" ]
+"deeplabcut/**/__init__.py" = [ "F403" ]
+"deeplabcut/gui/window.py" = [ "F403" ]
+"deeplabcut/pose_estimation_tensorflow/lib/crossvalutils.py" = [ "F403" ]
+"deeplabcut/pose_estimation_tensorflow/lib/inferenceutils.py" = [ "F403" ]
+"deeplabcut/pose_estimation_tensorflow/lib/trackingutils.py" = [ "F403" ]
+"*.ipynb" = [ "E402" ]
+[tool.ruff.lint.pydocstyle]
+convention = "google"
+
+[tool.pyproject-fmt]
+max_supported_python = "3.12"
+generate_python_version_classifiers = true
+# Avoid collapsing tables to field.key = value format (less readable)
+table_format = "long"
+
+[tool.pytest.ini_options]
+markers = [
+ "require_models: mark test as requiring models to run",
+ "fmpose3d: tests for fmpose3d integration",
+ "unittest: fast unit-level tests",
+ "functional: functional/integration-style tests",
+ "deprecated: tests for deprecated APIs kept for backward-compatibility",
+]
diff --git a/reinstall.sh b/reinstall.sh
index 7286aaae30..ec60dc6a8e 100755
--- a/reinstall.sh
+++ b/reinstall.sh
@@ -1,3 +1,4 @@
pip uninstall deeplabcut
+rm -rf dist/ build/ *.egg-info
python3 setup.py sdist bdist_wheel
-pip install dist/deeplabcut-2.3.5-py3-none-any.whl
+pip install dist/deeplabcut-3.0.0-py3-none-any.whl
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 653a6813a7..0000000000
--- a/requirements.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-dlclibrary
-ipython
-filterpy
-ruamel.yaml>=0.15.0
-intel-openmp
-imageio-ffmpeg
-imgaug==0.4.0
-numba>=0.54.0
-matplotlib<=3.5.2
-networkx>=2.6
-numpy>=1.18.5
-pandas>=1.0.1,!=1.5.0
-pyyaml
-scikit-image>=0.17
-scikit-learn>=1.0
-scipy>=1.4
-statsmodels>=0.11
-tensorflow>=2.0
-tables==3.7.0
-tensorpack==0.11
-tf_slim==1.1.0
-torch==1.12
-tqdm
-Pillow>=7.1
diff --git a/setup.py b/setup.py
index 97ad54c3fa..c3e301e85e 100644
--- a/setup.py
+++ b/setup.py
@@ -1,100 +1,12 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-DeepLabCut2.0-2.2 Toolbox (deeplabcut.org)
-© A. & M. Mathis Labs
-https://github.com/DeepLabCut/DeepLabCut
-Please see AUTHORS for contributors.
-https://github.com/DeepLabCut/DeepLabCut/blob/master/AUTHORS
+"""DeepLabCut2.0-3.0 Toolbox (deeplabcut.org) © A.
+
+& M. 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 setuptools
-
-with open("README.md", encoding="utf-8", errors="replace") as fh:
- long_description = fh.read()
-
-
-setuptools.setup(
- name="deeplabcut",
- version="2.3.5",
- author="A. & M. Mathis Labs",
- author_email="alexander@deeplabcut.org",
- description="Markerless pose-estimation of user-defined features with deep learning",
- long_description=long_description,
- long_description_content_type="text/markdown",
- url="https://github.com/DeepLabCut/DeepLabCut",
- install_requires=[
- "dlclibrary",
- "filterpy>=1.4.4",
- "ruamel.yaml>=0.15.0",
- "imgaug>=0.4.0",
- "imageio-ffmpeg",
- "numba>=0.54",
- "matplotlib>=3.3",
- "networkx>=2.6",
- "numpy>=1.18.5",
- "pandas>=1.0.1,!=1.5.0",
- "scikit-image>=0.17",
- "scikit-learn>=1.0",
- "scipy>=1.4",
- "statsmodels>=0.11",
- "tables>=3.7.0",
- "torch<=1.12",
- "tensorpack>=0.11",
- "tf_slim>=1.1.0",
- "tqdm",
- "pyyaml",
- "Pillow>=7.1",
- ],
- extras_require={
- "gui": [
- "pyside6<6.3.2",
- "qdarkstyle==3.1",
- "napari-deeplabcut>=0.0.9",
- ],
- "openvino": ["openvino-dev==2022.1.0"],
- "docs": ["numpydoc"],
- "tf": ["tensorflow>=2.0,<=2.10"], # Last supported TF version on Windows Native is 2.10
- "apple_mchips": ["tensorflow-macos","tensorflow-metal"],
- "modelzoo": ["huggingface_hub"],
- },
- scripts=["deeplabcut/pose_estimation_tensorflow/models/pretrained/download.sh"],
- packages=setuptools.find_packages(),
- data_files=[
- (
- "deeplabcut",
- [
- "deeplabcut/pose_cfg.yaml",
- "deeplabcut/inference_cfg.yaml",
- "deeplabcut/reid_cfg.yaml",
- "deeplabcut/pose_estimation_tensorflow/models/pretrained/pretrained_model_urls.yaml",
- "deeplabcut/pose_estimation_tensorflow/superanimal_configs/superquadruped.yaml",
- "deeplabcut/pose_estimation_tensorflow/superanimal_configs/supertopview.yaml",
- "deeplabcut/gui/style.qss",
- "deeplabcut/gui/media/logo.png",
- "deeplabcut/gui/media/dlc_1-01.png",
- "deeplabcut/gui/assets/logo.png",
- "deeplabcut/gui/assets/logo_transparent.png",
- "deeplabcut/gui/assets/welcome.png",
- "deeplabcut/gui/assets/icons/help.png",
- "deeplabcut/gui/assets/icons/help2.png",
- "deeplabcut/gui/assets/icons/new_project.png",
- "deeplabcut/gui/assets/icons/new_project2.png",
- "deeplabcut/gui/assets/icons/open.png",
- "deeplabcut/gui/assets/icons/open2.png",
- "deeplabcut/modelzoo/models.json",
- ],
- )
- ],
- include_package_data=True,
- classifiers=[
- "Programming Language :: Python :: 3",
- "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
- "Operating System :: OS Independent",
- ],
- entry_points="""[console_scripts]
- dlc=dlc:main""",
-)
+from setuptools import setup
-# https://www.python.org/dev/peps/pep-0440/#compatible-release
+# All configuration is now in pyproject.toml. This file is kept for backward compatibility
+setup()
diff --git a/tests/conftest.py b/tests/conftest.py
index 1e67d0ca9f..30bd45364d 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -8,23 +8,34 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
-import numpy as np
+
import os
import pickle
-import pytest
-import shutil
import urllib.request
import zipfile
-from deeplabcut.pose_estimation_tensorflow.lib import inferenceutils
from io import BytesIO
+
+import numpy as np
+import pytest
from PIL import Image
from tqdm import tqdm
+from deeplabcut.core import inferenceutils
-TEST_DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data")
+TESTS_DIR = os.path.dirname(os.path.realpath(__file__))
+TEST_DATA_DIR = os.path.join(TESTS_DIR, "data")
+REQUIRED_TEST_FILES = [
+ os.path.join(TEST_DATA_DIR, "dets.pickle"),
+ os.path.join(TEST_DATA_DIR, "outputs.pickle"),
+ os.path.join(TEST_DATA_DIR, "image.png"),
+ os.path.join(TEST_DATA_DIR, "trimouse_assemblies.pickle"),
+ os.path.join(TEST_DATA_DIR, "montblanc_tracks.h5"),
+ os.path.join(TEST_DATA_DIR, "trimouse_calib.h5"),
+]
-def unzip_from_url(url, dest_folder):
+
+def unzip_from_url(url: str, dest_folder: str) -> None:
"""Directly extract files without writing the archive to disk."""
os.makedirs(dest_folder, exist_ok=True)
resp = urllib.request.urlopen(url)
@@ -36,16 +47,23 @@ def unzip_from_url(url, dest_folder):
pass
-def pytest_sessionstart(session):
- unzip_from_url(
- "https://github.com/DeepLabCut/UnitTestData/raw/main/data.zip",
- os.path.split(TEST_DATA_DIR)[0],
- )
- session.__DATA_FOLDER = TEST_DATA_DIR
+def _test_data_ready() -> bool:
+ return all(os.path.exists(path) for path in REQUIRED_TEST_FILES)
+
+@pytest.fixture(scope="session", autouse=True)
+def ensure_test_data():
+ """Ensure shared test data exists once per pytest session.
-def pytest_sessionfinish(session, exitstatus):
- shutil.rmtree(session.__DATA_FOLDER)
+ This is autouse so tests that directly open files under tests/data/
+ keep working without being rewritten.
+ """
+ if not _test_data_ready():
+ unzip_from_url(
+ "https://github.com/DeepLabCut/UnitTestData/raw/main/data.zip",
+ TESTS_DIR,
+ )
+ yield
@pytest.fixture(scope="function")
diff --git a/tests/core/debug/test_debug_logger.py b/tests/core/debug/test_debug_logger.py
new file mode 100644
index 0000000000..c00a4566b6
--- /dev/null
+++ b/tests/core/debug/test_debug_logger.py
@@ -0,0 +1,483 @@
+from __future__ import annotations
+
+import logging
+from uuid import uuid4
+
+import pytest
+
+import deeplabcut.core.debug.debug_logger as debug_mod
+from deeplabcut.core.debug import (
+ DebugSection,
+ ExecutableSpec,
+ InMemoryDebugRecorder,
+ LibrarySpec,
+ build_debug_report,
+ collect_executable_summary,
+ collect_version_summary,
+ format_debug_report,
+ get_debug_recorder,
+ install_debug_recorder,
+ log_timing,
+)
+
+
+@pytest.fixture
+def logger_name() -> str:
+ return f"deeplabcut.tests.debug.{uuid4()}"
+
+
+@pytest.fixture
+def clean_logger(logger_name: str):
+ """Create an isolated logger namespace and fully clean it afterwards."""
+ logger = logging.getLogger(logger_name)
+ old_level = logger.level
+ old_propagate = logger.propagate
+ old_handlers = list(logger.handlers)
+
+ logger.setLevel(logging.DEBUG)
+ logger.propagate = False
+
+ yield logger
+
+ for handler in list(logger.handlers):
+ logger.removeHandler(handler)
+ try:
+ handler.close()
+ except Exception:
+ pass
+
+ for handler in old_handlers:
+ logger.addHandler(handler)
+
+ logger.setLevel(old_level)
+ logger.propagate = old_propagate
+
+ # Remove recorder marker installed by install_debug_recorder().
+ logger.__dict__.pop("_dlc_debug_recorder", None)
+
+
+def test_install_debug_recorder_is_idempotent(logger_name: str, clean_logger):
+ recorder1 = install_debug_recorder(logger_name=logger_name, capacity=10)
+ recorder2 = install_debug_recorder(logger_name=logger_name, capacity=99)
+
+ assert recorder1 is recorder2
+ assert isinstance(recorder1, InMemoryDebugRecorder)
+ assert get_debug_recorder(logger_name=logger_name) is recorder1
+
+
+def test_recorder_captures_messages_and_exceptions(logger_name: str, clean_logger):
+ logger = clean_logger
+ recorder = install_debug_recorder(logger_name=logger_name, capacity=10, handler_level=logging.DEBUG)
+
+ logger.info("hello %s", "dlc")
+ try:
+ raise ValueError("boom")
+ except ValueError:
+ logger.exception("something failed")
+
+ records = recorder.snapshot()
+
+ assert len(records) == 2
+ assert records[0].message == "hello dlc"
+ assert records[0].level == "INFO"
+ assert records[1].message == "something failed"
+ assert records[1].level == "ERROR"
+ assert records[1].exc_text is not None
+ assert "ValueError: boom" in records[1].exc_text
+
+
+def test_recorder_is_bounded(logger_name: str, clean_logger):
+ logger = clean_logger
+ recorder = install_debug_recorder(logger_name=logger_name, capacity=2, handler_level=logging.DEBUG)
+
+ logger.debug("first")
+ logger.debug("second")
+ logger.debug("third")
+
+ messages = [rec.message for rec in recorder.snapshot()]
+ assert messages == ["second", "third"]
+
+
+def test_render_text_contains_recent_messages(logger_name: str, clean_logger):
+ logger = clean_logger
+ recorder = install_debug_recorder(logger_name=logger_name, capacity=5)
+
+ logger.warning("alpha")
+ logger.error("beta")
+
+ text = recorder.render_text(limit=10)
+
+ assert "WARNING" in text
+ assert "ERROR" in text
+ assert "alpha" in text
+ assert "beta" in text
+ assert logger_name in text
+
+
+def test_clear_resets_records_and_drop_count(logger_name: str, clean_logger):
+ logger = clean_logger
+ recorder = install_debug_recorder(logger_name=logger_name, capacity=5)
+
+ logger.info("before clear")
+ assert recorder.snapshot()
+
+ recorder.clear()
+
+ assert recorder.snapshot() == []
+ assert recorder.dropped_count == 0
+ assert recorder.render_text() == ""
+
+
+def test_log_timing_emits_when_enabled(
+ monkeypatch: pytest.MonkeyPatch,
+ logger_name: str,
+ clean_logger,
+):
+ logger = clean_logger
+ calls: list[tuple[int, str, tuple[object, ...]]] = []
+
+ wrapped = log_timing.__wrapped__
+
+ monkeypatch.setitem(wrapped.__globals__, "DLC_LOG_TIMING", True)
+
+ ticks = iter([1_000_000_000, 1_005_000_000]) # 5.000 ms
+ monkeypatch.setitem(wrapped.__globals__, "perf_counter_ns", lambda: next(ticks))
+
+ monkeypatch.setattr(logger, "isEnabledFor", lambda level: True)
+
+ def fake_log(level, msg, *args):
+ calls.append((level, msg, args))
+
+ monkeypatch.setattr(logger, "log", fake_log)
+
+ with log_timing(logger, "tiny-step", threshold_ms=0.0):
+ pass
+
+ assert calls == [
+ (logging.DEBUG, "%s took %.3f ms", ("tiny-step", 5.0)),
+ ]
+
+
+def test_log_timing_is_silent_when_disabled(
+ monkeypatch: pytest.MonkeyPatch,
+ logger_name: str,
+ clean_logger,
+):
+ logger = clean_logger
+ calls: list[tuple[int, str, tuple[object, ...]]] = []
+
+ wrapped = log_timing.__wrapped__
+
+ monkeypatch.setitem(wrapped.__globals__, "DLC_LOG_TIMING", False)
+ monkeypatch.setattr(logger, "isEnabledFor", lambda level: True)
+
+ def fake_log(level, msg, *args):
+ calls.append((level, msg, args))
+
+ monkeypatch.setattr(logger, "log", fake_log)
+
+ with log_timing(logger, "should-not-appear", threshold_ms=0.0):
+ pass
+
+ assert calls == []
+
+
+def test_log_timing_respects_threshold(
+ monkeypatch: pytest.MonkeyPatch,
+ logger_name: str,
+ clean_logger,
+):
+ logger = clean_logger
+ calls: list[tuple[int, str, tuple[object, ...]]] = []
+
+ wrapped = log_timing.__wrapped__
+
+ monkeypatch.setitem(wrapped.__globals__, "DLC_LOG_TIMING", True)
+
+ ticks = iter([1_000_000_000, 1_001_000_000]) # 1.000 ms
+ monkeypatch.setitem(wrapped.__globals__, "perf_counter_ns", lambda: next(ticks))
+
+ monkeypatch.setattr(logger, "isEnabledFor", lambda level: True)
+
+ def fake_log(level, msg, *args):
+ calls.append((level, msg, args))
+
+ monkeypatch.setattr(logger, "log", fake_log)
+
+ with log_timing(logger, "tiny-step", threshold_ms=2.0):
+ pass
+
+ assert calls == []
+
+
+# ----------- Report building tests -----------
+def test_build_debug_report_includes_runtime_libraries_tools_and_recent_logs(
+ monkeypatch: pytest.MonkeyPatch,
+):
+ recorder = InMemoryDebugRecorder(capacity=10, level=logging.DEBUG)
+
+ record = logging.LogRecord(
+ name="deeplabcut.tests.debug",
+ level=logging.INFO,
+ pathname=__file__,
+ lineno=123,
+ msg="hello %s",
+ args=("report",),
+ exc_info=None,
+ )
+ recorder.handle(record)
+
+ monkeypatch.setattr(
+ debug_mod,
+ "collect_runtime_summary",
+ lambda: {
+ "python": "3.11.9",
+ "platform": "TestOS-1.0",
+ "executable": "bin/python",
+ },
+ )
+
+ monkeypatch.setattr(
+ debug_mod,
+ "_version",
+ lambda dist_name: {
+ "alpha": "1.2.3",
+ "opencv-python": "9.9.9-dist",
+ }.get(dist_name, "not-installed"),
+ )
+
+ monkeypatch.setattr(
+ debug_mod,
+ "_module_version",
+ lambda module_name: {
+ "cv2": "4.10.0",
+ }.get(module_name, "not-installed"),
+ )
+
+ monkeypatch.setattr(
+ debug_mod,
+ "_module_path",
+ lambda module_name: {
+ "alpha": "/tmp/site-packages/alpha/__init__.py",
+ "cv2": "/tmp/site-packages/cv2/__init__.py",
+ }.get(module_name, "unknown"),
+ )
+
+ monkeypatch.setattr(
+ debug_mod,
+ "_command_version",
+ lambda command, version_args: {
+ "ffmpeg": "ffmpeg 6.1",
+ }.get(command, "unavailable"),
+ )
+
+ monkeypatch.setattr(
+ debug_mod,
+ "_which",
+ lambda command: {
+ "ffmpeg": "/usr/bin/ffmpeg",
+ }.get(command, "not-found"),
+ )
+
+ report = build_debug_report(
+ recorder=recorder,
+ libraries=(
+ LibrarySpec("alpha"),
+ LibrarySpec(
+ "opencv-python",
+ dist_name="opencv-python",
+ module_name="cv2",
+ prefer_module_version=True,
+ ),
+ ),
+ executables=(ExecutableSpec("ffmpeg"),),
+ include_module_paths=True,
+ include_executable_paths=True,
+ log_limit=20,
+ )
+
+ assert "## Runtime" in report
+ assert "- python: 3.11.9" in report
+ assert "- platform: TestOS-1.0" in report
+ assert "- executable: bin/python" in report
+
+ assert "## Libraries" in report
+ assert "- alpha: 1.2.3" in report
+ assert "- opencv-python: 4.10.0" in report
+ assert "- alpha_module_path: alpha/__init__.py" in report
+ assert "- opencv-python_module_path: cv2/__init__.py" in report
+
+ assert "## External tools" in report
+ assert "- ffmpeg: ffmpeg 6.1" in report
+ assert "- ffmpeg_path: bin/ffmpeg" in report
+
+ assert "## Recent logs" in report
+ assert "deeplabcut.tests.debug" in report
+ assert "INFO" in report
+ assert "hello report" in report
+ assert "```text" in report
+
+
+def test_build_debug_report_default_grouped_sections_and_skips_unavailable_tf(
+ monkeypatch: pytest.MonkeyPatch,
+):
+ monkeypatch.setattr(
+ debug_mod,
+ "collect_runtime_summary",
+ lambda: {
+ "python": "3.12.0",
+ "platform": "GroupedTestOS",
+ "executable": "python",
+ },
+ )
+
+ def fake_collect_version_summary(*, libraries=None, include_module_paths=False):
+ if libraries == debug_mod.DLC_CORE_LIBS:
+ return {"deeplabcut": "1.0.0", "numpy": "2.0.0"}
+ if libraries == debug_mod.DLC_GUI_LIBS:
+ return {"PySide6": "6.8.0"}
+ if libraries == debug_mod.DLC_TF_LIBS:
+ return {
+ "tensorflow": "not-installed",
+ "tf_keras": "not-installed",
+ "tensorpack": "unknown",
+ "tf_slim": "not-installed",
+ }
+ raise AssertionError("unexpected libraries input")
+
+ monkeypatch.setattr(debug_mod, "collect_version_summary", fake_collect_version_summary)
+
+ monkeypatch.setattr(
+ debug_mod,
+ "collect_executable_summary",
+ lambda *, executables=None, include_paths=True: {
+ "ffmpeg": "unavailable",
+ "ffmpeg_path": "not-found",
+ },
+ )
+
+ report = build_debug_report(
+ recorder=None,
+ libraries=None,
+ executables=None,
+ )
+
+ assert "## Runtime" in report
+ assert "## DeepLabCut core libraries" in report
+ assert "- deeplabcut: 1.0.0" in report
+ assert "## GUI libraries" in report
+ assert "- PySide6: 6.8.0" in report
+
+ # All TF values are unavailable/unknown, so the section should be omitted.
+ assert "## TensorFlow libraries" not in report
+
+ # External tools should still be shown even when unavailable.
+ assert "## External tools" in report
+ assert "- ffmpeg: unavailable" in report
+ assert "- ffmpeg_path: not-found" in report
+
+ assert "## Recent logs" in report
+ assert "" in report
+
+
+def test_collect_version_summary_prefers_module_version_and_falls_back_to_distribution(
+ monkeypatch: pytest.MonkeyPatch,
+):
+ monkeypatch.setattr(
+ debug_mod,
+ "_module_version",
+ lambda module_name: {
+ "cv2": "not-installed",
+ }.get(module_name, "unknown"),
+ )
+
+ monkeypatch.setattr(
+ debug_mod,
+ "_version",
+ lambda dist_name: {
+ "opencv-python": "4.9.0.80",
+ "missing-lib": "not-installed",
+ }.get(dist_name, "not-installed"),
+ )
+
+ summary = collect_version_summary(
+ libraries=(
+ LibrarySpec(
+ "opencv-python",
+ dist_name="opencv-python",
+ module_name="cv2",
+ prefer_module_version=True,
+ ),
+ LibrarySpec("missing-lib"),
+ )
+ )
+
+ assert summary["opencv-python"] == "4.9.0.80"
+ assert summary["missing-lib"] == "not-installed"
+
+
+def test_collect_executable_summary_reports_unavailable_tool_and_path(
+ monkeypatch: pytest.MonkeyPatch,
+):
+ monkeypatch.setattr(debug_mod, "_command_version", lambda command, version_args: "unavailable")
+ monkeypatch.setattr(debug_mod, "_which", lambda command: "not-found")
+
+ summary = collect_executable_summary(
+ executables=(ExecutableSpec("ghosttool"),),
+ include_paths=True,
+ )
+
+ assert summary == {
+ "ghosttool": "unavailable",
+ "ghosttool_path": "not-found",
+ }
+
+
+def test_build_debug_report_uses_no_captured_logs_placeholder_for_empty_recorder(
+ monkeypatch: pytest.MonkeyPatch,
+):
+ recorder = InMemoryDebugRecorder(capacity=5, level=logging.DEBUG)
+
+ monkeypatch.setattr(
+ debug_mod,
+ "collect_runtime_summary",
+ lambda: {
+ "python": "3.11.0",
+ "platform": "EmptyLogsOS",
+ "executable": "python",
+ },
+ )
+
+ monkeypatch.setattr(
+ debug_mod,
+ "collect_executable_summary",
+ lambda *, executables=None, include_paths=True: {},
+ )
+
+ report = build_debug_report(
+ recorder=recorder,
+ libraries=(),
+ executables=(),
+ )
+
+ assert "## Runtime" in report
+ assert "## Libraries" in report
+ assert "- " in report
+ assert "## Recent logs" in report
+ assert "" in report
+
+
+def test_format_debug_report_renders_empty_section_and_logs_block():
+ text = format_debug_report(
+ sections=[
+ DebugSection(title="Example", items={}),
+ ],
+ logs_text="line one\nline two",
+ )
+
+ assert "## Example" in text
+ assert "- " in text
+ assert "## Recent logs" in text
+ assert "```text" in text
+ assert "line one" in text
+ assert "line two" in text
diff --git a/tests/core/inferenceutils/test_map_computation.py b/tests/core/inferenceutils/test_map_computation.py
new file mode 100644
index 0000000000..c2cd4fffe9
--- /dev/null
+++ b/tests/core/inferenceutils/test_map_computation.py
@@ -0,0 +1,418 @@
+"""Tests mAP computation from inferenceutils."""
+
+from __future__ import annotations
+
+import numpy as np
+import pytest
+
+from deeplabcut.core import inferenceutils
+from deeplabcut.pose_estimation_pytorch.data.utils import bbox_from_keypoints
+
+
+@pytest.mark.parametrize(
+ "ground_truth",
+ [
+ {
+ "img0": [
+ [
+ [100.0, 10.0, 2],
+ [150.0, 15.0, 2],
+ [202.0, 20.0, 2],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 2],
+ [140.0, 17.0, 2],
+ [192.0, 22.0, 2],
+ ],
+ ],
+ },
+ ],
+)
+@pytest.mark.parametrize(
+ "predictions",
+ [
+ {
+ "img0": [
+ [
+ [100.0, 10.0, 0.9],
+ [150.0, 15.0, 0.7],
+ [202.0, 20.0, 0.8],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 0.9],
+ [140.0, 17.0, 0.7],
+ [192.0, 22.0, 0.8],
+ ],
+ [
+ [97.0, 11.0, 0.5],
+ [148.0, 14.0, 0.2],
+ [202.0, 21.0, 0.3],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 0.9],
+ [np.nan, np.nan, 0.0],
+ [192.0, 22.0, 0.8],
+ ],
+ [
+ [97.0, 11.0, 0.5],
+ [148.0, 14.0, 0.2],
+ [202.0, 21.0, 0.3],
+ ],
+ ],
+ },
+ ],
+)
+def test_map_single_image_simple(ground_truth: dict, predictions: dict):
+ gt = {k: np.array(v) for k, v in ground_truth.items()}
+ pred = {k: np.array(v) for k, v in predictions.items()}
+ _evaluate(gt, pred)
+
+
+@pytest.mark.parametrize(
+ "ground_truth",
+ [
+ {
+ "img0": [
+ [
+ [100.0, 10.0, 2],
+ [150.0, 15.0, 2],
+ [202.0, 20.0, 2],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 2],
+ [140.0, 17.0, 2],
+ [192.0, 22.0, 2],
+ ],
+ [
+ [726.0, 325.0, 2],
+ [326.0, 236.0, 2],
+ [457.0, 832.0, 2],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 2],
+ [140.0, 17.0, 2],
+ [192.0, 22.0, 2],
+ ],
+ [
+ [726.0, 325.0, 2],
+ [0.0, 0.0, 0],
+ [457.0, 832.0, 2],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 2],
+ [140.0, 17.0, 2],
+ [192.0, 22.0, 2],
+ ],
+ [
+ [726.0, 325.0, 2],
+ [0, 0, 0],
+ [457.0, 832.0, 2],
+ ],
+ [
+ [452.0, 321.0, 2],
+ [213.0, 387.0, 2],
+ [213.0, 832.0, 2],
+ ],
+ [
+ [253.0, 238.0, 2],
+ [213.0, 238.0, 2],
+ [457.0, 832.0, 2],
+ ],
+ ],
+ },
+ ],
+)
+def test_map_single_image_random_errors(ground_truth: dict):
+ rng = np.random.default_rng(seed=0)
+
+ gt = {k: np.array(v) for k, v in ground_truth.items()}
+ pred = {}
+ for k, gt_kpts in gt.items():
+ num_idv, num_bpt = gt_kpts.shape[:2]
+
+ error = rng.integers(low=-30, high=30, size=(num_idv, num_bpt, 2))
+ scores = rng.random(size=(num_idv, num_bpt))
+
+ pred[k] = np.zeros(shape=(num_idv, num_bpt, 3))
+ pred[k][..., :2] = np.clip(gt_kpts[..., :2] + error, 0, 1024)
+ pred[k][..., 2] = scores
+
+ _evaluate(gt, pred)
+
+
+@pytest.mark.parametrize("num_images", [1, 2, 5, 10])
+@pytest.mark.parametrize("num_joints", [2, 5, 8, 20])
+@pytest.mark.parametrize("max_error", [1, 2, 5, 20, 40])
+def test_random_map_computation(num_images, num_joints, max_error):
+ rng = np.random.default_rng(seed=0)
+
+ num_individuals = rng.integers(low=0, high=20, size=(num_images, 2))
+ max_idv = num_individuals.max(initial=0)
+
+ gt = {}
+ pred = {}
+ for i, (gt_idv, pred_idv) in enumerate(num_individuals):
+ # padding needed as we then stack
+ gt_kpts = np.zeros((max_idv, num_joints, 3))
+ pred_kpts = -np.ones((max_idv, num_joints, 3))
+
+ gt_kpts[:gt_idv] = 2 * np.ones((gt_idv, num_joints, 3))
+ gt_kpts[:gt_idv, :, :2] = rng.integers(low=0, high=1024, size=(gt_idv, num_joints, 2))
+ gt[f"img_{i}"] = gt_kpts
+
+ # set scores
+ pred_kpts[:pred_idv, :, 2] = rng.random(size=(pred_idv, num_joints))
+
+ # predictions that are ground truth + error
+ matched = min(gt_idv, pred_idv)
+ if matched > 0:
+ error = rng.integers(low=-max_error, high=max_error, size=(matched, num_joints, 2))
+ matched_pred = gt_kpts[:matched, :, :2] + error
+ pred_kpts[:matched, :, :2] = np.clip(matched_pred, 0, 1024)
+
+ # random predictions
+ unmatched = pred_idv - matched
+ if unmatched > 0:
+ pred_kpts[matched:pred_idv, :, :2] = rng.integers(low=0, high=1024, size=(unmatched, num_joints, 2))
+
+ pred[f"img_{i}"] = pred_kpts
+
+ _evaluate(gt, pred)
+
+
+@pytest.mark.parametrize("num_images", [1, 2, 5, 10])
+@pytest.mark.parametrize("num_joints", [2, 5, 8, 20])
+@pytest.mark.parametrize("max_error", [1, 2, 5, 20, 40])
+def test_random_map_computation_with_missing_kpts(num_images, num_joints, max_error):
+ rng = np.random.default_rng(seed=0)
+
+ num_individuals = rng.integers(low=0, high=20, size=(num_images, 2))
+ max_idv = num_individuals.max(initial=0)
+
+ gt = {}
+ pred = {}
+ for i, (gt_idv, pred_idv) in enumerate(num_individuals):
+ # padding needed as we then stack
+ gt_kpts = np.zeros((max_idv, num_joints, 3))
+ pred_kpts = -np.ones((max_idv, num_joints, 3))
+
+ gt_kpts[:gt_idv] = 2 * np.ones((gt_idv, num_joints, 3))
+ gt_kpts[:gt_idv, :, :2] = rng.integers(low=0, high=1024, size=(gt_idv, num_joints, 2))
+ gt[f"img_{i}"] = gt_kpts
+
+ # drop some ground truth keypoints
+ gt_vis_mask = rng.random(size=(max_idv, num_joints)) < 0.2
+ gt_kpts[gt_vis_mask, 2] = 0
+
+ # set scores
+ pred_kpts[:pred_idv, :, 2] = rng.random(size=(pred_idv, num_joints))
+
+ # predictions that are ground truth + error
+ matched = min(gt_idv, pred_idv)
+ if matched > 0:
+ error = rng.integers(low=-max_error, high=max_error, size=(matched, num_joints, 2))
+ matched_pred = gt_kpts[:matched, :, :2] + error
+ pred_kpts[:matched, :, :2] = np.clip(matched_pred, 0, 1024)
+
+ # random predictions
+ unmatched = pred_idv - matched
+ if unmatched > 0:
+ pred_kpts[matched:pred_idv, :, :2] = rng.integers(low=0, high=1024, size=(unmatched, num_joints, 2))
+
+ pred[f"img_{i}"] = pred_kpts
+
+ _evaluate(gt, pred)
+
+
+def _evaluate(gt: dict[str, np.ndarray], pred: dict[str, np.ndarray]):
+ for k, v in gt.items():
+ print(20 * "-")
+ print(k)
+ print("GT")
+ print(v)
+ print("PR")
+ print(pred[k])
+
+ gt_assemblies = _to_assemblies(gt, ground_truth=True)
+ pred_assemblies = _to_assemblies(pred, ground_truth=False)
+ oks = inferenceutils.evaluate_assembly_greedy(
+ assemblies_gt=gt_assemblies,
+ assemblies_pred=pred_assemblies,
+ oks_sigma=0.1,
+ oks_thresholds=np.linspace(0.5, 0.95, 10),
+ margin=0.0,
+ symmetric_kpts=None,
+ )
+
+ num_joints = gt[list(gt.keys())[0]].shape[1]
+ coco_gt = _to_coco_ground_truth(gt, num_joints, bbox_margin=0)
+ coco_pred = _to_coco_predictions(coco_gt, pred, bbox_margin=0)
+ coco_oks = eval_coco(coco_gt, coco_pred, num_joints)
+ print(20 * "-")
+ print("dlc mAP:")
+ for k, v in oks.items():
+ print(k)
+ print(v)
+ print()
+ print(20 * "-")
+ print(f"pycocotools mAP: {coco_oks}")
+ print()
+ assert oks["mAP"] == coco_oks
+
+
+def _to_assemblies(
+ data: dict[str, np.ndarray],
+ ground_truth: bool,
+) -> dict[str, list[inferenceutils.Assembly]]:
+ images = list(data.keys())
+ raw_data = np.stack([data[i] for i in images], axis=0)
+
+ # mask not visible entries
+ mask = raw_data[..., 2] <= 0
+ raw_data[mask] = np.nan
+
+ # set the "score" to 1 for ground truth
+ if ground_truth:
+ raw_data[~mask, 2] = 1
+
+ return {images[i]: assembly for i, assembly in inferenceutils._parse_ground_truth_data(raw_data).items()}
+
+
+def _to_coco_ground_truth(
+ data: dict[str, np.ndarray],
+ num_joints: int,
+ bbox_margin: int = 0,
+ image_size: tuple[int, int] = (1024, 1024),
+) -> dict[str, list[dict]]:
+ w, h = image_size
+ anns, images = [], []
+ for path, image_keypoints in data.items():
+ id_ = len(images) + 1
+ images.append(dict(id=id_, file_name=path, width=w, height=h))
+
+ assert image_keypoints.shape[1] == num_joints
+ for _idv_id, kpts in enumerate(image_keypoints):
+ visible = kpts[:, 2] > 0
+ num_keypoints = visible.sum()
+
+ if num_keypoints > 1:
+ bbox = bbox_from_keypoints(
+ keypoints=kpts,
+ image_h=h,
+ image_w=w,
+ margin=bbox_margin,
+ )
+ area = bbox[2].item() * bbox[3].item()
+ anns.append(
+ {
+ "id": len(anns) + 1,
+ "image_id": id_,
+ "category_id": 1,
+ "area": area,
+ "bbox": bbox.tolist(),
+ "keypoints": kpts.reshape(-1).tolist(),
+ "iscrowd": 0,
+ "num_keypoints": num_keypoints,
+ }
+ )
+
+ keypoints = [f"bpt{i}" for i in range(num_joints)]
+ category = dict(id=1, name="animal", supercategory="animal", keypoints=keypoints)
+ return {"annotations": anns, "categories": [category], "images": images}
+
+
+def _to_coco_predictions(
+ ground_truth: dict,
+ predictions: dict[str, np.ndarray],
+ bbox_margin: int = 0,
+ image_size: tuple[int, int] = (1024, 1024),
+) -> list[dict]:
+ w, h = image_size
+ num_joints = len(ground_truth["categories"][0]["keypoints"])
+ path_to_id = {img["file_name"]: img["id"] for img in ground_truth["images"]}
+
+ coco_predictions = []
+ for path, image_keypoints in predictions.items():
+ assert image_keypoints.shape[1] == num_joints
+
+ img_id = path_to_id[path]
+ valid_predictions = [kpt for kpt in image_keypoints if np.any(np.all(~np.isnan(kpt), axis=-1))]
+ for kpts in valid_predictions:
+ score = float(np.nanmean(kpts[:, 2]).item())
+ kpts = kpts.copy()
+ kpts[:, 2] = 2
+
+ # NaN predictions to infinity
+ kpts[np.isnan(kpts)] = np.inf
+
+ bbox = bbox_from_keypoints(
+ keypoints=kpts,
+ image_h=h,
+ image_w=w,
+ margin=bbox_margin,
+ )
+ area = bbox[2].item() * bbox[3].item()
+ coco_predictions.append(
+ {
+ "image_id": img_id,
+ "category_id": 1,
+ "keypoints": kpts.reshape(-1).tolist(),
+ "bbox": bbox.tolist(),
+ "area": area,
+ "score": score,
+ }
+ )
+
+ return coco_predictions
+
+
+def eval_coco(
+ ground_truth: dict,
+ predictions: list[dict],
+ num_joints: int,
+) -> float | None:
+ try:
+ from pycocotools.coco import COCO
+ from pycocotools.cocoeval import COCOeval
+
+ coco = COCO()
+ coco.dataset["annotations"] = ground_truth["annotations"]
+ coco.dataset["categories"] = ground_truth["categories"]
+ coco.dataset["images"] = ground_truth["images"]
+ coco.dataset["info"] = {"description": "Generated by DeepLabCut"}
+ coco.createIndex()
+
+ coco_det = coco.loadRes(predictions)
+ coco_eval = COCOeval(coco, coco_det, iouType="keypoints")
+ coco_eval.params.kpt_oks_sigmas = np.array(num_joints * [0.1])
+ coco_eval.evaluate()
+ coco_eval.accumulate()
+ coco_eval.summarize()
+ return float(coco_eval.stats[0])
+
+ except ModuleNotFoundError:
+ print("pycocotools is not installed")
diff --git a/tests/core/metrics/test_metrics_api.py b/tests/core/metrics/test_metrics_api.py
new file mode 100644
index 0000000000..a38516ab58
--- /dev/null
+++ b/tests/core/metrics/test_metrics_api.py
@@ -0,0 +1,110 @@
+#
+# 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/master/AUTHORS
+#
+# Licensed under GNU Lesser General Public License v3.0
+#
+"""General tests for the metrics API."""
+
+import numpy as np
+import pytest
+from numpy.testing import assert_almost_equal
+
+import deeplabcut.core.metrics as metrics
+
+
+def _get_gt_and_pred_with_constant_err(num_idv: int, num_bpt: int, error: float) -> tuple[np.ndarray, np.ndarray]:
+ gt = np.arange(num_idv * num_bpt * 3).astype(float).reshape((num_idv, num_bpt, 3))
+ gt[..., 2] = 2
+ predictions = gt.copy()
+ predictions[..., 2] = 0.9
+ predictions[..., :2] += error
+ return gt, predictions
+
+
+def test_computing_metrics_with_no_predictions():
+ gt = np.arange(5 * 6 * 3).astype(float).reshape((5, 6, 3))
+ gt[..., 2] = 2
+ metrics.compute_metrics(
+ ground_truth={"image": gt},
+ predictions={"image": np.zeros((0, 12, 3))},
+ unique_bodypart_gt=None,
+ unique_bodypart_poses=None,
+ )
+
+
+@pytest.mark.parametrize("error", [0.5, 1, 2])
+def test_computing_metrics_with_constant_error(error):
+ # only works for small errors: otherwise another matching can be found
+ gt, predictions = _get_gt_and_pred_with_constant_err(5, 6, error)
+ results = metrics.compute_metrics(
+ ground_truth={"image": gt},
+ predictions={"image": predictions},
+ unique_bodypart_gt=None,
+ unique_bodypart_poses=None,
+ )
+ assert_almost_equal(results["rmse"], np.sqrt(2) * error)
+ assert_almost_equal(results["rmse_pcutoff"], np.sqrt(2) * error)
+
+
+@pytest.mark.parametrize("error", [0.5, 1, 2])
+def test_metrics_with_unique_with_constant_error(error):
+ # only works for small errors: otherwise another matching can be found
+ gt, predictions = _get_gt_and_pred_with_constant_err(5, 6, error)
+ gt_unique, pred_unique = _get_gt_and_pred_with_constant_err(1, 8, error)
+ results = metrics.compute_metrics(
+ ground_truth={"image": gt},
+ predictions={"image": predictions},
+ unique_bodypart_gt={"image": gt_unique},
+ unique_bodypart_poses={"image": pred_unique},
+ )
+ assert_almost_equal(results["rmse"], np.sqrt(2) * error)
+ assert_almost_equal(results["rmse_pcutoff"], np.sqrt(2) * error)
+
+
+@pytest.mark.parametrize("error", [0.5, 1, 2])
+def test_metrics_per_bpt_with_unique_with_constant_error(error):
+ # only works for small errors: otherwise another matching can be found
+ gt, predictions = _get_gt_and_pred_with_constant_err(5, 6, error)
+ gt_unique, pred_unique = _get_gt_and_pred_with_constant_err(1, 8, error)
+ results = metrics.compute_metrics(
+ ground_truth={"image": gt},
+ predictions={"image": predictions},
+ unique_bodypart_gt={"image": gt_unique},
+ unique_bodypart_poses={"image": pred_unique},
+ per_keypoint_rmse=True,
+ )
+ assert_almost_equal(results["rmse"], np.sqrt(2) * error)
+ assert_almost_equal(results["rmse_pcutoff"], np.sqrt(2) * error)
+
+ for bpt_idx in range(gt.shape[1]):
+ key = f"rmse_keypoint_{bpt_idx}"
+ assert key in results
+ assert_almost_equal(results[key], np.sqrt(2) * error)
+ for bpt_idx in range(gt_unique.shape[1]):
+ key = f"rmse_unique_keypoint_{bpt_idx}"
+ assert key in results
+ assert_almost_equal(results[key], np.sqrt(2) * error)
+
+
+@pytest.mark.parametrize("error", [0.5, 1, 2])
+def test_computing_metrics_single_animal(error):
+ # only works for small errors: otherwise another matching can be found
+ gt = np.arange(6 * 3).astype(float).reshape((1, 6, 3))
+ gt[..., 2] = 2
+ predictions = gt.copy()
+ predictions[..., 2] = 0.9
+ predictions[..., :2] += error
+ results = metrics.compute_metrics(
+ ground_truth={"image": gt},
+ predictions={"image": predictions},
+ single_animal=True,
+ unique_bodypart_gt=None,
+ unique_bodypart_poses=None,
+ )
+ assert_almost_equal(results["rmse"], np.sqrt(2) * error)
+ assert_almost_equal(results["rmse_pcutoff"], np.sqrt(2) * error)
diff --git a/tests/core/metrics/test_metrics_identity_accuracy.py b/tests/core/metrics/test_metrics_identity_accuracy.py
new file mode 100644
index 0000000000..017653d59e
--- /dev/null
+++ b/tests/core/metrics/test_metrics_identity_accuracy.py
@@ -0,0 +1,219 @@
+#
+# 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
+#
+"""Tests for the scoring methods."""
+
+import numpy as np
+import pytest
+
+import deeplabcut.core.metrics.identity
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "individuals": ["i1", "i2"],
+ "bodyparts": ["arm"],
+ "predictions": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, 3)
+ [[2.0, 2.0, 0.8]],
+ [[1.0, 1.0, 0.7]], # x, y, score
+ ],
+ },
+ "identity_scores": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, num_individuals)
+ [[0.8, 0.5]],
+ [[0.51, 0.49]],
+ ],
+ },
+ "ground_truth": {
+ "img0.png": [ # (num_individuals, num_bodyparts, 3)
+ [[1.0, 1.0, 2]],
+ [[0, 0, 0]], # x, y, visibility
+ ]
+ },
+ "accuracy": {
+ "arm_accuracy": 1.0,
+ },
+ },
+ {
+ "individuals": ["i1", "i2"],
+ "bodyparts": ["arm"],
+ "predictions": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, 3)
+ [[1.0, 1.0, 0.7]],
+ [[2.0, 2.0, 0.7]], # x, y, score
+ ],
+ },
+ "identity_scores": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, num_individuals)
+ [[0.4, 0.6]],
+ [[0.6, 0.4]],
+ ],
+ },
+ "ground_truth": {
+ "img0.png": [ # (num_individuals, num_bodyparts, 3)
+ [[2.0, 2.0, 2]],
+ [[1.0, 1.0, 2]], # x, y, visibility
+ ]
+ },
+ "accuracy": {
+ "arm_accuracy": 1.0,
+ },
+ },
+ {
+ "individuals": ["i1", "i2"],
+ "bodyparts": ["arm"],
+ "predictions": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, 3)
+ [[1.0, 1.0, 0.7]],
+ [[2.0, 2.0, 0.7]], # x, y, score
+ ],
+ },
+ "identity_scores": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, num_individuals)
+ [[0.6, 0.4]],
+ [[0.6, 0.4]], # both assemblies assigned to idv 1
+ ],
+ },
+ "ground_truth": {
+ "img0.png": [ # (num_individuals, num_bodyparts, 3)
+ [[2.0, 2.0, 2]],
+ [[1.0, 1.0, 2]], # x, y, visibility
+ ]
+ },
+ "accuracy": {
+ "arm_accuracy": 0.5,
+ },
+ },
+ {
+ "individuals": ["i1", "i2"],
+ "bodyparts": ["arm"],
+ "predictions": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, 3)
+ [[1.0, 1.0, 0.7]],
+ [[2.0, 2.0, 0.7]], # x, y, score
+ ],
+ },
+ "identity_scores": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, num_individuals)
+ [[0.6, 0.4]],
+ [[0.4, 0.6]], # both assigned to wrong ID
+ ],
+ },
+ "ground_truth": {
+ "img0.png": [ # (num_individuals, num_bodyparts, 3)
+ [[2.0, 2.0, 2]], # x, y, visibility
+ [[1.0, 1.0, 2]],
+ ]
+ },
+ "accuracy": {
+ "arm_accuracy": 0.0,
+ },
+ },
+ {
+ "individuals": ["i1", "i2"],
+ "bodyparts": ["arm", "leg"],
+ "predictions": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, 3)
+ [[1.0, 1.0, 0.7], [10.0, 10.0, 0.9]],
+ [[100.0, 100.0, 0.9], [90.0, 90.9, 0.8]],
+ ],
+ },
+ "identity_scores": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, num_individuals)
+ [[0.7, 0.3], [0.6, 0.2]],
+ [[0.6, 0.3], [0.6, 0.2]], # should not matter, not assigned to GT
+ ],
+ },
+ "ground_truth": {
+ "img0.png": [ # (num_individuals, num_bodyparts, 3)
+ [[2.0, 2.0, 2], [8.0, 8.0, 2]], # x, y, visibility
+ [[-1, -1, 0.0], [-1, -1, 0.0]], # not visible
+ ]
+ },
+ "accuracy": {
+ "arm_accuracy": 1.0,
+ "leg_accuracy": 1.0,
+ },
+ },
+ {
+ "individuals": ["i1", "i2", "i3"],
+ "bodyparts": ["arm", "leg"],
+ "predictions": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, 3)
+ [[1.0, 1.0, 0.7], [10.0, 10.0, 0.9]],
+ [[100.0, 100.0, 0.9], [90.0, 90.9, 0.8]],
+ [[110.0, 110.0, 0.9], [98.0, 91.9, 0.8]],
+ ],
+ },
+ "identity_scores": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, num_individuals)
+ [[0.7, 0.3, 0.0], [0.6, 0.2, 0.2]], # assigned to correct ID
+ [[0.6, 0.3, 0.1], [0.6, 0.2, 0.2]], # should not matter, not assigned to GT
+ [[0.6, 0.3, 0.1], [0.6, 0.2, 0.2]], # should not matter, not assigned to GT
+ ],
+ },
+ "ground_truth": {
+ "img0.png": [ # (num_individuals, num_bodyparts, 3)
+ [[2.0, 2.0, 2], [8.0, 8.0, 2]], # x, y, visibility
+ [[-1, -1, 0.0], [-1, -1, 0.0]], # not visible
+ [[-1, -1, 0.0], [-1, -1, 0.0]], # not visible
+ ]
+ },
+ "accuracy": {
+ "arm_accuracy": 1.0,
+ "leg_accuracy": 1.0,
+ },
+ },
+ {
+ "individuals": ["i1", "i2", "i3"],
+ "bodyparts": ["arm", "leg"],
+ "predictions": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, 3)
+ [[1.0, 1.0, 0.7], [10.0, 10.0, 0.9]],
+ [[100.0, 100.0, 0.9], [90.0, 90.9, 0.8]],
+ [[110.0, 110.0, 0.9], [98.0, 91.9, 0.8]],
+ ],
+ },
+ "identity_scores": {
+ "img0.png": [ # (num_assemblies, num_bodyparts, num_individuals)
+ [[0.7, 0.3, 0.1], [0.6, 0.2, 0.1]], # assigned to correct ID
+ [[0.1, 0.2, 0.7], [0.4, 0.3, 0.2]], # 1st correct, 2nd wrong
+ [
+ [0.6, 0.3, 0.5],
+ [0.6, 0.2, 0.4],
+ ], # should not matter, not assigned to GT
+ ],
+ },
+ "ground_truth": {
+ "img0.png": [ # (num_individuals, num_bodyparts, 3)
+ [[2.0, 2.0, 2], [8.0, 8.0, 2]], # x, y, visibility
+ [[-1, -1, 0.0], [-1, -1, 0.0]], # not visible
+ [[90.0, 90, 2], [80, 80, 2.0]], # x, y, visibility
+ ]
+ },
+ "accuracy": {
+ "arm_accuracy": 1.0,
+ "leg_accuracy": 0.5,
+ },
+ },
+ ],
+)
+def test_id_accuracy(data) -> None:
+ scores = deeplabcut.core.metrics.identity.compute_identity_scores(
+ individuals=data["individuals"],
+ bodyparts=data["bodyparts"],
+ predictions={k: np.array(v) for k, v in data["predictions"].items()},
+ identity_scores={k: np.array(v) for k, v in data["identity_scores"].items()},
+ ground_truth={k: np.array(v) for k, v in data["ground_truth"].items()},
+ )
+ assert scores == data["accuracy"]
diff --git a/tests/core/metrics/test_metrics_map_computation.py b/tests/core/metrics/test_metrics_map_computation.py
new file mode 100644
index 0000000000..5ef6a64f75
--- /dev/null
+++ b/tests/core/metrics/test_metrics_map_computation.py
@@ -0,0 +1,399 @@
+#
+# 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/master/AUTHORS
+#
+# Licensed under GNU Lesser General Public License v3.0
+#
+"""Tests that mAP computation is correct."""
+
+from __future__ import annotations
+
+import numpy as np
+import pytest
+from numpy.testing import assert_almost_equal
+
+from deeplabcut.core.metrics.api import prepare_evaluation_data
+from deeplabcut.core.metrics.distance_metrics import compute_oks
+from deeplabcut.pose_estimation_pytorch.data.utils import bbox_from_keypoints
+
+
+@pytest.mark.parametrize(
+ "ground_truth",
+ [
+ {
+ "img0": [
+ [
+ [100.0, 10.0, 2],
+ [150.0, 15.0, 2],
+ [202.0, 20.0, 2],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 2],
+ [140.0, 17.0, 2],
+ [192.0, 22.0, 2],
+ ],
+ ],
+ },
+ ],
+)
+@pytest.mark.parametrize(
+ "predictions",
+ [
+ {
+ "img0": [
+ [
+ [100.0, 10.0, 0.9],
+ [150.0, 15.0, 0.7],
+ [202.0, 20.0, 0.8],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 0.9],
+ [140.0, 17.0, 0.7],
+ [192.0, 22.0, 0.8],
+ ],
+ [
+ [97.0, 11.0, 0.5],
+ [148.0, 14.0, 0.2],
+ [202.0, 21.0, 0.3],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 0.9],
+ [np.nan, np.nan, 0.0],
+ [192.0, 22.0, 0.8],
+ ],
+ [
+ [97.0, 11.0, 0.5],
+ [148.0, 14.0, 0.2],
+ [202.0, 21.0, 0.3],
+ ],
+ ],
+ },
+ ],
+)
+def test_map_single_image_simple(ground_truth: dict, predictions: dict):
+ gt = {k: np.array(v) for k, v in ground_truth.items()}
+ pred = {k: np.array(v) for k, v in predictions.items()}
+ _evaluate(gt, pred)
+
+
+@pytest.mark.parametrize(
+ "ground_truth",
+ [
+ {
+ "img0": [
+ [
+ [100.0, 10.0, 2],
+ [150.0, 15.0, 2],
+ [202.0, 20.0, 2],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 2],
+ [140.0, 17.0, 2],
+ [192.0, 22.0, 2],
+ ],
+ [
+ [726.0, 325.0, 2],
+ [326.0, 236.0, 2],
+ [457.0, 832.0, 2],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 2],
+ [140.0, 17.0, 2],
+ [192.0, 22.0, 2],
+ ],
+ [
+ [726.0, 325.0, 2],
+ [0.0, 0.0, 0],
+ [457.0, 832.0, 2],
+ ],
+ ],
+ },
+ {
+ "img0": [
+ [
+ [90.0, 12.0, 2],
+ [140.0, 17.0, 2],
+ [192.0, 22.0, 2],
+ ],
+ [
+ [726.0, 325.0, 2],
+ [0, 0, 0],
+ [457.0, 832.0, 2],
+ ],
+ [
+ [452.0, 321.0, 2],
+ [213.0, 387.0, 2],
+ [213.0, 832.0, 2],
+ ],
+ [
+ [253.0, 238.0, 2],
+ [213.0, 238.0, 2],
+ [457.0, 832.0, 2],
+ ],
+ ],
+ },
+ ],
+)
+def test_map_single_image_random_errors(ground_truth: dict):
+ rng = np.random.default_rng(seed=0)
+
+ gt = {k: np.array(v) for k, v in ground_truth.items()}
+ pred = {}
+ for k, gt_kpts in gt.items():
+ num_idv, num_bpt = gt_kpts.shape[:2]
+
+ error = rng.integers(low=-30, high=30, size=(num_idv, num_bpt, 2))
+ scores = rng.random(size=(num_idv, num_bpt))
+
+ pred[k] = np.zeros(shape=(num_idv, num_bpt, 3))
+ pred[k][..., :2] = np.clip(gt_kpts[..., :2] + error, 0, 1024)
+ pred[k][..., 2] = scores
+
+ _evaluate(gt, pred)
+
+
+@pytest.mark.parametrize("num_images", [1, 2, 5, 10])
+@pytest.mark.parametrize("num_joints", [2, 5, 8, 20])
+@pytest.mark.parametrize("max_error", [1, 2, 5, 20, 40])
+def test_random_map_computation(num_images, num_joints, max_error):
+ rng = np.random.default_rng(seed=0)
+
+ num_individuals = rng.integers(low=0, high=20, size=(num_images, 2))
+
+ gt, pred = {}, {}
+ for i, (gt_idv, pred_idv) in enumerate(num_individuals):
+ gt_kpts = 2 * np.ones((gt_idv, num_joints, 3))
+ gt_kpts[..., :2] = rng.integers(low=0, high=1024, size=(gt_idv, num_joints, 2))
+ gt[f"img_{i}"] = gt_kpts
+
+ # create predictions array
+ pred_kpts = np.zeros((pred_idv, num_joints, 3))
+ # set scores
+ pred_kpts[..., 2] = rng.random(size=(pred_idv, num_joints))
+
+ # predictions that are ground truth + error
+ matched = min(gt_idv, pred_idv)
+ if matched > 0:
+ error = rng.integers(low=-max_error, high=max_error, size=(matched, num_joints, 2))
+ matched_pred = gt_kpts[:matched, :, :2] + error
+ pred_kpts[:matched, :, :2] = np.clip(matched_pred, 0, 1024)
+
+ # random predictions
+ unmatched = pred_idv - matched
+ if unmatched > 0:
+ pred_kpts[matched:, :, :2] = rng.integers(low=0, high=1024, size=(unmatched, num_joints, 2))
+
+ pred[f"img_{i}"] = pred_kpts
+
+ _evaluate(gt, pred)
+
+
+@pytest.mark.parametrize("num_images", [1, 2, 5, 10])
+@pytest.mark.parametrize("num_joints", [2, 5, 8, 20])
+@pytest.mark.parametrize("max_error", [1, 2, 5, 20, 40])
+def test_random_map_computation_with_missing_kpts(num_images, num_joints, max_error):
+ rng = np.random.default_rng(seed=0)
+ num_individuals = rng.integers(low=0, high=20, size=(num_images, 2))
+
+ gt, pred = {}, {}
+ for i, (gt_idv, pred_idv) in enumerate(num_individuals):
+ gt_kpts = 2 * np.ones((gt_idv, num_joints, 3))
+ gt_kpts[..., :2] = rng.integers(low=0, high=1024, size=(gt_idv, num_joints, 2))
+ gt[f"img_{i}"] = gt_kpts
+
+ # drop some ground truth keypoints
+ gt_vis_mask = rng.random(size=(gt_idv, num_joints)) < 0.2
+ gt_kpts[gt_vis_mask, 2] = 0
+
+ # generate predicted keypoints
+ pred_kpts = np.zeros((pred_idv, num_joints, 3))
+ pred_kpts[:pred_idv, :, 2] = rng.random(size=(pred_idv, num_joints))
+
+ # predictions that are ground truth + error
+ matched = min(gt_idv, pred_idv)
+ if matched > 0:
+ error = rng.integers(low=-max_error, high=max_error, size=(matched, num_joints, 2))
+ matched_pred = gt_kpts[:matched, :, :2] + error
+ pred_kpts[:matched, :, :2] = np.clip(matched_pred, 0, 1024)
+
+ # random predictions
+ unmatched = pred_idv - matched
+ if unmatched > 0:
+ pred_kpts[matched:, :, :2] = rng.integers(low=0, high=1024, size=(unmatched, num_joints, 2))
+
+ pred[f"img_{i}"] = pred_kpts
+
+ _evaluate(gt, pred)
+
+
+def _evaluate(gt: dict[str, np.ndarray], pred: dict[str, np.ndarray]):
+ for k, v in gt.items():
+ print(20 * "-")
+ print(k)
+ print("GT")
+ print(v)
+ print("PR")
+ print(pred[k])
+
+ data = prepare_evaluation_data(gt, pred)
+ oks = compute_oks(data, oks_bbox_margin=0)
+
+ num_joints = gt[list(gt.keys())[0]].shape[1]
+ coco_gt = _to_coco_ground_truth(gt, num_joints, bbox_margin=0)
+ coco_pred = _to_coco_predictions(coco_gt, pred, bbox_margin=0)
+ coco_oks = eval_coco(coco_gt, coco_pred, num_joints)
+ print(20 * "-")
+ print("dlc mAP:")
+ for k, v in oks.items():
+ print(k)
+ print(v)
+ print(20 * "-")
+ print(f"pycocotools mAP: {coco_oks}")
+ print()
+ dlc_map = oks["mAP"] / 100
+ assert_almost_equal(dlc_map, coco_oks)
+
+
+def _to_coco_ground_truth(
+ data: dict[str, np.ndarray],
+ num_joints: int,
+ bbox_margin: int = 0,
+ image_size: tuple[int, int] = (1024, 1024),
+) -> dict[str, list[dict]]:
+ w, h = image_size
+ anns, images = [], []
+ for path, image_keypoints in data.items():
+ id_ = len(images) + 1
+ images.append(dict(id=id_, file_name=path, width=w, height=h))
+
+ assert image_keypoints.shape[1] == num_joints
+ for _idv_id, kpts in enumerate(image_keypoints):
+ visible = kpts[:, 2] > 0
+ num_keypoints = visible.sum()
+
+ if num_keypoints > 1:
+ bbox = bbox_from_keypoints(
+ keypoints=kpts,
+ image_h=h,
+ image_w=w,
+ margin=bbox_margin,
+ )
+ area = bbox[2].item() * bbox[3].item()
+ anns.append(
+ {
+ "id": len(anns) + 1,
+ "image_id": id_,
+ "category_id": 1,
+ "area": area,
+ "bbox": bbox.tolist(),
+ "keypoints": kpts.reshape(-1).tolist(),
+ "iscrowd": 0,
+ "num_keypoints": num_keypoints,
+ }
+ )
+
+ keypoints = [f"bpt{i}" for i in range(num_joints)]
+ category = dict(id=1, name="animal", supercategory="animal", keypoints=keypoints)
+ return {
+ "info": {"description": "Generated COCO ground truth dataset"}, # Add this
+ "annotations": anns,
+ "categories": [category],
+ "images": images,
+ }
+
+
+def _to_coco_predictions(
+ ground_truth: dict,
+ predictions: dict[str, np.ndarray],
+ bbox_margin: int = 0,
+ image_size: tuple[int, int] = (1024, 1024),
+) -> list[dict]:
+ w, h = image_size
+ num_joints = len(ground_truth["categories"][0]["keypoints"])
+ path_to_id = {img["file_name"]: img["id"] for img in ground_truth["images"]}
+
+ coco_predictions = []
+ for path, image_keypoints in predictions.items():
+ assert image_keypoints.shape[1] == num_joints
+
+ img_id = path_to_id[path]
+ valid_predictions = [kpt for kpt in image_keypoints if np.any(np.all(~np.isnan(kpt), axis=-1))]
+ for kpts in valid_predictions:
+ score = float(np.nanmean(kpts[:, 2]).item())
+ kpts = kpts.copy()
+ kpts[:, 2] = 2
+
+ # NaN predictions to infinity
+ kpts[np.isnan(kpts)] = np.inf
+
+ bbox = bbox_from_keypoints(
+ keypoints=kpts,
+ image_h=h,
+ image_w=w,
+ margin=bbox_margin,
+ )
+ area = bbox[2].item() * bbox[3].item()
+ coco_predictions.append(
+ {
+ "image_id": img_id,
+ "category_id": 1,
+ "keypoints": kpts.reshape(-1).tolist(),
+ "bbox": bbox.tolist(),
+ "area": area,
+ "score": score,
+ }
+ )
+
+ return coco_predictions
+
+
+def eval_coco(
+ ground_truth: dict,
+ predictions: list[dict],
+ num_joints: int,
+) -> float | None:
+ try:
+ from pycocotools.coco import COCO
+ from pycocotools.cocoeval import COCOeval
+
+ coco = COCO()
+ coco.dataset["annotations"] = ground_truth["annotations"]
+ coco.dataset["categories"] = ground_truth["categories"]
+ coco.dataset["images"] = ground_truth["images"]
+ coco.dataset["info"] = {"description": "Generated by DeepLabCut"}
+ coco.createIndex()
+
+ coco_det = coco.loadRes(predictions)
+ coco_eval = COCOeval(coco, coco_det, iouType="keypoints")
+ coco_eval.params.kpt_oks_sigmas = np.array(num_joints * [0.1])
+ coco_eval.evaluate()
+ coco_eval.accumulate()
+ coco_eval.summarize()
+ return float(coco_eval.stats[0])
+
+ except ModuleNotFoundError:
+ print("pycocotools is not installed")
diff --git a/tests/core/metrics/test_metrics_rmse_computation.py b/tests/core/metrics/test_metrics_rmse_computation.py
new file mode 100644
index 0000000000..187279df31
--- /dev/null
+++ b/tests/core/metrics/test_metrics_rmse_computation.py
@@ -0,0 +1,374 @@
+#
+# 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/master/AUTHORS
+#
+# Licensed under GNU Lesser General Public License v3.0
+#
+"""Tests RMSE computation."""
+
+import numpy as np
+import pytest
+from numpy.testing import assert_almost_equal
+
+from deeplabcut.core.metrics.distance_metrics import (
+ compute_detection_rmse,
+ compute_rmse,
+)
+
+
+@pytest.mark.parametrize(
+ "gt, pred, result",
+ [
+ (
+ [ # ground truth pose
+ [[100.0, 10.0, 2], [150.0, 15.0, 2], [200.0, 20.0, 2]],
+ ],
+ [ # predicted pose
+ [[100.0, 10.0, 0.9], [150.0, 15.0, 0.8], [200.0, 20.0, 0.8]],
+ ],
+ (0, 0),
+ ),
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [10.0, 10.0, 2], [10.0, 10.0, 2]],
+ [[20.0, 20.0, 2], [20.0, 20.0, 2], [20.0, 20.0, 2]],
+ ],
+ [ # predicted pose
+ [[12.0, 10.0, 0.9], [12.0, 10.0, 0.9], [12.0, 10.0, 0.9]],
+ [[22.0, 20.0, 0.9], [22.0, 20.0, 0.9], [22.0, 20.0, 0.9]],
+ ],
+ (2, 2),
+ ),
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [10.0, 10.0, 2], [10.0, 10.0, 2]],
+ [[20.0, 20.0, 2], [20.0, 20.0, 2], [20.0, 20.0, 2]],
+ ],
+ [ # predicted pose
+ [[10.0, 12.0, 0.9], [10.0, 12.0, 0.9], [10.0, 12.0, 0.9]],
+ [[20.0, 22.0, 0.9], [20.0, 22.0, 0.9], [20.0, 22.0, 0.9]],
+ ],
+ (2, 2),
+ ),
+ ],
+)
+def test_rmse_single_image(gt: list, pred: list, result: tuple[float, float]):
+ data = [(np.asarray(gt), np.asarray(pred))]
+ computed_results = compute_rmse(data, False, pcutoff=0.6, oks_bbox_margin=10.0)
+ rmse, rmse_cutoff = computed_results["rmse"], computed_results["rmse_pcutoff"]
+ expected_rmse, expected_rmse_cutoff = result
+ assert_almost_equal(rmse, expected_rmse)
+ assert_almost_equal(rmse_cutoff, expected_rmse_cutoff)
+
+
+@pytest.mark.parametrize(
+ "gt, pred, result",
+ [
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [10.0, 10.0, 2], [10.0, 10.0, 2]],
+ [[20.0, 20.0, 2], [20.0, 20.0, 2], [20.0, 20.0, 2]],
+ ],
+ [ # predicted pose
+ [[10.0, 10.0, 0.9], [10.0, 10.0, 0.9], [10.0, 10.0, 0.9]],
+ [[20.0, 22.0, 0.2], [20.0, 22.0, 0.2], [20.0, 22.0, 0.2]],
+ ],
+ (1, 0), # 2 pixel error on half of keypoints, 0 on the other half
+ ),
+ ],
+)
+def test_rmse_pcutoff(gt: list, pred: list, result: tuple[float, float]):
+ data = [(np.asarray(gt), np.asarray(pred))]
+ expected_rmse, expected_rmse_cutoff = result
+
+ computed_results = compute_rmse(data, False, pcutoff=0.6, oks_bbox_margin=10.0)
+ rmse, rmse_cutoff = computed_results["rmse"], computed_results["rmse_pcutoff"]
+ assert_almost_equal(rmse, expected_rmse)
+ assert_almost_equal(rmse_cutoff, expected_rmse_cutoff)
+
+
+@pytest.mark.parametrize(
+ "gt, pred, result",
+ [
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [float("nan"), float("nan"), 0], [10.0, 10.0, 2]],
+ ],
+ [ # predicted pose
+ [[12.0, 10.0, 0.9], [10.0, 10.0, 0.4], [10.0, 10.0, 0.9]],
+ ],
+ (1, 1), # only 2 valid ground truth bodyparts
+ ),
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [10.0, 10.0, 2], [float("nan"), float("nan"), 0]],
+ [[float("nan"), float("nan"), 0], [20.0, 20.0, 2], [20.0, 20.0, 2]],
+ ],
+ [ # predicted pose, swapped prediction order
+ [[20.0, 20.0, 0.9], [21.0, 20.0, 0.9], [21.0, 20.0, 0.9]],
+ [[15.0, 10.0, 0.4], [15.0, 10.0, 0.4], [10.0, 10.0, 0.9]],
+ ],
+ (3, 1), # only 2 valid GT bodyparts
+ ),
+ ],
+)
+def test_rmse_with_nans(gt: list, pred: list, result: tuple[float, float]):
+ data = [(np.asarray(gt), np.asarray(pred))]
+ expected_rmse, expected_rmse_cutoff = result
+
+ results = compute_rmse(data, False, pcutoff=0.6, oks_bbox_margin=10.0)
+ rmse, rmse_cutoff = results["rmse"], results["rmse_pcutoff"]
+ assert_almost_equal(rmse, expected_rmse)
+ assert_almost_equal(rmse_cutoff, expected_rmse_cutoff)
+
+
+@pytest.mark.parametrize(
+ "gt, pred, data_unique, result",
+ [
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [np.nan, np.nan, 0], [10.0, 10.0, 2]],
+ ],
+ [ # predicted pose
+ [[12.0, 10.0, 0.9], [10.0, 10.0, 0.4], [10.0, 10.0, 0.9]],
+ ],
+ None, # unique data
+ (1, 1), # error 2 on one, 0 on the other; only 2 valid GT
+ ),
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [20.0, 20.0, 2], [30.0, 30.0, 2]],
+ [[40.0, 40.0, 2], [50.0, 50.0, 2], [60.0, 60.0, 2]],
+ ],
+ [ # predicted pose, perfect detections but misassembled
+ [[10.0, 10.0, 0.9], [50.0, 50.0, 0.9], [30.0, 30.0, 0.9]],
+ [[40.0, 40.0, 0.9], [20.0, 20.0, 0.4], [60.0, 60.0, 0.9]],
+ ],
+ None, # unique data
+ (0, 0), # all pose perfect
+ ),
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [20.0, 20.0, 2], [30.0, 30.0, 2]],
+ [[40.0, 40.0, 2], [50.0, 50.0, 2], [60.0, 60.0, 2]],
+ ],
+ [ # predicted pose, small error in pose and misassembled
+ [[12.0, 10.0, 0.9], [52.0, 50.0, 0.9], [32.0, 30.0, 0.9]],
+ [[42.0, 40.0, 0.9], [18.0, 20.0, 0.4], [62.0, 60.0, 0.9]],
+ ],
+ None, # unique data
+ (2, 2), # pixel error of 2 on x-axis for all predictions
+ ),
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [20.0, 20.0, 2], [30.0, 30.0, 2]],
+ [[40.0, 40.0, 2], [50.0, 50.0, 2], [60.0, 60.0, 2]],
+ ],
+ [ # predicted pose, small error in low-conf pose and misassembled
+ [[12.0, 10.0, 0.4], [50.0, 50.0, 0.9], [30.0, 30.0, 0.9]],
+ [[40.0, 40.0, 0.9], [22.0, 20.0, 0.4], [62.0, 60.0, 0.4]],
+ ],
+ None, # unique data
+ (1, 0), # error of 2 on half, 0 on the other half (with good conf)
+ ),
+ ( # more ground truth than detections
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [20.0, 20.0, 2], [30.0, 30.0, 2]],
+ [[40.0, 40.0, 2], [50.0, 50.0, 2], [60.0, 60.0, 2]],
+ [[70.0, 70.0, 2], [80.0, 80.0, 2], [90.0, 90.0, 2]],
+ ],
+ [ # predicted pose, no error
+ [[70.0, 70.0, 2], [80.0, 80.0, 2], [90.0, 90.0, 2]],
+ [[40.0, 40.0, 2], [50.0, 50.0, 2], [60.0, 60.0, 2]],
+ ],
+ None, # unique data
+ (0, 0),
+ ),
+ ( # more detections than GT
+ [ # ground truth pose
+ [[70.0, 70.0, 2], [80.0, 80.0, 2], [90.0, 90.0, 2]],
+ [[40.0, 40.0, 2], [50.0, 50.0, 2], [60.0, 60.0, 2]],
+ ],
+ [ # predicted pose, no error
+ [[10.0, 10.0, 2], [20.0, 20.0, 2], [30.0, 30.0, 2]],
+ [[40.0, 40.0, 2], [50.0, 50.0, 2], [60.0, 60.0, 2]],
+ [[70.0, 70.0, 2], [80.0, 80.0, 2], [90.0, 90.0, 2]],
+ ],
+ None, # unique data
+ (0, 0),
+ ),
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [np.nan, np.nan, 0], [10.0, 10.0, 2]],
+ ],
+ [ # predicted pose
+ [[12.0, 10.0, 0.9], [10.0, 10.0, 0.4], [10.0, 10.0, 0.9]],
+ ],
+ ( # unique data
+ [[[20, 20, 2], [22, 23, 2]]],
+ [[[20, 20, 0.8], [22, 23, 0.7]]],
+ ),
+ (0.5, 0.5), # error 2 on one, 0 on the other; only 2 valid GT
+ ),
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [20.0, 20.0, 2], [30.0, 30.0, 2]],
+ [[40.0, 40.0, 2], [50.0, 50.0, 2], [60.0, 60.0, 2]],
+ ],
+ [ # predicted pose, perfect detections but misassembled
+ [[10.0, 10.0, 0.9], [50.0, 50.0, 0.9], [30.0, 30.0, 0.9]],
+ [[40.0, 40.0, 0.9], [20.0, 20.0, 0.4], [60.0, 60.0, 0.9]],
+ ],
+ ( # unique data
+ [], # missing ground truth for unique bodyparts
+ [[[20, 20, 0.8], [22, 23, 0.7]]],
+ ),
+ (0, 0), # all pose perfect
+ ),
+ ],
+)
+def test_detection_rmse(gt: list, pred: list, data_unique: tuple[list, list] | None, result: tuple[float, float]):
+ data = [(np.asarray(gt), np.asarray(pred))]
+ data_unique = [(np.asarray(data_unique[0]), np.asarray(data_unique[1]))] if data_unique else None
+ expected_rmse, expected_rmse_cutoff = result
+ rmse, rmse_cutoff = compute_detection_rmse(data, pcutoff=0.6, data_unique=data_unique)
+ assert_almost_equal(rmse, expected_rmse)
+ assert_almost_equal(rmse_cutoff, expected_rmse_cutoff)
+
+
+@pytest.mark.parametrize(
+ "gt, pred, unique_gt, unique_pred, result",
+ [
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [10.0, 10.0, 2], [10.0, 10.0, 2]],
+ [[20.0, 20.0, 2], [20.0, 20.0, 2], [20.0, 20.0, 2]],
+ ],
+ [ # predicted pose
+ [[10.0, 10.0, 0.9], [10.0, 10.0, 0.9], [10.0, 10.0, 0.9]],
+ [[20.0, 24.0, 0.2], [20.0, 24.0, 0.2], [20.0, 20.0, 0.2]],
+ ],
+ [ # Unique GT
+ [[10.0, 10.0, 2], [10.0, 10.0, 2]],
+ ],
+ [ # Unique Pred
+ [[10.0, 10.0, 0.9], [10.0, 10.0, 0.9]],
+ ],
+ # 4 pixel error on 2 keypoints, 0 error on 5 keypoints
+ (1.0, 0.0),
+ ),
+ (
+ [np.zeros((0, 3, 2))], # no GT pose
+ [ # predicted pose
+ [[10.0, 10.0, 0.9], [10.0, 10.0, 0.9], [10.0, 10.0, 0.9]],
+ ],
+ [ # Unique GT
+ [[10.0, 10.0, 2], [10.0, 10.0, 2]],
+ ],
+ [ # Unique Pred
+ [[15.0, 10.0, 0.5], [11.0, 10.0, 0.9]],
+ ],
+ # 5 pixel error on 1 keypoint, 1 pixel error on the other
+ (3.0, 1.0),
+ ),
+ ],
+)
+def test_rmse_with_unique(
+ gt: list, pred: list, unique_gt: list, unique_pred: list, result: tuple[float, float]
+) -> None:
+ data = [(np.asarray(gt), np.asarray(pred))]
+ data_unique = [(np.asarray(unique_gt), np.asarray(unique_pred))]
+ expected_rmse, expected_rmse_cutoff = result
+
+ results = compute_rmse(
+ data,
+ False,
+ pcutoff=0.6,
+ data_unique=data_unique,
+ oks_bbox_margin=10.0,
+ )
+ rmse, rmse_cutoff = results["rmse"], results["rmse_pcutoff"]
+ assert_almost_equal(rmse, expected_rmse)
+ assert_almost_equal(rmse_cutoff, expected_rmse_cutoff)
+
+
+@pytest.mark.parametrize(
+ "gt, pred, unique_gt, unique_pred, result",
+ [
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [10.0, 10.0, 2], [10.0, 10.0, 2]],
+ [[20.0, 20.0, 2], [20.0, 20.0, 2], [20.0, 20.0, 2]],
+ ],
+ [ # predicted pose
+ [[10.0, 10.0, 0.9], [10.0, 10.0, 0.9], [10.0, 10.0, 0.9]],
+ [[20.0, 24.0, 0.2], [20.0, 24.0, 0.2], [20.0, 20.0, 0.2]],
+ ],
+ [ # Unique GT
+ [[10.0, 10.0, 2], [10.0, 10.0, 2]],
+ ],
+ [ # Unique Pred
+ [[10.0, 10.0, 0.9], [10.0, 10.0, 0.9]],
+ ],
+ # 4 pixel error on 2 keypoints, 0 error on 5 keypoints
+ [(1.0, 0.0), [2.0, 2.0, 0.0], [0.0, 0.0]],
+ ),
+ (
+ [ # ground truth pose
+ [[10.0, 10.0, 2], [10.0, 10.0, 2], [10.0, 10.0, 2]],
+ [[20.0, 20.0, 2], [20.0, 20.0, 2], [20.0, 20.0, 2]],
+ ],
+ [ # predicted pose
+ [[10.0, 12.0, 0.9], [10.0, 10.0, 0.9], [10.0, 10.0, 0.9]],
+ [[20.0, 24.0, 0.7], [20.0, 24.0, 0.6], [20.0, 20.0, 0.8]],
+ ],
+ [ # Unique GT
+ [[10.0, 10.0, 2], [10.0, 10.0, 2]],
+ ],
+ [ # Unique Pred
+ [[12.0, 10.0, 0.9], [11.0, 10.0, 0.9]],
+ ],
+ [ # errors: 3 with 0px, 1 with 1px, 2 with 2px, 2 with 4px => 13/8
+ (1.625, 1.625),
+ [3.0, 2.0, 0.0],
+ [2.0, 1.0],
+ ],
+ ),
+ ],
+)
+def test_rmse_per_bodypart_with_unique(
+ gt: list,
+ pred: list,
+ unique_gt: list,
+ unique_pred: list,
+ result: tuple[tuple[float, float], list[float], list[float]],
+) -> None:
+ data = [(np.asarray(gt), np.asarray(pred))]
+ data_unique = [(np.asarray(unique_gt), np.asarray(unique_pred))]
+ expected_rmse, expected_rmse_cutoff = result[0]
+ bodypart_rmse = result[1]
+ unique_rmse = result[2]
+
+ results = compute_rmse(
+ data,
+ single_animal=False,
+ pcutoff=0.6,
+ data_unique=data_unique,
+ per_keypoint_results=True,
+ oks_bbox_margin=10.0,
+ )
+ assert_almost_equal(results["rmse"], expected_rmse)
+ assert_almost_equal(results["rmse_pcutoff"], expected_rmse_cutoff)
+ for bpt_index, bpt_rmse in enumerate(bodypart_rmse):
+ key = f"rmse_keypoint_{bpt_index}"
+ assert key in results
+ assert_almost_equal(results[key], bpt_rmse)
+
+ for bpt_index, bpt_rmse in enumerate(unique_rmse):
+ key = f"rmse_unique_keypoint_{bpt_index}"
+ assert key in results
+ assert_almost_equal(results[key], bpt_rmse)
diff --git a/tests/create_project/test_video_set_configuration.py b/tests/create_project/test_video_set_configuration.py
new file mode 100644
index 0000000000..86e50eecc9
--- /dev/null
+++ b/tests/create_project/test_video_set_configuration.py
@@ -0,0 +1,264 @@
+#
+# 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/master/AUTHORS
+#
+# Licensed under GNU Lesser General Public License v3.0
+#
+"""Unit tests for deeplabcut.create_project.new module."""
+
+import logging
+import warnings
+from pathlib import Path
+from unittest.mock import Mock, patch
+
+import pytest
+
+import deeplabcut.create_project.new as new_module
+from deeplabcut.utils.auxfun_videos import VideoReader
+
+
+@pytest.fixture
+def project_directory(tmpdir_factory) -> Path:
+ proj_dir = Path(tmpdir_factory.mktemp("test-project"))
+ return proj_dir
+
+
+@pytest.fixture
+def mock_video_file(tmpdir_factory) -> Path:
+ """Create a mock video file for testing."""
+ fake_folder = tmpdir_factory.mktemp("some_video")
+ video_path = Path(fake_folder) / "test_video.avi"
+ video_path.write_bytes(b"fake video content")
+ return video_path
+
+
+@pytest.fixture
+def mock_video_reader() -> VideoReader:
+ """Create a mock VideoReader."""
+ mock_reader = Mock(spec=VideoReader)
+ mock_reader.get_bbox.return_value = (0, 640, 277, 624)
+ return mock_reader
+
+
+@pytest.fixture
+def video_directory(tmpdir_factory) -> Path:
+ """Create a directory with multiple video files."""
+ video_dir = Path(tmpdir_factory.mktemp("some_videos"))
+ video_dir.mkdir(exist_ok=True)
+
+ # Create multiple video files with different extensions
+ (video_dir / "video1.avi").write_bytes(b"fake video 1")
+ (video_dir / "video2.mp4").write_bytes(b"fake video 2")
+ (video_dir / "video3.mov").write_bytes(b"fake video 3")
+ (video_dir / "not_a_video.txt").write_text("text file")
+
+ return video_dir
+
+
+def test_project_directory_creation_basic(
+ tmpdir: Path,
+ mock_video_file: Path,
+ mock_video_reader: VideoReader,
+):
+ """Test that project directories are created correctly."""
+ with patch("deeplabcut.create_project.new.VideoReader", return_value=mock_video_reader):
+ config_path = new_module.create_new_project(
+ project="test-project",
+ experimenter="test-user",
+ videos=[str(mock_video_file)],
+ working_directory=str(tmpdir),
+ copy_videos=False,
+ )
+
+ project_path = Path(config_path).parent
+ assert project_path.exists()
+ assert (project_path / "videos").exists()
+ assert (project_path / "labeled-data").exists()
+ assert (project_path / "training-datasets").exists()
+ assert (project_path / "dlc-models").exists()
+
+
+@pytest.mark.parametrize("copy_videos", [True, False])
+def test_single_video_file(
+ tmpdir: Path,
+ mock_video_file: Path,
+ mock_video_reader: VideoReader,
+ copy_videos: bool,
+):
+ """Test adding a single video file."""
+ with patch("deeplabcut.create_project.new.VideoReader", return_value=mock_video_reader):
+ config_path = new_module.create_new_project(
+ project="test",
+ experimenter="user",
+ videos=[str(mock_video_file)],
+ working_directory=str(tmpdir),
+ copy_videos=copy_videos,
+ )
+
+ project_path = Path(config_path).parent
+ video_path = project_path / "videos" / "test_video.avi"
+ assert video_path.exists() or video_path.is_symlink()
+
+ # Content should match
+ if copy_videos:
+ assert mock_video_file.read_bytes() == video_path.read_bytes()
+
+
+@pytest.mark.parametrize("copy_videos", [True, False])
+def test_video_directory(
+ tmpdir: Path,
+ video_directory: Path,
+ mock_video_reader: VideoReader,
+ copy_videos: bool,
+):
+ """Test adding videos from a directory."""
+ with patch("deeplabcut.create_project.new.VideoReader", return_value=mock_video_reader):
+ config_path = new_module.create_new_project(
+ project="test",
+ experimenter="user",
+ videos=[str(video_directory)],
+ working_directory=str(tmpdir),
+ video_extensions=".avi",
+ copy_videos=copy_videos,
+ )
+
+ project_path = Path(config_path).parent
+ assert (project_path / "videos" / "video1.avi").exists() or (project_path / "videos" / "video1.avi").is_symlink()
+
+ # Content should match
+ if copy_videos:
+ assert (project_path / "videos" / "video1.avi").read_bytes() == (video_directory / "video1.avi").read_bytes()
+
+
+@pytest.mark.parametrize("copy_videos", [True, False])
+def test_mixed_video_files_and_directories(
+ tmpdir,
+ mock_video_file: Path,
+ video_directory: Path,
+ mock_video_reader: VideoReader,
+ copy_videos: bool,
+):
+ """Test adding both video files and directories."""
+ with patch("deeplabcut.create_project.new.VideoReader", return_value=mock_video_reader):
+ config_path = new_module.create_new_project(
+ project="test",
+ experimenter="user",
+ videos=[str(mock_video_file), str(video_directory)],
+ working_directory=str(tmpdir),
+ video_extensions=".avi",
+ copy_videos=copy_videos,
+ )
+
+ project_path = Path(config_path).parent
+ videos_dir = project_path / "videos"
+ # Should have both the single file and files from directory
+ assert (videos_dir / mock_video_file.name).exists() or (videos_dir / mock_video_file.name).is_symlink()
+ assert (videos_dir / "video1.avi").exists() or (videos_dir / "video1.avi").is_symlink()
+
+
+def test_empty_video_directory(
+ tmpdir: Path,
+ mock_video_reader: VideoReader,
+):
+ """Test handling of empty video directory."""
+ empty_dir = tmpdir / "empty_videos"
+ empty_dir.mkdir()
+
+ with patch("deeplabcut.create_project.new.VideoReader", return_value=mock_video_reader):
+ with warnings.catch_warnings(record=True) as w:
+ result = new_module.create_new_project(
+ project="test",
+ experimenter="user",
+ videos=[str(empty_dir)],
+ working_directory=str(tmpdir),
+ video_extensions=".avi",
+ copy_videos=False,
+ )
+ # Should return "nothingcreated" when no valid videos found
+ assert result == "nothingcreated" or len(w) > 0
+
+
+def test_valid_video_included_in_config(
+ tmpdir: Path,
+ mock_video_file: Path,
+ mock_video_reader: VideoReader,
+):
+ """Test that valid videos are included in the config file."""
+ with patch("deeplabcut.create_project.new.VideoReader", return_value=mock_video_reader):
+ config_path = new_module.create_new_project(
+ project="test",
+ experimenter="user",
+ videos=[str(mock_video_file)],
+ working_directory=str(tmpdir),
+ copy_videos=False,
+ )
+
+ from deeplabcut.utils import auxiliaryfunctions
+
+ cfg = auxiliaryfunctions.read_config(config_path)
+ logging.debug(f"Config content: {cfg}")
+ logging.debug(f"Video sets in config: {cfg.get('video_sets', {})}")
+ logging.debug(f"Video sets keys: {list(cfg.get('video_sets', {}).keys())}")
+
+ assert "video_sets" in cfg
+ assert len(cfg["video_sets"]) > 0
+ # Check that video path is in video_sets
+ video_keys = [Path(k) for k in cfg["video_sets"].keys()]
+ project_video = Path(config_path).parent / "videos" / mock_video_file.name
+
+ assert any(k.resolve() == project_video.resolve() for k in video_keys)
+
+
+def test_invalid_video_removed_from_project(
+ tmpdir: Path,
+ mock_video_file: Path,
+):
+ """Test that invalid videos are removed from the project."""
+ # Mock VideoReader to raise IOError
+ mock_reader = Mock(side_effect=OSError("Cannot open video"))
+
+ with patch("deeplabcut.create_project.new.VideoReader", mock_reader):
+ with warnings.catch_warnings(record=True):
+ result = new_module.create_new_project(
+ project="test",
+ experimenter="user",
+ videos=[str(mock_video_file)],
+ working_directory=str(tmpdir),
+ copy_videos=False,
+ )
+
+ # Should return "nothingcreated" when no valid videos
+ assert result == "nothingcreated"
+
+
+def test_config_file_video_sets_format(
+ tmpdir: Path,
+ mock_video_file: Path,
+ mock_video_reader: VideoReader,
+):
+ """Test that video_sets in config has correct format."""
+ with patch("deeplabcut.create_project.new.VideoReader", return_value=mock_video_reader):
+ config_path = new_module.create_new_project(
+ project="test",
+ experimenter="user",
+ videos=[str(mock_video_file)],
+ working_directory=str(tmpdir),
+ copy_videos=False,
+ )
+
+ from deeplabcut.utils import auxiliaryfunctions
+
+ cfg = auxiliaryfunctions.read_config(config_path)
+
+ assert "video_sets" in cfg
+ assert isinstance(cfg["video_sets"], dict)
+
+ # Check format of video_sets entries
+ for _video_path, video_info in cfg["video_sets"].items():
+ assert isinstance(video_info, dict)
+ assert "crop" in video_info
+ assert isinstance(video_info["crop"], str)
diff --git a/tests/generate_training_dataset/test_trainingset_manipulation.py b/tests/generate_training_dataset/test_trainingset_manipulation.py
new file mode 100644
index 0000000000..cf78ba3996
--- /dev/null
+++ b/tests/generate_training_dataset/test_trainingset_manipulation.py
@@ -0,0 +1,37 @@
+#
+# 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
+#
+"""Tests for deeplabcut/generate_training_dataset/metadata.py."""
+
+from __future__ import annotations
+
+import pytest
+
+import deeplabcut.generate_training_dataset.trainingsetmanipulation as trainingsetmanipulation
+
+
+@pytest.mark.parametrize("train_fraction", [1, 2, 5, 17, 24, 29, 34, 47, 50, 53, 61, 68, 75, 90, 95, 97, 99])
+@pytest.mark.parametrize("n_train", [1, 2, 3, 5, 7, 11, 37, 62, 153])
+@pytest.mark.parametrize("n_test", [1, 2, 3, 5, 7, 13, 19, 85, 112])
+def test_compute_padding(train_fraction: int, n_train: int, n_test: int) -> None:
+ """
+ More complete tests can be run with:
+ "train_fraction": list(range(1, 100))
+ "n_train": list(range(1, 200))
+ "n_test": list(range(1, 200))
+
+ This was done locally, but as it's many many tests to run a subset was selected here
+ """
+ train_frac = train_fraction / 100
+ train_pad, test_pad = trainingsetmanipulation._compute_padding(train_frac, n_train, n_test)
+ print()
+ print(train_fraction, n_train, n_test, train_pad, test_pad)
+ frac = round((n_train + train_pad) / (n_train + n_test + train_pad + test_pad), 2)
+ assert train_frac == frac
diff --git a/tests/generate_training_dataset/test_trainset_metadata.py b/tests/generate_training_dataset/test_trainset_metadata.py
new file mode 100644
index 0000000000..dacd9978cf
--- /dev/null
+++ b/tests/generate_training_dataset/test_trainset_metadata.py
@@ -0,0 +1,580 @@
+#
+# 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
+#
+"""Tests for deeplabcut/generate_training_dataset/metadata.py."""
+
+from __future__ import annotations
+
+import logging
+import pickle
+from unittest.mock import MagicMock, patch
+
+import pytest
+from ruamel.yaml import YAML
+
+import deeplabcut.generate_training_dataset.metadata as metadata
+from deeplabcut.core.engine import Engine
+from deeplabcut.utils import auxiliaryfunctions
+
+SHUFFLE_DATA = [
+ {"name": "pJun17-t50s1", "index": 1, "train_fraction": 0.5, "split": 1, "engine": "torch"},
+ {"name": "pJun17-t50s2", "index": 2, "train_fraction": 0.5, "split": 1, "engine": "tf"},
+ {"name": "pJun17-t60s1", "index": 1, "train_fraction": 0.6, "split": 2, "engine": "torch"},
+ {"name": "pJun17-t60s2", "index": 2, "train_fraction": 0.6, "split": 3, "engine": "torch"},
+]
+SPLITS_DATA = {
+ 1: {"train": [0, 1], "test": [2, 3]},
+ 2: {"train": [0, 1, 2], "test": [3, 4]},
+ 3: {"train": [4, 3, 2], "test": [1, 0]},
+}
+
+BASE_SPLIT = metadata.DataSplit(train_indices=(1, 2), test_indices=(3, 4))
+# Splits that should be equal to the base
+EQ_SPLIT = metadata.DataSplit(train_indices=(1, 2), test_indices=(3, 4))
+# Splits that should not be equal to the base
+ADD_SPLIT = metadata.DataSplit(train_indices=(1, 2, 5), test_indices=(3, 4))
+ADD_SPLIT2 = metadata.DataSplit(train_indices=(1, 2), test_indices=(3, 4, 5))
+SUBS_SPLIT = metadata.DataSplit(train_indices=(1, 3), test_indices=(2, 4))
+DEL_SPLIT = metadata.DataSplit(train_indices=(1,), test_indices=(3, 4))
+DEL_SPLIT2 = metadata.DataSplit(train_indices=(1, 2), test_indices=(3,))
+
+SHUFFLES = {
+ 1: metadata.ShuffleMetadata("pJun17-t50s1", 0.5, 1, Engine.PYTORCH, BASE_SPLIT),
+ 2: metadata.ShuffleMetadata("pJun17-t50s2", 0.5, 2, Engine.PYTORCH, ADD_SPLIT),
+ 3: metadata.ShuffleMetadata("pJun17-t50s3", 0.5, 3, Engine.TF, BASE_SPLIT),
+ 4: metadata.ShuffleMetadata("pJun17-t50s4", 0.5, 4, Engine.PYTORCH, DEL_SPLIT),
+}
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "shuffles": {SHUFFLE_DATA[idx]["name"]: SHUFFLE_DATA[idx] for idx in [0, 1, 2]},
+ "splits": {idx: SPLITS_DATA[idx] for idx in [1, 2]},
+ },
+ {
+ "shuffles": {SHUFFLE_DATA[idx]["name"]: SHUFFLE_DATA[idx] for idx in [0]},
+ "splits": {idx: SPLITS_DATA[idx] for idx in [1, 2]},
+ },
+ ],
+)
+@pytest.mark.parametrize("load_splits", [True, False])
+def test_load_metadata(tmpdir, data: dict, load_splits: bool):
+ """Tests that loading the metadata from files doesn't fail."""
+ # write data to tmp file
+ cfg, cfg_path, trainset_dir, meta_path = _create_project_with_config(tmpdir)
+ with open(meta_path, "w") as f:
+ YAML().dump(data, f)
+
+ print(cfg_path)
+ print(meta_path)
+ print(data["shuffles"])
+ print(data["splits"])
+ print()
+
+ for _name, s in data["shuffles"].items():
+ split = data["splits"][s["split"]]
+ train, test = split["train"], split["test"]
+ _create_doc_data(cfg, trainset_dir, s["train_fraction"], s["index"], train, test)
+
+ trainset_meta = metadata.TrainingDatasetMetadata.load(str(cfg_path), load_splits=load_splits)
+ for s in trainset_meta.shuffles:
+ print(s)
+
+ assert len(data["shuffles"]) == len(trainset_meta.shuffles)
+
+ for s in trainset_meta.shuffles:
+ shuffle_in = data["shuffles"][s.name]
+ split_idx = data["splits"][shuffle_in["split"]]
+ assert s.train_fraction == shuffle_in["train_fraction"]
+ assert s.engine == Engine(shuffle_in["engine"])
+ if load_splits:
+ assert s.split is not None
+ assert s.split.train_indices == tuple(split_idx["train"])
+ assert s.split.test_indices == tuple(split_idx["test"])
+ else:
+ assert s.split is None
+ s_with_split = s.load_split(cfg, trainset_dir)
+ assert s_with_split.split.train_indices == tuple(split_idx["train"])
+ assert s_with_split.split.test_indices == tuple(split_idx["test"])
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "task": "ch",
+ "date": "Aug1",
+ "shuffles": (SHUFFLES[1],),
+ "expected": {
+ "shuffles": {SHUFFLES[1].name: {"index": 1, "train_fraction": 0.5, "split": 1, "engine": "pytorch"}},
+ },
+ },
+ {
+ "task": "t",
+ "date": "Jan1",
+ "shuffles": (SHUFFLES[1], SHUFFLES[3]),
+ "expected": {
+ "shuffles": {
+ SHUFFLES[1].name: {"index": 1, "train_fraction": 0.5, "split": 1, "engine": "pytorch"},
+ SHUFFLES[3].name: {
+ "index": 3,
+ "train_fraction": 0.5,
+ "split": 1,
+ "engine": "tensorflow",
+ },
+ },
+ },
+ },
+ {
+ "task": "t",
+ "date": "Jan1",
+ "shuffles": (SHUFFLES[1], SHUFFLES[2]),
+ "expected": {
+ "shuffles": {
+ SHUFFLES[1].name: {"index": 1, "train_fraction": 0.5, "split": 1, "engine": "pytorch"},
+ SHUFFLES[2].name: {"index": 2, "train_fraction": 0.5, "split": 2, "engine": "pytorch"},
+ },
+ },
+ },
+ {
+ "shuffles": (SHUFFLES[1], SHUFFLES[2], SHUFFLES[3]),
+ "expected": {
+ "shuffles": {
+ SHUFFLES[1].name: {"index": 1, "train_fraction": 0.5, "split": 1, "engine": "pytorch"},
+ SHUFFLES[2].name: {"index": 2, "train_fraction": 0.5, "split": 2, "engine": "pytorch"},
+ SHUFFLES[3].name: {
+ "index": 3,
+ "train_fraction": 0.5,
+ "split": 1,
+ "engine": "tensorflow",
+ },
+ },
+ },
+ },
+ ],
+)
+def test_save_metadata_simple(tmpdir, data):
+ """Tests that saving the metadata creates the expected file."""
+ cfg, cfg_path, trainset_dir, meta_path = _create_project_with_config(tmpdir)
+ trainset_meta = metadata.TrainingDatasetMetadata(cfg, data["shuffles"])
+ print(trainset_meta)
+
+ trainset_meta.save()
+ with open(meta_path) as f:
+ meta = YAML().load(f)
+ print(data)
+ print(meta)
+ assert data["expected"] == meta
+
+
+@pytest.mark.parametrize(
+ "shuffles",
+ [[SHUFFLES[i] for i in indices] for indices in [[1], [1, 2], [1, 2, 3], [1, 2, 4], [1, 3, 4], [1, 2, 3, 4]]],
+)
+def test_save_metadata(tmpdir, shuffles):
+ """Tests that saving the metadata and reloading it leads to the same instance."""
+ cfg, cfg_path, trainset_dir, meta_path = _create_project_with_config(tmpdir)
+ for s in shuffles:
+ train, test = (
+ s.split.train_indices,
+ s.split.test_indices,
+ )
+ _create_doc_data(cfg, trainset_dir, s.train_fraction, s.index, train, test)
+
+ trainset_meta = metadata.TrainingDatasetMetadata(cfg, tuple(shuffles))
+ print(trainset_meta)
+ trainset_meta.save()
+ reloaded = metadata.TrainingDatasetMetadata.load(cfg)
+ print(reloaded)
+ print()
+
+ for s in trainset_meta.shuffles:
+ print(s)
+ print()
+ for s in reloaded.shuffles:
+ print(s)
+ print()
+ reloaded_with_splits = [s.load_split(cfg, trainset_dir) for s in reloaded.shuffles]
+ assert len(reloaded.shuffles) == len(trainset_meta.shuffles)
+ assert len(reloaded_with_splits) == len(trainset_meta.shuffles)
+ assert tuple(reloaded_with_splits) == trainset_meta.shuffles
+
+
+def test_add_shuffle(tmpdir):
+ """Tests that a shuffle can be added correctlt."""
+ cfg, cfg_path, trainset_dir, meta_path = _create_project_with_config(tmpdir)
+ trainset_meta = metadata.TrainingDatasetMetadata(cfg, (SHUFFLES[1],))
+ trainset_meta_added = trainset_meta.add(SHUFFLES[2])
+ assert len(trainset_meta.shuffles) == 1
+ assert len(trainset_meta_added.shuffles) == 2
+ assert trainset_meta_added.shuffles == (SHUFFLES[1], SHUFFLES[2])
+
+
+def test_add_shuffle_twice(tmpdir):
+ """Tests that a shuffle can be added correctlt."""
+ cfg, cfg_path, trainset_dir, meta_path = _create_project_with_config(tmpdir)
+ trainset_meta = metadata.TrainingDatasetMetadata(cfg, (SHUFFLES[1],))
+ trainset_meta_added = trainset_meta.add(SHUFFLES[2])
+ trainset_meta_added_2 = trainset_meta.add(SHUFFLES[2])
+ assert len(trainset_meta.shuffles) == 1
+ assert trainset_meta.shuffles == (SHUFFLES[1],)
+ assert len(trainset_meta_added.shuffles) == len(trainset_meta_added_2.shuffles)
+ assert trainset_meta_added.shuffles == trainset_meta_added_2.shuffles
+
+
+def test_add_shuffle_sorts_to_correct_order(tmpdir):
+ """Tests that a shuffle can be added correctlt."""
+ cfg, cfg_path, trainset_dir, meta_path = _create_project_with_config(tmpdir)
+ trainset_meta = metadata.TrainingDatasetMetadata(cfg, (SHUFFLES[1], SHUFFLES[3]))
+ trainset_meta_added = trainset_meta.add(SHUFFLES[2])
+ assert len(trainset_meta.shuffles) == 2
+ assert len(trainset_meta_added.shuffles) == 3
+ assert trainset_meta_added.shuffles == (SHUFFLES[1], SHUFFLES[2], SHUFFLES[3])
+
+
+@pytest.mark.parametrize(
+ "shuffles", [indices for indices in [[1], [1, 2], [1, 2, 3], [1, 2, 4], [1, 3, 4], [1, 2, 3, 4]]]
+)
+@pytest.mark.parametrize("shuffle_to_add", [1, 2, 3, 4])
+def test_add_shuffle_indices(tmpdir, shuffles, shuffle_to_add):
+ """Tests."""
+ cfg, cfg_path, trainset_dir, meta_path = _create_project_with_config(tmpdir)
+ trainset_meta = metadata.TrainingDatasetMetadata(cfg, tuple([SHUFFLES[i] for i in shuffles]))
+ if shuffle_to_add in shuffles:
+ with pytest.raises(RuntimeError):
+ trainset_meta_added = trainset_meta.add(SHUFFLES[shuffle_to_add], overwrite=False)
+
+ trainset_meta_added = trainset_meta.add(SHUFFLES[shuffle_to_add], overwrite=True)
+ assert len(trainset_meta_added.shuffles) == len(shuffles)
+ assert [s.index for s in trainset_meta_added.shuffles] == shuffles
+ else:
+ trainset_meta_added = trainset_meta.add(SHUFFLES[shuffle_to_add], overwrite=False)
+ indices = [s.index for s in trainset_meta_added.shuffles]
+ assert len(trainset_meta_added.shuffles) == len(shuffles) + 1
+ assert indices == list(sorted(shuffles + [shuffle_to_add]))
+
+
+@pytest.mark.parametrize(
+ "split1, split2, equal",
+ [
+ (BASE_SPLIT, EQ_SPLIT, True),
+ (BASE_SPLIT, ADD_SPLIT, False),
+ (BASE_SPLIT, ADD_SPLIT2, False),
+ (BASE_SPLIT, SUBS_SPLIT, False),
+ (BASE_SPLIT, DEL_SPLIT, False),
+ (BASE_SPLIT, DEL_SPLIT2, False),
+ ],
+)
+def test_data_split_equality(split1, split2, equal):
+ """Tests that equality functions as expected for DataSplits."""
+ print(split1)
+ print(split2)
+ print(equal)
+ assert (split1 == split2) == equal
+
+
+@pytest.mark.parametrize("split_idx", [1, 4, 20, 1000])
+@pytest.mark.parametrize("indices", [(2, 1), (10, 1), (1, 21, 20), (1, 2, 4, 3)])
+@pytest.mark.parametrize("sorted_indices", [(1, 2), (10, 12), (3, 4), (1, 1000, 1200)])
+def test_data_split_requires_sorted(split_idx: int, indices: tuple[int], sorted_indices: tuple[int]):
+ """Tests that equality functions as expected for DataSplits."""
+ with pytest.raises(RuntimeError):
+ metadata.DataSplit(train_indices=tuple(indices), test_indices=tuple(sorted_indices))
+
+ with pytest.raises(RuntimeError):
+ metadata.DataSplit(train_indices=tuple(sorted_indices), test_indices=tuple(indices))
+
+ with pytest.raises(RuntimeError):
+ metadata.DataSplit(train_indices=tuple(indices), test_indices=tuple(indices))
+
+ metadata.DataSplit(train_indices=tuple(sorted_indices), test_indices=tuple(sorted_indices))
+
+
+@pytest.mark.parametrize(
+ "shuffles",
+ [
+ ({"idx": 3, "train": [1], "test": [2], "train_fraction": 0.5},),
+ (
+ {"idx": 1, "train": [1], "test": [2], "train_fraction": 0.5},
+ {"idx": 5, "train": [1, 2, 3], "test": [4, 5], "train_fraction": 0.6},
+ {"idx": 4, "train": [1, 3], "test": [2], "train_fraction": 0.66},
+ ),
+ ],
+)
+def test_create_metadata_from_shuffles(tmpdir, shuffles):
+ """Tests that equality functions as expected for DataSplits."""
+ cfg, cfg_path, trainset_dir, meta_path = _create_project_with_config(tmpdir)
+ print(trainset_dir)
+ for s in shuffles:
+ doc = f"Documentation_data-ex_{s['train_fraction']}shuffle{s['idx']}.pickle"
+ doc_path = trainset_dir.join(doc)
+ with open(doc_path, "wb") as f:
+ pickle.dump([[], s["train"], s["test"], s["train_fraction"]], f, pickle.HIGHEST_PROTOCOL)
+
+ trainset_metadata = metadata.TrainingDatasetMetadata.create(cfg)
+ print()
+ print(trainset_metadata)
+ assert len(trainset_metadata.shuffles) == len(shuffles)
+
+ for shuffle_data, shuffle in zip(shuffles, trainset_metadata.shuffles, strict=False):
+ print(shuffle.index)
+ assert shuffle_data["idx"] == shuffle.index
+ assert shuffle_data["train_fraction"] == shuffle.train_fraction
+ assert tuple(shuffle_data["train"]) == shuffle.split.train_indices
+ assert tuple(shuffle_data["test"]) == shuffle.split.test_indices
+ print()
+
+
+def test_get_shuffle_engine_warns_when_metadata_get_fails_then_uses_model_folder(caplog):
+ """ValueError from metadata lookup is logged; engine is inferred from model folders."""
+ caplog.set_level(logging.WARNING)
+
+ cfg = {
+ "project_path": "/tmp/dlc-nonexistent-project-path",
+ "TrainingFraction": [0.95],
+ "Task": "t",
+ "date": "d",
+ "scorer": "s",
+ "iteration": 0,
+ }
+
+ meta_path_mock = MagicMock()
+ meta_path_mock.exists.return_value = True
+
+ training_meta_mock = MagicMock()
+ training_meta_mock.get.side_effect = ValueError("no shuffle for this index")
+
+ with (
+ patch.object(metadata.TrainingDatasetMetadata, "path", return_value=meta_path_mock),
+ patch.object(metadata.TrainingDatasetMetadata, "load", return_value=training_meta_mock),
+ patch.object(metadata, "find_engines_from_model_folders", return_value={Engine.PYTORCH}),
+ ):
+ engine = metadata.get_shuffle_engine(cfg, trainingsetindex=0, shuffle=1)
+
+ assert engine == Engine.PYTORCH
+ assert "no shuffle for this index" in caplog.text
+ assert "Falling back to detecting the engine from model folders" in caplog.text
+
+
+# ---------------------------------------------------------------------------
+# TrainingDatasetMetadata.__post_init__
+# ---------------------------------------------------------------------------
+
+
+def test_training_dataset_metadata_requires_sorted_shuffles(tmpdir):
+ """Constructor raises RuntimeError when shuffles are not sorted."""
+ cfg, *_ = _create_project_with_config(tmpdir)
+ with pytest.raises(RuntimeError):
+ metadata.TrainingDatasetMetadata(cfg, (SHUFFLES[2], SHUFFLES[1]))
+
+
+# ---------------------------------------------------------------------------
+# TrainingDatasetMetadata.get
+# ---------------------------------------------------------------------------
+
+
+def test_get_returns_matching_shuffle(tmpdir):
+ """get() returns the correct ShuffleMetadata."""
+ cfg, *_ = _create_project_with_config(tmpdir)
+ cfg["TrainingFraction"] = [0.5]
+ trainset_meta = metadata.TrainingDatasetMetadata(cfg, (SHUFFLES[1],))
+
+ result = trainset_meta.get(trainset_index=0, index=1)
+ assert result == SHUFFLES[1]
+
+
+def test_get_raises_when_trainset_index_out_of_bounds(tmpdir):
+ """get() raises ValueError when trainset_index >= len(TrainingFraction)."""
+ cfg, *_ = _create_project_with_config(tmpdir)
+ cfg["TrainingFraction"] = [0.5]
+ trainset_meta = metadata.TrainingDatasetMetadata(cfg, (SHUFFLES[1],))
+
+ with pytest.raises(ValueError, match="out of bounds"):
+ trainset_meta.get(trainset_index=1, index=1)
+
+
+def test_get_raises_when_shuffle_not_found(tmpdir):
+ """get() raises ValueError when no shuffle matches the given index."""
+ cfg, *_ = _create_project_with_config(tmpdir)
+ cfg["TrainingFraction"] = [0.5]
+ trainset_meta = metadata.TrainingDatasetMetadata(cfg, (SHUFFLES[1],))
+
+ with pytest.raises(ValueError, match="Could not find"):
+ trainset_meta.get(trainset_index=0, index=99)
+
+
+# ---------------------------------------------------------------------------
+# TrainingDatasetMetadata.save — lazy load_split branch
+# ---------------------------------------------------------------------------
+
+
+def test_save_loads_split_when_shuffle_has_no_split(tmpdir):
+ """save() calls load_split for shuffles where split is None."""
+ cfg, _cfg_path, trainset_dir, _meta_path = _create_project_with_config(tmpdir)
+ _create_doc_data(cfg, trainset_dir, 0.5, 1, [0, 1], [2, 3])
+
+ # Build a shuffle with split=None — mimics a load(load_splits=False) result
+ shuffle_no_split = metadata.ShuffleMetadata(
+ name=SHUFFLES[1].name,
+ train_fraction=0.5,
+ index=1,
+ engine=Engine.PYTORCH,
+ split=None,
+ )
+ trainset_meta = metadata.TrainingDatasetMetadata(cfg, (shuffle_no_split,))
+ # Should not raise; save() must call load_split internally
+ trainset_meta.save()
+
+ reloaded = metadata.TrainingDatasetMetadata.load(cfg, load_splits=True)
+ assert len(reloaded.shuffles) == 1
+ assert reloaded.shuffles[0].split is not None
+
+
+# ---------------------------------------------------------------------------
+# TrainingDatasetMetadata.load
+# ---------------------------------------------------------------------------
+
+
+def test_load_raises_when_metadata_file_missing(tmpdir):
+ """load() raises FileNotFoundError when metadata.yaml does not exist."""
+ cfg, *_ = _create_project_with_config(tmpdir)
+ with pytest.raises(FileNotFoundError):
+ metadata.TrainingDatasetMetadata.load(cfg)
+
+
+# ---------------------------------------------------------------------------
+# TrainingDatasetMetadata.create — empty trainset_path branch
+# ---------------------------------------------------------------------------
+
+
+def test_create_returns_empty_when_trainset_dir_missing(tmp_path):
+ """create() returns metadata with no shuffles when the trainset dir is absent."""
+ cfg = {
+ "Task": "t",
+ "date": "d",
+ "scorer": "s",
+ "iteration": 0,
+ "project_path": str(tmp_path),
+ }
+ trainset_meta = metadata.TrainingDatasetMetadata.create(cfg)
+ assert len(trainset_meta.shuffles) == 0
+
+
+# ---------------------------------------------------------------------------
+# update_metadata
+# ---------------------------------------------------------------------------
+
+
+def test_update_metadata_adds_shuffle(tmpdir):
+ """update_metadata adds a new shuffle and persists it."""
+ cfg, _cfg_path, trainset_dir, _meta_path = _create_project_with_config(tmpdir)
+ # Seed an existing metadata file with one shuffle
+ _create_doc_data(cfg, trainset_dir, 0.5, 1, [0, 1], [2, 3])
+ seed = metadata.TrainingDatasetMetadata(cfg, (SHUFFLES[1],))
+ seed.save()
+
+ metadata.update_metadata(
+ cfg,
+ train_fraction=0.5,
+ shuffle=2,
+ engine=Engine.PYTORCH,
+ train_indices=[0, 1],
+ test_indices=[2, 3],
+ )
+
+ reloaded = metadata.TrainingDatasetMetadata.load(cfg)
+ indices = [s.index for s in reloaded.shuffles]
+ assert 2 in indices
+
+
+def test_update_metadata_overwrite(tmpdir):
+ """update_metadata with overwrite=True replaces an existing shuffle."""
+ cfg, _cfg_path, trainset_dir, _meta_path = _create_project_with_config(tmpdir)
+ _create_doc_data(cfg, trainset_dir, 0.5, 1, [0, 1], [2, 3])
+ seed = metadata.TrainingDatasetMetadata(cfg, (SHUFFLES[1],))
+ seed.save()
+
+ metadata.update_metadata(
+ cfg,
+ train_fraction=0.5,
+ shuffle=1,
+ engine=Engine.TF,
+ train_indices=[0, 1],
+ test_indices=[2, 3],
+ overwrite=True,
+ )
+
+ reloaded = metadata.TrainingDatasetMetadata.load(cfg)
+ assert len(reloaded.shuffles) == 1
+ assert reloaded.shuffles[0].engine == Engine.TF
+
+
+def test_update_metadata_raises_without_overwrite(tmpdir):
+ """update_metadata raises RuntimeError when shuffle exists and overwrite=False."""
+ cfg, _cfg_path, trainset_dir, _meta_path = _create_project_with_config(tmpdir)
+ _create_doc_data(cfg, trainset_dir, 0.5, 1, [0, 1], [2, 3])
+ seed = metadata.TrainingDatasetMetadata(cfg, (SHUFFLES[1],))
+ seed.save()
+
+ with pytest.raises(RuntimeError):
+ metadata.update_metadata(
+ cfg,
+ train_fraction=0.5,
+ shuffle=1,
+ engine=Engine.PYTORCH,
+ train_indices=[0, 1],
+ test_indices=[2, 3],
+ overwrite=False,
+ )
+
+
+def _create_project_with_config(
+ tmp,
+ task: str = "example",
+ date: str = "Feb21",
+ scorer: str = "wayneRooney",
+ iteration: int = 0,
+ engine: str | None = None,
+):
+ project_dir = tmp.mkdir("ex-ample-2024-02-21")
+ cfg = {
+ "Task": task,
+ "date": date,
+ "scorer": scorer,
+ "iteration": iteration,
+ "project_path": str(project_dir),
+ }
+ if engine is not None:
+ cfg["engine"] = engine
+
+ cfg_path = project_dir.join("config.yaml")
+ with open(cfg_path, "w") as file:
+ YAML().dump(cfg, file)
+
+ it = f"iteration-{iteration}"
+ dir_name = "UnaugmentedDataSet_" + task + date
+ trainset_dir = project_dir.mkdir("training-datasets").mkdir(it).mkdir(dir_name)
+
+ meta_path = trainset_dir.join("metadata.yaml")
+ return cfg, cfg_path, trainset_dir, meta_path
+
+
+def _create_doc_data(
+ cfg,
+ trainset_dir,
+ train_frac,
+ shuffle,
+ train_indices,
+ test_indices,
+) -> None:
+ _, doc_path = auxiliaryfunctions.get_data_and_metadata_filenames(trainset_dir, train_frac, shuffle, cfg)
+ auxiliaryfunctions.save_metadata(doc_path, {}, list(train_indices), list(test_indices), train_frac)
diff --git a/tests/pose_estimation_pytorch/apis/test_apis_evaluate.py b/tests/pose_estimation_pytorch/apis/test_apis_evaluate.py
new file mode 100644
index 0000000000..253841df54
--- /dev/null
+++ b/tests/pose_estimation_pytorch/apis/test_apis_evaluate.py
@@ -0,0 +1,470 @@
+#
+# 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
+#
+from dataclasses import dataclass
+from unittest.mock import Mock, patch
+
+import numpy as np
+import pytest
+
+import deeplabcut.pose_estimation_pytorch.apis as apis
+import deeplabcut.pose_estimation_pytorch.data as data
+
+PREDICT = Mock()
+
+
+@patch("deeplabcut.pose_estimation_pytorch.apis.evaluation.predict", PREDICT)
+@pytest.mark.parametrize("num_individuals", [1, 2, 5])
+@pytest.mark.parametrize(
+ "bodyparts, error",
+ [
+ (["nose", "left_ear"], [5, 10]),
+ (["nose", "left_ear", "right_ear"], [2, 3, 4]),
+ ],
+)
+def test_evaluate_basic(
+ num_individuals: int,
+ bodyparts: list[str],
+ error: list[float],
+) -> None:
+ print()
+ gt, pred = generate_data(1, num_individuals, len(bodyparts), error)
+
+ pose_runner = Mock()
+
+ PREDICT.return_value = {img: {"bodyparts": pose} for img, pose in pred.items()}
+ loader = build_mock_loader(gt, num_individuals, bodyparts)
+ results, preds = apis.evaluate(pose_runner, loader, mode="test")
+ print("results", results)
+ np.testing.assert_almost_equal(results["rmse"], np.mean(error))
+
+
+@patch("deeplabcut.pose_estimation_pytorch.apis.evaluation.predict", PREDICT)
+@pytest.mark.parametrize("num_individuals", [1, 2, 5])
+@pytest.mark.parametrize(
+ "bodyparts, error",
+ [
+ (["nose", "left_ear"], [5, 10]),
+ (["nose", "left_ear", "right_ear"], [2, 3, 4]),
+ ],
+)
+@pytest.mark.parametrize(
+ "unique_bodyparts, unique_error",
+ [
+ (["top_left"], [2]),
+ (["top_left", "bottom_right"], [2, 3]),
+ ],
+)
+def test_evaluate_with_unique_bodyparts(
+ num_individuals: int,
+ bodyparts: list[str],
+ error: list[float],
+ unique_bodyparts: list[str],
+ unique_error: list[float],
+) -> None:
+ print()
+ num_images = 5
+ gt, pred = generate_data(num_images, num_individuals, len(bodyparts), error)
+ gt_unique, pred_unique = generate_data(num_images, 1, len(unique_bodyparts), unique_error)
+
+ pose_runner = Mock()
+ PREDICT.return_value = {
+ img: {"bodyparts": pose, "unique_bodyparts": pred_unique[img]} for img, pose in pred.items()
+ }
+ loader = build_mock_loader(gt, num_individuals, bodyparts, gt_unique=gt_unique, unique=unique_bodyparts)
+ results, preds = apis.evaluate(pose_runner, loader, mode="test")
+ idv_errors = np.tile(error, (num_individuals, 1)).reshape(-1)
+ expected_rmse = np.mean(np.concatenate([idv_errors, unique_error]))
+ print(num_individuals)
+ print(error)
+ print(idv_errors)
+ print(unique_error)
+ print(np.concatenate([idv_errors, unique_error]))
+ print(expected_rmse)
+ print("results", results)
+ np.testing.assert_almost_equal(results["rmse"], expected_rmse)
+
+
+@dataclass
+class CompTestConfig:
+ num_individuals: int = 1
+ bodyparts: tuple[str, ...] = ("nose", "left_ear")
+ error: tuple[float, ...] = (5, 10)
+ unique_bodyparts: tuple[str, ...] = ("top_left",)
+ unique_error: tuple[float, ...] = (2,)
+ comparison_bodyparts: str | list[str] | None = None
+ expected_error: float = (2 + 5 + 10) / 3
+
+ def num_bpt(self) -> int:
+ return len(self.bodyparts)
+
+ def num_unique(self) -> int:
+ return len(self.unique_bodyparts)
+
+
+@patch("deeplabcut.pose_estimation_pytorch.apis.evaluation.predict", PREDICT)
+@pytest.mark.parametrize(
+ "cfg",
+ [
+ CompTestConfig(comparison_bodyparts=None),
+ CompTestConfig(comparison_bodyparts="all"),
+ CompTestConfig(comparison_bodyparts=["nose", "left_ear", "top_left"]),
+ CompTestConfig(num_individuals=2, expected_error=(2 + 5 + 5 + 10 + 10) / 5),
+ CompTestConfig(comparison_bodyparts="nose", expected_error=5),
+ CompTestConfig(comparison_bodyparts=["nose"], expected_error=5),
+ CompTestConfig(comparison_bodyparts=["left_ear"], expected_error=10),
+ CompTestConfig(comparison_bodyparts=["nose", "left_ear"], expected_error=7.5),
+ CompTestConfig(comparison_bodyparts="top_left", expected_error=2),
+ CompTestConfig(comparison_bodyparts=["top_left"], expected_error=2),
+ CompTestConfig(
+ unique_bodyparts=("a", "b", "c"),
+ unique_error=(3.0, 4.0, 5.0),
+ comparison_bodyparts=["a", "b", "c"],
+ expected_error=4,
+ ),
+ CompTestConfig(
+ num_individuals=1,
+ unique_bodyparts=("a", "b", "c"),
+ unique_error=(3.0, 4.0, 5.0),
+ comparison_bodyparts=["nose", "a", "b", "c"],
+ expected_error=(5.0 + 3.0 + 4.0 + 5.0) / 4,
+ ),
+ CompTestConfig(
+ num_individuals=7,
+ unique_bodyparts=("a", "b", "c"),
+ unique_error=(3.0, 4.0, 5.0),
+ comparison_bodyparts=["nose", "left_ear", "a", "b"],
+ expected_error=((7 * 5) + (7 * 10) + 3.0 + 4.0) / (7 + 7 + 2),
+ ),
+ ],
+)
+def test_evaluate_with_comparison_bodyparts(cfg: CompTestConfig) -> None:
+ print()
+ num_images = 5
+ gt, pred = generate_data(num_images, cfg.num_individuals, cfg.num_bpt(), cfg.error)
+ gt_unique, pred_unique = generate_data(num_images, 1, cfg.num_unique(), cfg.unique_error)
+
+ pose_runner = Mock()
+ PREDICT.return_value = {
+ img: {"bodyparts": pose, "unique_bodyparts": pred_unique[img]} for img, pose in pred.items()
+ }
+ loader = build_mock_loader(
+ gt,
+ cfg.num_individuals,
+ cfg.bodyparts,
+ gt_unique=gt_unique,
+ unique=cfg.unique_bodyparts,
+ )
+ results, preds = apis.evaluate(
+ pose_runner,
+ loader,
+ mode="test",
+ comparison_bodyparts=cfg.comparison_bodyparts,
+ )
+ print(cfg)
+ print("results", results)
+ np.testing.assert_almost_equal(results["rmse"], cfg.expected_error)
+
+
+@dataclass
+class KeypointData:
+ img: int
+ idv: int
+ bodypart: str
+ gt: tuple[float, float]
+ pred: tuple[float, float]
+ score: float
+
+ def image(self) -> str:
+ return f"image_{self.img:04d}.png"
+
+ def error(self) -> float:
+ return np.linalg.norm(np.asarray(self.gt, dtype=float) - np.asarray(self.pred, dtype=float)).item()
+
+
+@patch("deeplabcut.pose_estimation_pytorch.apis.evaluation.predict", PREDICT)
+@pytest.mark.parametrize(
+ "pcutoff",
+ [0.4, 0.6, 0.8, [0.3, 0.5, 0.7]],
+)
+@pytest.mark.parametrize(
+ "keypoints",
+ [
+ [
+ KeypointData(img=0, idv=0, bodypart="a", gt=(10, 10), pred=(11, 10), score=0.7),
+ KeypointData(img=0, idv=0, bodypart="b", gt=(20, 20), pred=(21, 20), score=0.7),
+ KeypointData(img=0, idv=0, bodypart="c", gt=(20, 20), pred=(20, 22), score=0.5),
+ ],
+ [
+ KeypointData(img=0, idv=0, bodypart="a", gt=(10, 10), pred=(11, 10), score=0.7),
+ KeypointData(img=0, idv=0, bodypart="b", gt=(20, 20), pred=(21, 20), score=0.5),
+ KeypointData(img=0, idv=0, bodypart="c", gt=(30, 30), pred=(30, 32), score=0.2),
+ KeypointData(img=0, idv=1, bodypart="a", gt=(40, 10), pred=(41, 10), score=0.7),
+ KeypointData(img=0, idv=1, bodypart="b", gt=(50, 20), pred=(49, 20), score=0.5),
+ KeypointData(img=0, idv=1, bodypart="c", gt=(60, 20), pred=(58, 20), score=0.2),
+ ],
+ ],
+)
+def test_evaluate_with_pcutoff(
+ pcutoff: float | list[float],
+ keypoints: list[KeypointData],
+) -> None:
+ print()
+
+ images = {d.image() for d in keypoints}
+ individuals = list({d.idv for d in keypoints if d.idv != -1})
+ bodyparts = list({d.bodypart for d in keypoints if d.idv != -1})
+ unique_bodyparts = list({d.bodypart for d in keypoints if d.idv == -1})
+
+ num_idv = len(individuals)
+ num_bodyparts = len(bodyparts)
+ len(unique_bodyparts)
+
+ gt, pred = {}, {}
+ for img in images:
+ gt[img] = np.zeros((num_idv, num_bodyparts, 3))
+ pred[img] = np.zeros((num_idv, num_bodyparts, 3))
+
+ errors = []
+ errors_cutoff = []
+ for kpt in keypoints:
+ img = kpt.image()
+ bpt = bodyparts.index(kpt.bodypart)
+
+ gt[img][kpt.idv, bpt, :2] = kpt.gt
+ gt[img][kpt.idv, bpt, 2] = 2
+ pred[img][kpt.idv, bpt, :2] = kpt.pred
+ pred[img][kpt.idv, bpt, 2] = kpt.score
+
+ if isinstance(pcutoff, list):
+ bpt_cutoff = pcutoff[bpt]
+ else:
+ bpt_cutoff = pcutoff
+
+ errors.append(kpt.error())
+ if kpt.score >= bpt_cutoff:
+ errors_cutoff.append(kpt.error())
+
+ print(errors)
+ print(errors_cutoff)
+
+ pose_runner = Mock()
+ PREDICT.return_value = {img: {"bodyparts": pose} for img, pose in pred.items()}
+ loader = build_mock_loader(gt, num_idv, bodyparts)
+ results, preds = apis.evaluate(pose_runner, loader, mode="test", pcutoff=pcutoff)
+ print("results", results)
+ np.testing.assert_almost_equal(results["rmse"], np.mean(errors))
+ np.testing.assert_almost_equal(results["rmse_pcutoff"], np.mean(errors_cutoff))
+ if "rmse_detections" in results:
+ np.testing.assert_almost_equal(results["rmse_detections"], np.mean(errors))
+ np.testing.assert_almost_equal(results["rmse_detections_pcutoff"], np.mean(errors_cutoff))
+
+
+@patch("deeplabcut.pose_estimation_pytorch.apis.evaluation.predict", PREDICT)
+@pytest.mark.parametrize(
+ "pcutoff",
+ [
+ 0.4,
+ 0.6,
+ 0.8,
+ [0.3, 0.5, 0.7, 0.4, 0.6],
+ [0.25, 0.43, 0.61, 0.46, 0.92],
+ [0.12, 0.15, 0.92, 0.97, 0.85],
+ [0.92, 0.97, 0.85, 0.12, 0.15],
+ ],
+)
+@pytest.mark.parametrize(
+ "keypoints",
+ [
+ [
+ KeypointData(img=0, idv=0, bodypart="a", gt=(10, 10), pred=(11, 10), score=0.7),
+ KeypointData(img=0, idv=0, bodypart="b", gt=(20, 20), pred=(21, 20), score=0.7),
+ KeypointData(img=0, idv=0, bodypart="c", gt=(20, 20), pred=(20, 22), score=0.5),
+ KeypointData(img=0, idv=-1, bodypart="u1", gt=(20, 20), pred=(20, 22), score=0.5),
+ KeypointData(img=0, idv=-1, bodypart="u2", gt=(20, 20), pred=(20, 22), score=0.3),
+ ],
+ [
+ KeypointData(img=0, idv=0, bodypart="a", gt=(10, 10), pred=(11, 10), score=0.7),
+ KeypointData(img=0, idv=0, bodypart="b", gt=(20, 20), pred=(21, 20), score=0.5),
+ KeypointData(img=0, idv=0, bodypart="c", gt=(30, 30), pred=(30, 32), score=0.2),
+ KeypointData(img=0, idv=1, bodypart="a", gt=(40, 10), pred=(41, 10), score=0.7),
+ KeypointData(img=0, idv=1, bodypart="b", gt=(50, 20), pred=(49, 20), score=0.5),
+ KeypointData(img=0, idv=1, bodypart="c", gt=(60, 20), pred=(58, 20), score=0.2),
+ KeypointData(img=0, idv=-1, bodypart="u1", gt=(2, 3), pred=(3, 3), score=0.7),
+ KeypointData(img=0, idv=-1, bodypart="u2", gt=(20, 20), pred=(20, 22), score=0.9),
+ ],
+ [
+ KeypointData(img=0, idv=0, bodypart="a", gt=(8, 13), pred=(11, 10), score=0.7),
+ KeypointData(img=0, idv=0, bodypart="b", gt=(20, 27), pred=(21, 20), score=0.5),
+ KeypointData(img=0, idv=0, bodypart="c", gt=(30, 36), pred=(30, 32), score=0.2),
+ KeypointData(img=0, idv=-1, bodypart="u1", gt=(2, 3), pred=(3, 3), score=0.7),
+ KeypointData(img=0, idv=-1, bodypart="u2", gt=(20, 20), pred=(20, 22), score=0.9),
+ KeypointData(img=1, idv=0, bodypart="a", gt=(15, 20), pred=(41, 10), score=0.7),
+ KeypointData(img=1, idv=0, bodypart="b", gt=(20, 12), pred=(49, 20), score=0.5),
+ KeypointData(img=1, idv=0, bodypart="c", gt=(17, 32), pred=(58, 20), score=0.2),
+ KeypointData(img=1, idv=-1, bodypart="u1", gt=(37, 4), pred=(3, 3), score=0.7),
+ KeypointData(img=1, idv=-1, bodypart="u2", gt=(12, 6), pred=(20, 22), score=0.9),
+ ],
+ [
+ KeypointData(img=0, idv=0, bodypart="a", gt=(8, 13), pred=(11, 10), score=0.7),
+ KeypointData(img=0, idv=0, bodypart="b", gt=(20, 27), pred=(21, 20), score=0.5),
+ KeypointData(img=0, idv=-1, bodypart="u1", gt=(30, 36), pred=(30, 32), score=0.2),
+ KeypointData(img=0, idv=-1, bodypart="u2", gt=(2, 3), pred=(3, 3), score=0.7),
+ KeypointData(img=0, idv=-1, bodypart="u3", gt=(20, 20), pred=(20, 22), score=0.9),
+ KeypointData(img=1, idv=0, bodypart="a", gt=(15, 20), pred=(41, 10), score=0.7),
+ KeypointData(img=1, idv=0, bodypart="b", gt=(20, 12), pred=(49, 20), score=0.5),
+ KeypointData(img=1, idv=-1, bodypart="u1", gt=(17, 32), pred=(58, 20), score=0.2),
+ KeypointData(img=1, idv=-1, bodypart="u2", gt=(37, 4), pred=(3, 3), score=0.7),
+ KeypointData(img=1, idv=-1, bodypart="u3", gt=(12, 6), pred=(20, 22), score=0.9),
+ ],
+ ],
+)
+def test_evaluate_with_pcutoff_and_unique_bodyparts(
+ pcutoff: float | list[float],
+ keypoints: list[KeypointData],
+) -> None:
+ print()
+
+ images = {d.image() for d in keypoints}
+ individuals = list({d.idv for d in keypoints if d.idv != -1})
+ bodyparts = list({d.bodypart for d in keypoints if d.idv != -1})
+ unique_bodyparts = list({d.bodypart for d in keypoints if d.idv == -1})
+
+ num_idv = len(individuals)
+ num_bodyparts = len(bodyparts)
+ num_unique = len(unique_bodyparts)
+
+ gt, pred, gt_unique, pred_unique = {}, {}, {}, {}
+ for img in images:
+ gt[img] = np.zeros((num_idv, num_bodyparts, 3))
+ pred[img] = np.zeros((num_idv, num_bodyparts, 3))
+ gt_unique[img] = np.zeros((1, num_unique, 3))
+ pred_unique[img] = np.zeros((1, num_unique, 3))
+
+ errors, errors_cutoff = [], []
+ for kpt in keypoints:
+ img = kpt.image()
+ if kpt.idv == -1:
+ idv, bpt = 0, unique_bodyparts.index(kpt.bodypart)
+ pcutoff_idx = bpt + len(bodyparts) # offset by number of bodyparts
+ gt_data, pred_data = gt_unique[img], pred_unique[img]
+ else:
+ idv, bpt = kpt.idv, bodyparts.index(kpt.bodypart)
+ pcutoff_idx = bpt
+ gt_data, pred_data = gt[img], pred[img]
+
+ gt_data[idv, bpt, :2] = kpt.gt
+ gt_data[idv, bpt, 2] = 2
+ pred_data[idv, bpt, :2] = kpt.pred
+ pred_data[idv, bpt, 2] = kpt.score
+
+ if isinstance(pcutoff, list):
+ bpt_cutoff = pcutoff[pcutoff_idx]
+ else:
+ bpt_cutoff = pcutoff
+
+ errors.append(kpt.error())
+ if kpt.score >= bpt_cutoff:
+ errors_cutoff.append(kpt.error())
+
+ print(errors)
+ print(errors_cutoff)
+
+ pose_runner = Mock()
+ PREDICT.return_value = {
+ img: {"bodyparts": pose, "unique_bodyparts": pred_unique[img]} for img, pose in pred.items()
+ }
+ loader = build_mock_loader(gt, num_idv, bodyparts, gt_unique, unique_bodyparts)
+ results, preds = apis.evaluate(pose_runner, loader, mode="test", pcutoff=pcutoff)
+
+ print("results", results)
+ np.testing.assert_almost_equal(results["rmse"], np.mean(errors))
+ np.testing.assert_almost_equal(results["rmse_pcutoff"], np.mean(errors_cutoff))
+ if "rmse_detections" in results:
+ np.testing.assert_almost_equal(results["rmse_detections"], np.mean(errors))
+ np.testing.assert_almost_equal(results["rmse_detections_pcutoff"], np.mean(errors_cutoff))
+
+
+def generate_data(
+ num_images: int,
+ num_individuals: int,
+ num_bodyparts: int,
+ error: list[float] | tuple[float, ...] | np.ndarray,
+ cutoffs: list[float] | tuple[float, ...] | np.ndarray | None = None,
+ error_cutoff: list[float] | tuple[float, ...] | np.ndarray | None = None,
+) -> tuple[dict[str, np.ndarray], dict[str, np.ndarray]]:
+ num_elems = num_individuals * num_bodyparts
+ shape = num_individuals, num_bodyparts, 3
+ error = np.asarray(error)
+ coord_error = (np.sqrt(2) / 2) * error
+
+ gt, pred = {}, {}
+ for img in range(num_images):
+ gt_pose = 100 * np.arange(3 * num_elems, dtype=float).reshape(shape)
+ gt_pose[..., 2] = 2
+ gt[f"img_{img:04d}.png"] = gt_pose
+
+ pred_pose = np.ones(shape, dtype=float)
+ pred_pose[..., :2] = gt_pose[..., :2]
+ pred_pose[:, :, 0] += coord_error
+ pred_pose[:, :, 1] += coord_error
+ pred[f"img_{img:04d}.png"] = pred_pose
+
+ if error_cutoff is not None and cutoffs is not None:
+ for img in range(num_images):
+ gt_pose = 100 * np.arange(3 * num_elems, dtype=float).reshape(shape)
+ gt_pose[..., 2] = 2
+ gt[f"img_{num_images + img:04d}.png"] = gt_pose
+
+ pred_pose = np.ones(shape, dtype=float)
+ pred_pose[..., :2] = gt_pose[..., :2]
+ pred_pose[..., 2] = cutoffs
+ pred_pose[:, :, 0] += coord_error
+ pred_pose[:, :, 1] += coord_error
+ pred[f"img_{num_images + img:04d}.png"] = pred_pose
+
+ return gt, pred
+
+
+def build_mock_loader(
+ gt: dict[str, np.ndarray],
+ num_individuals: int,
+ bodyparts: list[str] | tuple[str, ...],
+ gt_unique: dict[str, np.ndarray] | None = None,
+ unique: list[str] | tuple[str, ...] | None = None,
+) -> Mock:
+ if unique is None:
+ unique = []
+
+ def _gt(mode: str, unique_bodypart: bool = False) -> dict[str, np.ndarray]:
+ if unique_bodypart:
+ print("LOADING UNIQUE GT")
+ return gt_unique
+ print("LOADING GT")
+ return gt
+
+ individuals = [f"animal_{i:03d}" for i in range(num_individuals)]
+ loader = Mock()
+ loader.get_dataset_parameters.return_value = data.PoseDatasetParameters(
+ bodyparts=bodyparts,
+ unique_bpts=unique,
+ individuals=individuals,
+ )
+ loader.ground_truth_keypoints = _gt
+ loader.model_cfg = {
+ "metadata": {
+ "bodyparts": bodyparts,
+ "unique_bodyparts": unique,
+ "individuals": individuals,
+ "with_identity": False,
+ },
+ "train_settings": {},
+ }
+ return loader
diff --git a/tests/pose_estimation_pytorch/apis/test_apis_export.py b/tests/pose_estimation_pytorch/apis/test_apis_export.py
new file mode 100644
index 0000000000..021a994f96
--- /dev/null
+++ b/tests/pose_estimation_pytorch/apis/test_apis_export.py
@@ -0,0 +1,308 @@
+#
+# 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
+#
+"""Tests exporting models."""
+
+import copy
+import shutil
+from pathlib import Path
+from unittest.mock import Mock, patch
+
+import pytest
+import torch
+from ruamel.yaml.scalarstring import SingleQuotedScalarString as SQS
+
+import deeplabcut.pose_estimation_pytorch.apis.export as export
+import deeplabcut.utils.auxiliaryfunctions as af
+from deeplabcut.pose_estimation_pytorch import Task
+from deeplabcut.pose_estimation_pytorch.runners.snapshots import Snapshot
+
+
+@pytest.fixture()
+def project_dir(tmp_path_factory) -> Path:
+ project_dir = tmp_path_factory.mktemp("tmp-project")
+ print("\nTemporary project directory:")
+ print(str(project_dir))
+ print("---")
+ yield project_dir
+ shutil.rmtree(str(project_dir))
+
+
+def _mock_multianimal_project(project_dir: Path):
+ video_dir = project_dir / "videos"
+ video_dir.mkdir(exist_ok=True)
+
+ cfg_file, yaml_file = af.create_config_template(multianimal=True)
+ yaml_file.width = 10_000
+
+ cfg_file["Task"] = "mock"
+ cfg_file["scorer"] = "mock"
+ cfg_file["video_sets"] = {SQS((video_dir / "vid.mp4").as_posix()): {"crop": "0, 640, 0, 480"}}
+ cfg_file["project_path"] = project_dir.as_posix()
+ cfg_file["individuals"] = ["a", "b"]
+ cfg_file["uniquebodyparts"] = []
+ cfg_file["multianimalbodyparts"] = ["k1", "k2", "k3"]
+ cfg_file["bodyparts"] = "MULTI!"
+
+ with open(project_dir / "config.yaml", "w", encoding="utf-8") as f:
+ yaml_file.dump(cfg_file, f)
+
+
+def _make_mock_loader(
+ project_path: Path,
+ project_task: str,
+ project_iteration: int,
+ model_folder: Path,
+ net_type: str,
+ pose_task: Task,
+ default_snapshot_index: int | str,
+ default_detector_snapshot_index: int | str,
+) -> Mock:
+ loader = Mock()
+ loader.project_path = project_path
+ loader.model_folder = model_folder
+ loader.pose_task = pose_task
+ loader.shuffle = 0
+
+ loader.project_cfg = dict(
+ project_path=str(project_path),
+ Task=project_task,
+ date="Jan12",
+ TrainingFraction=[0.95],
+ snapshotindex=default_snapshot_index,
+ detector_snapshotindex=default_detector_snapshot_index,
+ iteration=project_iteration,
+ )
+ loader.model_cfg = dict(
+ net_type=net_type,
+ metadata=dict(
+ project_path=str(project_path),
+ pose_config_path=str(loader.model_folder / "pytorch_config.yaml"),
+ ),
+ weight_init=None,
+ resume_training_from=None,
+ )
+ if pose_task == Task.TOP_DOWN:
+ loader.model_cfg["detector"] = dict(resume_training_from=None)
+
+ return loader
+
+
+def _get_export_model_data(
+ project_dir: Path,
+ num_snapshots: int,
+ task: Task,
+ project_iteration: int = 0,
+):
+ _mock_multianimal_project(project_dir)
+
+ model_dir = Path(project_dir) / f"iteration-{project_iteration}" / "fake-shuffle-0"
+ model_dir.mkdir(exist_ok=True, parents=True)
+ snapshots = []
+ snapshot_data = []
+ for i in range(num_snapshots):
+ snapshot = dict(model=dict(idx=i))
+ snapshot_path = model_dir / f"snapshot-{i:03}.pt"
+ torch.save(snapshot, snapshot_path)
+ snapshots.append(Snapshot(best=False, epochs=i, path=snapshot_path))
+ snapshot_data.append(snapshot)
+
+ detector_snapshots = []
+ detector_data = []
+ if task == Task.TOP_DOWN:
+ for i in range(num_snapshots):
+ snapshot = dict(model=dict(idx=i))
+ snapshot_path = model_dir / f"snapshot-detector-{i:03}.pt"
+ torch.save(snapshot, snapshot_path)
+ detector_data.append(snapshot)
+ detector_snapshots.append(Snapshot(best=False, epochs=i, path=snapshot_path))
+
+ mock_loader = _make_mock_loader(
+ project_path=project_dir,
+ project_task="mock",
+ project_iteration=project_iteration,
+ model_folder=model_dir,
+ net_type="fake-net",
+ pose_task=task,
+ default_snapshot_index=-1,
+ default_detector_snapshot_index=-1,
+ )
+ return mock_loader, snapshots, snapshot_data, detector_snapshots, detector_data
+
+
+@pytest.mark.parametrize(
+ "task, num_snapshots, idx, detector_idx",
+ [
+ (Task.BOTTOM_UP, 10, 0, None),
+ (Task.BOTTOM_UP, 10, 5, None),
+ (Task.BOTTOM_UP, 10, -1, None),
+ (Task.TOP_DOWN, 10, 0, 0),
+ (Task.TOP_DOWN, 10, -1, 0),
+ (Task.TOP_DOWN, 10, -1, 5),
+ (Task.TOP_DOWN, 10, -1, -1),
+ ],
+)
+def test_export_model(
+ project_dir,
+ task: Task,
+ num_snapshots: int,
+ idx: int,
+ detector_idx: int | None,
+):
+ test_data = _get_export_model_data(project_dir, num_snapshots, task)
+ mock_loader, snapshots, snapshot_data, detector_snapshots, detector_data = test_data
+
+ def get_mock_loader(*args, **kwargs):
+ return mock_loader
+
+ with patch(
+ "deeplabcut.pose_estimation_pytorch.apis.export.dlc3_data.DLCLoader",
+ get_mock_loader,
+ ):
+ # export the model
+ export.export_model(
+ project_dir / "config.yaml",
+ snapshotindex=idx,
+ detector_snapshot_index=detector_idx,
+ )
+
+ # check that the correct snapshot was exported
+ snapshot = snapshots[idx]
+ detector = None
+ if task == Task.TOP_DOWN:
+ detector = detector_snapshots[detector_idx]
+
+ dir_name = export.get_export_folder_name(mock_loader)
+ filename = export.get_export_filename(mock_loader, snapshot, detector)
+ expected_export = project_dir / "exported-models-pytorch" / dir_name / filename
+ assert expected_export.exists()
+
+ # check that content of the exports are correct
+ exported_data = torch.load(expected_export, weights_only=True)
+ assert isinstance(exported_data, dict)
+ assert "config" in exported_data
+ assert exported_data["config"] == mock_loader.model_cfg
+
+ assert "pose" in exported_data
+ assert exported_data["pose"] == snapshot_data[idx]["model"]
+
+ if task == Task.TOP_DOWN:
+ assert "detector" in exported_data
+ assert exported_data["detector"] == detector_data[detector_idx]["model"]
+
+
+@patch("deeplabcut.pose_estimation_pytorch.apis.export.wipe_paths_from_model_config")
+@pytest.mark.parametrize("task", [Task.BOTTOM_UP, Task.TOP_DOWN])
+def test_export_model_clear_paths(mock_wipe: Mock, project_dir, task: Task):
+ test_data = _get_export_model_data(project_dir, 1, task)
+ mock_loader, snapshots, snapshot_data, detector_snapshots, detector_data = test_data
+
+ def get_mock_loader(*args, **kwargs):
+ return mock_loader
+
+ with patch(
+ "deeplabcut.pose_estimation_pytorch.apis.export.dlc3_data.DLCLoader",
+ get_mock_loader,
+ ):
+ export.export_model(project_dir / "config.yaml", wipe_paths=True)
+
+ # check that wipe_paths_from_model_config was called
+ assert mock_wipe.call_count == 1
+
+
+@pytest.mark.parametrize("task", [Task.BOTTOM_UP, Task.TOP_DOWN])
+@pytest.mark.parametrize("overwrite", [True, False])
+def test_export_overwrite(project_dir, task: Task, overwrite: bool):
+ test_data = _get_export_model_data(project_dir, 1, task)
+ mock_loader, snapshots, snapshot_data, detector_snapshots, detector_data = test_data
+ snapshot = snapshots[0]
+ detector = None if task == Task.BOTTOM_UP else detector_snapshots[0]
+
+ def get_mock_loader(*args, **kwargs):
+ return mock_loader
+
+ with patch(
+ "deeplabcut.pose_estimation_pytorch.apis.export.dlc3_data.DLCLoader",
+ get_mock_loader,
+ ):
+ dir_name = export.get_export_folder_name(mock_loader)
+ filename = export.get_export_filename(mock_loader, snapshot, detector)
+ expected_export = project_dir / "exported-models-pytorch" / dir_name / filename
+ expected_export.parent.mkdir(exist_ok=False, parents=True)
+
+ # add existing data
+ assert not expected_export.exists()
+ existing_data = dict()
+ torch.save(existing_data, expected_export)
+
+ # export data
+ export.export_model(project_dir / "config.yaml", overwrite=overwrite)
+
+ exported_data = torch.load(expected_export, weights_only=True)
+
+ if overwrite:
+ assert existing_data != exported_data
+ else:
+ assert existing_data == exported_data
+
+
+@pytest.mark.parametrize("task", [Task.BOTTOM_UP, Task.TOP_DOWN])
+@pytest.mark.parametrize("iteration", [5, 12])
+def test_export_change_iteration(project_dir, task: Task, iteration: int):
+ test_data = _get_export_model_data(
+ project_dir,
+ 1,
+ task,
+ project_iteration=0,
+ )
+ mock_loader, snapshots, snapshot_data, detector_snapshots, detector_data = test_data
+ snapshot = snapshots[0]
+ detector = None if task == Task.BOTTOM_UP else detector_snapshots[0]
+
+ loader_diff_iter = _get_export_model_data(project_dir, 1, task, project_iteration=iteration)[0]
+
+ def get_mock_loader(config, *args, **kwargs):
+ _loader = copy.deepcopy(mock_loader)
+ if isinstance(config, dict):
+ _loader = copy.deepcopy(mock_loader)
+ _loader.project_cfg = config
+ return _loader
+
+ def read_mock_config(*args, **kwargs):
+ return copy.deepcopy(mock_loader.project_cfg)
+
+ # patch the DLCLoader but also read_config
+ with patch(
+ "deeplabcut.pose_estimation_pytorch.apis.export.dlc3_data.DLCLoader",
+ get_mock_loader,
+ ):
+ with patch(
+ "deeplabcut.pose_estimation_pytorch.apis.export.af.read_config",
+ read_mock_config,
+ ):
+ # check no exports exist yet
+ for loader in [mock_loader, loader_diff_iter]:
+ dir_name = export.get_export_folder_name(loader)
+ filename = export.get_export_filename(loader, snapshot, detector)
+ assert not (project_dir / "exported-models-pytorch" / dir_name / filename).exists()
+
+ # export data
+ export.export_model(project_dir / "config.yaml", iteration=iteration)
+
+ # check the export exists for the correct iteration
+ for loader, file_should_exist in [
+ (mock_loader, False),
+ (loader_diff_iter, True),
+ ]:
+ dir_name = export.get_export_folder_name(loader)
+ filename = export.get_export_filename(loader, snapshot, detector)
+ expected = project_dir / "exported-models-pytorch" / dir_name / filename
+ expected_exists = expected.exists()
+ assert expected_exists == file_should_exist
diff --git a/tests/pose_estimation_pytorch/apis/test_create_tracking_dataset.py b/tests/pose_estimation_pytorch/apis/test_create_tracking_dataset.py
new file mode 100644
index 0000000000..e9ba4996d3
--- /dev/null
+++ b/tests/pose_estimation_pytorch/apis/test_create_tracking_dataset.py
@@ -0,0 +1,74 @@
+#
+# 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
+#
+"""Tests method to create the tracking dataset in PyTorch."""
+
+from pathlib import Path
+
+import torch
+
+import deeplabcut.pose_estimation_pytorch as dlc_torch
+import deeplabcut.pose_estimation_pytorch.apis.tracking_dataset as tracking_dataset
+import deeplabcut.pose_estimation_pytorch.models as models
+
+
+class MockLoader(dlc_torch.Loader):
+ """Mock loader for data."""
+
+ def __init__(self, tmp_folder: Path, bodyparts: list[str] | None = None):
+ if bodyparts is None:
+ bodyparts = ["nose", "left_eye", "right_eye", "tail_base"]
+ self.bodyparts = bodyparts
+
+ model_config_path = tmp_folder / "pytorch_config.yaml"
+ dlc_torch.config.make_pytorch_pose_config(
+ project_config=dlc_torch.config.make_basic_project_config(
+ dataset_path=str(tmp_folder),
+ bodyparts=self.bodyparts,
+ max_individuals=3,
+ ),
+ pose_config_path=tmp_folder / "pytorch_config.yaml",
+ net_type="resnet_50",
+ save=True,
+ )
+ super().__init__(
+ str(tmp_folder),
+ str(tmp_folder / "labeled-data"),
+ model_config_path,
+ )
+
+ def load_data(self, mode: str = "train") -> dict[str, list[dict]]:
+ return {
+ "annotations": [],
+ "categories": [],
+ "images": [],
+ }
+
+ def get_dataset_parameters(self) -> dlc_torch.PoseDatasetParameters:
+ return dlc_torch.PoseDatasetParameters(
+ bodyparts=self.bodyparts,
+ unique_bpts=[],
+ individuals=self.model_cfg["metadata"]["individuals"],
+ )
+
+
+def test_build_feature_extraction_runner(tmp_path_factory):
+ tmp_folder = Path(tmp_path_factory.mktemp("tmp-project"))
+
+ loader = MockLoader(tmp_folder=tmp_folder)
+ model = models.PoseModel.build(loader.model_cfg["model"])
+ snapshot_path = loader.model_folder / "snapshot.pt"
+ torch.save(dict(model=model.state_dict()), snapshot_path)
+ _ = tracking_dataset.build_feature_extraction_runner(
+ loader=loader,
+ snapshot_path=snapshot_path,
+ device="cpu",
+ batch_size=1,
+ )
diff --git a/tests/pose_estimation_pytorch/apis/test_tracklets.py b/tests/pose_estimation_pytorch/apis/test_tracklets.py
new file mode 100644
index 0000000000..8e06b4a55e
--- /dev/null
+++ b/tests/pose_estimation_pytorch/apis/test_tracklets.py
@@ -0,0 +1,100 @@
+import numpy as np
+import pandas as pd
+import pytest
+
+from deeplabcut.pose_estimation_pytorch.apis.tracklets import build_tracklets
+
+
+@pytest.mark.parametrize(
+ "assemblies_data, inference_cfg, joints, scorer, num_frames, unique_bodyparts",
+ [
+ (
+ # assemblies_data
+ {
+ "single": {
+ 0: np.array([[1, 2, 0.9]]),
+ 1: np.array([[1, 3, 0.7]]),
+ 2: np.array([[0, 1, 0.9]]),
+ },
+ 0: [
+ np.array([[10, 20, 0.9, -1], [30, 40, 0.8, -1]]),
+ np.array([[13, 23, 0.9, -1], [33, 43, 0.8, -1]]),
+ ],
+ 1: [
+ np.array([[9, 19, 0.9, -1], [29, 41, 0.8, -1]]),
+ np.array([[15, 21, 0.9, -1], [35, 45, 0.8, -1]]),
+ ],
+ 2: [
+ np.array([[13, 23, 0.9, -1], [33, 43, 0.8, -1]]),
+ np.array([[10, 20, 0.9, -1], [30, 40, 0.8, -1]]),
+ ],
+ },
+ # inference_cfg
+ {"max_age": 3, "min_hits": 1, "topktoretain": 1, "pcutoff": 0.5},
+ # joints
+ ["nose", "ear"],
+ # scorer
+ "DLC",
+ # num_frames
+ 3,
+ # unique_bodyparts
+ ["led"],
+ ),
+ (
+ # assemblies_data
+ {
+ 0: [
+ np.array([[10, 20, 0.9, -1], [30, 40, 0.8, -1]]),
+ np.array([[13, 23, 0.9, -1], [33, 43, 0.8, -1]]),
+ ],
+ 1: [
+ np.array([[9, 19, 0.9, -1], [29, 41, 0.8, -1]]),
+ np.array([[15, 21, 0.9, -1], [35, 45, 0.8, -1]]),
+ ],
+ 2: [
+ np.array([[13, 23, 0.9, -1], [33, 43, 0.8, -1]]),
+ np.array([[10, 20, 0.9, -1], [30, 40, 0.8, -1]]),
+ ],
+ },
+ # inference_cfg
+ {"max_age": 3, "min_hits": 1, "topktoretain": 1, "pcutoff": 0.5},
+ # joints
+ ["nose", "ear"],
+ # scorer
+ "DLC",
+ # num_frames
+ 3,
+ # unique_bodyparts
+ None,
+ ),
+ ],
+)
+def test_build_tracklets(
+ assemblies_data: dict,
+ inference_cfg: dict,
+ joints: list,
+ scorer: str,
+ num_frames: int,
+ unique_bodyparts: list,
+):
+ # Run the function
+ tracklets = build_tracklets(
+ assemblies_data=assemblies_data,
+ track_method="box",
+ inference_cfg=inference_cfg,
+ joints=joints,
+ scorer=scorer,
+ num_frames=num_frames,
+ unique_bodyparts=unique_bodyparts,
+ identity_only=False,
+ )
+
+ # # Assertions
+ assert "header" in tracklets
+ assert isinstance(tracklets["header"], pd.MultiIndex)
+ if unique_bodyparts:
+ assert "single" in tracklets
+ else:
+ assert "single" not in tracklets
+
+ assert isinstance(tracklets, dict)
diff --git a/tests/pose_estimation_pytorch/config/test_config_utils.py b/tests/pose_estimation_pytorch/config/test_config_utils.py
new file mode 100644
index 0000000000..4a28ef567a
--- /dev/null
+++ b/tests/pose_estimation_pytorch/config/test_config_utils.py
@@ -0,0 +1,67 @@
+#
+# 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
+#
+"""Test util functions for config creation."""
+
+import pytest
+
+import deeplabcut.pose_estimation_pytorch.config.utils as utils
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ dict(
+ config={},
+ num_bodyparts=None,
+ num_individuals=None,
+ backbone_output_channels=None,
+ output_config={},
+ ),
+ dict(
+ config={
+ "a": "num_bodyparts",
+ "b": ["num_bodyparts // 2", "num_bodyparts // 3"],
+ "c": "num_bodyparts x 2",
+ "d": "num_bodyparts + 2",
+ },
+ num_bodyparts=10,
+ num_individuals=None,
+ backbone_output_channels=None,
+ output_config={
+ "a": 10,
+ "b": [5, 3],
+ "c": 20,
+ "d": 12,
+ },
+ ),
+ dict(
+ config={
+ "a": [{"b": "num_individuals x 3"}],
+ "b": [[{"b": "num_bodyparts x 3"}]],
+ },
+ num_bodyparts=10,
+ num_individuals=1,
+ backbone_output_channels=None,
+ output_config={
+ "a": [{"b": 3}],
+ "b": [[{"b": 30}]],
+ },
+ ),
+ ],
+)
+def test_replace_default_values_no_extras(data: dict):
+ output_config = utils.replace_default_values(
+ config=data["config"],
+ num_bodyparts=data["num_bodyparts"],
+ num_individuals=data["num_individuals"],
+ backbone_output_channels=data["backbone_output_channels"],
+ )
+ assert output_config == data["output_config"]
diff --git a/tests/pose_estimation_pytorch/config/test_make_pose_config.py b/tests/pose_estimation_pytorch/config/test_make_pose_config.py
new file mode 100644
index 0000000000..a8fbc7b6ff
--- /dev/null
+++ b/tests/pose_estimation_pytorch/config/test_make_pose_config.py
@@ -0,0 +1,484 @@
+#
+# 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
+#
+"""Tests the pre-processors."""
+
+import pytest
+
+import deeplabcut.utils.auxiliaryfunctions as af
+from deeplabcut.core.config import pretty_print
+from deeplabcut.pose_estimation_pytorch.config.make_pose_config import (
+ make_basic_project_config,
+ make_pytorch_pose_config,
+)
+from deeplabcut.pose_estimation_pytorch.config.utils import (
+ update_config,
+ update_config_by_dotpath,
+)
+
+
+@pytest.mark.parametrize("bodyparts", [["nose"], ["nose", "ear", "eye"]])
+@pytest.mark.parametrize("net_type", ["resnet_50", "resnet_101", "hrnet_w18", "hrnet_w32", "hrnet_w48"])
+def test_make_single_animal_config(bodyparts: list[str], net_type: str):
+ # Single animal projects can't have unique bodyparts
+ project_config = _make_project_config(
+ project_path="my/little/project",
+ multianimal=False,
+ identity=False,
+ individuals=[],
+ bodyparts=bodyparts,
+ unique_bodyparts=[],
+ )
+ pytorch_pose_config = make_pytorch_pose_config(
+ project_config,
+ "pytorch_config.yaml",
+ net_type=net_type,
+ )
+ pretty_print(pytorch_pose_config)
+
+ # check heads are there
+ assert "bodypart" in pytorch_pose_config["model"]["heads"].keys()
+ # check that the bodypart head has locref and heatmaps and the correct output shapes
+ bodypart_head = pytorch_pose_config["model"]["heads"]["bodypart"]
+
+ outputs = [("heatmap_config", len(bodyparts))]
+ if bodypart_head["predictor"]["location_refinement"]:
+ outputs += [("locref_config", 2 * len(bodyparts))]
+
+ for name, output_channels in outputs:
+ head = bodypart_head[name]
+ if "final_conv" in head:
+ actual_output_channels = head["final_conv"]["out_channels"]
+ else:
+ actual_output_channels = head["channels"][-1]
+ assert name in bodypart_head
+ assert actual_output_channels == output_channels
+
+
+@pytest.mark.parametrize("multianimal", [True])
+@pytest.mark.parametrize("individuals", [["single"], ["bugs", "daffy"]])
+@pytest.mark.parametrize("bodyparts", [["nose"], ["nose", "ear", "eye"]])
+@pytest.mark.parametrize("identity", [False, True])
+@pytest.mark.parametrize("unique_bodyparts", [[], ["tail"]])
+@pytest.mark.parametrize("net_type", ["resnet_50", "resnet_101", "hrnet_w18", "hrnet_w32", "hrnet_w48"])
+def test_backbone_plus_paf_config(
+ multianimal: bool,
+ individuals: list[str],
+ bodyparts: list[str],
+ identity: bool,
+ unique_bodyparts: list[str],
+ net_type: str,
+):
+ # Single animal projects can't have unique bodyparts
+ project_config = _make_project_config(
+ project_path="my/little/project",
+ multianimal=multianimal,
+ identity=identity,
+ individuals=individuals,
+ bodyparts=bodyparts,
+ unique_bodyparts=unique_bodyparts,
+ )
+ pytorch_pose_config = make_pytorch_pose_config(
+ project_config,
+ "pytorch_config.yaml",
+ net_type=net_type,
+ )
+ pretty_print(pytorch_pose_config)
+
+ graph = [[i, j] for i in range(len(bodyparts)) for j in range(i + 1, len(bodyparts))]
+ num_limbs = len(graph) * 2
+
+ # check heads are there
+ assert "bodypart" in pytorch_pose_config["model"]["heads"].keys()
+ bodypart_head = pytorch_pose_config["model"]["heads"]["bodypart"]
+
+ # check PAF head
+ assert bodypart_head["type"] == "DLCRNetHead"
+ assert bodypart_head["predictor"]["type"] == "PartAffinityFieldPredictor"
+
+ for name, output_channels in [
+ ("heatmap_config", len(bodyparts)),
+ ("locref_config", len(bodyparts) * 2),
+ ("paf_config", num_limbs),
+ ]:
+ print(name, bodypart_head[name]["channels"])
+ assert name in bodypart_head
+ assert bodypart_head[name]["channels"][-1] == output_channels
+
+ if len(unique_bodyparts) > 0:
+ assert "unique_bodypart" in pytorch_pose_config["model"]["heads"].keys()
+ unique_bodypart_head = pytorch_pose_config["model"]["heads"]["unique_bodypart"]
+ for name, output_channels in [
+ ("heatmap_config", len(unique_bodyparts)),
+ ("locref_config", 2 * len(unique_bodyparts)),
+ ]:
+ assert name in unique_bodypart_head
+ assert unique_bodypart_head[name]["channels"][-1] == output_channels
+ assert unique_bodypart_head["target_generator"]["heatmap_mode"] == "KEYPOINT"
+
+ if identity:
+ assert "identity" in pytorch_pose_config["model"]["heads"].keys()
+ id_head = pytorch_pose_config["model"]["heads"]["identity"]
+ assert "heatmap_config" in id_head
+ assert id_head["heatmap_config"]["channels"][-1] == len(individuals)
+ assert "locref_config" not in id_head
+ assert id_head["target_generator"]["heatmap_mode"] == "INDIVIDUAL"
+
+
+@pytest.mark.parametrize(
+ "detector",
+ [
+ (None, "SSDLite"),
+ ("ssdlite", "SSDLite"),
+ ("fasterrcnn_mobilenet_v3_large_fpn", "FasterRCNN"),
+ ("fasterrcnn_resnet50_fpn_v2", "FasterRCNN"),
+ ],
+)
+@pytest.mark.parametrize("individuals", [["single"], ["bugs", "daffy"]])
+@pytest.mark.parametrize("bodyparts", [["nose"], ["nose", "ear", "eye"]])
+@pytest.mark.parametrize("net_type", ["resnet_50", "resnet_101", "hrnet_w18", "hrnet_w32", "hrnet_w48"])
+def test_top_down_config(
+ detector: tuple[str, str],
+ individuals: list[str],
+ bodyparts: list[str],
+ net_type: str,
+):
+ # Single animal projects can't have unique bodyparts
+ detector_type, expected_detector_type = detector
+ project_config = _make_project_config(
+ project_path="my/little/project",
+ multianimal=True,
+ identity=False,
+ individuals=individuals,
+ bodyparts=bodyparts,
+ unique_bodyparts=[],
+ )
+ pytorch_pose_config = make_pytorch_pose_config(
+ project_config,
+ "pytorch_config.yaml",
+ net_type=net_type,
+ top_down=True,
+ detector_type=detector_type,
+ )
+ pretty_print(pytorch_pose_config)
+
+ # check no collate function
+ collate = pytorch_pose_config["data"]["train"].get("collate")
+ print(f"Collate: {collate}")
+ assert not collate
+
+ # check heads are there
+ assert "bodypart" in pytorch_pose_config["model"]["heads"].keys()
+ bodypart_head = pytorch_pose_config["model"]["heads"]["bodypart"]
+
+ # check detector is there
+ assert "detector" in pytorch_pose_config.keys()
+ assert pytorch_pose_config["detector"]["model"]["type"] == expected_detector_type
+
+ for name, output_channels in [
+ ("heatmap_config", len(bodyparts)),
+ ]:
+ print(name, bodypart_head[name]["channels"])
+ assert name in bodypart_head
+ assert bodypart_head[name]["final_conv"]["out_channels"] == output_channels
+
+
+@pytest.mark.parametrize("multianimal", [True])
+@pytest.mark.parametrize("individuals", [["single"], ["bugs", "daffy"]])
+@pytest.mark.parametrize("bodyparts", [["nose"], ["nose", "ear", "eye"]])
+@pytest.mark.parametrize("identity", [False, True])
+@pytest.mark.parametrize("unique_bodyparts", [[], ["tail"]])
+@pytest.mark.parametrize("net_type", ["dekr_w18", "dekr_w32", "dekr_w48"])
+def test_make_dekr_config(
+ multianimal: bool,
+ individuals: list[str],
+ bodyparts: list[str],
+ identity: bool,
+ unique_bodyparts: list[str],
+ net_type: str,
+):
+ project_config = _make_project_config(
+ project_path="my/little/project",
+ multianimal=multianimal,
+ identity=identity,
+ individuals=individuals,
+ bodyparts=bodyparts,
+ unique_bodyparts=unique_bodyparts,
+ )
+ pytorch_pose_config = make_pytorch_pose_config(
+ project_config,
+ "pytorch_config.yaml",
+ net_type=net_type,
+ )
+ pretty_print(pytorch_pose_config)
+
+ # check heads are there
+ assert "bodypart" in pytorch_pose_config["model"]["heads"].keys()
+ bodypart_head = pytorch_pose_config["model"]["heads"]["bodypart"]
+ for name, output_channels in [
+ ("heatmap_config", len(bodyparts) + 1),
+ ("offset_config", len(bodyparts)),
+ ]:
+ print(name, bodypart_head[name]["channels"])
+ assert name in bodypart_head
+ assert bodypart_head[name]["channels"][-1] == output_channels
+
+ if len(unique_bodyparts) > 0:
+ assert "unique_bodypart" in pytorch_pose_config["model"]["heads"].keys()
+ unique_bodypart_head = pytorch_pose_config["model"]["heads"]["unique_bodypart"]
+ for name, output_channels in [
+ ("heatmap_config", len(unique_bodyparts)),
+ ("locref_config", 2 * len(unique_bodyparts)),
+ ]:
+ assert name in unique_bodypart_head
+ assert unique_bodypart_head[name]["channels"][-1] == output_channels
+ assert unique_bodypart_head["target_generator"]["heatmap_mode"] == "KEYPOINT"
+
+ if identity:
+ assert "identity" in pytorch_pose_config["model"]["heads"].keys()
+ id_head = pytorch_pose_config["model"]["heads"]["identity"]
+ assert "heatmap_config" in id_head
+ assert id_head["heatmap_config"]["channels"][-1] == len(individuals)
+ assert "locref_config" not in id_head
+ assert id_head["target_generator"]["heatmap_mode"] == "INDIVIDUAL"
+
+
+@pytest.mark.parametrize("multianimal", [True])
+@pytest.mark.parametrize("individuals", [["single"], ["bugs", "daffy"]])
+@pytest.mark.parametrize("bodyparts", [["nose", "ears"], ["nose", "ear", "eye"]])
+@pytest.mark.parametrize("identity", [False, True])
+@pytest.mark.parametrize("unique_bodyparts", [[], ["tail"]])
+@pytest.mark.parametrize("net_type", ["dlcrnet_stride16_ms5", "dlcrnet_stride32_ms5"])
+def test_make_dlcrnet_config(
+ multianimal: bool,
+ individuals: list[str],
+ bodyparts: list[str],
+ identity: bool,
+ unique_bodyparts: list[str],
+ net_type: str,
+):
+ project_config = _make_project_config(
+ project_path="my/little/project",
+ multianimal=multianimal,
+ identity=identity,
+ individuals=individuals,
+ bodyparts=bodyparts,
+ unique_bodyparts=unique_bodyparts,
+ )
+ pytorch_pose_config = make_pytorch_pose_config(
+ project_config,
+ "pytorch_config.yaml",
+ net_type=net_type,
+ )
+ pretty_print(pytorch_pose_config)
+ paf_graph = [[i, j] for i in range(len(bodyparts)) for j in range(i + 1, len(bodyparts))]
+ num_limbs = len(paf_graph)
+
+ # check heads are there
+ assert "bodypart" in pytorch_pose_config["model"]["heads"].keys()
+ bodypart_head = pytorch_pose_config["model"]["heads"]["bodypart"]
+ for name, output_channels in [
+ ("heatmap_config", len(bodyparts)),
+ ("locref_config", 2 * len(bodyparts)),
+ ("paf_config", 2 * num_limbs),
+ ]:
+ print(name, bodypart_head[name]["channels"])
+ assert name in bodypart_head
+ assert bodypart_head[name]["channels"][-1] == output_channels
+
+ if len(unique_bodyparts) > 0:
+ assert "unique_bodypart" in pytorch_pose_config["model"]["heads"].keys()
+ unique_bodypart_head = pytorch_pose_config["model"]["heads"]["unique_bodypart"]
+ for name, output_channels in [
+ ("heatmap_config", len(unique_bodyparts)),
+ ("locref_config", 2 * len(unique_bodyparts)),
+ ]:
+ assert name in unique_bodypart_head
+ assert unique_bodypart_head[name]["channels"][-1] == output_channels
+ assert unique_bodypart_head["target_generator"]["heatmap_mode"] == "KEYPOINT"
+
+ if identity:
+ assert "identity" in pytorch_pose_config["model"]["heads"].keys()
+ id_head = pytorch_pose_config["model"]["heads"]["identity"]
+ assert "heatmap_config" in id_head
+ assert id_head["heatmap_config"]["channels"][-1] == len(individuals)
+ assert "locref_config" not in id_head
+ assert id_head["target_generator"]["heatmap_mode"] == "INDIVIDUAL"
+
+
+@pytest.mark.parametrize("individuals", [["single"], ["bugs", "daffy"]])
+@pytest.mark.parametrize("bodyparts", [["nose", "eyes"], ["nose", "ear", "eye"]])
+@pytest.mark.parametrize("identity", [False, True])
+@pytest.mark.parametrize("unique_bodyparts", [[], ["tail"]])
+@pytest.mark.parametrize("net_type", ["animaltokenpose_base"])
+def test_make_tokenpose_config(
+ individuals: list[str],
+ bodyparts: list[str],
+ identity: bool,
+ unique_bodyparts: list[str],
+ net_type: str,
+):
+ project_config = _make_project_config(
+ project_path="my/little/project",
+ multianimal=True,
+ identity=identity,
+ individuals=individuals,
+ bodyparts=bodyparts,
+ unique_bodyparts=unique_bodyparts,
+ )
+
+ if identity or len(unique_bodyparts) > 0:
+ with pytest.raises(ValueError) as _:
+ # Not yet implemented!
+ _ = make_pytorch_pose_config(
+ project_config,
+ "pytorch_config.yaml",
+ net_type=net_type,
+ )
+ else:
+ pytorch_pose_config = make_pytorch_pose_config(
+ project_config,
+ "pytorch_config.yaml",
+ net_type=net_type,
+ )
+ pretty_print(pytorch_pose_config)
+
+ # check no collate function
+ collate = pytorch_pose_config["data"]["train"].get("collate")
+ print(f"Collate: {collate}")
+ assert not collate
+
+ # check detector is there
+ assert "detector" in pytorch_pose_config
+ assert "data" in pytorch_pose_config["detector"]
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "config": {"a": 0, "b": 0},
+ "updates": {"b": 1},
+ "expected_result": {"a": 0, "b": 1},
+ },
+ {
+ "config": {"a": 0, "b": {"i0": 1, "i1": 2}},
+ "updates": {"b": 1},
+ "expected_result": {"a": 0, "b": 1},
+ },
+ {
+ "config": {"a": 0, "b": {"i0": 1, "i1": 2}},
+ "updates": {"b": {"i0": [1, 2, 3]}},
+ "expected_result": {"a": 0, "b": {"i0": [1, 2, 3], "i1": 2}},
+ },
+ {
+ "config": {"detector": {"batch_size": 1, "epochs": 10, "save_epochs": 5}},
+ "updates": {
+ "batch_size": 1,
+ "detector": {"batch_size": 8, "save_epochs": 1},
+ },
+ "expected_result": {
+ "batch_size": 1,
+ "detector": {"batch_size": 8, "epochs": 10, "save_epochs": 1},
+ },
+ },
+ ],
+)
+def test_update_config(data: dict):
+ result = update_config(config=data["config"], updates=data["updates"])
+ print("\nResult")
+ pretty_print(result)
+ assert result == data["expected_result"]
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "config": {"a": 0, "b": 0},
+ "updates": {"b": 1},
+ "expected_result": {"a": 0, "b": 1},
+ },
+ {
+ "config": {"a": 0, "b": {"i0": 1, "i1": 2}},
+ "updates": {"b": 1},
+ "expected_result": {"a": 0, "b": 1},
+ },
+ {
+ "config": {"a": 0, "b": {"i0": 1, "i1": 2}},
+ "updates": {"b.i0": [1, 2, 3]},
+ "expected_result": {"a": 0, "b": {"i0": [1, 2, 3], "i1": 2}},
+ },
+ {
+ "config": {"detector": {"batch_size": 1, "epochs": 10, "save_epochs": 5}},
+ "updates": {
+ "batch_size": 1,
+ "detector.batch_size": 8,
+ "detector.save_epochs": 1,
+ },
+ "expected_result": {
+ "batch_size": 1,
+ "detector": {"batch_size": 8, "epochs": 10, "save_epochs": 1},
+ },
+ },
+ ],
+)
+def test_update_config_by_dotpath(data: dict):
+ result = update_config_by_dotpath(config=data["config"], updates=data["updates"])
+ print("\nResult")
+ pretty_print(result)
+ assert result == data["expected_result"]
+
+
+def _make_project_config(
+ project_path: str,
+ multianimal: bool,
+ identity: bool,
+ individuals: list[str],
+ bodyparts: list[str],
+ unique_bodyparts: list[str],
+) -> dict:
+ project_config = {
+ "project_path": project_path,
+ "multianimalproject": multianimal,
+ "identity": identity,
+ "uniquebodyparts": unique_bodyparts,
+ }
+
+ if multianimal:
+ project_config["multianimalbodyparts"] = bodyparts
+ project_config["bodyparts"] = "MULTI!"
+ project_config["individuals"] = individuals
+ else:
+ project_config["bodyparts"] = bodyparts
+
+ return project_config
+
+
+@pytest.mark.parametrize("bodyparts", [["nose"], ["nose", "ear", "eye"]])
+@pytest.mark.parametrize("max_idv", [1, 12, 20])
+@pytest.mark.parametrize("multi", [True, False])
+def test_make_basic_project_config(bodyparts: list[str], max_idv: int, multi: bool):
+ if not multi and max_idv > 1:
+ return
+
+ project_config = make_basic_project_config(
+ dataset_path="path/dataset",
+ bodyparts=bodyparts,
+ max_individuals=max_idv,
+ multi_animal=multi,
+ )
+
+ bpts = af.get_bodyparts(project_config)
+ assert bodyparts == bpts
+
+ individuals = project_config["individuals"]
+ assert len(individuals) == max_idv
+ assert len(set(individuals)) == max_idv
diff --git a/tests/pose_estimation_pytorch/data/test_data_ctd.py b/tests/pose_estimation_pytorch/data/test_data_ctd.py
new file mode 100644
index 0000000000..37bd834d03
--- /dev/null
+++ b/tests/pose_estimation_pytorch/data/test_data_ctd.py
@@ -0,0 +1,184 @@
+#
+# 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 json
+import platform
+from pathlib import Path
+
+import numpy as np
+import pandas as pd
+import pytest
+
+from deeplabcut.pose_estimation_pytorch.data.ctd import CondFromFile
+
+CONDITIONS = [
+ np.zeros((4, 3, 3)).tolist(),
+ np.ones((4, 3, 3)).tolist(),
+ 2 * np.ones((4, 3, 3)).tolist(),
+ 3 * np.ones((4, 3, 3)).tolist(),
+]
+
+
+@pytest.mark.parametrize("path_prefix", ["/a/b"])
+@pytest.mark.parametrize(
+ "data",
+ [
+ [("/a/b/c/d.png", "/a/b/c/d.png", CONDITIONS[1])],
+ [("/a/b/c/d.png", "c/d.png", CONDITIONS[1])],
+ [
+ ("/a/b/c.png", "c.png", CONDITIONS[1]),
+ ("/a/b/c/d.png", "c/d.png", CONDITIONS[2]),
+ ("/a/b/c/e.png", "/a/b/c/e.png", CONDITIONS[3]),
+ ],
+ ],
+)
+def test_ctd_load_json_containing_rel_paths(
+ tmp_path_factory,
+ path_prefix: str | Path,
+ data: tuple[list[str], list[str], list],
+) -> None:
+ print("Starting test")
+ # convert the image paths to Windows format
+ if platform.system() == "Windows":
+ print("Converting to windows filesystem")
+
+ print("Path Prefix:", path_prefix)
+ if isinstance(path_prefix, Path):
+ print(f" As string: {str(path_prefix)}")
+ path_prefix = Path(_to_windows_path(str(path_prefix)))
+ else:
+ path_prefix = _to_windows_path(path_prefix)
+ print(f" Converted {path_prefix}")
+
+ data = [(_to_windows_path(img), _to_windows_path(key), cond) for img, key, cond in data]
+ print(f"Images: {[d[0] for d in data]}")
+ print(f"Condition keys: {[d[1] for d in data]}")
+ print("---")
+
+ images = [img for img, _, _ in data]
+ conditions = {key: cond for _, key, cond in data}
+
+ tmp_folder = Path(tmp_path_factory.mktemp("tmp-project"))
+ conditions_filepath = tmp_folder / "conditions.json"
+ with open(conditions_filepath, "w") as f:
+ json.dump(conditions, f)
+
+ conditions = CondFromFile.load_conditions_json(
+ conditions_filepath,
+ images,
+ path_prefix=path_prefix,
+ )
+ for img_path, _, condition in data:
+ assert img_path in conditions
+ np.testing.assert_allclose(condition, conditions[img_path])
+
+
+@pytest.mark.parametrize("path_prefix", ["/p"])
+@pytest.mark.parametrize("num_conditions", [1, 2, 3, 5, 10])
+@pytest.mark.parametrize("num_bodyparts", [1, 2, 3, 5, 10])
+@pytest.mark.parametrize(
+ "data",
+ [
+ [("/p/data/video0/img0.png", ("data", "video0", "img0.png"))],
+ [("/p/data/video0/img0.png", "data/video0/img0.png")],
+ [
+ ("/p/b/c/d0.png", ("b", "c", "d0.png")),
+ ("/p/b/c/d1.png", ("b", "c", "d1.png")),
+ ("/p/b/c/d2.png", ("b", "c", "d2.png")),
+ ],
+ [
+ ("/p/b/c/d0.png", "b/c/d0.png"),
+ ("/p/b/c/d1.png", "b/c/d1.png"),
+ ("/p/b/c/d2.png", "b/c/d2.png"),
+ ],
+ ],
+)
+def test_ctd_load_hdf_containing_rel_paths(
+ tmp_path_factory,
+ path_prefix: str | Path,
+ num_conditions: int,
+ num_bodyparts: int,
+ data: tuple[list[str], list[str]],
+) -> None:
+ print("\nStarting test")
+
+ # convert the image paths to Windows format
+ if platform.system() == "Windows":
+ print("Converting to windows filesystem")
+
+ print("Path Prefix:", path_prefix)
+ if isinstance(path_prefix, Path):
+ print(f" As string: {str(path_prefix)}")
+ path_prefix = Path(_to_windows_path(str(path_prefix)))
+ else:
+ path_prefix = _to_windows_path(path_prefix)
+ print(f" Converted {path_prefix}")
+
+ data = [(_to_windows_path(img), idx) for img, idx in data]
+ print(f"Images: {[d[0] for d in data]}")
+ print("---")
+
+ num_images = len(data)
+ images = [img for img, _ in data]
+ index = [idx for _, idx in data]
+ if isinstance(index[0], tuple):
+ index = pd.MultiIndex.from_tuples(index)
+
+ # generate random pose data
+ size = (num_images, num_conditions, num_bodyparts, 3)
+ rng = np.random.default_rng(0)
+ pose = rng.integers(low=0, high=1024, size=size).astype(float)
+ pose[:, :, :, 2] = rng.random(size=(num_images, num_conditions, num_bodyparts))
+
+ # set some missing data
+ is_nans = rng.random(size=size) > 0.8
+ pose[is_nans] = np.nan
+
+ # create what the output data will look like
+ keypoint_mask = np.any(is_nans, axis=3)
+ output_pose = pose.copy()
+ output_pose[keypoint_mask] = 0.0
+ idv_mask = ~np.all(keypoint_mask, axis=2)
+
+ output_pose = [
+ p[p_mask] if np.any(p_mask) else np.zeros((0, num_bodyparts, 3))
+ for p, p_mask in zip(output_pose, idv_mask, strict=False)
+ ]
+
+ # generate columns for the dataframe
+ columns = pd.MultiIndex.from_product(
+ [
+ ["scorer"],
+ [f"idv{i}" for i in range(num_conditions)],
+ [f"bpt{i}" for i in range(num_bodyparts)],
+ ["x", "y", "likelihood"],
+ ],
+ names=["scorer", "individuals", "bodyparts", "coords"],
+ )
+ df = pd.DataFrame(data=pose.reshape(num_images, -1), index=index, columns=columns)
+
+ print(df.head())
+
+ tmp_folder = Path(tmp_path_factory.mktemp("tmp-project"))
+ conditions_filepath = tmp_folder / "conditions.h5"
+ df.to_hdf(conditions_filepath, key="df_with_missing")
+
+ conditions = CondFromFile.load_conditions_h5(conditions_filepath, images, path_prefix=path_prefix)
+ for idx, (img_path, _img_index) in enumerate(data):
+ assert img_path in conditions
+ np.testing.assert_allclose(output_pose[idx], conditions[img_path])
+
+
+def _to_windows_path(s: str) -> str:
+ # Convert absolute paths to paths on C:
+ if s.startswith("/"):
+ return str(Path("C:\\", *s[1:].split("/")))
+
+ return s
diff --git a/tests/pose_estimation_pytorch/data/test_dlc_dataloader.py b/tests/pose_estimation_pytorch/data/test_dlc_dataloader.py
new file mode 100644
index 0000000000..76fe04ca61
--- /dev/null
+++ b/tests/pose_estimation_pytorch/data/test_dlc_dataloader.py
@@ -0,0 +1,68 @@
+from types import SimpleNamespace
+
+import numpy as np
+import pandas as pd
+
+import deeplabcut.pose_estimation_pytorch.data.dlcloader as dlcloader_mod
+from deeplabcut.pose_estimation_pytorch.data.dlcloader import DLCLoader
+
+
+def test_to_coco_ignores_likelihood_columns(monkeypatch, tmp_path):
+ fake_shape = (3, 480, 640)
+ monkeypatch.setattr(
+ dlcloader_mod,
+ "read_image_shape_fast",
+ lambda _: fake_shape,
+ )
+
+ scorer = "testscorer"
+ bodyparts = ["nose", "tail"]
+
+ index = pd.MultiIndex.from_tuples(
+ [("labeled-data", "video1", "img0001.png")],
+ names=["set", "video", "image"],
+ )
+
+ # Baseline dataframe: x/y only
+ columns_xy = pd.MultiIndex.from_product(
+ [[scorer], bodyparts, ["x", "y"]],
+ names=["scorer", "bodyparts", "coords"],
+ )
+ df_xy = pd.DataFrame(
+ [[10.0, 20.0, 30.0, 40.0]],
+ index=index,
+ columns=columns_xy,
+ )
+
+ # Same data, but with likelihood columns added
+ columns_xyl = pd.MultiIndex.from_product(
+ [[scorer], bodyparts, ["x", "y", "likelihood"]],
+ names=["scorer", "bodyparts", "coords"],
+ )
+ df_xyl = pd.DataFrame(
+ [[10.0, 20.0, 0.9, 30.0, 40.0, 0.8]],
+ index=index,
+ columns=columns_xyl,
+ )
+
+ # to_coco only needs these attributes from parameters
+ params = SimpleNamespace(
+ bodyparts=bodyparts,
+ unique_bpts=[],
+ individuals=["animal"],
+ )
+
+ baseline = DLCLoader.to_coco(tmp_path, df_xy, params)
+ got = DLCLoader.to_coco(tmp_path, df_xyl, params)
+
+ assert len(got["images"]) == len(baseline["images"]) == 1
+ assert len(got["annotations"]) == len(baseline["annotations"]) == 1
+
+ got_ann = got["annotations"][0]
+ expected_ann = baseline["annotations"][0]
+
+ assert got_ann["image_id"] == expected_ann["image_id"]
+ assert got_ann["category_id"] == expected_ann["category_id"]
+ assert got_ann["num_keypoints"] == expected_ann["num_keypoints"] == 2
+ assert np.array_equal(got_ann["keypoints"], expected_ann["keypoints"])
+ assert np.allclose(got_ann["bbox"], expected_ann["bbox"])
diff --git a/tests/pose_estimation_pytorch/data/test_postprocessor.py b/tests/pose_estimation_pytorch/data/test_postprocessor.py
new file mode 100644
index 0000000000..7148fdf1c2
--- /dev/null
+++ b/tests/pose_estimation_pytorch/data/test_postprocessor.py
@@ -0,0 +1,381 @@
+#
+# 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
+#
+"""Tests the pre-processors."""
+
+import numpy as np
+import pytest
+
+from deeplabcut.pose_estimation_pytorch.data.postprocessor import (
+ PredictKeypointIdentities,
+ PrepareBackboneFeatures,
+ RemoveLowConfidenceBoxes,
+ RescaleAndOffset,
+ TrimOutputs,
+)
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "predictions": [[[0, 0, 0.95], [20, 30, 0.5]]],
+ "offsets": [(0, 0)],
+ "scales": [(1, 1)],
+ "rescaled": [[[0, 0, 0.95], [20, 30, 0.5]]],
+ },
+ {
+ "predictions": [
+ [[0, 0, 0.12], [1000, 0, 0.5]], # individual 1
+ [[18, 2, 0.24], [0, 1000, 0.6]], # individual 2
+ ],
+ "offsets": [(0, 0), (0, 0)],
+ "scales": [(1, 1), (0.5, 1.0)],
+ "rescaled": [
+ [[0, 0, 0.12], [1000, 0, 0.5]], # individual 1
+ [[9, 2, 0.24], [0, 1000, 0.6]], # individual 2
+ ],
+ },
+ {
+ "predictions": [
+ [[0, 0, 0.95], [20, 30, 0.5]], # individual 1
+ [[110, 5, 0.95], [60, 1200, 0.5]], # individual 2
+ ],
+ "offsets": [(12, 5), (27, 10)],
+ "scales": [(0.5, 0.5), (0.2, 0.2)],
+ "rescaled": [
+ [[12, 5, 0.95], [22, 20, 0.5]], # individual 1
+ [[49, 11, 0.95], [39, 250, 0.5]], # individual 2
+ ],
+ },
+ ],
+)
+def test_rescale_topdown(data):
+ """Expects x_processed = x * scale + offset."""
+ postprocessor = RescaleAndOffset(
+ keys_to_rescale=["bodyparts"],
+ mode=RescaleAndOffset.Mode.KEYPOINT_TD,
+ )
+ context = {"scales": data["scales"], "offsets": data["offsets"]}
+ predictions = {"bodyparts": np.array(data["predictions"])}
+ predictions, context = postprocessor(predictions, context=context)
+ print(predictions["bodyparts"].tolist())
+ print(data["rescaled"])
+ np.testing.assert_array_equal(predictions["bodyparts"], np.array(data["rescaled"]))
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "bboxes": [[0, 0, 0, 0], [1, 1, 1, 1]],
+ "bbox_scores": [0, 0],
+ "max_individuals": {"bboxes": 1, "bbox_scores": 1},
+ },
+ {
+ "bboxes": [[0, 0, 0, 0], [1, 1, 1, 1]],
+ "bbox_scores": [0, 0],
+ "max_individuals": {"bboxes": 2, "bbox_scores": 2},
+ },
+ ],
+)
+def test_trim_outputs(data):
+ """Expects x_processed = x * scale + offset."""
+ postprocessor = TrimOutputs(max_individuals=data["max_individuals"])
+ context = {}
+ predictions = {"bboxes": np.array(data["bboxes"]), "bbox_scores": np.array(data["bbox_scores"])}
+ predictions, context = postprocessor(predictions, context=context)
+ print(predictions["bboxes"].tolist())
+ print(predictions["bbox_scores"].tolist())
+ assert len(predictions["bboxes"]) == data["max_individuals"]["bboxes"]
+ assert len(predictions["bbox_scores"]) == data["max_individuals"]["bbox_scores"]
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "predictions": [[[0, 0, 0.95], [20, 30, 0.5]]],
+ "offsets": (0, 0),
+ "scales": (1, 1),
+ "rescaled": [[[0, 0, 0.95], [20, 30, 0.5]]],
+ },
+ {
+ "predictions": [
+ [[0, 0, 0.12], [10, 0, 0.5]], # individual 1
+ [[1000, 500, 0.24], [50, 250, 0.6]], # individual 2
+ ],
+ "offsets": (5, 7),
+ "scales": (0.2, 0.5),
+ "rescaled": [
+ [[5, 7, 0.12], [7, 7, 0.5]], # individual 1
+ [[205, 257, 0.24], [15, 132, 0.6]], # individual 2
+ ],
+ },
+ ],
+)
+def test_rescale_bottom_up(data):
+ """Expects x_processed = x * scale + offset."""
+ postprocessor = RescaleAndOffset(
+ keys_to_rescale=["bodyparts"],
+ mode=RescaleAndOffset.Mode.KEYPOINT,
+ )
+ context = {"scales": data["scales"], "offsets": data["offsets"]}
+ predictions = {"bodyparts": np.array(data["predictions"])}
+ predictions, context = postprocessor(predictions, context=context)
+ print(predictions["bodyparts"].tolist())
+ print(data["rescaled"])
+ np.testing.assert_array_equal(predictions["bodyparts"], np.array(data["rescaled"]))
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "bboxes": [[222.0, 562.0, 721.0, 637.0]],
+ "offsets": (0, 0),
+ "scales": (1, 1),
+ "rescaled": [[222.0, 562.0, 721.0, 637.0]],
+ },
+ {
+ "bboxes": [[386.71875, 219.53125, 281.640625, 248.828125]],
+ "offsets": (-768, 0),
+ "scales": (2.56, 2.56),
+ "rescaled": [[222.0, 562.0, 721.0, 637.0]],
+ },
+ {
+ "bboxes": [
+ [0, 0, 100, 100],
+ [5, 10, 100, 100],
+ [5, 10, 10, 20],
+ ],
+ "offsets": (3, 7),
+ "scales": (2, 0.5),
+ "rescaled": [
+ [3, 7, 200, 50],
+ [13, 12, 200, 50],
+ [13, 12, 20, 10],
+ ],
+ },
+ ],
+)
+def test_rescale_detector(data):
+ """Expects x_processed = x * scale + offset."""
+ postprocessor = RescaleAndOffset(
+ keys_to_rescale=["bboxes"],
+ mode=RescaleAndOffset.Mode.BBOX_XYWH,
+ )
+ context = {"scales": data["scales"], "offsets": data["offsets"]}
+ predictions = {"bboxes": np.array(data["bboxes"])}
+ predictions, context = postprocessor(predictions, context=context)
+ print(predictions["bboxes"].tolist())
+ print(data["rescaled"])
+ np.testing.assert_array_equal(predictions["bboxes"], np.array(data["rescaled"]))
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "bodyparts": [
+ [[3.1, 1, 0.8], [1, 0, 0.9]], # assembly 1 (x, y, score)
+ [[2.2, 1.6, 0.5], [3, 3, 0.4]], # assembly 2 (x, y, score)
+ ],
+ "id_heatmap": [ # id1, id2 score for each pixel
+ [[0.1, 0.1], [0.2, 0.1], [0.3, 0.1], [0.4, 0.1]],
+ [[0.1, 0.2], [0.2, 0.2], [0.3, 0.2], [0.4, 0.2]],
+ [[0.1, 0.3], [0.2, 0.3], [0.3, 0.3], [0.4, 0.3]],
+ [[0.1, 0.4], [0.2, 0.4], [0.3, 0.4], [0.4, 0.4]],
+ ],
+ "id_scores": [ # id1, id2 score for each bodypart
+ [[0.4, 0.2], [0.2, 0.1]], # assembly 1 (id_1 proba, id_2 proba)
+ [[0.3, 0.3], [0.4, 0.4]], # assembly 2 (id_1 proba, id_2 proba)
+ ],
+ },
+ ],
+)
+def test_assign_id_scores(data):
+ p = PredictKeypointIdentities(
+ identity_key="keypoint_identity",
+ identity_map_key="identity_map",
+ pose_key="bodyparts",
+ keep_id_maps=True,
+ )
+ bodyparts = np.array(data["bodyparts"])
+ id_heatmap = np.array(data["id_heatmap"])
+ expected_ids = np.array(data["id_scores"])
+ print()
+ print(bodyparts.shape)
+ print(id_heatmap.shape)
+ print(expected_ids.shape)
+ predictions_in = {"bodyparts": bodyparts, "identity_map": id_heatmap}
+ predictions, _ = p(predictions_in, {})
+ np.testing.assert_array_equal(
+ predictions["keypoint_identity"],
+ expected_ids,
+ )
+
+
+def test_prepare_backbone_features():
+ p = PrepareBackboneFeatures(top_down=False)
+
+ img_w, img_h = 256, 128
+ features = np.zeros((1, img_h, img_w))
+
+ features[0, 15, 10] = 1
+ features[0, 25, 20] = 2
+ features[0, 35, 30] = 3
+
+ pose = np.array(
+ [
+ [
+ [10.1, 15.1, 0.95],
+ [20.1, 25.1, 0.95],
+ [29.9, 34.9, 0.95],
+ ],
+ ]
+ )
+
+ predictions = [dict(backbone=dict(features=features), bodypart=dict(poses=pose))]
+ context = dict(image_size=(img_w, img_h))
+ predictions_out, context_out = p(predictions, context)
+
+ assert len(predictions_out) == 1
+ assert len(context_out) == 1
+ preds = predictions_out[0]
+
+ assert "backbone" in preds
+ assert "bodypart_features" in preds["backbone"]
+ bodypart_features = preds["backbone"]["bodypart_features"]
+ print(f"Bodypart features: {bodypart_features.shape}")
+ print(bodypart_features)
+ assert bodypart_features.shape == (1, 3, 1)
+ assert bodypart_features.reshape(-1).tolist() == [1, 2, 3]
+
+
+def test_prepare_top_down_backbone_features():
+ p = PrepareBackboneFeatures(top_down=True)
+
+ img_w, img_h = 256, 256
+
+ features = np.zeros((2, 1, img_h, img_w))
+ features[0, 0, 15, 10] = 1
+ features[0, 0, 25, 20] = 2
+ features[0, 0, 35, 30] = 3
+ features[1, 0, 95, 10] = 11
+ features[1, 0, 85, 20] = 12
+ features[1, 0, 75, 30] = 13
+
+ pose_idv0 = np.array(
+ [
+ [
+ [10.1, 15.1, 0.95],
+ [20.1, 25.1, 0.95],
+ [29.9, 34.9, 0.95],
+ ],
+ ]
+ )
+ pose_idv1 = np.array(
+ [
+ [
+ [10.1, 95.1, 0.95],
+ [20.1, 85.1, 0.95],
+ [29.9, 74.9, 0.95],
+ ],
+ ]
+ )
+
+ predictions = [
+ dict(backbone=dict(features=features[0]), bodypart=dict(poses=pose_idv0)),
+ dict(backbone=dict(features=features[1]), bodypart=dict(poses=pose_idv1)),
+ ]
+ context = dict(top_down_crop_size=(img_w, img_h))
+ predictions_out, context_out = p(predictions, context)
+
+ assert len(predictions_out) == 2
+ assert len(context_out) == 1
+ for preds, expected in zip(predictions_out, [[1, 2, 3], [11, 12, 13]], strict=True):
+ assert "backbone" in preds
+ assert "bodypart_features" in preds["backbone"]
+ bodypart_features = preds["backbone"]["bodypart_features"]
+ print(f"Bodypart features: {bodypart_features.shape}")
+ print(bodypart_features)
+ assert bodypart_features.shape == (1, 3, 1)
+ assert bodypart_features.reshape(-1).tolist() == expected
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "bboxes": [[0, 0, 10, 10], [20, 20, 30, 30], [40, 40, 50, 50]],
+ "bbox_scores": [0.1, 0.5, 0.9],
+ "threshold": 0.3,
+ "expected_bboxes": [[20, 20, 30, 30], [40, 40, 50, 50]],
+ "expected_scores": [0.5, 0.9],
+ },
+ {
+ "bboxes": [[0, 0, 10, 10], [20, 20, 30, 30], [40, 40, 50, 50]],
+ "bbox_scores": [0.1, 0.2, 0.3],
+ "threshold": 0.5,
+ "expected_bboxes": [],
+ "expected_scores": [],
+ },
+ {
+ "bboxes": [[0, 0, 10, 10], [20, 20, 30, 30]],
+ "bbox_scores": [0.3, 0.7],
+ "threshold": 0.3,
+ "expected_bboxes": [[0, 0, 10, 10], [20, 20, 30, 30]],
+ "expected_scores": [0.3, 0.7],
+ },
+ {
+ "bboxes": [],
+ "bbox_scores": [],
+ "threshold": 0.5,
+ "expected_bboxes": [],
+ "expected_scores": [],
+ },
+ ],
+)
+def test_remove_low_confidence_boxes(data):
+ """Tests that RemoveLowConfidenceBoxes filters boxes below threshold."""
+ postprocessor = RemoveLowConfidenceBoxes(bbox_score_thresh=data["threshold"])
+ context = {}
+
+ # Handle empty input arrays with proper shape
+ if len(data["bboxes"]) == 0:
+ bboxes = np.empty((0, 4))
+ else:
+ bboxes = np.array(data["bboxes"])
+
+ if len(data["bbox_scores"]) == 0:
+ bbox_scores = np.empty((0,))
+ else:
+ bbox_scores = np.array(data["bbox_scores"])
+
+ predictions = {
+ "bboxes": bboxes,
+ "bbox_scores": bbox_scores,
+ }
+ predictions, context = postprocessor(predictions, context=context)
+
+ # Handle empty expected arrays with proper shape
+ if len(data["expected_bboxes"]) == 0:
+ expected_bboxes = np.empty((0, 4))
+ else:
+ expected_bboxes = np.array(data["expected_bboxes"])
+
+ if len(data["expected_scores"]) == 0:
+ expected_scores = np.empty((0,))
+ else:
+ expected_scores = np.array(data["expected_scores"])
+
+ np.testing.assert_array_equal(predictions["bboxes"], expected_bboxes)
+ np.testing.assert_array_equal(predictions["bbox_scores"], expected_scores)
diff --git a/tests/pose_estimation_pytorch/data/test_preprocessor.py b/tests/pose_estimation_pytorch/data/test_preprocessor.py
new file mode 100644
index 0000000000..9a68d76fe7
--- /dev/null
+++ b/tests/pose_estimation_pytorch/data/test_preprocessor.py
@@ -0,0 +1,158 @@
+#
+# 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
+#
+"""Tests the pre-processors."""
+
+import albumentations as A
+import numpy as np
+import pytest
+
+from deeplabcut.pose_estimation_pytorch.data.preprocessor import (
+ AugmentImage,
+ build_conditional_top_down_preprocessor,
+)
+from deeplabcut.pose_estimation_pytorch.data.transforms import build_resize_transforms
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "image_shape": (2, 4, 4),
+ "resize_transform": {"height": 5, "width": 4, "keep_ratio": True},
+ "output_shape": (2, 4, 4),
+ "padded_shape": (5, 4, 4), # single offset as not a batch
+ "output_context": {"offsets": (0, 0), "scales": (1, 1)},
+ },
+ {
+ "image_shape": (1, 2, 4, 4), # as batch
+ "resize_transform": {"height": 10, "width": 4, "keep_ratio": True},
+ "output_shape": (1, 2, 4, 4),
+ "padded_shape": (1, 10, 4, 4),
+ "output_context": {"offsets": [(0, 0)], "scales": [(1, 1)]},
+ },
+ {
+ "image_shape": (2, 4, 3),
+ "resize_transform": {"height": 10, "width": 8, "keep_ratio": True},
+ "output_shape": (4, 8, 3),
+ "padded_shape": (10, 8, 3),
+ "output_context": {"offsets": (0, 0), "scales": (0.5, 0.5)},
+ },
+ ],
+)
+def test_augment_image_rescaling(data):
+ resize_transform = build_resize_transforms(data["resize_transform"])
+ transform = A.Compose(
+ resize_transform,
+ keypoint_params=A.KeypointParams("xy", remove_invisible=False),
+ bbox_params=A.BboxParams(format="coco", label_fields=["bbox_labels"]),
+ )
+ preprocessor = AugmentImage(transform)
+ img = np.ones(data["image_shape"])
+ transformed_image, context = preprocessor(img, context={})
+ print()
+ print(transformed_image[:, :, 0]) # first channel
+ print(context)
+ assert np.sum(transformed_image) == np.sum(np.ones(data["output_shape"]))
+ assert context == data["output_context"]
+ assert transformed_image.shape == data["padded_shape"]
+
+
+ctd_preprocessor = build_conditional_top_down_preprocessor(
+ color_mode="RGB",
+ transform=A.Compose(
+ build_resize_transforms({"height": 100, "width": 100, "keep_ratio": True}),
+ keypoint_params=A.KeypointParams("xy", remove_invisible=False),
+ bbox_params=A.BboxParams(format="coco", label_fields=["bbox_labels"]),
+ ),
+ bbox_margin=0,
+ top_down_crop_size=(256, 256),
+)
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ # two well-defined individuals
+ {
+ "image_shape": (100, 100, 3),
+ "context": {"cond_kpts": np.array([[[10, 10, 0.8], [20, 20, 0.8]], [[60, 60, 0.8], [70, 70, 0.8]]])},
+ "output_context": {
+ "cond_kpts": np.array([[[10, 10, 0.8], [20, 20, 0.8]], [[60, 60, 0.8], [70, 70, 0.8]]]),
+ "bboxes": [np.array([10, 10, 10, 10]), np.array([60, 60, 10, 10])],
+ "offsets": [(10, 10), (60, 60)],
+ "scales": [(0.1, 0.1), (0.1, 0.1)],
+ },
+ },
+ # one individual has 0 keypoints
+ {
+ "image_shape": (100, 100, 3),
+ "context": {"cond_kpts": np.array([[[10, 10, 0.8], [20, 20, 0.8]], [[60, 60, 0.0], [70, 70, 0.0]]])},
+ "output_context": {
+ "cond_kpts": np.array(
+ [
+ [[10, 10, 0.8], [20, 20, 0.8]],
+ ]
+ ),
+ "bboxes": [np.array([10, 10, 10, 10])],
+ "offsets": [(10, 10)],
+ "scales": [(0.1, 0.1)],
+ },
+ },
+ # one individual has only 1 keypoints
+ {
+ "image_shape": (100, 100, 3),
+ "context": {"cond_kpts": np.array([[[10, 10, 0.8], [20, 20, 0.8]], [[60, 60, 0.0], [70, 70, 0.9]]])},
+ "output_context": {
+ "cond_kpts": np.array(
+ [
+ [[10, 10, 0.8], [20, 20, 0.8]],
+ ]
+ ),
+ "bboxes": [np.array([10, 10, 10, 10])],
+ "offsets": [(10, 10)],
+ "scales": [(0.1, 0.1)],
+ },
+ },
+ # two individuals but one is low confidence
+ {
+ "image_shape": (100, 100, 3),
+ "context": {"cond_kpts": np.array([[[10, 10, 0.8], [20, 20, 0.8]], [[60, 60, 0.01], [70, 70, 0.01]]])},
+ "output_context": {
+ "cond_kpts": np.array(
+ [
+ [[10, 10, 0.8], [20, 20, 0.8]],
+ ]
+ ),
+ "bboxes": [np.array([10, 10, 10, 10])],
+ "offsets": [(10, 10)],
+ "scales": [(0.1, 0.1)],
+ },
+ },
+ ],
+)
+def test_conditional_top_down_preprocessor(data):
+ input_img = np.ones(data["image_shape"])
+
+ output_img, output_context = ctd_preprocessor(input_img, context=data["context"])
+
+ for context_key in ["cond_kpts", "bboxes", "offsets", "scales"]:
+ assert deep_equal(output_context[context_key], data["output_context"][context_key])
+
+
+def deep_equal(a, b):
+ if isinstance(a, np.ndarray) and isinstance(b, np.ndarray):
+ return np.array_equal(a, b)
+ elif isinstance(a, list) and isinstance(b, list):
+ if len(a) != len(b):
+ return False
+ return all(deep_equal(x, y) for x, y in zip(a, b, strict=False))
+ else:
+ return a == b
diff --git a/tests/pose_estimation_pytorch/data/test_transforms.py b/tests/pose_estimation_pytorch/data/test_transforms.py
new file mode 100644
index 0000000000..f85ce00ffe
--- /dev/null
+++ b/tests/pose_estimation_pytorch/data/test_transforms.py
@@ -0,0 +1,302 @@
+#
+# 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
+#
+"""Tests the custom transforms."""
+
+import random
+
+import albumentations as A
+import numpy as np
+import pytest
+
+from deeplabcut.pose_estimation_pytorch.data import transforms
+
+
+@pytest.mark.parametrize(
+ "height, width, image_shapes",
+ [
+ (200, 200, [(300, 300, 3), (1000, 1000, 3), (1024, 1024, 1)]),
+ (512, 512, [(1024, 1024, 3), (128, 128, 4), (300, 300, 1)]),
+ (1024, 512, [(600, 300, 3), (4096, 2048, 3), (50, 25, 1)]),
+ (800, 1300, [(80, 130, 3), (1600, 2600, 4), (1200, 1950, 1)]),
+ ],
+)
+def test_dlc_resize_pad_good_aspect_ratio(height, width, image_shapes):
+ aug = transforms.KeepAspectRatioResize(width=width, height=height, mode="pad")
+ for image_shape in image_shapes:
+ fake_image = np.zeros(image_shape)
+ transformed = aug(image=fake_image, keypoints=[])
+ assert transformed["image"].shape[:2] == (height, width)
+ assert transformed["image"].shape[2] == fake_image.shape[2]
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "height": 200,
+ "width": 200,
+ "in_shapes": [(100, 50, 3), (50, 400, 3)],
+ "out_shapes": [(200, 100, 3), (25, 200, 3)],
+ },
+ {
+ "height": 128,
+ "width": 256,
+ "in_shapes": [(100, 100, 3), (512, 256, 3)],
+ "out_shapes": [(128, 128, 3), (128, 64, 3)],
+ },
+ ],
+)
+def test_dlc_resize_pad_bad_aspect_ratio(data):
+ aug = transforms.KeepAspectRatioResize(width=data["width"], height=data["height"], mode="pad")
+ for in_shape, out_shape in zip(data["in_shapes"], data["out_shapes"], strict=False):
+ fake_image = np.zeros(in_shape)
+ transformed = aug(image=fake_image, keypoints=[])
+ assert transformed["image"].shape == out_shape
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "height": 200,
+ "width": 200,
+ "in_shape": (100, 50, 3),
+ "out_shape": (200, 100, 3),
+ "in_keypoints": [(50.0, 50.0), (25.0, 10.0)],
+ "out_keypoints": [(100.0, 100.0), (50.0, 20.0)],
+ },
+ {
+ "height": 512,
+ "width": 256,
+ "in_shape": (1024, 1024, 3),
+ "out_shape": (256, 256, 3),
+ "in_keypoints": [(512.0, 512.0), (100.0, 10.0)],
+ "out_keypoints": [(128.0, 128.0), (25.0, 2.5)],
+ },
+ ],
+)
+def test_dlc_resize_pad_bad_aspect_ratio_with_keypoints(data):
+ aug = transforms.KeepAspectRatioResize(width=data["width"], height=data["height"], mode="pad")
+ transform = A.Compose(
+ [aug],
+ keypoint_params=A.KeypointParams("xy", remove_invisible=False),
+ )
+ fake_image = np.zeros(data["in_shape"])
+ transformed = transform(image=fake_image, keypoints=data["in_keypoints"])
+ assert transformed["image"].shape == data["out_shape"]
+ assert transformed["keypoints"] == data["out_keypoints"]
+
+
+def test_coarse_dropout():
+ transforms.CoarseDropout(
+ max_holes=10,
+ max_height=0.05,
+ min_height=0.01,
+ max_width=0.05,
+ min_width=0.01,
+ p=0.5,
+ )
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "image_shape": [480, 640, 3],
+ "transform_config": dict(
+ shift_factor=10.0,
+ shift_prob=0.0,
+ scale_factor=[0.1, 2.0],
+ scale_prob=0.0,
+ ),
+ },
+ {
+ "image_shape": [480, 640, 3],
+ "transform_config": dict(
+ shift_factor=0.0,
+ shift_prob=1.0,
+ scale_factor=[1.0, 1.0],
+ scale_prob=1.0,
+ sampling="uniform", # truncnorm throws an error if delta is 0
+ ),
+ },
+ ],
+)
+def test_random_bbox_transform_does_not_modify_with_base_config(data: dict) -> None:
+ _set_random_seed()
+ h, w, c = data["image_shape"]
+
+ # generate 100 bboxes
+ bboxes = _gen_random_bboxes(np.random.default_rng(seed=0), 100, w, h)
+
+ t = A.Compose(
+ [transforms.RandomBBoxTransform(**data["transform_config"])],
+ bbox_params=A.BboxParams(format="coco", label_fields=["bbox_labels"]),
+ )
+ output = t(
+ image=np.zeros((h, w, c)),
+ bboxes=bboxes,
+ bbox_labels=np.zeros(len(bboxes)),
+ )
+ print("Output bounding boxes")
+ for out_bbox in output["bboxes"]:
+ print(out_bbox)
+ print()
+ bboxes_out = np.asarray(output["bboxes"])
+ print("bboxes")
+ print(bboxes_out)
+ print()
+ np.testing.assert_array_almost_equal(bboxes, bboxes_out)
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "image_shape": [480, 640, 3],
+ "transform_config": dict(
+ shift_factor=0.0,
+ shift_prob=0.0,
+ scale_factor=[0.25, 0.5],
+ scale_prob=1.0,
+ ),
+ },
+ {
+ "image_shape": [480, 640, 3],
+ "transform_config": dict(
+ shift_factor=0.0,
+ shift_prob=0.0,
+ scale_factor=[1.0, 1.5],
+ scale_prob=1.0,
+ ),
+ },
+ {
+ "image_shape": [480, 640, 3],
+ "transform_config": dict(
+ shift_factor=0.0,
+ shift_prob=0.0,
+ scale_factor=[0.5, 1.25],
+ scale_prob=1.0,
+ ),
+ },
+ {
+ "image_shape": [480, 640, 3],
+ "transform_config": dict(
+ shift_factor=0.0,
+ shift_prob=0.0,
+ scale_factor=[0.5, 1.5],
+ scale_prob=0.5,
+ ),
+ },
+ ],
+)
+def test_random_bbox_transform_scale(data: dict) -> None:
+ _set_random_seed()
+ h, w, c = data["image_shape"]
+
+ # generate 100 bboxes
+ bboxes = _gen_random_bboxes(np.random.default_rng(seed=0), 100, w, h)
+
+ t = A.Compose(
+ [transforms.RandomBBoxTransform(**data["transform_config"])],
+ bbox_params=A.BboxParams(format="coco", label_fields=["bbox_labels"]),
+ )
+ output = t(
+ image=np.zeros((h, w, c)),
+ bboxes=bboxes,
+ bbox_labels=np.zeros(len(bboxes)),
+ )
+ print("Output bounding boxes")
+ for out_bbox in output["bboxes"]:
+ print(out_bbox)
+ print()
+
+ bboxes_out = np.asarray(output["bboxes"])
+ scale_low, scale_high = data["transform_config"]["scale_factor"]
+ for bbox_in_wh, bbox_out_wh in zip(bboxes[:, 2:], bboxes_out[:, 2:], strict=False):
+ print("bbox_in_wh", bbox_in_wh)
+ w, h = bbox_in_wh[0].item(), bbox_in_wh[1].item()
+ w_low, w_high = w * scale_low, w * scale_high
+ h_low, h_high = h * scale_low, h * scale_high
+ print("(w, w_low, w_high)", w, w_low, w_high)
+ print("(h, h_low, h_high)", h, h_low, h_high)
+ assert w_low <= bbox_out_wh[0].item() <= w_high
+ assert h_low <= bbox_out_wh[1].item() <= h_high
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "image_shape": [480, 640, 3],
+ "transform_config": dict(
+ shift_factor=0.1,
+ shift_prob=1.0,
+ scale_factor=[1.0, 1.0],
+ scale_prob=0.0,
+ ),
+ },
+ ],
+)
+def test_random_bbox_transform_shift(data: dict) -> None:
+ _set_random_seed()
+ h, w, c = data["image_shape"]
+
+ # generate 100 bboxes
+ bboxes = _gen_random_bboxes(np.random.default_rng(seed=0), 100, w, h)
+
+ t = A.Compose(
+ [transforms.RandomBBoxTransform(**data["transform_config"])],
+ bbox_params=A.BboxParams(format="coco", label_fields=["bbox_labels"]),
+ )
+ output = t(
+ image=np.zeros((h, w, c)),
+ bboxes=bboxes,
+ bbox_labels=np.zeros(len(bboxes)),
+ )
+ print("Output bounding boxes")
+ for out_bbox in output["bboxes"]:
+ print(out_bbox)
+ print()
+
+ bboxes_out = np.asarray(output["bboxes"])
+ shift = data["transform_config"]["shift_factor"]
+ for bbox_in, bbox_out in zip(bboxes, bboxes_out, strict=False):
+ print("bbox_in", bbox_in)
+ x, y, w, h = bbox_in
+ x_out, y_out, w_out, h_out = bbox_out
+ max_shift_x, max_shift_y = w * shift, h * shift
+ assert x - max_shift_x <= x_out <= x + max_shift_x
+ assert y - max_shift_y <= y_out <= y + max_shift_y
+
+
+def _set_random_seed():
+ np.random.seed(0)
+ random.seed(0)
+
+
+def _gen_random_bboxes(
+ gen: np.random.Generator,
+ num_bboxes: int,
+ w: int,
+ h: int,
+) -> np.ndarray:
+ image_wh = np.array([w, h])
+ bboxes = np.zeros((num_bboxes, 4))
+ # sample x, y in the images
+ bboxes[:, :2] = image_wh * gen.random((num_bboxes, 2))
+ # sample w, h with the space remaining
+ bboxes[:, 2:] = (image_wh - bboxes[:, :2]) * gen.random((num_bboxes, 2))
+
+ print()
+ print("Input bounding boxes")
+ print(bboxes)
+ return bboxes
diff --git a/tests/pose_estimation_pytorch/data/test_utils.py b/tests/pose_estimation_pytorch/data/test_utils.py
new file mode 100644
index 0000000000..1494e01787
--- /dev/null
+++ b/tests/pose_estimation_pytorch/data/test_utils.py
@@ -0,0 +1,97 @@
+#
+# 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
+#
+"""Tests data utils."""
+
+import numpy as np
+import pytest
+
+import deeplabcut.pose_estimation_pytorch.data.utils as utils
+
+
+@pytest.mark.parametrize(
+ "keypoints, expected_bboxes, params",
+ [
+ (
+ [[0, 0, 2], [10, 5, 2]],
+ [0, 0, 10, 5],
+ dict(image_w=1024, image_h=1024, margin=0),
+ ),
+ (
+ [[-1, -1, 2], [3, 4, 2]],
+ [0, 0, 3, 4],
+ dict(image_w=1024, image_h=1024, margin=0),
+ ),
+ (
+ [[0, 0, 2], [10, 5, 2]],
+ [0, 0, 5, 3],
+ dict(image_w=5, image_h=3, margin=0),
+ ),
+ (
+ [[0, 0, 2], [10, 5, 2]],
+ [0, 0, 5, 3],
+ dict(image_w=5, image_h=3, margin=10),
+ ),
+ (
+ [[[0, 0, 2], [10, 5, 2]]],
+ [[0, 0, 10, 5]],
+ dict(image_w=1024, image_h=1024, margin=0),
+ ),
+ (
+ [
+ [[4, 1, 2], [10, 5, 2], [3, 12, 0]],
+ [[7, 3, 2], [2, 0, -1], [1, 12, 2]],
+ ],
+ [
+ [4, 1, 6, 4],
+ [1, 3, 6, 9],
+ ],
+ dict(image_w=1024, image_h=1024, margin=0),
+ ),
+ (
+ [
+ [[4, 1, 2], [10, 5, 2], [3, 12, 0]],
+ [[7, 3, 2], [2, 0, -1], [1, 12, 2]],
+ ],
+ [
+ [2, 0, 10, 7],
+ [0, 1, 9, 13],
+ ],
+ dict(image_w=1024, image_h=1024, margin=2),
+ ),
+ (
+ [
+ [[4, 1, 2], [10, 5, 2], [3, 12, 0]],
+ [[7, 3, 2], [2, 0, -1], [1, 12, 2]],
+ ],
+ [
+ [2, 0, 8, 7],
+ [0, 1, 9, 9],
+ ],
+ dict(image_w=10, image_h=10, margin=2),
+ ),
+ (
+ [
+ [[4, 1, 2], [10, 5, 2], [3, 12, 0]],
+ [[7, 3, 0], [2, 0, -1], [1, 12, 0]],
+ ],
+ [
+ [2, 0, 8, 7],
+ [0, 0, 0, 0],
+ ],
+ dict(image_w=10, image_h=10, margin=2),
+ ),
+ ],
+)
+def test_bbox_from_keypoints(keypoints, expected_bboxes, params):
+ keypoints = np.asarray(keypoints, dtype=float)
+ bboxes = utils.bbox_from_keypoints(keypoints, **params)
+ expected_bboxes = np.asarray(expected_bboxes, dtype=float)
+ np.testing.assert_array_almost_equal(bboxes, expected_bboxes)
diff --git a/tests/pose_estimation_pytorch/models/target_generators/test_heatmap_targets.py b/tests/pose_estimation_pytorch/models/target_generators/test_heatmap_targets.py
new file mode 100644
index 0000000000..7f418f4912
--- /dev/null
+++ b/tests/pose_estimation_pytorch/models/target_generators/test_heatmap_targets.py
@@ -0,0 +1,131 @@
+#
+# 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
+#
+"""Tests the heatmap target generators (plateau and gaussian)"""
+
+import numpy as np
+import pytest
+import torch
+
+from deeplabcut.pose_estimation_pytorch.models.target_generators.heatmap_targets import (
+ HeatmapGaussianGenerator,
+)
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "dist_thresh": 3,
+ "num_heatmaps": 1,
+ "in_shape": (3, 3),
+ "out_shape": (3, 3),
+ "centers": [(1, 1)],
+ "expected_output": [
+ [0.7788, 0.8825, 0.7788],
+ [0.8825, 1.0000, 0.8825],
+ [0.7788, 0.8825, 0.7788],
+ ],
+ },
+ {
+ "dist_thresh": 3,
+ "num_heatmaps": 1,
+ "in_shape": (5, 5),
+ "out_shape": (5, 5),
+ "centers": [[1, 1], [2, 2]],
+ "expected_output": [
+ [0.7788, 0.8825, 0.7788, 0.5353, 0.3679],
+ [0.8825, 1.0000, 0.8825, 0.7788, 0.5353],
+ [0.7788, 0.8825, 1.0000, 0.8825, 0.6065],
+ [0.5353, 0.7788, 0.8825, 0.7788, 0.5353],
+ [0.3679, 0.5353, 0.6065, 0.5353, 0.3679],
+ ],
+ },
+ {
+ "dist_thresh": 1,
+ "num_heatmaps": 1,
+ "in_shape": (4, 4),
+ "out_shape": (4, 4),
+ "centers": [[1, 1]],
+ "expected_output": [
+ [0.1054, 0.3247, 0.1054, 0.0036],
+ [0.3247, 1.0, 0.3247, 0.0111],
+ [0.1054, 0.3247, 0.1054, 0.0036],
+ [0.0036, 0.0111, 0.0036, 0.0001],
+ ],
+ },
+ ],
+)
+def test_gaussian_heatmap_generation_single_keypoint(data):
+ dist_thresh = data["dist_thresh"]
+ generator = HeatmapGaussianGenerator(
+ num_heatmaps=data["num_heatmaps"],
+ pos_dist_thresh=dist_thresh,
+ heatmap_mode=HeatmapGaussianGenerator.Mode.KEYPOINT,
+ generate_locref=False,
+ )
+ stride = data["in_shape"][0] / data["out_shape"][0]
+ outputs = torch.zeros((1, data["num_heatmaps"], *data["out_shape"]))
+ ann_shape = (1, len(data["centers"]), data["num_heatmaps"], 2)
+ annotations = {
+ "keypoints": torch.tensor(data["centers"]).reshape(ann_shape) # x, y
+ }
+ targets = generator(stride, {"heatmap": outputs}, annotations)
+
+ print("Targets")
+ print(targets["heatmap"]["target"])
+ print()
+ np.testing.assert_almost_equal(
+ targets["heatmap"]["target"].cpu().numpy().reshape(data["out_shape"]),
+ np.array(data["expected_output"]),
+ decimal=3,
+ )
+
+
+@pytest.mark.parametrize(
+ "batch_size, num_keypoints, image_size",
+ [(2, 2, (64, 64)), (1, 5, (48, 64)), (15, 50, (64, 48))],
+)
+def test_random_gaussian_target_generation(batch_size: int, num_keypoints: int, image_size: tuple, num_animals=1):
+ # generate annotations
+ annotations = {
+ "keypoints": torch.randint(1, min(image_size), (batch_size, num_animals, num_keypoints, 2))
+ } # batch size, num animals, num keypoints, 2 for x,y
+
+ # model stride 1
+ stride = 1
+
+ # generate predictions
+ predicted_heatmaps = {"heatmap": torch.zeros((batch_size, num_keypoints, *image_size))}
+
+ # generate heatmap
+ generator = HeatmapGaussianGenerator(
+ num_heatmaps=num_keypoints,
+ pos_dist_thresh=17,
+ heatmap_mode=HeatmapGaussianGenerator.Mode.KEYPOINT,
+ generate_locref=False,
+ )
+ targets = generator(stride, predicted_heatmaps, annotations)
+ target_heatmap = targets["heatmap"]["target"].reshape(batch_size, num_keypoints, image_size[0] * image_size[1])
+
+ # get coords of max value of the heatmap
+ gaus_max = torch.argmax(target_heatmap, dim=2)
+
+ # get unraveled coords
+ x = gaus_max % image_size[1]
+ y = gaus_max // image_size[1]
+
+ # get heatmap center tensor
+ predict_kp = torch.stack((x, y), dim=-1)
+ # Remove num_animals dimension - only one animal is supported
+ annotations["keypoints"] = torch.squeeze(annotations["keypoints"], dim=1)
+
+ # compare heatmap center to annotation
+ assert torch.eq(annotations["keypoints"], predict_kp).all().item()
diff --git a/tests/pose_estimation_pytorch/models/target_generators/test_plateau_targets.py b/tests/pose_estimation_pytorch/models/target_generators/test_plateau_targets.py
new file mode 100644
index 0000000000..d335fc5524
--- /dev/null
+++ b/tests/pose_estimation_pytorch/models/target_generators/test_plateau_targets.py
@@ -0,0 +1,90 @@
+#
+# 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
+#
+"""Tests the heatmap target generators (plateau and gaussian)"""
+
+import numpy as np
+import pytest
+import torch
+
+from deeplabcut.pose_estimation_pytorch.models.target_generators.heatmap_targets import (
+ HeatmapGenerator,
+ HeatmapPlateauGenerator,
+)
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ {
+ "dist_thresh": 1,
+ "num_heatmaps": 1,
+ "in_shape": (3, 3),
+ "out_shape": (3, 3),
+ "centers": [(1, 1)],
+ "expected_output": [
+ [0.0, 1.0, 0.0],
+ [1.0, 1.0, 1.0],
+ [0.0, 1.0, 0.0],
+ ],
+ },
+ {
+ "dist_thresh": 2,
+ "num_heatmaps": 1,
+ "in_shape": (5, 5),
+ "out_shape": (5, 5),
+ "centers": [[1, 1], [2, 2]],
+ "expected_output": [
+ [1.0, 1.0, 1.0, 0.0, 0.0],
+ [1.0, 1.0, 1.0, 1.0, 0.0],
+ [1.0, 1.0, 1.0, 1.0, 1.0],
+ [0.0, 1.0, 1.0, 1.0, 0.0],
+ [0.0, 0.0, 1.0, 0.0, 0.0],
+ ],
+ },
+ {
+ "dist_thresh": 2,
+ "num_heatmaps": 1,
+ "in_shape": (4, 4),
+ "out_shape": (4, 4),
+ "centers": [[1, 1]],
+ "expected_output": [
+ [1.0, 1.0, 1.0, 0.0],
+ [1.0, 1.0, 1.0, 1.0],
+ [1.0, 1.0, 1.0, 0.0],
+ [0.0, 1.0, 0.0, 0.0],
+ ],
+ },
+ ],
+)
+def test_plateau_heatmap_generation_single_keypoint(data):
+ dist_thresh = data["dist_thresh"]
+ generator = HeatmapPlateauGenerator(
+ num_heatmaps=data["num_heatmaps"],
+ pos_dist_thresh=dist_thresh,
+ heatmap_mode=HeatmapGenerator.Mode.KEYPOINT,
+ generate_locref=False,
+ )
+ stride = data["in_shape"][0] / data["out_shape"][0]
+ outputs = torch.zeros((1, data["num_heatmaps"], *data["out_shape"]))
+ ann_shape = (1, len(data["centers"]), data["num_heatmaps"], 2)
+ annotations = {
+ "keypoints": torch.tensor(data["centers"]).reshape(ann_shape) # x, y
+ }
+ targets = generator(stride, {"heatmap": outputs}, annotations)
+
+ print("Targets")
+ print(targets["heatmap"]["target"])
+ print()
+ np.testing.assert_almost_equal(
+ targets["heatmap"]["target"].cpu().numpy().reshape(data["out_shape"]),
+ np.array(data["expected_output"]),
+ decimal=3,
+ )
diff --git a/tests/tests_modelzoo.py b/tests/pose_estimation_pytorch/modelzoo/test_download.py
similarity index 91%
rename from tests/tests_modelzoo.py
rename to tests/pose_estimation_pytorch/modelzoo/test_download.py
index 555c590307..06cc9857e1 100644
--- a/tests/tests_modelzoo.py
+++ b/tests/pose_estimation_pytorch/modelzoo/test_download.py
@@ -4,12 +4,13 @@
# https://github.com/DeepLabCut/DeepLabCut
#
# Please see AUTHORS for contributors.
-# https://github.com/DeepLabCut/DeepLabCut/blob/master/AUTHORS
+# https://github.com/DeepLabCut/DeepLabCut/blob/main/AUTHORS
#
# Licensed under GNU Lesser General Public License v3.0
#
-import dlclibrary
import os
+
+import dlclibrary
import pytest
from dlclibrary.dlcmodelzoo.modelzoo_download import MODELOPTIONS
@@ -29,7 +30,7 @@ def test_download_huggingface_wrong_model():
dlclibrary.download_huggingface_model("wrong_model_name")
-@pytest.mark.skip
+@pytest.mark.skip(reason="slow")
@pytest.mark.parametrize("model", MODELOPTIONS)
def test_download_all_models(tmp_path_factory, model):
test_download_huggingface_model(tmp_path_factory, model)
diff --git a/tests/pose_estimation_pytorch/modelzoo/test_fmpose_integration.py b/tests/pose_estimation_pytorch/modelzoo/test_fmpose_integration.py
new file mode 100644
index 0000000000..e7608625fa
--- /dev/null
+++ b/tests/pose_estimation_pytorch/modelzoo/test_fmpose_integration.py
@@ -0,0 +1,198 @@
+#
+# 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 pathlib
+import socket
+from types import SimpleNamespace
+
+import numpy as np
+import pandas as pd
+import pytest
+
+fmpose3d = pytest.importorskip("fmpose3d", reason="fmpose3d not installed")
+pytestmark = pytest.mark.fmpose3d
+
+# DLC fmpose_3d modules import fmpose3d; load only after importorskip above.
+from deeplabcut.pose_estimation_pytorch.modelzoo.fmpose_3d import inference as fmp_inf # noqa: E402
+from deeplabcut.pose_estimation_pytorch.modelzoo.fmpose_3d.fmpose3d import ( # noqa: E402
+ get_fmpose3d_inference_api,
+)
+
+
+def _has_network(host="huggingface.co", port=443, timeout=3) -> bool:
+ """Return True if we can reach *host* (used to download model weights)."""
+ try:
+ socket.create_connection((host, port), timeout=timeout).close()
+ return True
+ except OSError:
+ return False
+
+
+requires_network = pytest.mark.skipif(
+ not _has_network(),
+ reason="No network connection (needed to download model weights)",
+)
+
+_REPO_ROOT = pathlib.Path(__file__).resolve().parents[3]
+_EXAMPLE_IMAGE = (
+ _REPO_ROOT / "examples" / "Reaching-Mackenzie-2018-08-30" / "labeled-data" / "reachingvideo1" / "img005.png"
+)
+
+
+# ---------------------------------------------------------------------------
+# Lightweight: verifies the API object is constructed correctly
+# ---------------------------------------------------------------------------
+@pytest.mark.parametrize("model_type", ["fmpose3d_humans", "fmpose3d_animals"])
+@pytest.mark.unittest
+def test_api_init(model_type):
+ api = get_fmpose3d_inference_api(model_type, device="cpu")
+ assert api is not None
+ assert hasattr(api, "prepare_2d")
+ assert hasattr(api, "pose_3d")
+ assert hasattr(api, "predict")
+
+
+# ---------------------------------------------------------------------------
+# Integration: downloads weights and runs inference (needs network)
+# ---------------------------------------------------------------------------
+@requires_network
+@pytest.mark.functional
+def test_prepare_2d_and_pose_3d():
+ """2D detection followed by 3D lifting on a real image."""
+ api = get_fmpose3d_inference_api("fmpose3d_animals", device="cpu")
+
+ result_2d = api.prepare_2d(source=str(_EXAMPLE_IMAGE))
+ assert isinstance(result_2d.keypoints, np.ndarray)
+ assert result_2d.keypoints.shape[-1] == 2
+
+ keypoints_3d = api.pose_3d(
+ keypoints_2d=result_2d.keypoints,
+ image_size=result_2d.image_size,
+ )
+ assert isinstance(keypoints_3d.poses_3d, np.ndarray)
+ assert keypoints_3d.poses_3d.shape[-1] == 3
+
+
+@requires_network
+@pytest.mark.functional
+def test_predict_end_to_end():
+ """Full pipeline (2D -> 3D) in a single call."""
+ api = get_fmpose3d_inference_api("fmpose3d_animals", device="cpu")
+ predictions_3d = api.predict(source=str(_EXAMPLE_IMAGE))
+
+ assert isinstance(predictions_3d.poses_3d, np.ndarray)
+ assert predictions_3d.poses_3d.shape[-1] == 3
+
+
+@pytest.mark.unittest
+def test_pose2d_to_dlc_predictions_shapes():
+ pose_2d = SimpleNamespace(
+ keypoints=np.random.rand(2, 3, 4, 2).astype(np.float32),
+ scores=np.random.rand(2, 3, 4).astype(np.float32),
+ )
+ preds = fmp_inf._pose2d_to_dlc_predictions(
+ pose_2d=pose_2d,
+ max_individuals=1,
+ num_bodyparts=4,
+ )
+
+ assert len(preds) == 3
+ assert preds[0]["bodyparts"].shape == (1, 4, 3)
+ np.testing.assert_allclose(preds[0]["bodyparts"][0, :, :2], pose_2d.keypoints[0, 0])
+
+
+@pytest.mark.unittest
+def test_poses3d_to_dataframe_layout():
+ scorer = "DLC_test"
+ bodyparts = ["bp1", "bp2", "bp3"]
+ columns_2d = pd.MultiIndex.from_product(
+ [[scorer], ["individual1"], bodyparts, ["x", "y", "likelihood"]],
+ names=["scorer", "individuals", "bodyparts", "coords"],
+ )
+ df_2d = pd.DataFrame(np.zeros((2, len(columns_2d))), columns=columns_2d)
+
+ poses_3d = [
+ np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]),
+ np.array([[10.0, 11.0, 12.0], [13.0, 14.0, 15.0], [16.0, 17.0, 18.0]]),
+ ]
+ df_3d = fmp_inf._poses3d_to_dataframe(poses_3d, df_2d, f"{scorer}_3d")
+
+ assert df_3d.columns.names == ["scorer", "bodyparts", "coords"]
+ assert set(df_3d.columns.get_level_values("coords")) == {"x", "y", "z"}
+ assert df_3d.loc[0, (f"{scorer}_3d", "bp1", "x")] == 1.0
+ assert df_3d.loc[1, (f"{scorer}_3d", "bp3", "z")] == 18.0
+
+
+@pytest.mark.functional
+def test_video_inference_fmpose3d_include_3d_return(tmp_path, monkeypatch):
+ frames = [np.zeros((8, 8, 3), dtype=np.uint8) for _ in range(2)]
+
+ class FakeVideoIterator:
+ def __init__(self, _path, cropping=None):
+ self.dimensions = (8, 8)
+ self.fps = 30
+ self._frames = frames
+
+ def __iter__(self):
+ return iter(self._frames)
+
+ class FakeAPI:
+ def prepare_2d(self, source):
+ n_frames = source.shape[0]
+ return SimpleNamespace(
+ keypoints=np.zeros((1, n_frames, 26, 2), dtype=np.float32),
+ scores=np.ones((1, n_frames, 26), dtype=np.float32),
+ image_size=(8, 8),
+ )
+
+ def pose_3d(self, keypoints_2d, image_size):
+ n_frames = keypoints_2d.shape[1]
+ return SimpleNamespace(
+ poses_3d=np.zeros((n_frames, 26, 3), dtype=np.float32),
+ )
+
+ def _fake_create_df_from_prediction(predictions, dlc_scorer, multi_animal, model_cfg, output_path, output_prefix):
+ bodyparts = model_cfg["metadata"]["bodyparts"]
+ individuals = model_cfg["metadata"]["individuals"]
+ columns = pd.MultiIndex.from_product(
+ [[dlc_scorer], individuals, bodyparts, ["x", "y", "likelihood"]],
+ names=["scorer", "individuals", "bodyparts", "coords"],
+ )
+ return pd.DataFrame(np.zeros((len(predictions), len(columns))), columns=columns)
+
+ monkeypatch.setattr(fmp_inf, "VideoIterator", FakeVideoIterator)
+ monkeypatch.setattr(
+ fmp_inf,
+ "get_fmpose3d_inference_api",
+ lambda model_type, device: FakeAPI(),
+ )
+ monkeypatch.setattr(fmp_inf, "create_df_from_prediction", _fake_create_df_from_prediction)
+ monkeypatch.setattr(
+ fmp_inf,
+ "get_superanimal_colormaps",
+ lambda: {
+ "superanimal_quadruped": "viridis",
+ "superanimal_humanbody": "viridis",
+ },
+ )
+
+ result = fmp_inf._video_inference_fmpose3d(
+ video_paths=[str(tmp_path / "dummy.mp4")],
+ model_name="fmpose3d_animals",
+ dest_folder=tmp_path,
+ create_labeled_video=False,
+ include_3d_in_return=True,
+ )
+
+ payload = result[str(tmp_path / "dummy.mp4")]
+ assert "df_2d" in payload
+ assert "df_3d" in payload
+ assert isinstance(payload["df_3d"], pd.DataFrame)
+ assert (tmp_path / "dummy_DLC_fmpose3d_animals_3d.h5").exists()
diff --git a/tests/pose_estimation_pytorch/modelzoo/test_inference_helpers.py b/tests/pose_estimation_pytorch/modelzoo/test_inference_helpers.py
new file mode 100644
index 0000000000..50e58e234d
--- /dev/null
+++ b/tests/pose_estimation_pytorch/modelzoo/test_inference_helpers.py
@@ -0,0 +1,172 @@
+#
+# 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
+#
+
+from types import SimpleNamespace
+
+import pytest
+
+import deeplabcut.pose_estimation_pytorch.modelzoo.inference_helpers as helpers
+
+
+def _dummy_cfg(method: str = "TD") -> dict:
+ return {
+ "method": method,
+ "metadata": {"bodyparts": ["nose"], "unique_bodyparts": []},
+ }
+
+
+def test_create_superanimal_inference_runners_uses_custom_config_path(monkeypatch):
+ cfg = _dummy_cfg("TD")
+ read_calls = []
+
+ def fake_read_config_as_dict(path):
+ read_calls.append(path)
+ return cfg
+
+ monkeypatch.setattr(helpers, "read_config_as_dict", fake_read_config_as_dict)
+ monkeypatch.setattr(helpers, "update_config", lambda config, max_individuals, device: config)
+ monkeypatch.setattr(
+ helpers,
+ "get_inference_runners",
+ lambda **kwargs: ("pose_runner", "det_runner"),
+ )
+
+ import deeplabcut.modelzoo.weight_initialization as wi
+
+ monkeypatch.setattr(
+ wi,
+ "build_weight_init",
+ lambda **kwargs: SimpleNamespace(
+ snapshot_path="pose.pt",
+ detector_snapshot_path="det.pt",
+ ),
+ )
+
+ pose_runner, detector_runner, model_cfg = helpers.create_superanimal_inference_runners(
+ superanimal_name="superanimal_quadruped",
+ model_name="hrnet_w32",
+ detector_name="fasterrcnn_resnet50_fpn_v2",
+ customized_model_config="/tmp/custom_model_cfg.yaml",
+ )
+
+ assert read_calls == ["/tmp/custom_model_cfg.yaml"]
+ assert pose_runner == "pose_runner"
+ assert detector_runner == "det_runner"
+ assert model_cfg is cfg
+
+
+def test_create_superanimal_inference_runners_uses_deepcopy_for_custom_dict(monkeypatch):
+ custom_cfg = _dummy_cfg("TD")
+ monkeypatch.setattr(
+ helpers,
+ "read_config_as_dict",
+ lambda path: pytest.fail("read_config_as_dict should not be called for dict input"),
+ )
+
+ def fake_update_config(config, max_individuals, device):
+ # Mutate nested structure; caller-owned dict should stay unchanged.
+ config["metadata"]["bodyparts"].append("tail")
+ return config
+
+ monkeypatch.setattr(helpers, "update_config", fake_update_config)
+ monkeypatch.setattr(
+ helpers,
+ "get_inference_runners",
+ lambda **kwargs: ("pose_runner", None),
+ )
+
+ import deeplabcut.modelzoo.weight_initialization as wi
+
+ monkeypatch.setattr(
+ wi,
+ "build_weight_init",
+ lambda **kwargs: SimpleNamespace(
+ snapshot_path="pose.pt",
+ detector_snapshot_path=None,
+ ),
+ )
+
+ _, _, model_cfg = helpers.create_superanimal_inference_runners(
+ superanimal_name="superanimal_quadruped",
+ model_name="hrnet_w32",
+ detector_name=None,
+ customized_model_config=custom_cfg,
+ )
+
+ assert custom_cfg["metadata"]["bodyparts"] == ["nose"]
+ assert model_cfg["metadata"]["bodyparts"] == ["nose", "tail"]
+
+
+@pytest.mark.parametrize("input_device", ["auto", None])
+def test_create_superanimal_inference_runners_auto_device_selection(monkeypatch, input_device):
+ cfg = _dummy_cfg("TD")
+ captured = {}
+
+ monkeypatch.setattr(helpers, "read_config_as_dict", lambda path: cfg)
+
+ def fake_update_config(config, max_individuals, device):
+ captured["device"] = device
+ return config
+
+ monkeypatch.setattr(helpers, "update_config", fake_update_config)
+ monkeypatch.setattr(
+ helpers,
+ "get_inference_runners",
+ lambda **kwargs: ("pose_runner", "det_runner"),
+ )
+
+ import deeplabcut.modelzoo.weight_initialization as wi
+
+ monkeypatch.setattr(
+ wi,
+ "build_weight_init",
+ lambda **kwargs: SimpleNamespace(
+ snapshot_path="pose.pt",
+ detector_snapshot_path="det.pt",
+ ),
+ )
+
+ helpers.create_superanimal_inference_runners(
+ superanimal_name="superanimal_quadruped",
+ model_name="hrnet_w32",
+ detector_name="fasterrcnn_resnet50_fpn_v2",
+ customized_model_config="/tmp/custom_model_cfg.yaml",
+ device=input_device,
+ )
+ assert captured["device"] == "auto"
+
+
+def test_create_superanimal_inference_runners_raises_for_fmpose3d():
+ with pytest.raises(NotImplementedError, match="FMPose3D"):
+ helpers.create_superanimal_inference_runners(
+ superanimal_name="superanimal_quadruped",
+ model_name="FMPose3D_resnet",
+ detector_name="fasterrcnn_resnet50_fpn_v2",
+ customized_model_config=_dummy_cfg("TD"),
+ )
+
+
+def test_create_superanimal_inference_runners_propagates_unsupported_dataset_error(
+ monkeypatch,
+):
+ monkeypatch.setattr(
+ helpers,
+ "load_super_animal_config",
+ lambda **kwargs: (_ for _ in ()).throw(ValueError("Unsupported dataset for model zoo config")),
+ )
+
+ with pytest.raises(ValueError, match="Unsupported dataset"):
+ helpers.create_superanimal_inference_runners(
+ superanimal_name="superanimal_unknown",
+ model_name="hrnet_w32",
+ detector_name="fasterrcnn_resnet50_fpn_v2",
+ customized_model_config=None,
+ )
diff --git a/tests/pose_estimation_pytorch/modelzoo/test_load_superanimal_models.py b/tests/pose_estimation_pytorch/modelzoo/test_load_superanimal_models.py
new file mode 100644
index 0000000000..fab8e2fb3f
--- /dev/null
+++ b/tests/pose_estimation_pytorch/modelzoo/test_load_superanimal_models.py
@@ -0,0 +1,31 @@
+#
+# 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 dlclibrary
+import pytest
+import torch
+
+from deeplabcut.pose_estimation_pytorch.modelzoo import get_super_animal_snapshot_path
+
+
+@pytest.mark.skip(reason="require-models")
+def test_load_superanimal_models_weights_only():
+ super_animal_names = dlclibrary.get_available_datasets()
+ for super_animal in super_animal_names:
+ print(f"\nTesting {super_animal}")
+ for detector in dlclibrary.get_available_detectors(super_animal):
+ print(super_animal, detector)
+ path = get_super_animal_snapshot_path(super_animal, detector)
+ _snapshot = torch.load(path, map_location="cpu", weights_only=True)
+
+ for pose_model in dlclibrary.get_available_models(super_animal):
+ print(super_animal, pose_model)
+ path = get_super_animal_snapshot_path(super_animal, pose_model)
+ _snapshot = torch.load(path, map_location="cpu", weights_only=True)
diff --git a/tests/pose_estimation_pytorch/modelzoo/test_modelzoo_utils.py b/tests/pose_estimation_pytorch/modelzoo/test_modelzoo_utils.py
new file mode 100644
index 0000000000..571dc9f5cd
--- /dev/null
+++ b/tests/pose_estimation_pytorch/modelzoo/test_modelzoo_utils.py
@@ -0,0 +1,35 @@
+#
+# 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 pytest
+
+import deeplabcut.pose_estimation_pytorch.modelzoo as modelzoo
+
+# TODO: make a proper test incl. human model, bird model and that skips the require... at least once per week.
+
+
+@pytest.mark.parametrize("super_animal", ["superanimal_quadruped", "superanimal_topviewmouse"])
+@pytest.mark.parametrize("model_name", ["hrnet_w32"])
+@pytest.mark.parametrize("detector_name", [None, "fasterrcnn_resnet50_fpn_v2"])
+def test_get_config_model_paths(super_animal, model_name, detector_name):
+ model_config = modelzoo.load_super_animal_config(
+ super_animal=super_animal,
+ model_name=model_name,
+ detector_name=detector_name,
+ )
+
+ assert isinstance(model_config, dict)
+ if detector_name is None:
+ assert model_config["method"].lower() == "bu"
+ assert "detector" not in model_config
+ else:
+ assert model_config["method"].lower() == "td"
+ assert "detector" in model_config
diff --git a/tests/pose_estimation_pytorch/modelzoo/test_webapp.py b/tests/pose_estimation_pytorch/modelzoo/test_webapp.py
new file mode 100644
index 0000000000..34a210b462
--- /dev/null
+++ b/tests/pose_estimation_pytorch/modelzoo/test_webapp.py
@@ -0,0 +1,69 @@
+#
+# 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 numpy as np
+import pytest
+
+from deeplabcut.modelzoo.webapp.inference import SuperanimalPyTorchInference
+from deeplabcut.utils import auxiliaryfunctions
+
+# TODO: make a proper test incl. human model, bird model and that skips the require... at least once per week.
+
+
+@pytest.mark.parametrize("max_individuals", [1, 3])
+@pytest.mark.parametrize("project_name", ["superanimal_quadruped", "superanimal_topviewmouse"])
+@pytest.mark.parametrize("pose_model_type", ["hrnet_w32"])
+def test_class_init(project_name, pose_model_type, max_individuals):
+ inference_pipeline = SuperanimalPyTorchInference(project_name, pose_model_type, max_individuals=max_individuals)
+
+ assert isinstance(inference_pipeline.config, dict)
+ assert inference_pipeline.config["metadata"]["bodyparts"]
+ assert len(inference_pipeline.config["metadata"]["bodyparts"]) > 0
+
+
+@pytest.mark.skip(reason="require-models")
+@pytest.mark.parametrize("project_name", ["superanimal_quadruped", "superanimal_topviewmouse"])
+@pytest.mark.parametrize("pose_model_type", ["hrnet_w32"])
+def test_runner_init(project_name, pose_model_type):
+ inference_pipeline = SuperanimalPyTorchInference(project_name, pose_model_type, max_individuals=1)
+ weight_folder = f"{auxiliaryfunctions.get_deeplabcut_path()}/modelzoo/checkpoints"
+ snapshot_path = f"{weight_folder}/{project_name}_{pose_model_type}.pth"
+ detector_path = f"{weight_folder}/{project_name}_fasterrcnn.pt"
+
+ inference_pipeline.initialize_models(snapshot_path, detector_path)
+
+ assert inference_pipeline.models.pose_runner
+ assert inference_pipeline.models.detector_runner
+
+
+@pytest.mark.skip(reason="require-models")
+@pytest.mark.parametrize("max_individuals", [10, 4, 1])
+@pytest.mark.parametrize("project_name", ["superanimal_quadruped", "superanimal_topviewmouse", "superanimal_humanbody"])
+@pytest.mark.parametrize("pose_model_type", ["hrnet_w32"])
+def test_predict(project_name, pose_model_type, max_individuals):
+ inference_pipeline = SuperanimalPyTorchInference(project_name, pose_model_type, max_individuals=max_individuals)
+ image_path = "img0001.png"
+ weight_folder = f"{auxiliaryfunctions.get_deeplabcut_path()}/modelzoo/checkpoints"
+ snapshot_path = f"{weight_folder}/{project_name}_{pose_model_type}.pth"
+ detector_path = f"{weight_folder}/{project_name}_fasterrcnn.pt"
+
+ inference_pipeline.initialize_models(snapshot_path, detector_path)
+ frame = {image_path: np.random.rand(100, 100, 3)}
+ response = inference_pipeline.predict(frame)
+
+ assert isinstance(response, dict)
+ assert response["joint_names"] == inference_pipeline.config["bodyparts"]
+ assert response["predictions"][0]["markers"].shape == (
+ max_individuals,
+ len(inference_pipeline.config["bodyparts"]),
+ 3,
+ )
+ assert response["predictions"][0]["image_path"] == image_path
diff --git a/tests/pose_estimation_pytorch/other/test_api_utils.py b/tests/pose_estimation_pytorch/other/test_api_utils.py
new file mode 100644
index 0000000000..0efbfbb52c
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_api_utils.py
@@ -0,0 +1,94 @@
+#
+# 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 random
+
+import numpy as np
+import pytest
+
+import deeplabcut.pose_estimation_pytorch.data.transforms as transforms
+
+transform_dicts = [
+ {"auto_padding": {"pad_height_divisor": 64, "pad_width_divisor": 27}},
+ {"resize": {"height": 512, "width": 256, "keep_ration": True}},
+ {
+ "covering": True,
+ "gaussian_noise": 12.75,
+ "hist_eq": True,
+ "motion_blur": True,
+ "normalize_images": True,
+ "rotation": 30,
+ "scale_jitter": [0.5, 1.25],
+ "auto_padding": {"pad_width_divisor": 64, "pad_height_divisor": 27},
+ },
+ {
+ "covering": True,
+ "gaussian_noise": 100,
+ "hist_eq": True,
+ "motion_blur": True,
+ "normalize_images": True,
+ "rotation": 180,
+ "scale_jitter": [0.03, 20],
+ "auto_padding": {"pad_width_divisor": 64, "pad_height_divisor": 27},
+ },
+]
+
+
+def _get_random_params(transform_idx):
+ return (
+ transform_dicts[transform_idx],
+ (random.randint(100, 1000), random.randint(100, 1000)),
+ random.randint(1, 100),
+ random.randint(1, 100),
+ )
+
+
+@pytest.mark.parametrize(
+ "transform_dict, size_image, num_keypoints, num_animals",
+ [_get_random_params(i) for i in range(4)],
+)
+def test_build_transforms(transform_dict, size_image, num_keypoints, num_animals):
+ transform_bbox_aug = transforms.build_transforms(transform_dict)
+ w, h = size_image
+ for i in range(10):
+ test_image = np.random.randint(0, 255, (h, w, 3), dtype=np.uint8)
+ bboxes = np.random.randint(0, min(w - 1, h - 1), (num_animals, 4))
+ bboxes[:, 2] = w - bboxes[:, 0]
+ bboxes[:, 3] = h - bboxes[:, 1]
+ keypoints = np.random.randint(0, min(w, h), (num_keypoints, 2))
+
+ with pytest.raises(ValueError) as _err_info:
+ _ = transform_bbox_aug(image=test_image)
+ _ = transform_bbox_aug(image=test_image, bboxes=bboxes.copy())
+ _ = transform_bbox_aug(image=test_image, keypoints=keypoints.copy(), bboxes=bboxes.copy())
+
+ transformed_with_bbox = transform_bbox_aug(
+ image=test_image,
+ keypoints=keypoints.copy(),
+ bboxes=bboxes.copy(),
+ bbox_labels=np.arange(num_animals),
+ class_labels=[0 for _ in range(len(keypoints))],
+ )
+
+ if "resize" in transform_dict.keys():
+ assert transformed_with_bbox["image"].shape[:2] == (
+ transform_dict["resize"]["height"],
+ transform_dict["resize"]["width"],
+ )
+
+ if "auto_padding" in transform_dict.keys():
+ modh, modw = (
+ transform_dict["auto_padding"]["pad_height_divisor"],
+ transform_dict["auto_padding"]["pad_width_divisor"],
+ )
+ assert transformed_with_bbox["image"].shape[0] % modh == 0
+ assert transformed_with_bbox["image"].shape[1] % modw == 0
+
+ assert len(transformed_with_bbox["keypoints"]) == len(keypoints)
diff --git a/tests/pose_estimation_pytorch/other/test_configs/config.yaml b/tests/pose_estimation_pytorch/other/test_configs/config.yaml
new file mode 100644
index 0000000000..15ad6f4678
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_configs/config.yaml
@@ -0,0 +1,106 @@
+ # Project definitions (do not edit)
+Task: openfield
+scorer: Pranav
+date: Aug20
+multianimalproject: false
+identity:
+
+ # Project path (change when moving around)
+project_path: /home/quentin/datasets/Openfield_pytorch
+
+ # Annotation data set configuration (and individual video cropping parameters)
+video_sets:
+ /Data/openfield-Pranav-2018-08-20/videos/m1s1.mp4:
+ crop: 0, 640, 0, 480
+ /Data/openfield-Pranav-2018-08-20/videos/m1s2.mp4:
+ crop: 0, 640, 0, 480
+ /Data/openfield-Pranav-2018-08-20/videos/m2s1.mp4:
+ crop: 0, 640, 0, 480
+ /Data/openfield-Pranav-2018-08-20/videos/m3s1.mp4:
+ crop: 0, 640, 0, 480
+ /Data/openfield-Pranav-2018-08-20/videos/m3s2.mp4:
+ crop: 0, 640, 0, 480
+ /Data/openfield-Pranav-2018-08-20/videos/m4s1.mp4:
+ crop: 0, 640, 0, 480
+ /Data/openfield-Pranav-2018-08-20/videos/m5s1.mp4:
+ crop: 0, 800, 0, 800
+ /Data/openfield-Pranav-2018-08-20/videos/m6s1.mp4:
+ crop: 0, 800, 0, 800
+ /Data/openfield-Pranav-2018-08-20/videos/m6s2.mp4:
+ crop: 0, 800, 0, 800
+ /Data/openfield-Pranav-2018-08-20/videos/m7s1.mp4:
+ crop: 0, 800, 0, 800
+ /Data/openfield-Pranav-2018-08-20/videos/m7s2.mp4:
+ crop: 0, 800, 0, 800
+ /Data/openfield-Pranav-2018-08-20/videos/m7s3.mp4:
+ crop: 0, 800, 0, 800
+ /Data/openfield-Pranav-2018-08-20/videos/m8s1.mp4:
+ crop: 0, 800, 0, 800
+
+ /Users/mwmathis/Downloads/ARCricket1.avi:
+ crop: 0, 720, 0, 540
+bodyparts:
+- snout
+- leftear
+- rightear
+- tailbase
+
+flipped_keypoints:
+- 0
+- 2
+- 1
+- 3
+
+
+ # Fraction of video to start/stop when extracting frames for labeling/refinement
+
+ # Fraction of video to start/stop when extracting frames for labeling/refinement
+
+ # Fraction of video to start/stop when extracting frames for labeling/refinement
+
+ # Fraction of video to start/stop when extracting frames for labeling/refinement
+
+ # Fraction of video to start/stop when extracting frames for labeling/refinement
+
+ # Fraction of video to start/stop when extracting frames for labeling/refinement
+
+ # Fraction of video to start/stop when extracting frames for labeling/refinement
+
+ # Fraction of video to start/stop when extracting frames for labeling/refinement
+
+ # Fraction of video to start/stop when extracting frames for labeling/refinement
+start: 0
+stop: 1
+numframes2pick: 20
+
+ # Plotting configuration
+skeleton: []
+skeleton_color: black
+pcutoff: 0.4
+dotsize: 8
+alphavalue: 0.7
+colormap: jet
+
+ # Training,Evaluation and Analysis configuration
+TrainingFraction:
+- 0.95
+iteration: 1
+default_net_type: resnet_50
+default_augmenter: default
+snapshotindex: -1
+batch_size: 1
+
+ # Cropping Parameters (for analysis and outlier frame detection)
+cropping: false
+ #if cropping is true for analysis, then set the values here:
+x1: 0
+x2: 640
+y1: 277
+y2: 624
+
+ # Refinement configuration (parameters from annotation dataset configuration also relevant in this stage)
+corner2move2:
+- 50
+- 50
+move2corner: true
+croppedtraining:
diff --git a/tests/pose_estimation_pytorch/other/test_configs/pose_cfg.yaml b/tests/pose_estimation_pytorch/other/test_configs/pose_cfg.yaml
new file mode 100644
index 0000000000..ec41492bd4
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_configs/pose_cfg.yaml
@@ -0,0 +1,115 @@
+ # Project definitions (do not edit)
+Task:
+scorer:
+date:
+multianimalproject:
+identity:
+
+ # Project path (change when moving around)
+project_path: /home/quentin/datasets/Openfield_pytorch/dlc-models/iteration-1/openfieldAug20-trainset95shuffle1/train
+
+ # Annotation data set configuration (and individual video cropping parameters)
+video_sets:
+bodyparts:
+
+ # Fraction of video to start/stop when extracting frames for labeling/refinement
+start:
+stop:
+numframes2pick:
+
+ # Plotting configuration
+skeleton: []
+skeleton_color: black
+pcutoff:
+dotsize:
+alphavalue:
+colormap:
+
+ # Training,Evaluation and Analysis configuration
+TrainingFraction:
+iteration:
+default_net_type:
+default_augmenter:
+snapshotindex:
+batch_size: 1
+
+ # Cropping Parameters (for analysis and outlier frame detection)
+cropping:
+ #if cropping is true for analysis, then set the values here:
+x1:
+x2:
+y1:
+y2:
+
+ # Refinement configuration (parameters from annotation dataset configuration also relevant in this stage)
+corner2move2:
+move2corner:
+all_joints:
+- - 0
+- - 1
+- - 2
+- - 3
+all_joints_names:
+- snout
+- leftear
+- rightear
+- tailbase
+alpha_r: 0.02
+apply_prob: 0.5
+contrast:
+ clahe: true
+ claheratio: 0.1
+ histeq: true
+ histeqratio: 0.1
+convolution:
+ edge: false
+ emboss:
+ alpha:
+ - 0.0
+ - 1.0
+ strength:
+ - 0.5
+ - 1.5
+ embossratio: 0.1
+ sharpen: false
+ sharpenratio: 0.3
+cropratio: 0.4
+dataset: training-datasets/iteration-1/UnaugmentedDataSet_openfieldAug20/openfield_Pranav95shuffle1.mat
+dataset_type: default
+decay_steps: 30000
+display_iters: 1000
+global_scale: 0.8
+init_weights: /home/quentin/miniconda/envs/DEEPLABCUT/lib/python3.8/site-packages/deeplabcut/pose_estimation_tensorflow/models/pretrained/resnet_v1_50.ckpt
+intermediate_supervision: false
+intermediate_supervision_layer: 12
+location_refinement: true
+locref_huber_loss: true
+locref_loss_weight: 0.05
+locref_stdev: 7.2801
+lr_init: 0.0005
+max_input_size: 1500
+metadataset: training-datasets/iteration-1/UnaugmentedDataSet_openfieldAug20/Documentation_data-openfield_95shuffle1.pickle
+min_input_size: 64
+mirror: false
+multi_stage: false
+multi_step:
+- - 0.005
+ - 10000
+- - 0.02
+ - 430000
+- - 0.002
+ - 730000
+- - 0.001
+ - 1030000
+net_type: resnet_50
+num_joints: 4
+pairwise_huber_loss: false
+pairwise_predict: false
+partaffinityfield_predict: false
+pos_dist_thresh: 17
+rotation: 25
+rotratio: 0.4
+save_iters: 50000
+scale_jitter_lo: 0.5
+scale_jitter_up: 1.25
+scmap_type: plateau
diff --git a/tests/pose_estimation_pytorch/other/test_configs/pytorch_config.yaml b/tests/pose_estimation_pytorch/other/test_configs/pytorch_config.yaml
new file mode 100644
index 0000000000..0be2ca0ed8
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_configs/pytorch_config.yaml
@@ -0,0 +1,45 @@
+project_root: /home/quentin/datasets/Openfield_pytorch
+pose_cfg_path: /home/quentin/datasets/Openfield_pytorch/dlc-models/iteration-1/openfieldAug20-trainset95shuffle1/train/pose_cfg.yaml
+cfg_path: /home/quentin/datasets/Openfield_pytorch/config.yaml
+
+seed: 42
+device: 'cuda:2' #needs to be updated dynamically; some users might have CPUs
+model:
+ backbone:
+ type: 'ResNet'
+ pretrained: 'https://download.pytorch.org/models/resnet50-19c8e357.pth'
+ heatmap_head:
+ type: 'SimpleHead'
+ channels: [ 2048, 1024, 4 ]
+ kernel_size: [ 2, 2 ]
+ strides: [ 2, 2 ]
+ locref_head:
+ type: 'SimpleHead'
+ channels: [ 2048, 1024, 8 ]
+ kernel_size: [ 2, 2 ]
+ strides: [ 2, 2 ]
+ pose_model:
+ stride: 8
+ heatmap_type: 'plateau'
+optimizer:
+ type: 'SGD'
+ params:
+ lr: 0.005
+scheduler:
+ type: "LRListScheduler"
+ params:
+ milestones : [10, 430]
+ lr_list : [[0.02], [0.002]]
+criterion:
+ type: 'PoseLoss'
+ loss_weight_locref: 0.1
+ locref_huber_loss: True
+#logger:
+# type: 'WandbLogger'
+# project_name: 'deeplabcut'
+# run_name: 'tmp'
+solver:
+ type: 'BottomUpSingleAnimalSolver'
+pos_dist_thresh : 17
+batch_size: 1
+epochs: 600
diff --git a/tests/pose_estimation_pytorch/other/test_custom_transforms.py b/tests/pose_estimation_pytorch/other/test_custom_transforms.py
new file mode 100644
index 0000000000..28ddb2ffc7
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_custom_transforms.py
@@ -0,0 +1,53 @@
+#
+# 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 numpy as np
+import pytest
+
+from deeplabcut.pose_estimation_pytorch.data import transforms
+
+
+@pytest.mark.parametrize("width, height", [(200, 200), (300, 300), (400, 400)])
+def test_keypoint_aware_cropping(width, height):
+ fake_image = np.empty((600, 600, 3))
+ fake_keypoints = [(i * 100, i * 100, 0, 0) for i in range(1, 6)]
+ aug = transforms.KeypointAwareCrop(width=width, height=height, crop_sampling="density")
+ transformed = aug(image=fake_image, keypoints=fake_keypoints)
+ assert transformed["image"].shape[:2] == (height, width)
+ # Ensure at least a keypoint is visible in each crop
+ assert len(transformed["keypoints"])
+
+
+def test_grayscale():
+ fake_image = np.ones((600, 600, 3))
+ fake_image *= np.random.uniform(0, 255, size=fake_image.shape)
+ fake_image = fake_image.astype(np.uint8)
+ gray = transforms.Grayscale(alpha=1, p=1)
+ aug_image = gray(image=fake_image)["image"]
+ assert aug_image.shape == fake_image.shape
+
+ gray = transforms.Grayscale(alpha=0, p=1)
+ aug_image = gray(image=fake_image)["image"]
+ assert np.allclose(fake_image, aug_image)
+
+ with pytest.warns(UserWarning, match="clipped"):
+ gray = transforms.Grayscale(alpha=1.5)
+ assert gray.alpha == 1
+
+
+def test_coarse_dropout():
+ fake_image = np.ones((300, 300, 3))
+ fake_image *= np.random.uniform(0, 255, size=fake_image.shape)
+ fake_image = fake_image.astype(np.uint8)
+ cd = transforms.CoarseDropout(max_height=0.9999, max_width=0.9999, p=1)
+ kpts = np.random.rand(10, 2) * 298 + 1
+ aug_kpts = cd(image=fake_image, keypoints=kpts)["keypoints"]
+ assert len(aug_kpts) == kpts.shape[0]
+ assert np.isnan([c for kpt in aug_kpts for c in kpt]).all()
diff --git a/tests/pose_estimation_pytorch/other/test_data_helper.py b/tests/pose_estimation_pytorch/other/test_data_helper.py
new file mode 100644
index 0000000000..73db76316d
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_data_helper.py
@@ -0,0 +1,94 @@
+#
+# 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
+#
+from __future__ import annotations
+
+import os
+from unittest.mock import Mock, patch
+from zipfile import Path
+
+import numpy as np
+import pytest
+
+from deeplabcut.generate_training_dataset import create_training_dataset
+from deeplabcut.pose_estimation_pytorch.data.dlcloader import DLCLoader
+from deeplabcut.pose_estimation_pytorch.data.utils import merge_list_of_dicts
+
+
+def mock_aux() -> Mock:
+ aux_functions = Mock()
+ aux_functions.read_plainconfig = Mock()
+ aux_functions.read_plainconfig.return_value = {}
+ return aux_functions
+
+
+@patch("deeplabcut.pose_estimation_pytorch.data.base.auxiliaryfunctions", mock_aux())
+def _get_loader(project_root):
+ if not (Path(project_root) / "training-datasets").exists():
+ create_training_dataset(config=str(Path(project_root) / "config.yaml"))
+ return DLCLoader(Path(project_root) / "config.yaml", shuffle=1)
+
+
+@pytest.mark.skip
+@pytest.mark.parametrize("repo_path", ["/home/anastasiia/DLCdev"])
+def test_propertymeta_project(repo_path):
+ project_root = os.path.join(repo_path, "examples", "openfield-Pranav-2018-10-30")
+ dlc_loader = _get_loader(project_root)
+
+ for prop in dlc_loader.properties:
+ print(prop, getattr(dlc_loader, prop))
+
+
+@pytest.mark.skip
+@pytest.mark.parametrize(
+ "repo_path, mode",
+ [("/home/anastasiia/DLCdev", "train"), ("/home/anastasiia/DLCdev", "test")],
+)
+def test_propertymeta_dataset(repo_path, mode):
+ repo_path = "/home/anastasiia/DLCdev"
+ mode = "train"
+ project_root = os.path.join(repo_path, "examples", "openfield-Pranav-2018-10-30")
+ dlc_loader = _get_loader(project_root)
+ dataset = dlc_loader.create_dataset(transform=None, mode=mode)
+
+ for prop in dataset.properties:
+ print(prop, getattr(dataset, prop))
+
+
+@pytest.mark.parametrize(
+ "list_dicts, keys_to_include",
+ [
+ ([{"a": 1, "b": 2}, {"a": 3, "b": 4}], ["a"]),
+ (
+ [
+ *[
+ {
+ "keypoints": np.random.randn(27, 3),
+ "images": np.random.randn(256, 192),
+ }
+ ]
+ * 10
+ ],
+ [*["keypoints", "images"] * 10],
+ ),
+ ],
+)
+def test_merge_list_of_dicts(list_dicts, keys_to_include):
+ result_dict = merge_list_of_dicts(list_dicts, keys_to_include)
+ expected_result_dict = {}
+ for dictionary in list_dicts:
+ for key in dictionary:
+ if key not in keys_to_include:
+ continue
+ else:
+ if key not in expected_result_dict:
+ expected_result_dict[key] = []
+ expected_result_dict[key].append(dictionary[key])
+ assert result_dict == expected_result_dict
diff --git a/tests/pose_estimation_pytorch/other/test_dataset.py b/tests/pose_estimation_pytorch/other/test_dataset.py
new file mode 100644
index 0000000000..221ec64f19
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_dataset.py
@@ -0,0 +1,186 @@
+#
+# 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 os
+import random
+from pathlib import Path
+from unittest.mock import Mock, patch
+
+import albumentations as A
+import pytest
+from torch.utils.data import DataLoader
+
+import deeplabcut.pose_estimation_pytorch as dlc
+import deeplabcut.utils.auxiliaryfunctions as dlc_auxfun
+from deeplabcut.core.engine import Engine
+from deeplabcut.generate_training_dataset import create_training_dataset
+
+
+def mock_config() -> Mock:
+ aux_functions = Mock()
+ aux_functions.read_config_as_dict = Mock()
+ aux_functions.read_config_as_dict.return_value = {
+ "data": {"train": {}, "inference": {}},
+ "metadata": {
+ "project_path": "",
+ "pose_config_path": "",
+ "bodyparts": ["snout", "leftear", "rightear", "tailbase"],
+ "unique_bodyparts": [],
+ "individuals": ["animal"],
+ "with_identity": False,
+ },
+ "method": "bu",
+ }
+ return aux_functions
+
+
+@patch("deeplabcut.pose_estimation_pytorch.data.base.config", mock_config())
+def _get_dataset(path, transform, mode="train"):
+ project_root = Path(path)
+ if not (project_root / "training-datasets").exists():
+ print(str(project_root / "config.yaml"))
+ create_training_dataset(
+ config=str(project_root / "config.yaml"),
+ net_type="resnet_50",
+ engine=Engine.PYTORCH,
+ )
+
+ loader = dlc.DLCLoader(Path(project_root) / "config.yaml", shuffle=1)
+ dataset = loader.create_dataset(transform=transform, mode=mode)
+ return dataset
+
+
+def _get_openfield_dataset(transform=None):
+ dlc_path = dlc_auxfun.get_deeplabcut_path()
+ repo_path = os.path.dirname(dlc_path)
+ openfield_path = os.path.join(repo_path, "examples", "openfield-Pranav-2018-10-30")
+
+ return _get_dataset(openfield_path, transform=transform)
+
+
+key_set = {
+ "offsets",
+ "path",
+ "scales",
+ "image",
+ "original_size",
+ "annotations",
+ "image_id",
+ "context",
+}
+anno_key_set = {
+ "keypoints",
+ "keypoints_unique",
+ "with_center_keypoints",
+ "area",
+ "boxes",
+ "is_crowd",
+ "labels",
+ "individual_ids",
+}
+
+
+@pytest.mark.parametrize("batch_size", [1, 2, random.randint(2, 20)])
+def test_iter_all_dataset_no_transform(batch_size):
+ if batch_size > 1: # if batched, all images need to be the same size
+ transform = A.Compose(
+ [A.Resize(512, 512)],
+ keypoint_params=A.KeypointParams(format="xy"),
+ bbox_params=A.BboxParams(format="coco", label_fields=["bbox_labels"]),
+ )
+ else:
+ transform = A.Compose(
+ [A.Normalize()],
+ keypoint_params=A.KeypointParams(format="xy"),
+ bbox_params=A.BboxParams(format="coco", label_fields=["bbox_labels"]),
+ )
+ dataset = _get_openfield_dataset(transform=transform)
+ dataloader = DataLoader(dataset, batch_size=batch_size)
+ max_num_animals = dataset.parameters.max_num_animals
+ num_keypoints = dataset.parameters.num_joints
+ for i, item in enumerate(dataloader):
+ is_last_batch = i == (len(dataloader) - 1)
+ assert set(item.keys()) == key_set, (
+ f"the key returned don't match the required ones: {item.keys()} != {key_set}"
+ )
+
+ anno = item["annotations"]
+ assert set(anno.keys()) == anno_key_set, "the annotation keys returned don't match the required ones"
+
+ assert (len(item["image"].shape) == 4) and ((item["image"].shape[:2] == (batch_size, 3)) or is_last_batch), (
+ "image shape is not (batch_size, 3, h, w)"
+ )
+
+ b, _, h, w = item["image"].shape
+ kpts, bboxes = anno["keypoints"], anno["boxes"]
+ assert kpts.shape == (batch_size, max_num_animals, num_keypoints, 3) or is_last_batch, (
+ "keypoints have the wrong shape"
+ )
+ assert bboxes.shape == (batch_size, max_num_animals, 4) or is_last_batch, "boxes have the wrong shape"
+ assert ((bboxes[:, :, 0] + bboxes[:, :, 2]) <= w).all() and ((bboxes[:, :, 1] + bboxes[:, :, 3]) <= h).all(), (
+ "boxes don't seem to be un the format (x, y, w, h)"
+ )
+
+
+def _generate_random_test_values_aug(min_exa):
+ batch_size = random.randint(1, 20)
+ x_size = random.randint(50, 600)
+ y_size = random.randint(50, 600)
+ exaggeration = random.randint(min_exa, 99)
+
+ return batch_size, x_size, y_size, exaggeration
+
+
+@pytest.mark.parametrize(
+ "batch_size, x_size, y_size, exaggeration",
+ [
+ (1, 512, 512, 1),
+ _generate_random_test_values_aug(1),
+ _generate_random_test_values_aug(50),
+ ],
+)
+def test_iter_all_augmented_dataset(batch_size, x_size, y_size, exaggeration):
+ transform = A.Compose(
+ [
+ A.Affine(
+ scale=(1 - exaggeration * 0.01, 1 + exaggeration),
+ rotate=(-exaggeration * 2, exaggeration * 2),
+ translate_px=(-exaggeration * 10, exaggeration * 10),
+ ),
+ A.Resize(y_size, x_size),
+ ],
+ keypoint_params=A.KeypointParams(format="xy", remove_invisible=False),
+ bbox_params=A.BboxParams(format="coco", label_fields=["bbox_labels"]),
+ )
+ dataset = _get_openfield_dataset(transform=transform)
+ dataloader = DataLoader(dataset, batch_size=batch_size)
+ max_num_animals = dataset.parameters.max_num_animals
+ num_keypoints = dataset.parameters.num_joints
+ for i, item in enumerate(dataloader):
+ is_last_batch = i == (len(dataloader) - 1)
+ assert set(item.keys()) == key_set, (
+ f"the key returned don't match the required ones: {item.keys()} != {key_set}"
+ )
+
+ anno = item["annotations"]
+ assert set(anno.keys()) == anno_key_set, "the annotation keys returned don't match the required ones"
+
+ assert (len(item["image"].shape) == 4) and ((item["image"].shape[:2] == (batch_size, 3)) or is_last_batch), (
+ "image shape is not (batch_size, 3, h, w)"
+ )
+
+ kpts, bboxes = anno["keypoints"], anno["boxes"]
+ b, _, h, w = item["image"].shape
+ assert (h == y_size) and (w == x_size)
+ assert kpts.shape == (batch_size, max_num_animals, num_keypoints, 3) or is_last_batch, (
+ "keypoints have the wrong shape"
+ )
+ assert bboxes.shape == (batch_size, max_num_animals, 4) or is_last_batch, "boxes have the wrong shape"
+ assert ((bboxes[:, :, 0] + bboxes[:, :, 2]) <= w).all() and ((bboxes[:, :, 1] + bboxes[:, :, 3]) <= h).all()
diff --git a/tests/pose_estimation_pytorch/other/test_gaussian_targets.py b/tests/pose_estimation_pytorch/other/test_gaussian_targets.py
new file mode 100644
index 0000000000..1c202b1902
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_gaussian_targets.py
@@ -0,0 +1,56 @@
+#
+# 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 pytest
+import torch
+
+from deeplabcut.pose_estimation_pytorch.models.target_generators import HeatmapGaussianGenerator
+
+
+@pytest.mark.parametrize(
+ "batch_size, num_keypoints, image_size",
+ [(2, 2, (64, 64)), (1, 5, (48, 64)), (15, 50, (64, 48))],
+)
+def test_gaussian_target_generation(batch_size: int, num_keypoints: int, image_size: tuple, num_animals=1):
+ # generate annotations
+ labels = {
+ "keypoints": torch.randint(1, min(image_size), (batch_size, num_animals, num_keypoints, 2))
+ } # batch size, num animals, num keypoints, 2 for x,y
+ # generate predictions
+ stride = 1
+ prediction = {
+ "heatmap": torch.rand((batch_size, num_keypoints, *image_size[:2])),
+ "locref": torch.rand((batch_size, 2 * num_keypoints, *image_size[:2])),
+ }
+
+ # generate heatmap
+ output = HeatmapGaussianGenerator(
+ num_heatmaps=num_keypoints,
+ pos_dist_thresh=17,
+ locref_std=5.0,
+ )
+ output = output(stride, prediction, labels)["heatmap"]["target"].reshape(
+ batch_size, num_keypoints, image_size[0] * image_size[1]
+ )
+
+ # get coords of max value of the heatmap
+ gaus_max = torch.argmax(output, dim=2)
+
+ # get unraveled coords
+ x = gaus_max % image_size[1]
+ y = gaus_max // image_size[1]
+
+ # get heatmap center tensor
+ predict_kp = torch.stack((x, y), dim=-1)
+ # Remove num_animals dimension - only one animal is supported
+ labels["keypoints"] = torch.squeeze(labels["keypoints"], dim=1)
+
+ # compare heatmap center to annotation
+ assert torch.eq(labels["keypoints"], predict_kp).all().item()
diff --git a/tests/pose_estimation_pytorch/other/test_heatmap_plateau_targets.py b/tests/pose_estimation_pytorch/other/test_heatmap_plateau_targets.py
new file mode 100644
index 0000000000..b44dc3f39f
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_heatmap_plateau_targets.py
@@ -0,0 +1,204 @@
+#
+# 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 pytest
+import torch
+
+from deeplabcut.pose_estimation_pytorch.models.target_generators import HeatmapPlateauGenerator
+
+
+def get_target(
+ batch_size: int,
+ num_animals: int,
+ num_joints: int,
+ image_size: tuple[int, int],
+ locref_std: float,
+ pos_dist_thresh: int,
+):
+ """Summary Getting the target generator for certain annotations, predictions and
+ image size.
+
+ Args:
+ batch_size (int): number of images
+ num_animals (int): number of animals
+ num_joints (int): number of bodyparts
+ image_size (tuple): image size in pixels
+ locref_std (float): scaling factor
+ pos_dist_thresh (int): radius plateau on the heatmap
+
+ Returns:
+ target_output (dict): containing the heatmaps, locref_maps and locref_masks.
+ annotations (dict): containing input keypoint annotations.
+
+ Examples:
+ input:
+ batch_size = 1
+ num_animals = 1
+ num_joints = 6
+ image_size = (256,256)
+ locref_stdev = 7.2801
+ pos_dist_thresh = 17
+ output:
+ """
+ labels = {
+ "keypoints": torch.randint(1, min(image_size), (batch_size, num_animals, num_joints, 2))
+ } # 2 for x,y coords
+ stride = 1
+ prediction = {
+ "heatmap": torch.rand((batch_size, num_joints, image_size[0], image_size[1])),
+ "locref": torch.rand((batch_size, 2 * num_joints, image_size[0], image_size[1])),
+ }
+ generator = HeatmapPlateauGenerator(
+ num_heatmaps=num_joints,
+ pos_dist_thresh=pos_dist_thresh,
+ locref_std=locref_std,
+ generate_locref=True,
+ )
+
+ targets_output = generator(stride, prediction, labels)
+ return targets_output, labels
+
+
+data = [(1, 1, 10, (256, 256), 7.2801, 17)]
+
+
+@pytest.mark.parametrize(
+ "batch_size, num_animals, num_joints, image_size, locref_stdev, pos_dist_thresh",
+ data,
+)
+def test_expected_output(
+ batch_size: int,
+ num_animals: int,
+ num_joints: int,
+ image_size: tuple[int, int],
+ locref_stdev: float,
+ pos_dist_thresh: int,
+):
+ """Summary:
+ Testing if plateau targets return the expected output. We take a target generator from
+ get_target function. Given a sequence of random numbers for batch_size, num_animals etc., we assert if
+ it returns the expected heatmaps and locrefmaps, as well as checking if the output has the expected shape.
+
+ Args:
+ batch_size (int): number of images
+ num_animals (int): number of animals
+ num_joints (int): number of bodyparts
+ image_size (tuple): image size in pixels
+ locref_stdev (float): scaling factor
+ pos_dist_thresh (int): radius plateau on heatmap
+
+ Returns:
+ None
+
+ Examples:
+ input:
+ batch_size = 1
+ num_animals = 1
+ num_joints = 6
+ image_size = (256,256)
+ locref_stdev = 7.2801
+ pos_dist_thresh = 17
+ """
+ targets_output, annotations = get_target(
+ batch_size, num_animals, num_joints, image_size, locref_stdev, pos_dist_thresh
+ )
+
+ assert "heatmap" in targets_output
+ assert "locref" in targets_output
+ assert targets_output["heatmap"]["target"].shape == (
+ batch_size,
+ num_joints,
+ image_size[0],
+ image_size[1],
+ ) # heatmaps score output
+ assert targets_output["locref"]["weights"].shape == (
+ batch_size,
+ num_joints * 2,
+ image_size[0],
+ image_size[1],
+ )
+ assert targets_output["locref"]["target"].shape == (
+ batch_size,
+ num_joints * 2,
+ image_size[0],
+ image_size[1],
+ )
+
+
+data = [(1, 1, 10, (256, 256), 7.2801, 17)]
+
+
+@pytest.mark.parametrize(
+ "batch_size, num_animals, num_joints, image_size, locref_stdev, pos_dist_thresh",
+ data,
+)
+def test_single_animal(
+ batch_size: int,
+ num_animals: int,
+ num_joints: int,
+ image_size: tuple[int, int],
+ locref_stdev: float,
+ pos_dist_thresh: int,
+):
+ """Summary Testing, for single animals experiments (num_animals=1) if the distance
+ between the expected keypoints and the annotations keypoints is smaller than the
+ radius plateau.
+
+ 'argmax' function returns the indices of the max values of all elements in the input tensor.
+ If there are multiple maximal values, such as in our case because it's a plateau, then the
+ indices of the first maximal value are returned. From this tensor we exctact x,y coords
+ and then concatenate these new tensors along a new dimension. Then, we assert if the distance between
+ each x,y element in annotations and predicted keypoints is smaller or equal to the 'pos_dist_thresh',
+ which represents the radius of the plateau heatmap.
+
+ Args:
+ batch_size (int): number of images
+ num_animals (int): number of animals
+ num_joints (int): number of bodyparts
+ image_size (tuple): image size in pixels
+ locref_stdev (float): scaling factor
+ pos_dist_thresh (int): radius plateau on heatmap
+
+ Returns:
+ None
+
+ Examples:
+ input:
+ batch_size = 1
+ num_animals = 1
+ num_joints = 6
+ image_size = (256,256)
+ locref_stdev = 7.2801
+ pos_dist_thresh = 17
+ """
+ targets_output, annotations = get_target(
+ batch_size, num_animals, num_joints, image_size, locref_stdev, pos_dist_thresh
+ )
+
+ targets_output = torch.tensor(
+ targets_output["heatmap"]["target"].reshape(1, 10, image_size[0] * image_size[1])
+ ) # converting from dict to tensor. 'argmax' works on tensors.
+
+ plt_max = torch.argmax(targets_output, dim=2)
+ # get unraveled coords
+ x = plt_max % image_size[1]
+ y = plt_max // image_size[1]
+
+ predict_kp = torch.stack((x, y), dim=-1)
+
+ predict_kp = predict_kp.float()
+
+ annotations["keypoints"] = torch.squeeze(annotations["keypoints"], dim=1)
+ annotations["keypoints"] = annotations["keypoints"].float()
+
+ dist = torch.norm(annotations["keypoints"] - predict_kp, p=2, dim=-1)
+ assert (dist <= pos_dist_thresh).all()
diff --git a/tests/pose_estimation_pytorch/other/test_helper.py b/tests/pose_estimation_pytorch/other/test_helper.py
new file mode 100644
index 0000000000..1dfa250109
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_helper.py
@@ -0,0 +1,21 @@
+#
+# 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 torch
+
+
+def test_train_valid_call():
+ tmp_model = torch.nn.Linear(3, 10)
+ to_train_mode = tmp_model.train
+ to_train_mode()
+ assert tmp_model.training
+ to_valid_mode = tmp_model.eval
+ to_valid_mode()
+ assert not tmp_model.training
diff --git a/tests/pose_estimation_pytorch/other/test_match_predictions_to_gt.py b/tests/pose_estimation_pytorch/other/test_match_predictions_to_gt.py
new file mode 100644
index 0000000000..1ee4073fc8
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_match_predictions_to_gt.py
@@ -0,0 +1,132 @@
+#
+# 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 numpy as np
+import pytest
+
+from deeplabcut.pose_estimation_pytorch.post_processing import (
+ match_predictions_to_gt as deeplabcut_torch_match_predictions_gt,
+)
+
+
+@pytest.fixture
+def animals_and_keypoints_invalid():
+ """Summary:
+ Fixture with invalid pred_kpts and gt_kpts shapes that will raise ValueErrors.
+
+ Returns:
+ tuple containing:
+ predicted keypoints(pred_kpts), of shape num_animals, num_keypoints, (x,y,score)
+ ground truth keypoints (gt_kpts), of shape num_animals, num_keypoints, (x,y)
+ individual names (indv_names)
+ """
+ gt_kpts = 2 * np.ones((6, 6, 3)) # num animals, num keypoints, (x,y,vis)
+ gt_kpts[:, :, :2] = np.random.rand(6, 6, 2)
+ pred_kpts = np.random.rand(6, 8, 3) # num animals, num keypoints, (x,y,score)
+ indv_names = ["indv1", "indv2"]
+ return pred_kpts, gt_kpts, indv_names
+
+
+@pytest.fixture
+def animals_and_keypoints():
+ """Summary:
+ Fixture with pred_kpts, gt_kpts shapes and indv_names.
+
+ Returns:
+ tuple containing:
+ predicted keypoints(pred_kpts), of shape num_animals, num_keypoints, (x,y,score)
+ ground truth keypoints (gt_kpts), of shape num_animals, num_keypoints, (x,y)
+ individual names (indv_names)
+ """
+ gt_kpts = 2 * np.ones((6, 6, 3)) # num animals, num keypoints, (x,y,vis)
+ gt_kpts[:, :, :2] = np.random.rand(6, 6, 2)
+
+ # adding score value because the shape of pred_kpts should be (6,6,3)
+ score = np.full((gt_kpts.shape[0], gt_kpts.shape[1], 1), 0.5)
+ pred_kpts = np.concatenate((gt_kpts, score), axis=2)
+ np.random.shuffle(pred_kpts) # shuffle predicted keypoints
+
+ indv_names = ["indv1", "indv2"]
+ return pred_kpts, gt_kpts, indv_names
+
+
+def test_invalid_rmse(animals_and_keypoints_invalid: tuple) -> None:
+ """Summary:
+ Tets if an invalid output really returns a ValueError in the rmse function.
+
+ Args:
+ animals_and_keypoints_invalid (tuple): containing predicted keypoints (pred_kpts),
+ ground truth keypoints (gt_kpts) and individual names (indv_names).
+ """
+ pred_kpts, gt_kpts, indv_names = animals_and_keypoints_invalid
+
+ with pytest.raises(ValueError):
+ deeplabcut_torch_match_predictions_gt.rmse_match_prediction_to_gt(pred_kpts, gt_kpts)
+
+
+def test_invalid_oks(animals_and_keypoints_invalid: tuple) -> None:
+ """Summary:
+ Test if an invalid output really returns a ValueError in the oks function.
+
+ Args:
+ animals_and_keypoints_invalid (tuple): containing predicted keypoints (pred_kpts), ground truth keypoints
+ (gt_kpts)
+ and individual names (indv_names)
+ """
+ pred_kpts, gt_kpts, indv_names = animals_and_keypoints_invalid
+
+ with pytest.raises(ValueError):
+ deeplabcut_torch_match_predictions_gt.oks_match_prediction_to_gt(pred_kpts, gt_kpts, indv_names)
+
+
+def test_rmse_match_predictions_to_gt(animals_and_keypoints: tuple, num_animals: int = 6) -> None:
+ """Summary:
+ Test if rmse_match_prediction_to_gt function returns the expected shape output.
+
+ Args:
+ animals_and_keypoints (tuple): containing predicted keypoints (pred_kpts), ground truth keypoints (gt_kpts)
+ and individual names (indv_names)
+ """
+ pred_kpts, gt_kpts, indv_names = animals_and_keypoints
+
+ col_ind = deeplabcut_torch_match_predictions_gt.rmse_match_prediction_to_gt(pred_kpts, gt_kpts)
+ assert isinstance(col_ind, np.ndarray)
+ assert col_ind.shape == (num_animals,)
+
+
+def test_oks_match_predictions_to_gt(animals_and_keypoints: tuple, num_animals: int = 6) -> None:
+ """Summary:
+ Test if oks_match_predictions_to_gt function returns the expected shape output.
+
+ Args:
+ animals_and_keypoints (tuple): containing predicted keypoints (pred_kpts), ground truth keypoints (gt_kpts)
+ and individual names (indv_names)
+ """
+ pred_kpts, gt_kpts, indv_names = animals_and_keypoints
+
+ col_ind = deeplabcut_torch_match_predictions_gt.rmse_match_prediction_to_gt(pred_kpts, gt_kpts)
+ assert isinstance(col_ind, np.ndarray)
+ assert col_ind.shape == (num_animals,)
+
+
+def test_extend_col_ind(animals_and_keypoints: tuple, num_animals: int = 6) -> None:
+ """Summary:
+ Test if the column indices have the expected shape.
+
+ Args:
+ animals_and_keypoints (tuple): containing predicted keypoints (pred_kpts), ground truth keypoints (gt_kpts)
+ and individual names (indv_names)
+ """
+ pred_kpts, gt_kpts, indv_names = animals_and_keypoints
+
+ col_ind = deeplabcut_torch_match_predictions_gt.rmse_match_prediction_to_gt(pred_kpts, gt_kpts)
+ extended_array = deeplabcut_torch_match_predictions_gt.extend_col_ind(col_ind, num_animals)
+ assert extended_array.shape == (num_animals,)
diff --git a/tests/pose_estimation_pytorch/other/test_modelzoo.py b/tests/pose_estimation_pytorch/other/test_modelzoo.py
new file mode 100644
index 0000000000..18fddfd931
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_modelzoo.py
@@ -0,0 +1,50 @@
+#
+# 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 os
+
+import pytest
+
+from deeplabcut.modelzoo.video_inference import video_inference_superanimal
+from deeplabcut.utils import auxiliaryfunctions
+
+examples_folder = os.path.join(
+ auxiliaryfunctions.get_deeplabcut_path(),
+ "modelzoo",
+ "examples",
+)
+
+
+# requires videos to be in the examples folder
+@pytest.mark.skip
+@pytest.mark.parametrize(
+ "video_paths, superanimal_name",
+ [
+ (f"{examples_folder}/black_dog.mp4", "superanimal_quadruped"),
+ (f"{examples_folder}/black_dog.mp4", "superanimal_quadruped_hrnetw32"),
+ (f"{examples_folder}/swear_mouse_tiny.mp4", "superanimal_topviewmouse"),
+ (
+ f"{examples_folder}/swear_mouse_tiny.mp4",
+ "superanimal_topviewmouse_hrnetw32",
+ ),
+ ],
+)
+def test_video_inference_saves_file(video_paths, superanimal_name):
+ video_inference_superanimal(
+ video_paths,
+ superanimal_name=superanimal_name,
+ )
+ if isinstance(video_paths, str):
+ video_paths = [video_paths]
+ for video_path in video_paths:
+ output_path = video_path.replace(".mp4", "_labeled.mp4")
+ assert os.path.exists(output_path), "Output video file does not exist"
+
+ assert os.stat(output_path).st_size > 0, "Output video file is empty"
diff --git a/tests/pose_estimation_pytorch/other/test_paf_targets.py b/tests/pose_estimation_pytorch/other/test_paf_targets.py
new file mode 100644
index 0000000000..9865cf732c
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_paf_targets.py
@@ -0,0 +1,37 @@
+#
+# 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 pytest
+import torch
+
+from deeplabcut.pose_estimation_pytorch.models.target_generators import pafs_targets
+
+
+@pytest.mark.parametrize(
+ "batch_size, num_keypoints, image_size",
+ [(2, 2, (64, 64)), (1, 5, (48, 64)), (8, 50, (64, 48))],
+)
+def test_paf_target_generation(batch_size: int, num_keypoints: int, image_size: tuple, num_animals=2):
+ labels = {
+ "keypoints": torch.randint(1, min(image_size), (batch_size, num_animals, num_keypoints, 2))
+ } # 2 for x,y coords
+ graph = [(i, j) for i in range(num_keypoints) for j in range(i + 1, num_keypoints)]
+ prediction = {
+ "heatmap": torch.rand((batch_size, num_keypoints, image_size[0], image_size[1])),
+ "paf": torch.rand((batch_size, len(graph) * 2, image_size[0], image_size[1])),
+ }
+ generator = pafs_targets.PartAffinityFieldGenerator(graph=graph, width=20)
+ targets_output = generator(1, prediction, labels)
+ assert targets_output["paf"]["target"].shape == (
+ batch_size,
+ len(graph) * 2,
+ image_size[0],
+ image_size[1],
+ )
diff --git a/tests/pose_estimation_pytorch/other/test_pose_model.py b/tests/pose_estimation_pytorch/other/test_pose_model.py
new file mode 100644
index 0000000000..de36678c8c
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_pose_model.py
@@ -0,0 +1,300 @@
+#
+# 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 copy
+import random
+
+import pytest
+import torch
+
+import deeplabcut.pose_estimation_pytorch.models as dlc_models
+from deeplabcut.pose_estimation_pytorch.models import CRITERIONS, PREDICTORS, TARGET_GENERATORS
+from deeplabcut.pose_estimation_pytorch.models.criterions import LOSS_AGGREGATORS
+from deeplabcut.pose_estimation_pytorch.models.modules import AdaptBlock, BasicBlock
+
+backbones_dicts = [
+ {
+ "type": "HRNet",
+ "model_name": "hrnet_w32",
+ "output_channels": 480,
+ "stride": 4,
+ "interpolate_branches": True,
+ },
+ {
+ "type": "HRNet",
+ "model_name": "hrnet_w18",
+ "output_channels": 270,
+ "stride": 4,
+ "interpolate_branches": True,
+ },
+ {
+ "type": "HRNet",
+ "model_name": "hrnet_w48",
+ "output_channels": 720,
+ "stride": 4,
+ "interpolate_branches": True,
+ },
+ {
+ "type": "HRNet",
+ "model_name": "hrnet_w32",
+ "output_channels": 32,
+ "interpolate_branches": False,
+ "increased_channel_count": False,
+ "stride": 4,
+ },
+ {
+ "type": "HRNet",
+ "model_name": "hrnet_w18",
+ "output_channels": 18,
+ "interpolate_branches": False,
+ "increased_channel_count": False,
+ "stride": 4,
+ },
+ {
+ "type": "HRNet",
+ "model_name": "hrnet_w48",
+ "output_channels": 48,
+ "interpolate_branches": False,
+ "increased_channel_count": False,
+ "stride": 4,
+ },
+ {"type": "ResNet", "model_name": "resnet50_gn", "output_channels": 2048, "stride": 32},
+]
+
+heads_dicts = [
+ {
+ "type": "HeatmapHead",
+ "predictor": {
+ "type": "HeatmapPredictor",
+ "location_refinement": True,
+ "locref_std": 7.2801,
+ },
+ "target_generator": {
+ "type": "HeatmapPlateauGenerator",
+ "num_heatmaps": "num_bodyparts",
+ "pos_dist_thresh": 17,
+ "heatmap_mode": "KEYPOINT",
+ "generate_locref": True,
+ "locref_std": 7.2801,
+ },
+ "criterion": {
+ "heatmap": {
+ "type": "WeightedBCECriterion",
+ "weight": 1.0,
+ },
+ "locref": {
+ "type": "WeightedHuberCriterion",
+ "weight": 0.05,
+ },
+ },
+ "heatmap_config": {
+ "channels": [2048, 1024, -1],
+ "kernel_size": [2, 2],
+ "strides": [2, 2],
+ },
+ "locref_config": {
+ "channels": [2048, 1024, -1],
+ "kernel_size": [2, 2],
+ "strides": [2, 2],
+ },
+ "output_channels": -1,
+ "input_channels": 2048,
+ "total_stride": 4,
+ },
+ {
+ "type": "TransformerHead",
+ "predictor": {
+ "type": "HeatmapPredictor",
+ "location_refinement": False,
+ },
+ "target_generator": {
+ "type": "HeatmapPlateauGenerator",
+ "num_heatmaps": "num_bodyparts",
+ "pos_dist_thresh": 17,
+ "heatmap_mode": "KEYPOINT",
+ "generate_locref": False,
+ },
+ "criterion": {"type": "WeightedBCECriterion"},
+ "dim": 192,
+ "hidden_heatmap_dim": 384,
+ "heatmap_dim": -1,
+ "apply_multi": True,
+ "heatmap_size": [-1, -1],
+ "apply_init": True,
+ "total_stride": 1,
+ "input_channels": -1,
+ "output_channels": -1,
+ "head_stride": 1,
+ },
+ {
+ "type": "DEKRHead",
+ "predictor": {
+ "type": "DEKRPredictor",
+ "num_animals": 1,
+ "keypoint_score_type": "heatmap",
+ "max_absorb_distance": 75,
+ },
+ "target_generator": {
+ "type": "DEKRGenerator",
+ "num_joints": "num_bodyparts",
+ "pos_dist_thresh": 17,
+ "bg_weight": 0.1,
+ },
+ "criterion": {
+ "heatmap": {
+ "type": "WeightedBCECriterion",
+ "weight": 1.0,
+ },
+ "offset": {
+ "type": "WeightedHuberCriterion",
+ "weight": 0.03,
+ },
+ },
+ "heatmap_config": {
+ "channels": [480, 64, -1],
+ "num_blocks": 1,
+ "dilation_rate": 1,
+ "final_conv_kernel": 1,
+ "block": BasicBlock,
+ },
+ "offset_config": {
+ "channels": [480, -1, -1],
+ "num_offset_per_kpt": 15,
+ "num_blocks": 1,
+ "dilation_rate": 1,
+ "final_conv_kernel": 1,
+ "block": AdaptBlock,
+ },
+ "total_stride": 1,
+ "input_channels": 480,
+ "output_channels": -1,
+ },
+]
+
+
+def _generate_random_backbone_inputs(i):
+ # Returns sizes that are divisible by 64to be able to predict consistently output size
+ # (and be able to do the forward pass of HRNet)
+ x_size_tmp, y_size_tmp = random.randint(100, 1000), random.randint(100, 1000)
+ return (
+ backbones_dicts[i],
+ (x_size_tmp - x_size_tmp % 64, y_size_tmp - y_size_tmp % 64),
+ )
+
+
+@pytest.mark.parametrize(
+ "backbone_dict, input_size",
+ [_generate_random_backbone_inputs(i) for i in range(len(backbones_dicts))],
+)
+def test_backbone(backbone_dict, input_size):
+ input_tensor = torch.Tensor(1, 3, input_size[1], input_size[0])
+
+ stride = backbone_dict.pop("stride")
+ output_channels = backbone_dict.pop("output_channels")
+ backbone = dlc_models.BACKBONES.build(backbone_dict)
+
+ features = backbone(input_tensor)
+ _, c, h, w = features.shape
+ assert c == output_channels
+ assert h == input_size[1] // stride
+ assert w == input_size[0] // stride
+
+
+def _generate_random_head_inputs(i):
+ # Returns sizes that are divisible by 64to be able to predict consistently output size
+ # (and be able to do the forward pass of HRNet)
+ x_size_tmp, y_size_tmp = random.randint(8, 500), random.randint(8, 500)
+ num_kpts = random.randint(2, 50)
+ return (
+ heads_dicts[i],
+ (x_size_tmp - x_size_tmp % 4, y_size_tmp - y_size_tmp % 4),
+ num_kpts,
+ )
+
+
+@pytest.mark.parametrize(
+ "head_dict, input_shape, num_keypoints",
+ [_generate_random_head_inputs(i) for i in range(len(heads_dicts))],
+)
+def test_head(head_dict, input_shape, num_keypoints):
+ w, h = input_shape
+ head_dict = copy.deepcopy(head_dict)
+
+ head_type = head_dict["type"]
+ input_channels = head_dict.pop("input_channels")
+ output_channels = head_dict.pop("output_channels")
+ total_stride = head_dict.pop("total_stride")
+ if head_type == "HeatmapHead":
+ output_channels = num_keypoints
+ head_dict["heatmap_config"]["channels"][2] = output_channels
+ head_dict["locref_config"]["channels"][2] = 2 * output_channels
+ head_dict["target_generator"]["num_heatmaps"] = output_channels
+ input_tensor = torch.zeros((1, input_channels, h, w))
+
+ elif head_type == "TransformerHead":
+ output_channels = num_keypoints
+ input_channels = num_keypoints
+ head_dict["heatmap_dim"] = h * w
+ head_dict["heatmap_size"] = [h, w]
+ head_dict["target_generator"]["num_heatmaps"] = output_channels
+ input_tensor = torch.zeros((1, input_channels, head_dict["dim"] * 3))
+
+ elif head_type == "DEKRHead":
+ output_channels = num_keypoints + 1
+ head_dict["target_generator"]["num_joints"] = num_keypoints
+ head_dict["heatmap_config"]["channels"][2] = num_keypoints + 1
+ head_dict["offset_config"]["channels"][1] = num_keypoints * head_dict["offset_config"]["num_offset_per_kpt"]
+ head_dict["offset_config"]["channels"][2] = num_keypoints
+ input_tensor = torch.zeros((1, input_channels, h, w))
+
+ if "type" in head_dict["criterion"]:
+ head_dict["criterion"] = CRITERIONS.build(head_dict["criterion"])
+ else:
+ weights = {}
+ criterions = {}
+ for loss_name, criterion_cfg in head_dict["criterion"].items():
+ weights[loss_name] = criterion_cfg.get("weight", 1.0)
+ criterion_cfg = {k: v for k, v in criterion_cfg.items() if k != "weight"}
+ criterions[loss_name] = CRITERIONS.build(criterion_cfg)
+
+ aggregator_cfg = {"type": "WeightedLossAggregator", "weights": weights}
+ head_dict["aggregator"] = LOSS_AGGREGATORS.build(aggregator_cfg)
+ head_dict["criterion"] = criterions
+
+ head_dict["target_generator"] = TARGET_GENERATORS.build(head_dict["target_generator"])
+ head_dict["predictor"] = PREDICTORS.build(head_dict["predictor"])
+ head = dlc_models.HEADS.build(head_dict)
+
+ output = head(input_tensor)["heatmap"]
+ _, c_out, h_out, w_out = output.shape
+ assert (h_out == h * total_stride) and (w_out == w * total_stride)
+ assert c_out == output_channels
+
+
+def test_msa_hrnet():
+ # TODO: build microsoft asia hrnet and check dimension of output
+ # TODO: check if hyperparameters are loaded correctly (from the config file)
+ pass
+
+
+def test_msa_tokenpose():
+ # TODO: build microsoft asia hrnet and check dimension of output
+ # TODO: check if hyperparameters are loaded correctly (from the config file)
+ # cf https://github.com/amathislab/BUCTDdev/blob/main/lib/models/transpose_h.py#L1
+ pass
+
+
+def test_msa_hrnetCOAM():
+ # TODO: build BUCTD COAM hrnet and check dimension of output
+ # TODO: check if hyperparameters are loaded correctly (from the config file)
+ pass
+
+
+# TODO: add other model variants our pipeline can build ;)
diff --git a/tests/pose_estimation_pytorch/other/test_seq_targets.py b/tests/pose_estimation_pytorch/other/test_seq_targets.py
new file mode 100644
index 0000000000..c2816c8650
--- /dev/null
+++ b/tests/pose_estimation_pytorch/other/test_seq_targets.py
@@ -0,0 +1,51 @@
+#
+# 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
+#
+from itertools import combinations
+
+import torch
+
+from deeplabcut.pose_estimation_pytorch.models.target_generators import (
+ TARGET_GENERATORS,
+)
+
+
+def test_sequential_generator():
+ batch_size = 4
+ image_size = 256, 256
+ num_keypoints = 12
+ num_animals = 2
+ graph = [list(edge) for edge in combinations(range(num_keypoints), 2)]
+ num_limbs = len(graph)
+ cfg = {
+ "type": "SequentialGenerator",
+ "generators": [
+ {
+ "type": "HeatmapPlateauGenerator",
+ "num_heatmaps": num_keypoints,
+ "pos_dist_thresh": 17,
+ "generate_locref": True,
+ "locref_std": 7.2801,
+ },
+ {"type": "PartAffinityFieldGenerator", "graph": graph, "width": 20},
+ ],
+ }
+ gen = TARGET_GENERATORS.build(cfg)
+
+ annotations = {"keypoints": torch.randint(1, min(image_size), (batch_size, num_animals, num_keypoints, 2))}
+ head_outputs = {
+ "heatmap": torch.rand(batch_size, num_keypoints, 32, 32),
+ "locref": torch.rand(batch_size, num_keypoints * 2, 32, 32),
+ "paf": torch.rand(batch_size, num_limbs * 2, 32, 32),
+ }
+ out = gen(stride=1, outputs=head_outputs, labels=annotations)
+ assert all(s in out for s in list(head_outputs))
+ for k, v in head_outputs.items():
+ assert out[k]["target"].shape == v.shape
diff --git a/tests/pose_estimation_pytorch/post_processing/test_identity.py b/tests/pose_estimation_pytorch/post_processing/test_identity.py
new file mode 100644
index 0000000000..3f16eade5f
--- /dev/null
+++ b/tests/pose_estimation_pytorch/post_processing/test_identity.py
@@ -0,0 +1,59 @@
+#
+# 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
+#
+"""Tests identity matching."""
+
+import numpy as np
+import pytest
+
+from deeplabcut.pose_estimation_pytorch.post_processing.identity import assign_identity
+
+
+@pytest.mark.parametrize(
+ "prediction, identity_scores, output_order",
+ [
+ (
+ [
+ [[0, 0, 1.0], [0, 0, 1.0]], # assembly 1
+ [[5, 5, 1.0], [5, 5, 1.0]], # assembly 2
+ [[9, 9, 1.0], [9, 9, 1.0]], # assembly 3
+ ],
+ [ # a0 -> idv1, a1 -> idv2, a2 -> idv0
+ [[0.1, 0.8, 0.3], [0.1, 0.7, 0.3]], # assembly 1 ID scores
+ [[0.2, 0.1, 0.6], [0.3, 0.1, 0.5]], # assembly 2 ID scores
+ [[0.7, 0.1, 0.1], [0.6, 0.2, 0.2]], # assembly 3 ID scores
+ ],
+ [2, 0, 1],
+ ),
+ (
+ [
+ [[0, 0, 1.0], [0, 0, 1.0]], # assembly 1
+ [[1, 1, 1.0], [5, 5, 1.0]], # assembly 2
+ [[0, 0, 1.0], [9, 9, 1.0]], # assembly 3
+ ],
+ [ # a0 -> idv0, a1 -> idv1, a2 -> idv2
+ [[0.4, 0.4, 0.3], [0.5, 0.3, 0.3]], # assembly 1 ID scores
+ [[0.4, 0.4, 0.3], [0.3, 0.5, 0.4]], # assembly 2 ID scores
+ [[0.2, 0.2, 0.4], [0.2, 0.2, 0.3]], # assembly 3 ID scores
+ ],
+ [0, 1, 2],
+ ),
+ ],
+)
+def test_single_identity_assignment(prediction, identity_scores, output_order):
+ predictions = np.array(prediction)
+ identity_scores = np.array(identity_scores)
+ new_order = assign_identity(predictions, identity_scores)
+ predictions_with_id = predictions[new_order]
+
+ print()
+ print(predictions.shape)
+ print(identity_scores.shape)
+ np.testing.assert_equal(predictions[output_order], predictions_with_id)
diff --git a/tests/pose_estimation_pytorch/post_processing/test_postprocessing_nms.py b/tests/pose_estimation_pytorch/post_processing/test_postprocessing_nms.py
new file mode 100644
index 0000000000..48f65fd5e1
--- /dev/null
+++ b/tests/pose_estimation_pytorch/post_processing/test_postprocessing_nms.py
@@ -0,0 +1,108 @@
+#
+# 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
+#
+"""Tests pose NMS."""
+
+import numpy as np
+import pytest
+
+import deeplabcut.pose_estimation_pytorch.post_processing.nms as nms
+
+
+@pytest.mark.parametrize(
+ "poses, score_threshold, expected_kept",
+ [
+ (
+ [
+ [[0.0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ ],
+ 0.1,
+ [True], # a single pose should be kept
+ ),
+ (
+ [
+ [[0.0, np.nan, 0], [0, 0, 0], [0, 0, 0]],
+ ],
+ 0.1,
+ [True], # a single pose should be kept
+ ),
+ (
+ [
+ [[0.0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ [[0.0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ ],
+ 0.1,
+ [False, False], # no valid poses
+ ),
+ (
+ [
+ [[0.0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ [[0.0, 0, 0.9], [10, 10, 0.9], [20, 20, 0.9]],
+ [[0.0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ [[0.0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ ],
+ 0.1,
+ [False, True, False, False], # a single valid pose
+ ),
+ (
+ [
+ [[0.0, 0, 0.9], [10, 10, 0.9], [20, 20, 0.9]],
+ [[100.0, 100, 0.89], [110, 110, 0.89], [120, 120, 0.89]],
+ ],
+ 0.1,
+ [True, True], # two valid poses, far apart
+ ),
+ (
+ [
+ [[0.0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ [[0.0, 0, 0.9], [10, 10, 0.9], [20, 20, 0.9]],
+ [[100.0, 100, 0.8], [110, 110, 0.8], [120, 120, 0.8]],
+ ],
+ 0.1,
+ [False, True, True], # two valid poses, far apart
+ ),
+ (
+ [
+ [[0.0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ [[100.0, 100, 0.8], [110, 110, 0.8], [120, 120, 0.8]],
+ [[0.0, 0, 0.9], [10, 10, 0.9], [20, 20, 0.9]],
+ ],
+ 0.1,
+ [False, True, True], # two valid poses, far apart, sorted by score
+ ),
+ (
+ [
+ [[0.0, 0, 0.89], [10, 10, 0.89], [20, 20, 0.89]],
+ [[100.0, 100, 0.8], [110, 110, 0.8], [120, 120, 0.8]],
+ [[0.0, 0, 0.9], [10, 10, 0.9], [20, 20, 0.9]],
+ ],
+ 0.1,
+ [False, True, True], # two valid poses, far apart, sorted by score, one suppressed
+ ),
+ (
+ [
+ [[1.0, 0, 0.89], [11, 10, 0.89], [21, 20, 0.89]],
+ [[100.0, 100, 0.8], [110, 110, 0.8], [120, 120, 0.8]],
+ [[0.0, 0, 0.9], [10, 10, 0.9], [20, 20, 0.9]],
+ ],
+ 0.1,
+ [False, True, True], # two valid poses, far apart, sorted by score, one suppressed
+ ),
+ ],
+)
+def test_oks_nms_post_processing(poses, score_threshold, expected_kept):
+ """Tests pose NMS."""
+ kept = nms.nms_oks(
+ predictions=np.asarray(poses),
+ oks_threshold=0.9,
+ oks_sigmas=0.1,
+ score_threshold=0.1,
+ )
+ assert kept.tolist() == expected_kept
diff --git a/tests/pose_estimation_pytorch/runners/test_bottom_up.py b/tests/pose_estimation_pytorch/runners/test_bottom_up.py
new file mode 100644
index 0000000000..ae6de38298
--- /dev/null
+++ b/tests/pose_estimation_pytorch/runners/test_bottom_up.py
@@ -0,0 +1,77 @@
+#
+# 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
+#
+"""Tests for the bottom-up pytorch runner."""
+
+from pathlib import Path
+from typing import Any
+
+import pytest
+
+from deeplabcut.pose_estimation_pytorch.config import make_pytorch_pose_config
+from deeplabcut.pose_estimation_pytorch.models import PoseModel
+from deeplabcut.pose_estimation_pytorch.runners.train import build_training_runner
+from deeplabcut.pose_estimation_pytorch.task import Task
+
+SINGLE_ANIMAL_NETS = ["resnet_50"]
+MULTI_ANIMAL_NETS = ["dekr_w18"]
+NETS = [(n, False) for n in SINGLE_ANIMAL_NETS] + [(n, True) for n in MULTI_ANIMAL_NETS]
+
+
+def print_dict(data: dict, indent: int = 0):
+ for k, v in data.items():
+ if isinstance(v, dict):
+ print_dict(v, indent=indent + 2)
+ else:
+ print(f"{indent * ' '}{k}: {v}")
+
+
+# @pytest.mark.skip(reason="This test is outdated and needs to be updated to reflect changes in the codebase.")
+
+
+@pytest.mark.parametrize("net_type, multianimal", NETS)
+def test_build_bottom_up_runner(
+ net_type: str,
+ multianimal: bool,
+ tmp_path: Path,
+) -> None:
+ project_cfg: dict[str, Any] = {
+ "multianimalproject": multianimal,
+ "project_path": str(tmp_path),
+ }
+ if multianimal:
+ project_cfg["bodyparts"] = "MULTI!"
+ project_cfg["multianimalbodyparts"] = ["head", "shoulder", "knee", "toe"]
+ project_cfg["uniquebodyparts"] = []
+ project_cfg["individuals"] = ["tom", "jerry"]
+ else:
+ project_cfg["bodyparts"] = ["head", "shoulder", "knee", "toe"]
+ project_cfg["uniquebodyparts"] = []
+ project_cfg["individuals"] = ["tom"]
+
+ root_path = Path(__file__).parent.parent
+ template_path = (root_path / "other/test_configs/pytorch_config.yaml").resolve()
+ assert template_path.is_file(), f"Template config not found at {template_path}"
+
+ pytorch_cfg = make_pytorch_pose_config(project_cfg, str(template_path), net_type)
+ pose_model = PoseModel.build(pytorch_cfg["model"])
+
+ # NOTE: @C-Achard 2026-03-18 This file was not named with test_* as a prefix,
+ # so it never ran in CI. A lot of imports are outdated and non-existent
+ # FIX: replace RUNNERS registry with build_training_runner and remove unused imports
+ runner = build_training_runner(
+ runner_config=pytorch_cfg["runner"],
+ model_folder=tmp_path,
+ task=Task.BOTTOM_UP,
+ model=pose_model,
+ device=pytorch_cfg["device"],
+ logger=None,
+ )
+ assert runner is not None
diff --git a/tests/pose_estimation_pytorch/runners/test_dynamic_cropper.py b/tests/pose_estimation_pytorch/runners/test_dynamic_cropper.py
new file mode 100644
index 0000000000..7c1ebb162f
--- /dev/null
+++ b/tests/pose_estimation_pytorch/runners/test_dynamic_cropper.py
@@ -0,0 +1,194 @@
+#
+# 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
+#
+"""Tests the dynamic cropper."""
+
+import numpy as np
+import pytest
+import torch
+
+from deeplabcut.pose_estimation_pytorch.runners.dynamic_cropping import (
+ DynamicCropper,
+ TopDownDynamicCropper,
+)
+
+
+@pytest.mark.parametrize("dynamic", [(False, 0.5, 10)])
+def test_build_dynamic_cropper(dynamic: tuple[bool, float, int]):
+ cropper = DynamicCropper.build(*dynamic)
+ should_be_built, threshold, margin = dynamic
+ if should_be_built:
+ assert isinstance(cropper, DynamicCropper)
+ assert cropper.threshold == threshold
+ assert cropper.margin == margin
+ else:
+ assert cropper is None
+
+
+@pytest.mark.parametrize("batch_size", [0, 2, 8])
+def test_dynamic_fails_with_image_batch(batch_size: int):
+ cropper = DynamicCropper(threshold=0.6, margin=10)
+ with pytest.raises(RuntimeError):
+ cropper.crop(torch.zeros(batch_size, 3, 128, 128))
+
+
+def test_dynamic_fails_with_variable_frame_size():
+ cropper = DynamicCropper(threshold=0.6, margin=10)
+ cropper.crop(torch.zeros(1, 3, 64, 64))
+ with pytest.raises(RuntimeError):
+ cropper.crop(torch.zeros(1, 3, 128, 128))
+
+
+def test_dynamic_fails_with_update_before_crop():
+ cropper = DynamicCropper(threshold=0.6, margin=10)
+ with pytest.raises(RuntimeError):
+ cropper.update(torch.ones(5, 17, 3))
+
+
+@pytest.mark.parametrize("threshold", [0.25, 0.5, 0.8])
+def test_dynamic_cropper_does_nothing_with_low_quality(threshold: float):
+ cropper = DynamicCropper(threshold=threshold, margin=10)
+ image_in = torch.ones((1, 3, 32, 32))
+ cropper.crop(image_in)
+ for i in range(10):
+ pose = _generate_random_pose(
+ (32, 64),
+ min_score=0.0,
+ max_score=threshold - 0.001,
+ seed=i,
+ )
+ cropper.update(pose)
+ image_out = cropper.crop(image_in)
+ assert torch.equal(image_in, image_out)
+
+
+@pytest.mark.parametrize(
+ "pose, threshold, margin, expected_crop",
+ [
+ ([[float("nan"), float("nan"), float("nan")]], 0.1, 10, [0, 0, 64, 64]),
+ ([[float("nan"), 30, 0.0]], 0.5, 10, [0, 0, 64, 64]),
+ ([[20, 30, 0.0]], 0.5, 10, [0, 0, 64, 64]),
+ ([[20, 30, 0.49]], 0.5, 10, [0, 0, 64, 64]),
+ ([[20, 30, 0.8]], 0.5, 10, [10, 20, 30, 40]),
+ ([[20, 30, 0.8], [float("nan"), float("nan"), 0.2]], 0.5, 15, [5, 15, 35, 45]),
+ ([[20, 30, 0.8], [5, 5, 0.2]], 0.5, 15, [0, 0, 35, 45]),
+ ([[20, 30, 0.8], [35, 30, 0.79]], 0.8, 5, [15, 25, 40, 35]),
+ ([[40, 10, 0.2], [35, 15, 0.79]], 0.3, 8, [27, 2, 48, 23]),
+ (
+ [
+ [[float("nan"), float("nan"), float("nan")]],
+ [[float("nan"), float("nan"), float("nan")]],
+ ],
+ 0.15,
+ 10,
+ [0, 0, 64, 64],
+ ),
+ (
+ [
+ [[20, 30, 0.8], [5, 12, 0.2]],
+ [[40, 10, 0.2], [35, 15, 0.79]],
+ ],
+ 0.15,
+ 5,
+ [0, 5, 45, 35],
+ ),
+ ],
+)
+def test_dynamic_cropper_basic_crop(
+ pose: list[list[float]], threshold: float, margin: int, expected_crop: tuple[int, int, int, int]
+) -> None:
+ x0, y0, x1, y1 = expected_crop
+ crop_w, crop_h = x1 - x0, y1 - y0
+
+ image_in = torch.zeros((1, 3, 64, 64))
+ image_in[:, :, y0:y1, x0:x1] = 1
+ expected_image_out = torch.ones((1, 3, crop_h, crop_w))
+
+ cropper = DynamicCropper(threshold=threshold, margin=margin)
+ image_out = cropper.crop(image_in)
+ assert torch.equal(image_out, image_in)
+
+ cropper.update(torch.tensor(pose))
+ image_out = cropper.crop(image_in)
+ assert image_out.shape == expected_image_out.shape
+ assert torch.equal(image_out, expected_image_out)
+
+ pose_out = torch.tensor(pose)
+ print("\nPose in")
+ print(pose_out.numpy())
+ pose_out[..., 0] -= x0
+ pose_out[..., 1] -= y0
+ print("Pose out before update")
+ print(pose_out.numpy())
+ cropper.update(pose_out)
+ print("Pose out after update")
+ print(pose_out.numpy())
+ np.testing.assert_allclose(pose_out.numpy(), np.array(pose))
+
+
+@pytest.mark.parametrize("size", [128, 256, 291, 320, 480, 500, 640, 800])
+@pytest.mark.parametrize("n", [1, 2, 3, 4, 5])
+@pytest.mark.parametrize("overlap", [0, 1, 5, 10, 100])
+def test_tddc_array_split(size: int, n: int, overlap: int) -> None:
+ print("\nTesting TopDownDynamicCropper array split")
+ print("Size:", size)
+ print("N:", n)
+ print("Overlap:", overlap)
+ sections = TopDownDynamicCropper.split_array(size, n, overlap)
+ print("Sections:")
+ for section in sections:
+ print(f" {section}")
+
+ # check that we have the desired number of sections
+ assert len(sections) == n
+
+ # check that the sections start at 0 and end at the array size
+ start, end = sections[0][0], sections[-1][1]
+ assert start == 0
+ assert end == size
+
+ # check all sections have size at least 1
+ for start, end in sections:
+ assert start < end
+
+ # check that all sections have the same size
+ sizes = [end - start for start, end in sections]
+ assert len(set(sizes)) == 1
+
+ # check the overlap is big enough for each section
+ for (_start_1, end_1), (start_2, _end_2) in zip(sections[:-1], sections[1:], strict=False):
+ assert end_1 >= start_2
+ assert end_1 - start_2 >= overlap
+
+ # check that the difference between overlaps is at most 1
+ # FIXME(niels) - auto-correct the overlap to spread it out more evenly
+ # if n > 1:
+ # overlaps = [
+ # end_1 - start_2
+ # for (start_1, end_1), (start_2, end_2) in zip(sections[:-1], sections[1:])
+ # ]
+ #
+ # assert max(overlaps) - min(overlaps) <= 1
+
+
+def _generate_random_pose(
+ image_shape: tuple[int, int],
+ min_score: float,
+ max_score: float,
+ num_animals: int = 3,
+ num_keypoints: int = 7,
+ seed: int = 0,
+) -> torch.Tensor:
+ gen = np.random.default_rng(seed)
+ pose = gen.random((num_animals, num_keypoints, 3))
+ pose[..., 0] *= image_shape[0]
+ pose[..., 1] *= image_shape[1]
+ pose[..., 2] = (pose[..., 2] * (max_score - min_score)) + min_score
+ return torch.from_numpy(pose)
diff --git a/tests/pose_estimation_pytorch/runners/test_filtered_detector_inference_runner.py b/tests/pose_estimation_pytorch/runners/test_filtered_detector_inference_runner.py
new file mode 100644
index 0000000000..4eaa6ad543
--- /dev/null
+++ b/tests/pose_estimation_pytorch/runners/test_filtered_detector_inference_runner.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+"""Test script for superanimal_humanbody with torchvision detector."""
+
+from deeplabcut.pose_estimation_pytorch.apis.utils import (
+ TORCHVISION_DETECTORS,
+ get_filtered_coco_detector_inference_runner,
+)
+from deeplabcut.pose_estimation_pytorch.models.detectors.filtered_detector import (
+ FilteredDetector,
+)
+from deeplabcut.pose_estimation_pytorch.modelzoo import load_super_animal_config
+from deeplabcut.pose_estimation_pytorch.modelzoo.utils import COCO_PERSON_CATEGORY_ID
+
+
+def test_torchvision_detector():
+ """Test that the torchvision detector works with superanimal_humanbody."""
+ for detector_name in TORCHVISION_DETECTORS:
+ # Load the superanimal_humanbody config
+ superanimal_config = load_super_animal_config(
+ super_animal="superanimal_humanbody",
+ model_name="rtmpose_x",
+ detector_name=detector_name,
+ )
+ print("Config loaded successfully!")
+
+ # Test loading the torchvision detector directly
+ print("\nTesting torchvision detector loading...")
+ entry = TORCHVISION_DETECTORS[detector_name]
+ weights = entry["weights"]
+ coco_detector = entry["fn"](weights=weights, box_score_thresh=0.6)
+ coco_detector.eval()
+ print("Torchvision detector loaded successfully!")
+
+ # Test loading the FilteredDetector
+ person_detector = FilteredDetector(coco_detector, class_id=COCO_PERSON_CATEGORY_ID)
+ person_detector.eval()
+ print("Filtered detector loaded successfully!")
+
+ _ = get_filtered_coco_detector_inference_runner(
+ model_name=detector_name,
+ category_id=COCO_PERSON_CATEGORY_ID,
+ batch_size=1,
+ model_config=superanimal_config,
+ )
+ print("Filtered detector runner created successfully!")
+
+ print("\n✅ All tests passed! The torchvision detector integration is working correctly.")
+ return True
+
+
+if __name__ == "__main__":
+ print("Testing superanimal_humanbody with torchvision detector...")
+ success = test_torchvision_detector()
+ if success:
+ print("\n✅ Test passed! The torchvision detector works with superanimal_humanbody")
+ else:
+ print("\n❌ Test failed! There's an issue with the torchvision detector integration")
diff --git a/tests/pose_estimation_pytorch/runners/test_inference_directml_no_grad.py b/tests/pose_estimation_pytorch/runners/test_inference_directml_no_grad.py
new file mode 100644
index 0000000000..318c909e3f
--- /dev/null
+++ b/tests/pose_estimation_pytorch/runners/test_inference_directml_no_grad.py
@@ -0,0 +1,67 @@
+#
+# 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
+#
+"""Tests DLC_DIRECTML_NO_GRAD toggles inference_mode vs no_grad (AMD DirectML)."""
+
+from __future__ import annotations
+
+import importlib
+import os
+from unittest.mock import Mock
+
+import numpy as np
+import pytest
+import torch
+
+import deeplabcut.pose_estimation_pytorch.runners.inference as inference
+
+
+def _reload_with_env(env_value: str | None):
+ if env_value is None:
+ os.environ.pop("DLC_DIRECTML_NO_GRAD", None)
+ else:
+ os.environ["DLC_DIRECTML_NO_GRAD"] = env_value
+ importlib.reload(inference)
+
+
+@pytest.fixture(autouse=True)
+def _restore_env():
+ yield
+ _reload_with_env(None) # always restore defaults after each test
+
+
+@pytest.mark.parametrize(
+ ("env_value", "directml_no_grad"),
+ [(None, False), ("false", False), ("true", True)],
+)
+def test_directml_no_grad_env(env_value, directml_no_grad):
+ """env var sets _directml_no_grad and selects the correct torch grad context."""
+ _reload_with_env(env_value)
+ assert inference._directml_no_grad is directml_no_grad
+
+ class _SniffRunner(inference.InferenceRunner):
+ def __init__(self):
+ super().__init__(
+ model=Mock(),
+ batch_size=1,
+ inference_cfg=inference.InferenceConfig(
+ multithreading=inference.MultithreadingConfig(enabled=False),
+ ),
+ )
+ self.saw_inference_mode: bool | None = None
+
+ def predict(self, inputs: torch.Tensor, **kwargs):
+ self.saw_inference_mode = torch.is_inference_mode_enabled()
+ return [{"mock": {"poses": np.zeros((1,), dtype=np.float32)}}]
+
+ runner = _SniffRunner()
+ runner.inference([np.zeros((1, 3, 8, 8), dtype=np.float32)])
+
+ assert runner.saw_inference_mode is not directml_no_grad
diff --git a/tests/pose_estimation_pytorch/runners/test_logger.py b/tests/pose_estimation_pytorch/runners/test_logger.py
new file mode 100644
index 0000000000..9b27314302
--- /dev/null
+++ b/tests/pose_estimation_pytorch/runners/test_logger.py
@@ -0,0 +1,102 @@
+#
+# 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
+#
+"""Tests loggers."""
+
+from pathlib import Path
+from typing import Any
+
+import pytest
+import torch
+
+import deeplabcut.pose_estimation_pytorch.runners.logger as logging
+
+
+class MockImageLogger(logging.ImageLoggerMixin):
+ """Mock image logger."""
+
+ def log_images(
+ self,
+ inputs: dict[str, Any],
+ outputs: dict[str, torch.Tensor],
+ targets: dict[str, dict[str, torch.Tensor]],
+ step: int,
+ ) -> None:
+ pass
+
+
+@pytest.mark.parametrize(
+ "keypoints",
+ [
+ [
+ [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]],
+ ],
+ [
+ [[float("nan"), float("nan")], [float("nan"), float("nan")]],
+ ],
+ [
+ [[0.0, 0.0], [1, 1], [2, 2]],
+ ],
+ [[[float("nan"), 0.0], [1, 1], [2, 2]]],
+ [[[-1.0, -1.0], [1, 1], [2, 2]]],
+ [
+ [[-1.0, -1.0], [-1.0, -1.0]],
+ ],
+ [
+ [[-1.0, -1.0], [-1.0, -1.0]],
+ [[1.0, 1.0], [1.0, 1.0]],
+ ],
+ ],
+)
+@pytest.mark.parametrize("denormalize", [True, False])
+def test_prepare_image(keypoints: list[list[float]], denormalize: bool) -> None:
+ image = torch.ones((3, 256, 256))
+ keypoints = torch.tensor(keypoints)
+
+ print()
+ print(f"IMAGE: {image.shape}")
+ print(f"KEYPOINTS: {keypoints.shape}")
+ for k in keypoints:
+ print(k)
+ print()
+ print()
+
+ logger = MockImageLogger()
+ logger._prepare_image(
+ image=image,
+ denormalize=denormalize,
+ keypoints=keypoints,
+ bboxes=None,
+ )
+
+
+def test_csv_logger_resume(tmp_path: Path) -> None:
+ """Test CSVLogger preserves data when resuming from snapshot."""
+ log_file = tmp_path / "learning_stats.csv"
+
+ # Initial training: log some metrics
+ logger1 = logging.CSVLogger(str(tmp_path), "learning_stats.csv")
+ logger1.log({"loss": 0.5, "accuracy": 0.8}, step=1)
+ logger1.log({"loss": 0.4, "accuracy": 0.9}, step=2)
+
+ assert log_file.exists()
+ assert len(logger1._steps) == 2
+
+ # Resume training: should load existing data
+ logger2 = logging.CSVLogger(str(tmp_path), "learning_stats.csv")
+ assert len(logger2._steps) == 2
+ assert logger2._steps == [1, 2]
+ assert logger2._metric_store[0]["loss"] == 0.5
+ assert logger2._metric_store[1]["accuracy"] == 0.9
+
+ # Log new data: should append, not overwrite
+ logger2.log({"loss": 0.3, "accuracy": 0.95}, step=3)
+ assert len(logger2._steps) == 3
+ assert logger2._steps == [1, 2, 3]
diff --git a/tests/pose_estimation_pytorch/runners/test_runners.py b/tests/pose_estimation_pytorch/runners/test_runners.py
new file mode 100644
index 0000000000..c3afa93db5
--- /dev/null
+++ b/tests/pose_estimation_pytorch/runners/test_runners.py
@@ -0,0 +1,38 @@
+#
+# 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 pickle
+from pathlib import Path
+from unittest.mock import Mock
+
+import numpy as np
+import pytest
+import torch
+
+import deeplabcut.pose_estimation_pytorch.runners as runners
+
+
+@pytest.mark.parametrize("value", [True, False])
+def test_set_load_weights_only(value: bool):
+ print(f"\nget_load_weights_only: {runners.get_load_weights_only()}")
+ print(f"setting value to {value}")
+ runners.set_load_weights_only(value)
+ print(f"get_load_weights_only: {runners.get_load_weights_only()}\n")
+ assert runners.get_load_weights_only() == value
+
+
+def test_load_snapshot_weights_only_error(tmpdir_factory):
+ snapshot_dir = Path(tmpdir_factory.mktemp("snapshot-dir"))
+ snapshot_path = snapshot_dir / "snapshot.pt"
+ torch.save(dict(content=np.zeros(10)), str(snapshot_path))
+
+ runners.set_load_weights_only(False)
+ with pytest.raises(pickle.UnpicklingError):
+ runners.Runner.load_snapshot(snapshot_path, device="cpu", model=Mock(), weights_only=True)
diff --git a/tests/pose_estimation_pytorch/runners/test_runners_inference.py b/tests/pose_estimation_pytorch/runners/test_runners_inference.py
new file mode 100644
index 0000000000..f59831ca4e
--- /dev/null
+++ b/tests/pose_estimation_pytorch/runners/test_runners_inference.py
@@ -0,0 +1,195 @@
+#
+# 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
+#
+"""Tests inference runners."""
+
+from unittest.mock import Mock, patch
+
+import numpy as np
+import pytest
+import torch
+
+import deeplabcut.pose_estimation_pytorch.data.postprocessor as post
+import deeplabcut.pose_estimation_pytorch.data.preprocessor as prep
+import deeplabcut.pose_estimation_pytorch.runners.inference as inference
+from deeplabcut.pose_estimation_pytorch import get_load_weights_only
+from deeplabcut.pose_estimation_pytorch.task import Task
+
+
+@patch("deeplabcut.pose_estimation_pytorch.runners.train.build_optimizer", Mock())
+@pytest.mark.parametrize("task", [Task.DETECT, Task.TOP_DOWN, Task.BOTTOM_UP])
+@pytest.mark.parametrize("weights_only", [None, True, False])
+def test_load_weights_only_with_build_training_runner(task: Task, weights_only: bool):
+ with patch("deeplabcut.pose_estimation_pytorch.runners.base.torch.load") as load:
+ snapshot = "snapshot.pt"
+ inference.build_inference_runner(
+ task=task,
+ model=Mock(),
+ device="cpu",
+ snapshot_path=snapshot,
+ load_weights_only=weights_only,
+ )
+ if weights_only is None:
+ weights_only = get_load_weights_only()
+ load.assert_called_once_with(snapshot, map_location="cpu", weights_only=weights_only)
+
+
+class MockInferenceRunner(inference.InferenceRunner):
+ """Mocks the predict function for an inference runner."""
+
+ def __init__(
+ self,
+ batch_size: int = 1,
+ preprocessor: prep.Preprocessor | None = None,
+ postprocessor: post.Postprocessor | None = None,
+ ) -> None:
+ super().__init__(
+ model=Mock(),
+ batch_size=batch_size,
+ preprocessor=preprocessor,
+ postprocessor=postprocessor,
+ )
+ self.batch_shapes = []
+
+ def predict(self, inputs: torch.Tensor) -> list[dict[str, dict[str, np.ndarray]]]:
+ self.batch_shapes.append(tuple(inputs.shape))
+ return [ # return first elem of input
+ {"mock": {"index": i[0, 0, 0].detach().numpy()}} for i in inputs
+ ]
+
+
+@pytest.mark.parametrize("batch_size", [1, 2, 4, 8])
+def test_mock_bottom_up(batch_size):
+ h, w = 640, 480
+ images = [i * np.ones((1, 3, h, w)) for i in range(10)]
+
+ runner = MockInferenceRunner(batch_size=batch_size)
+ predictions = runner.inference(images)
+
+ print()
+ print(f"Num images: {len(predictions)}")
+ print(f"Num predictions: {len(predictions)}")
+ print(f"Batch shapes: {runner.batch_shapes}")
+ print(80 * "-")
+ for i in images:
+ print(i[0, 0, 0, 0])
+ print("----")
+ print(80 * "-")
+ for p in predictions:
+ print(p)
+ print("----")
+
+ _check_batch_shapes(batch_size, h, w, runner.batch_shapes)
+ assert len(images) == len(predictions)
+ for i, p in zip(images, predictions, strict=True):
+ assert len(p) == 1 # only 1 output per image
+ assert i[0, 0, 0, 0] == p[0]["mock"]["index"]
+
+
+@pytest.mark.parametrize("batch_size", [1, 2, 4, 8])
+@pytest.mark.parametrize(
+ "detections_per_image",
+ [
+ [1, 1, 1, 1, 1],
+ [0, 1, 0, 1, 1], # some frames might not have predictions
+ [0, 0, 0, 5, 2],
+ [1, 2, 3, 4],
+ [3, 4, 2, 1, 4],
+ [4, 23, 5, 20, 64, 100],
+ ],
+)
+def test_mock_top_down(batch_size, detections_per_image):
+ h, w = 8, 8
+ images = []
+ for index, num_detections in enumerate(detections_per_image):
+ if num_detections == 0:
+ detections = np.zeros((0, 3, 1, 1)) # random shape when no detections
+ else:
+ detections = np.concatenate(
+ [(1_000_000 * (index + 1) + i) * np.ones((1, 3, h, w)) for i in range(num_detections)],
+ axis=0,
+ )
+
+ images.append(detections)
+
+ runner = MockInferenceRunner(batch_size=batch_size)
+ predictions = runner.inference(images)
+
+ print()
+ print(f"Num images: {len(predictions)}")
+ print(f"Num predictions: {len(predictions)}")
+ print(80 * "-")
+ for i in images:
+ for i_det in i:
+ print(i_det.shape)
+ print(i_det[0, 0, 0])
+ print("----")
+
+ print(80 * "-")
+ for p in predictions:
+ print(p)
+ print("----")
+
+ _check_batch_shapes(batch_size, h, w, runner.batch_shapes)
+
+ assert len(images) == len(predictions)
+ for i, p in zip(images, predictions, strict=True):
+ assert len(p) == len(i) # one prediction per input
+ for i_det, p_det in zip(i, p, strict=True):
+ print(i_det.shape)
+ print(p_det["mock"]["index"])
+ assert i_det[0, 0, 0] == p_det["mock"]["index"]
+
+
+def test_dynamic_pose_inference_calls_dynamic():
+ pose_batch = torch.zeros((1, 1, 1, 3))
+ pose_batch_updated = torch.ones((1, 1, 1, 3))
+
+ image_crop = Mock()
+ image_crop.__len__ = Mock(return_value=1)
+
+ model = Mock()
+ model.get_predictions = Mock()
+ model.get_predictions.return_value = dict(bodypart=dict(poses=pose_batch))
+
+ dynamic = Mock()
+ dynamic.crop = Mock()
+ dynamic.crop.return_value = image_crop
+ dynamic.update = Mock()
+ dynamic.update.return_value = pose_batch_updated
+
+ runner = inference.PoseInferenceRunner(
+ model=model,
+ dynamic=dynamic,
+ batch_size=1,
+ )
+ image = torch.zeros((1, 3, 64, 64))
+ updated_pose = runner.predict(image)
+ dynamic.crop.assert_called_once_with(image)
+ dynamic.update.assert_called_once_with(pose_batch)
+
+ assert len(updated_pose) == 1
+ np.testing.assert_allclose(
+ updated_pose[0]["bodypart"]["poses"],
+ pose_batch_updated[0].cpu().numpy(),
+ )
+
+
+def _check_batch_shapes(batch_size, h, w, batch_shapes) -> None:
+ for b in batch_shapes[:-1]:
+ assert b[0] == batch_size
+ assert b[1] == 3
+ assert b[2] == h
+ assert b[3] == w
+
+ assert batch_shapes[-1][0] <= batch_size
+ assert batch_shapes[-1][1] <= 3
+ assert batch_shapes[-1][2] <= h
+ assert batch_shapes[-1][3] <= w
diff --git a/tests/pose_estimation_pytorch/runners/test_runners_train.py b/tests/pose_estimation_pytorch/runners/test_runners_train.py
new file mode 100644
index 0000000000..3fa0994217
--- /dev/null
+++ b/tests/pose_estimation_pytorch/runners/test_runners_train.py
@@ -0,0 +1,320 @@
+#
+# 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
+#
+from dataclasses import dataclass
+from unittest.mock import Mock, patch
+
+import numpy as np
+import pytest
+import torch
+
+import deeplabcut.pose_estimation_pytorch.runners.schedulers as schedulers
+import deeplabcut.pose_estimation_pytorch.runners.train as train_runners
+from deeplabcut.pose_estimation_pytorch.models import PoseModel
+from deeplabcut.pose_estimation_pytorch.models.backbones import ResNet
+from deeplabcut.pose_estimation_pytorch.models.heads import HeatmapHead
+from deeplabcut.pose_estimation_pytorch.task import Task
+
+
+@patch("deeplabcut.pose_estimation_pytorch.runners.train.build_optimizer", Mock())
+@patch("deeplabcut.pose_estimation_pytorch.runners.train.CSVLogger", Mock())
+@pytest.mark.parametrize("task", [Task.DETECT, Task.TOP_DOWN, Task.BOTTOM_UP])
+@pytest.mark.parametrize("weights_only", [True, False])
+def test_load_weights_only_with_build_training_runner(task: Task, weights_only: bool):
+ runner_config = dict(
+ optimizer=dict(),
+ snapshots=dict(max_snapshots=1, save_epochs=5, save_optimizer_state=False),
+ load_weights_only=weights_only,
+ )
+ with patch("deeplabcut.pose_estimation_pytorch.runners.base.torch.load") as load:
+ train_runners.build_training_runner(
+ runner_config=runner_config,
+ model_folder=Mock(),
+ task=task,
+ model=Mock(),
+ device="cpu",
+ snapshot_path="snapshot.pt",
+ )
+ load.assert_called_once_with("snapshot.pt", map_location="cpu", weights_only=weights_only)
+
+
+@dataclass
+class SchedulerTestConfig:
+ cfg: dict
+ init_lr: float
+ expected_lrs: list[float]
+
+
+TEST_SCHEDULERS = [
+ SchedulerTestConfig(
+ cfg=dict(
+ type="LRListScheduler",
+ params=dict(milestones=[2, 5], lr_list=[[0.5], [0.1]]),
+ ),
+ init_lr=1.0,
+ expected_lrs=[1.0, 1.0, 0.5, 0.5, 0.5, 0.1, 0.1, 0.1],
+ ),
+ SchedulerTestConfig(
+ cfg=dict(type="LRListScheduler", params=dict(milestones=[1], lr_list=[[0.1]])),
+ init_lr=0.1,
+ expected_lrs=[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
+ ),
+ SchedulerTestConfig(
+ cfg=dict(type="LRListScheduler", params=dict(milestones=[1], lr_list=[[0.5]])),
+ init_lr=0.1,
+ expected_lrs=[0.1, 0.5, 0.5, 0.5],
+ ),
+ SchedulerTestConfig(
+ cfg=dict(type="StepLR", params=dict(step_size=3, gamma=0.1)),
+ init_lr=1.0,
+ expected_lrs=[1.0, 1.0, 1.0, 0.1, 0.1, 0.1, 0.01, 0.01, 0.01, 0.001],
+ ),
+]
+
+
+@pytest.mark.parametrize("load_head_weights", [True, False])
+def test_load_head_weights(tmp_path_factory, load_head_weights):
+ model_folder = tmp_path_factory.mktemp("model_folder")
+ runner_config = dict(
+ optimizer=dict(type="SGD", params=dict(lr=1)),
+ snapshots=dict(max_snapshots=1, save_epochs=1, save_optimizer_state=False),
+ )
+
+ model = PoseModel(
+ cfg=dict(),
+ backbone=ResNet(),
+ heads=dict(
+ bodyparts=HeatmapHead(
+ predictor=Mock(),
+ target_generator=Mock(),
+ criterion=Mock(),
+ aggregator=None,
+ heatmap_config=dict(channels=[2048, 10], kernel_size=[3], strides=[2]),
+ ),
+ ),
+ )
+
+ original_state_dict = model.state_dict()
+ zero_state_dict = {k: torch.zeros_like(v) for k, v in original_state_dict.items()}
+
+ load = Mock()
+ load.return_value = dict(model=zero_state_dict)
+
+ with patch("deeplabcut.pose_estimation_pytorch.runners.train.torch.load", load):
+ r = train_runners.build_training_runner(
+ runner_config,
+ model_folder=model_folder,
+ task=Task.BOTTOM_UP,
+ model=model,
+ device="cpu",
+ snapshot_path=model_folder / "snapshot.pt",
+ load_head_weights=load_head_weights,
+ )
+ loaded_state_dict = r.model.state_dict()
+ for k, v in loaded_state_dict.items():
+ if load_head_weights or k.startswith("backbone."):
+ assert torch.equal(v, zero_state_dict[k])
+ else:
+ assert torch.equal(v, original_state_dict[k])
+
+
+@pytest.mark.parametrize("load_head_weights", [True, False])
+def test_mocked_load_head_weights(tmp_path_factory, load_head_weights):
+ model_folder = tmp_path_factory.mktemp("model_folder")
+ snapshot_manager = Mock()
+ snapshot_manager.model_folder = model_folder
+
+ model = Mock()
+ model.backbone = Mock()
+ state_dict = {"backbone.test": 0, "head.test": 1}
+ state_dict_backbone = {"test": 0}
+ load = Mock()
+ load.return_value = dict(model=state_dict)
+
+ with patch("deeplabcut.pose_estimation_pytorch.runners.train.torch.load", load):
+ _ = train_runners.PoseTrainingRunner(
+ model=model,
+ optimizer=Mock(),
+ snapshot_manager=snapshot_manager,
+ device="cpu",
+ snapshot_path="snapshot.pt",
+ load_head_weights=load_head_weights,
+ )
+ if load_head_weights:
+ model.load_state_dict.assert_called_once_with(state_dict)
+ else:
+ model.backbone.load_state_dict.assert_called_once_with(state_dict_backbone)
+
+
+@patch("deeplabcut.pose_estimation_pytorch.runners.train.CSVLogger", Mock())
+@pytest.mark.parametrize(
+ "runner_cls",
+ [
+ train_runners.PoseTrainingRunner,
+ train_runners.DetectorTrainingRunner,
+ ],
+)
+@pytest.mark.parametrize("test_cfg", TEST_SCHEDULERS)
+def test_training_with_scheduler(runner_cls, test_cfg: SchedulerTestConfig) -> None:
+ runner = _fit_runner_and_check_lrs(
+ runner_cls,
+ test_cfg.init_lr,
+ test_cfg.cfg,
+ test_cfg.expected_lrs,
+ )
+ assert runner.current_epoch == len(test_cfg.expected_lrs)
+
+
+@patch("deeplabcut.pose_estimation_pytorch.runners.train.CSVLogger", Mock())
+@pytest.mark.parametrize(
+ "runner_cls",
+ [
+ train_runners.PoseTrainingRunner,
+ train_runners.DetectorTrainingRunner,
+ ],
+)
+@pytest.mark.parametrize("test_cfg", TEST_SCHEDULERS)
+def test_resuming_training_scheduler_every_epoch(
+ runner_cls,
+ test_cfg: SchedulerTestConfig,
+):
+ snapshot_to_load = None
+ for epoch, expected_lr in enumerate(test_cfg.expected_lrs):
+ runner = _fit_runner_and_check_lrs(
+ runner_cls,
+ test_cfg.init_lr,
+ test_cfg.cfg,
+ [expected_lr], # trains for 1 epoch
+ snapshot_to_load=snapshot_to_load,
+ )
+ snapshot_to_load = dict(metadata=dict(epoch=epoch + 1), scheduler=runner.scheduler.state_dict())
+
+
+@patch("deeplabcut.pose_estimation_pytorch.runners.train.CSVLogger", Mock())
+@pytest.mark.parametrize(
+ "runner_cls",
+ [
+ train_runners.PoseTrainingRunner,
+ train_runners.DetectorTrainingRunner,
+ ],
+)
+@pytest.mark.parametrize(
+ "test_cfg, resume_epoch",
+ [
+ (
+ SchedulerTestConfig(
+ cfg=dict(
+ type="LRListScheduler",
+ params=dict(milestones=[2, 5], lr_list=[[0.5], [0.1]]),
+ ),
+ init_lr=1.0,
+ expected_lrs=[1.0, 1.0, 0.5, 1.0, 1.0, 0.1, 0.1, 0.1],
+ ),
+ 3, # cut after the 3rd epoch - restart at LR=1 until epoch 5
+ ),
+ (
+ SchedulerTestConfig(
+ cfg=dict(type="StepLR", params=dict(step_size=4, gamma=0.1)),
+ init_lr=1.0,
+ expected_lrs=(4 * [1.0]) + (4 * [0.1]) + (4 * [0.01]) + (4 * [0.001]),
+ ),
+ 3, # cut after the 3rd epoch - restart at LR=1 and update at 4 correctly
+ ),
+ (
+ SchedulerTestConfig(
+ cfg=dict(type="StepLR", params=dict(step_size=4, gamma=0.1)),
+ init_lr=1.0,
+ expected_lrs=(4 * [1.0]) + [0.1, 1, 1, 1] + (4 * [0.1]),
+ ),
+ 5, # cut after the 5th epoch - restart at LR=1 and update again at 8
+ ),
+ ],
+)
+def test_resuming_training_with_no_scheduler_state(runner_cls, test_cfg: SchedulerTestConfig, resume_epoch: int):
+ """Without a scheduler config, there is no way to set the initial LR.
+
+ All we can do is set the last_epoch value, and adjust correctly at milestones going
+ forward.
+ """
+ runner = _fit_runner_and_check_lrs(
+ runner_cls,
+ test_cfg.init_lr,
+ test_cfg.cfg,
+ test_cfg.expected_lrs[:resume_epoch],
+ )
+ assert runner.current_epoch == resume_epoch
+
+ runner = _fit_runner_and_check_lrs(
+ runner_cls,
+ test_cfg.init_lr,
+ test_cfg.cfg,
+ expected_lrs=test_cfg.expected_lrs[resume_epoch:],
+ snapshot_to_load=dict(metadata=dict(epoch=resume_epoch)),
+ )
+ assert runner.current_epoch == len(test_cfg.expected_lrs)
+
+
+def _fit_runner_and_check_lrs(
+ runner_cls,
+ init_lr: float,
+ scheduler_cfg: dict,
+ expected_lrs: list[float],
+ snapshot_to_load: dict | None = None,
+) -> train_runners.TrainingRunner:
+ runner_kwargs = dict(device="cpu", eval_interval=1_000_000)
+ optimizer = torch.optim.SGD([torch.randn(2, 2)], lr=init_lr)
+ scheduler = schedulers.build_scheduler(scheduler_cfg, optimizer)
+ num_epochs = len(expected_lrs)
+
+ base_path = "deeplabcut.pose_estimation_pytorch.runners"
+ with patch(f"{base_path}.base.Runner.load_snapshot") as base_mock_load:
+ with patch(f"{base_path}.train.PoseTrainingRunner.load_snapshot") as mock_load:
+ snapshot_path = None
+ base_mock_load.return_value = dict()
+ mock_load.return_value = dict()
+ if snapshot_to_load is not None:
+ snapshot_path = "fake_snapshot.pt"
+ base_mock_load.return_value = snapshot_to_load
+ mock_load.return_value = snapshot_to_load
+
+ print()
+ print(f"Scheduler: {scheduler}")
+ print(f"Starting training for {num_epochs} epochs")
+ runner = runner_cls(
+ model=Mock(),
+ optimizer=optimizer,
+ snapshot_manager=Mock(),
+ scheduler=scheduler,
+ snapshot_path=snapshot_path,
+ **runner_kwargs,
+ )
+
+ # Mock the step call; check that the learning rate is correct for the epoch
+ def step(*args, **kwargs):
+ # the current_epoch value is indexed at 1
+ total_epoch = runner.current_epoch - 1
+ epoch = total_epoch - runner.starting_epoch
+ _assert_learning_rates_match(total_epoch, optimizer, expected_lrs[epoch])
+ optimizer.step()
+ return dict(total_loss=0)
+
+ train_loader, val_loader = [Mock()], [Mock()]
+ runner.step = step
+ runner.fit(train_loader, val_loader, epochs=num_epochs, display_iters=1000)
+
+ return runner
+
+
+def _assert_learning_rates_match(e, optimizer, expected):
+ current_lrs = [g["lr"] for g in optimizer.param_groups]
+ print(f"Epoch {e}: LR={current_lrs}, expected={expected}")
+ for lr in current_lrs:
+ assert isinstance(lr, float)
+ np.testing.assert_almost_equal(lr, expected)
diff --git a/tests/pose_estimation_pytorch/runners/test_schedulers.py b/tests/pose_estimation_pytorch/runners/test_schedulers.py
new file mode 100644
index 0000000000..37e98ea6a1
--- /dev/null
+++ b/tests/pose_estimation_pytorch/runners/test_schedulers.py
@@ -0,0 +1,271 @@
+#
+# 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
+#
+"""Tests building schedulers from config."""
+
+import random
+from dataclasses import dataclass
+
+import numpy as np
+import pytest
+import torch
+import torch.nn as nn
+
+import deeplabcut.pose_estimation_pytorch.runners.schedulers as schedulers
+
+
+def generate_random_lr_list(num_floats: int):
+ """Generate list of lists including random numbers.
+
+ Args:
+ num_floats: number of floats we want to include in our list
+
+ Returns:
+ ran_list: random list of sorted numbers, being first number bigger than the last
+ """
+ ran_list = []
+ for i in range(num_floats):
+ random_floats = [random.random()]
+ ran_list.append(random_floats)
+ return sorted(ran_list, reverse=True)
+
+
+@pytest.mark.parametrize(
+ "milestones, lr_list",
+ [([10, 430], [[0.05], [0.005]]), (list(sorted(random.sample(range(0, 999), 2))), generate_random_lr_list(2))],
+)
+def test_scheduler(milestones, lr_list):
+ """Testing schedulers.py.
+
+ Given a list of milestones and a list of learning rates, this function tests
+ if the length of each list is the same. Furthermore, it will assess if
+ the current learning rate (output from the function we are testing) is a float
+ and corresponds to the expected learning rate given the milestones.
+
+ Args:
+ milestones: list of epochs indices (number of epochs)
+ lr_list: learning rates list
+
+ Returns:
+ None
+
+ Examples:
+ input:
+ milestones = [10,25,50]
+ lr_list = [[0.00001],[0.000005],[0.000001]]
+ """
+
+ assert len(milestones) == len(lr_list)
+
+ optimizer = torch.optim.SGD([torch.randn(2, 2)], lr=0.01)
+ s = schedulers.LRListScheduler(optimizer, milestones=milestones, lr_list=lr_list)
+
+ index_rng = range(milestones[0], milestones[1])
+ for i in range((milestones[-1]) + 1):
+ if i < milestones[0]:
+ expected_lr = [0.01]
+ elif i in index_rng:
+ expected_lr = lr_list[0]
+ else:
+ expected_lr = lr_list[1]
+
+ current_lr = s.get_lr()[0]
+ assert s.get_lr() == expected_lr
+ assert isinstance(current_lr, float)
+ optimizer.step()
+ s.step()
+
+
+@dataclass
+class SchedulerTestConfig:
+ cfg: dict
+ init_lr: float
+ expected_lrs: list[float]
+
+
+TEST_SCHEDULERS = [
+ SchedulerTestConfig(
+ cfg=dict(type="LRListScheduler", params=dict(milestones=[2, 5], lr_list=[[0.5], [0.1]])),
+ init_lr=1.0,
+ expected_lrs=[1.0, 1.0, 0.5, 0.5, 0.5, 0.1, 0.1, 0.1],
+ ),
+ SchedulerTestConfig(
+ cfg=dict(type="LRListScheduler", params=dict(milestones=[1], lr_list=[[0.1]])),
+ init_lr=0.1,
+ expected_lrs=[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
+ ),
+ SchedulerTestConfig(
+ cfg=dict(type="LRListScheduler", params=dict(milestones=[1], lr_list=[[0.5]])),
+ init_lr=0.1,
+ expected_lrs=[0.1, 0.5, 0.5, 0.5],
+ ),
+ SchedulerTestConfig(
+ cfg=dict(type="StepLR", params=dict(step_size=3, gamma=0.1)),
+ init_lr=1.0,
+ expected_lrs=[1.0, 1.0, 1.0, 0.1, 0.1, 0.1, 0.01, 0.01, 0.01, 0.001],
+ ),
+]
+
+
+@pytest.mark.parametrize("test_cfg", TEST_SCHEDULERS)
+def test_build_scheduler(test_cfg: SchedulerTestConfig) -> None:
+ optimizer = torch.optim.SGD([torch.randn(2, 2)], lr=test_cfg.init_lr)
+ s = schedulers.build_scheduler(test_cfg.cfg, optimizer)
+ print()
+ print(f"Scheduler: {s}")
+ num_epochs = len(test_cfg.expected_lrs)
+ for e in range(num_epochs):
+ _assert_learning_rates_match(e, optimizer, test_cfg.expected_lrs[e])
+ optimizer.step()
+ s.step()
+
+
+@pytest.mark.parametrize("test_cfg", TEST_SCHEDULERS)
+def test_resume_scheduler_after_each_epoch(test_cfg: SchedulerTestConfig) -> None:
+ optimizer = torch.optim.SGD([torch.randn(2, 2)], lr=test_cfg.init_lr)
+ s = schedulers.build_scheduler(test_cfg.cfg, optimizer)
+ print()
+ print(f"Scheduler: {s}")
+ num_epochs = len(test_cfg.expected_lrs)
+ for e in range(num_epochs):
+ _assert_learning_rates_match(e, optimizer, test_cfg.expected_lrs[e])
+ optimizer.step()
+ s.step()
+
+ optimizer = torch.optim.SGD([torch.randn(2, 2)], lr=test_cfg.init_lr)
+ new_scheduler = schedulers.build_scheduler(test_cfg.cfg, optimizer)
+ schedulers.load_scheduler_state(new_scheduler, s.state_dict())
+ s = new_scheduler
+
+
+@pytest.mark.parametrize(
+ "test_cfg, middle_epoch",
+ [
+ (TEST_SCHEDULERS[0], 3),
+ (TEST_SCHEDULERS[1], 5),
+ (TEST_SCHEDULERS[2], 2),
+ (TEST_SCHEDULERS[3], 2),
+ (TEST_SCHEDULERS[3], 3),
+ (TEST_SCHEDULERS[3], 4),
+ ],
+)
+def test_two_stage_training(test_cfg: SchedulerTestConfig, middle_epoch: int) -> None:
+ num_epochs = len(test_cfg.expected_lrs)
+ optimizer = torch.optim.SGD([torch.randn(2, 2)], lr=test_cfg.init_lr)
+ s = schedulers.build_scheduler(test_cfg.cfg, optimizer)
+
+ print()
+ print(f"Scheduler: {s}")
+ for e in range(middle_epoch):
+ _assert_learning_rates_match(e, optimizer, test_cfg.expected_lrs[e])
+ optimizer.step()
+ s.step()
+
+ optimizer = torch.optim.SGD([torch.randn(2, 2)], lr=test_cfg.init_lr)
+ new_scheduler = schedulers.build_scheduler(test_cfg.cfg, optimizer)
+ schedulers.load_scheduler_state(new_scheduler, s.state_dict())
+ s = new_scheduler
+ for e in range(middle_epoch, num_epochs):
+ _assert_learning_rates_match(e, optimizer, test_cfg.expected_lrs[e])
+ s.step()
+
+
+@pytest.mark.parametrize(
+ "data",
+ [
+ dict( # example with 3 warm-up epochs
+ config=dict(
+ dict(
+ type="ConstantLR",
+ params=dict(factor=0.1, total_iters=3),
+ ),
+ ),
+ start_lr=1.0,
+ expected_lrs=[[0.1], [0.1], [0.1], [1.0], [1.0]],
+ ),
+ dict( # example from torch.optim.lr_scheduler.SequentialLR
+ config=dict(
+ type="SequentialLR",
+ params=dict(
+ schedulers=[
+ dict(
+ type="ConstantLR",
+ params=dict(factor=0.1, total_iters=2),
+ ),
+ dict(type="ExponentialLR", params=dict(gamma=0.9)),
+ ],
+ milestones=[2],
+ ),
+ ),
+ start_lr=1.0,
+ expected_lrs=[[0.1], [0.1], [1.0], [0.9], [0.81], [0.729]],
+ ),
+ dict( # example from torch.optim.lr_scheduler.SequentialLR
+ config=dict(
+ type="SequentialLR",
+ params=dict(
+ schedulers=[
+ dict(
+ type="ConstantLR",
+ params=dict(factor=0.1, total_iters=2),
+ ),
+ dict(type="StepLR", params=dict(step_size=2, gamma=0.1)),
+ ],
+ milestones=[5],
+ ),
+ ),
+ start_lr=1.0,
+ expected_lrs=[
+ [0.1],
+ [0.1],
+ [1.0],
+ [1.0],
+ [1.0], # ConstantLR
+ [1.0],
+ [1.0],
+ [0.1],
+ [0.1],
+ [0.01], # StepLR
+ ],
+ ),
+ ],
+)
+def test_build_sequential_lr(data):
+ print("\nTESTING")
+ start_lr = data["start_lr"]
+ print(f"Start LR: {start_lr}")
+ model = nn.Linear(in_features=1, out_features=1)
+ optimizer = torch.optim.SGD(params=model.parameters(), lr=start_lr)
+
+ print("BUILDING")
+ scheduler = schedulers.build_scheduler(data["config"], optimizer)
+
+ print("RUNNING")
+ lrs = []
+ for epoch in range(len(data["expected_lrs"])):
+ lrs.append(scheduler.get_last_lr())
+ print(scheduler.get_last_lr())
+ scheduler.step()
+
+ print(f"Expected: {data['expected_lrs']}")
+ print(f"Actual: {lrs}")
+ np.testing.assert_allclose(
+ np.asarray(data["expected_lrs"]),
+ np.asarray(lrs),
+ atol=1e-10,
+ )
+
+
+def _assert_learning_rates_match(e, optimizer, expected):
+ current_lrs = [g["lr"] for g in optimizer.param_groups]
+ print(f"Epoch {e}: LR={current_lrs}, expected={expected}")
+ for lr in current_lrs:
+ assert isinstance(lr, float)
+ np.testing.assert_almost_equal(lr, expected)
diff --git a/tests/pose_estimation_pytorch/runners/test_shelving.py b/tests/pose_estimation_pytorch/runners/test_shelving.py
new file mode 100644
index 0000000000..741c3c53c4
--- /dev/null
+++ b/tests/pose_estimation_pytorch/runners/test_shelving.py
@@ -0,0 +1,160 @@
+#
+# 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
+#
+"""Tests for ShelfWriter / ShelfReader."""
+
+from __future__ import annotations
+
+import numpy as np
+import pytest
+
+from deeplabcut.pose_estimation_pytorch.runners.shelving import (
+ ShelfReader,
+ ShelfWriter,
+)
+
+POSE_CFG = {
+ "all_joints": [[0], [1], [2]],
+ "all_joints_names": ["snout", "leftear", "rightear"],
+ "nmsradius": 5,
+ "minconfidence": 0.1,
+ "sigma": 1,
+}
+
+
+def _make_bodyparts(num_assemblies: int = 2, num_bpts: int = 3) -> np.ndarray:
+ """(num_assemblies, num_bpts, 3) — x, y, score."""
+ rng = np.random.default_rng(0)
+ return rng.random((num_assemblies, num_bpts, 3)).astype(np.float32)
+
+
+# -- lifecycle ----------------------------------------------------------------
+
+
+def test_write_before_open_raises(tmp_path):
+ writer = ShelfWriter(POSE_CFG, tmp_path / "shelf")
+ with pytest.raises(ValueError, match="open"):
+ writer.add_prediction(_make_bodyparts())
+
+
+def test_open_close_roundtrip(tmp_path):
+ path = tmp_path / "shelf"
+ writer = ShelfWriter(POSE_CFG, path)
+ writer.open()
+ writer.add_prediction(_make_bodyparts())
+ writer.close()
+
+ reader = ShelfReader(path)
+ reader.open()
+ assert "metadata" in reader.keys()
+ assert "frame00000" in reader.keys()
+ reader.close()
+
+
+# -- key formatting -----------------------------------------------------------
+
+
+@pytest.mark.parametrize("num_frames,width", [(9, 1), (100, 2), (1000, 3)])
+def test_key_str_width(tmp_path, num_frames, width):
+ writer = ShelfWriter(POSE_CFG, tmp_path / "shelf", num_frames=num_frames)
+ writer.open()
+ writer.add_prediction(_make_bodyparts())
+ writer.close()
+
+ reader = ShelfReader(tmp_path / "shelf")
+ reader.open()
+ expected_key = "frame" + "0".zfill(width)
+ assert expected_key in reader.keys()
+ reader.close()
+
+
+# -- data shape ---------------------------------------------------------------
+
+
+def test_add_prediction_stores_correct_shapes(tmp_path):
+ num_assemblies, num_bpts = 2, 3
+ bp = _make_bodyparts(num_assemblies, num_bpts)
+
+ writer = ShelfWriter(POSE_CFG, tmp_path / "shelf", num_frames=10)
+ writer.open()
+ writer.add_prediction(bp)
+ writer.close()
+
+ reader = ShelfReader(tmp_path / "shelf")
+ reader.open()
+ data = reader["frame0"]
+
+ coords = data["coordinates"][0]
+ assert len(coords) == num_bpts
+ assert coords[0].shape == (num_assemblies, 2)
+
+ scores = data["confidence"]
+ assert len(scores) == num_bpts
+ assert scores[0].shape == (num_assemblies, 1)
+ reader.close()
+
+
+# -- metadata on close --------------------------------------------------------
+
+
+def test_metadata_nframes_updated_on_close(tmp_path):
+ writer = ShelfWriter(POSE_CFG, tmp_path / "shelf", num_frames=100)
+ writer.open()
+ for _ in range(3):
+ writer.add_prediction(_make_bodyparts())
+ writer.close()
+
+ reader = ShelfReader(tmp_path / "shelf")
+ reader.open()
+ assert reader["metadata"]["nframes"] == 3
+ reader.close()
+
+
+# -- unique bodyparts ---------------------------------------------------------
+
+
+def test_unique_bodyparts_appended(tmp_path):
+ num_assemblies, num_bpts, num_unique = 2, 3, 1
+ bp = _make_bodyparts(num_assemblies, num_bpts)
+ ubp = np.random.default_rng(1).random((num_assemblies, num_unique, 3)).astype(np.float32)
+
+ writer = ShelfWriter(POSE_CFG, tmp_path / "shelf", num_frames=5)
+ writer.open()
+ writer.add_prediction(bp, unique_bodyparts=ubp)
+ writer.close()
+
+ reader = ShelfReader(tmp_path / "shelf")
+ reader.open()
+ data = reader["frame0"]
+ assert len(data["coordinates"][0]) == num_bpts + num_unique
+ assert len(data["confidence"]) == num_bpts + num_unique
+ reader.close()
+
+
+# -- identity scores ----------------------------------------------------------
+
+
+def test_identity_scores_stored(tmp_path):
+ num_assemblies, num_bpts, num_individuals = 2, 3, 2
+ bp = _make_bodyparts(num_assemblies, num_bpts)
+ ids = np.random.default_rng(2).random((num_assemblies, num_bpts, num_individuals)).astype(np.float32)
+
+ writer = ShelfWriter(POSE_CFG, tmp_path / "shelf", num_frames=5)
+ writer.open()
+ writer.add_prediction(bp, identity_scores=ids)
+ writer.close()
+
+ reader = ShelfReader(tmp_path / "shelf")
+ reader.open()
+ data = reader["frame0"]
+ assert "identity" in data
+ assert len(data["identity"]) == num_bpts
+ assert data["identity"][0].shape == (num_assemblies, num_individuals)
+ reader.close()
diff --git a/tests/pose_estimation_pytorch/runners/test_task.py b/tests/pose_estimation_pytorch/runners/test_task.py
new file mode 100644
index 0000000000..2f821d0aa3
--- /dev/null
+++ b/tests/pose_estimation_pytorch/runners/test_task.py
@@ -0,0 +1,28 @@
+#
+# 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
+#
+"""Tests the Task enum."""
+
+import pytest
+
+from deeplabcut.pose_estimation_pytorch.task import Task
+
+
+@pytest.mark.parametrize(
+ "task, task_strings",
+ [
+ (Task.BOTTOM_UP, ["bu", "BU", "bU", "Bu"]),
+ (Task.TOP_DOWN, ["TD", "tD"]),
+ (Task.DETECT, ["dt", "DT"]),
+ ],
+)
+def test_build_task(task: Task, task_strings: list[str]):
+ for s in task_strings:
+ assert task == Task(s)
diff --git a/tests/test_auxfun_models.py b/tests/test_auxfun_models.py
index dceb30f261..0684da7651 100644
--- a/tests/test_auxfun_models.py
+++ b/tests/test_auxfun_models.py
@@ -4,15 +4,15 @@
# https://github.com/DeepLabCut/DeepLabCut
#
# Please see AUTHORS for contributors.
-# https://github.com/DeepLabCut/DeepLabCut/blob/master/AUTHORS
+# https://github.com/DeepLabCut/DeepLabCut/blob/main/AUTHORS
#
# Licensed under GNU Lesser General Public License v3.0
#
+import unittest
from pathlib import Path
from tempfile import TemporaryDirectory
-import unittest
from unittest.mock import patch
from deeplabcut.utils.auxfun_models import MODELTYPE_FILEPATH_MAP, check_for_weights
@@ -21,24 +21,15 @@
class CheckForWeightsTestCase(unittest.TestCase):
def test_filepaths_for_modeltypes(self):
with TemporaryDirectory() as tmpdir:
- with patch(
- "deeplabcut.utils.auxfun_models.download_weights"
- ) as mocked_download:
+ with patch("deeplabcut.utils.auxfun_models.download_weights") as mocked_download:
for modeltype, expected_path in MODELTYPE_FILEPATH_MAP.items():
- actual_path, _ = check_for_weights(modeltype, Path(tmpdir), 1)
+ actual_path = check_for_weights(modeltype, Path(tmpdir))
self.assertIn(str(expected_path), actual_path)
if "efficientnet" in modeltype:
- mocked_download.assert_called_with(
- modeltype, tmpdir / expected_path.parent
- )
+ mocked_download.assert_called_with(modeltype, tmpdir / expected_path.parent)
else:
- mocked_download.assert_called_with(
- modeltype, tmpdir / expected_path
- )
+ mocked_download.assert_called_with(modeltype, tmpdir / expected_path)
def test_bad_modeltype(self):
- actual_path, actual_num_shuffles = check_for_weights(
- "dummymodel", "nonexistentpath", 1
- )
+ actual_path = check_for_weights("dummymodel", "nonexistentpath")
self.assertEqual(actual_path, "nonexistentpath")
- self.assertEqual(actual_num_shuffles, -1)
diff --git a/tests/test_auxfun_multianimal.py b/tests/test_auxfun_multianimal.py
index 1dbd67d2f6..8e8b7d28dc 100644
--- a/tests/test_auxfun_multianimal.py
+++ b/tests/test_auxfun_multianimal.py
@@ -8,12 +8,14 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
+from itertools import combinations
+
import networkx as nx
import numpy as np
import pandas as pd
import pytest
+
from deeplabcut.utils import auxfun_multianimal
-from itertools import combinations
def test_prune_paf_graph():
@@ -44,9 +46,7 @@ def test_reorder_individuals_in_df():
individuals = df.columns.get_level_values("individuals").unique().to_list()
# Generate a random permutation and reorder data. Ignore the unique bodypart
- permutation_indices = random.sample(
- range(len(individuals[:-1])), k=len(individuals[:-1])
- )
+ permutation_indices = random.sample(range(len(individuals[:-1])), k=len(individuals[:-1]))
permutation = [individuals[i] for i in permutation_indices]
permutation.append("single")
df_reordered = auxfun_multianimal.reorder_individuals_in_df(df, permutation)
@@ -56,9 +56,7 @@ def test_reorder_individuals_in_df():
inverse_permutation_indices = np.argsort(permutation_indices).tolist()
inverse_permutation = [individuals[i] for i in inverse_permutation_indices]
inverse_permutation.append("single")
- df_inverse_reordering = auxfun_multianimal.reorder_individuals_in_df(
- df_reordered, inverse_permutation
- )
+ df_inverse_reordering = auxfun_multianimal.reorder_individuals_in_df(df_reordered, inverse_permutation)
# Check
pd.testing.assert_frame_equal(df, df_inverse_reordering)
diff --git a/tests/test_auxiliaryfunctions.py b/tests/test_auxiliaryfunctions.py
index 7397060897..ff16dc1c1e 100644
--- a/tests/test_auxiliaryfunctions.py
+++ b/tests/test_auxiliaryfunctions.py
@@ -9,7 +9,9 @@
# Licensed under GNU Lesser General Public License v3.0
#
from pathlib import Path
+
import pytest
+
from deeplabcut.utils import auxiliaryfunctions
from deeplabcut.utils.auxfun_videos import SUPPORTED_VIDEOS
@@ -17,7 +19,7 @@
def test_find_analyzed_data(tmpdir_factory):
fake_folder = tmpdir_factory.mktemp("videos")
SUPPORTED_VIDEOS = ["avi"]
- n_ext = len(SUPPORTED_VIDEOS)
+ len(SUPPORTED_VIDEOS)
SCORER = "DLC_dlcrnetms5_multi_mouseApr11shuffle1_5"
WRONG_SCORER = "DLC_dlcrnetms5_multi_mouseApr11shuffle3_5"
@@ -36,22 +38,18 @@ def _create_fake_file(filename):
for ind, ext in enumerate(SUPPORTED_VIDEOS):
# test if existing models are found:
- assert auxiliaryfunctions.find_analyzed_data(
- fake_folder, "video" + str(ind), SCORER
- )
+ assert auxiliaryfunctions.find_analyzed_data(fake_folder, "video" + str(ind), SCORER)
# Test if nonexisting models are not found
with pytest.raises(FileNotFoundError):
- auxiliaryfunctions.find_analyzed_data(
- fake_folder, "video" + str(ind), WRONG_SCORER
- )
+ auxiliaryfunctions.find_analyzed_data(fake_folder, "video" + str(ind), WRONG_SCORER)
with pytest.raises(FileNotFoundError):
- auxiliaryfunctions.find_analyzed_data(
- fake_folder, "video" + str(ind), SCORER, filtered=True
- )
+ auxiliaryfunctions.find_analyzed_data(fake_folder, "video" + str(ind), SCORER, filtered=True)
+@pytest.mark.deprecated
+@pytest.mark.filterwarnings("ignore::DeprecationWarning")
def test_get_list_of_videos(tmpdir_factory):
fake_folder = tmpdir_factory.mktemp("videos")
n_ext = len(SUPPORTED_VIDEOS)
@@ -121,7 +119,7 @@ def _create_fake_file(filename):
def test_write_config_has_skeleton(tmpdir_factory):
- """Required for backward compatibility"""
+ """Required for backward compatibility."""
fake_folder = tmpdir_factory.mktemp("fakeConfigs")
fake_config_file = fake_folder / Path("fakeConfig")
auxiliaryfunctions.write_config(fake_config_file, {})
@@ -165,21 +163,15 @@ def test_intersection_of_body_parts_and_ones_given_by_user(
else:
all_bodyparts = bodyparts
- filtered_bpts = (
- auxiliaryfunctions.intersection_of_body_parts_and_ones_given_by_user(
- cfg, comparisonbodyparts="all"
- )
- )
+ filtered_bpts = auxiliaryfunctions.intersection_of_body_parts_and_ones_given_by_user(cfg, comparisonbodyparts="all")
print(all_bodyparts)
print(filtered_bpts)
assert len(all_bodyparts) == len(filtered_bpts)
assert all([bpt in all_bodyparts for bpt in filtered_bpts])
- filtered_bpts = (
- auxiliaryfunctions.intersection_of_body_parts_and_ones_given_by_user(
- cfg,
- comparisonbodyparts=comparison_bpts,
- )
+ filtered_bpts = auxiliaryfunctions.intersection_of_body_parts_and_ones_given_by_user(
+ cfg,
+ comparisonbodyparts=comparison_bpts,
)
print(filtered_bpts)
assert len(expected_bpts) == len(filtered_bpts)
@@ -230,3 +222,50 @@ def get_rglob_results(*args, **kwargs):
monkeypatch.setattr(Path, "rglob", get_rglob_results)
next_folder = auxiliaryfunctions.find_next_unlabeled_folder(fake_cfg)
assert str(next_folder) == str(Path(data_folder / next_folder_name))
+
+
+@pytest.fixture
+def mock_snapshot_folder(tmp_path):
+ """Mock folder with snapshots."""
+ folder = tmp_path / "train"
+ folder.mkdir()
+
+ # mock files
+ snapshot_files = [
+ "snapshot-4.index",
+ "snapshot-5.index",
+ "snapshot-6.index",
+ "snapshot-3.data-00000-of-00001",
+ "snapshot-3.index",
+ "snapshot-3.meta",
+ ]
+ for file_name in snapshot_files:
+ (folder / file_name).touch()
+
+ return folder
+
+
+@pytest.fixture
+def mock_no_snapshots_folder(tmp_path):
+ """Mock folder with no snapshots."""
+ folder = tmp_path / "train"
+ folder.mkdir()
+
+ # mock files
+ snapshot_files = ["log.txt", "pose_cfg.yaml"]
+ for file_name in snapshot_files:
+ (folder / file_name).touch()
+
+ return folder
+
+
+def test_get_snapshots_from_folder(mock_snapshot_folder):
+ """Test returns expected snapshots in order."""
+ snapshot_names = auxiliaryfunctions.get_snapshots_from_folder(mock_snapshot_folder)
+ assert snapshot_names == ["snapshot-3", "snapshot-4", "snapshot-5", "snapshot-6"]
+
+
+def test_get_snapshots_from_folder_none(mock_no_snapshots_folder):
+ """Test raises ValueError if no snapshots are found."""
+ with pytest.raises(FileNotFoundError):
+ auxiliaryfunctions.get_snapshots_from_folder(mock_no_snapshots_folder)
diff --git a/tests/test_conversioncode.py b/tests/test_conversioncode.py
index 3ec57e0197..ca287ba861 100644
--- a/tests/test_conversioncode.py
+++ b/tests/test_conversioncode.py
@@ -9,8 +9,10 @@
# Licensed under GNU Lesser General Public License v3.0
#
import os
+
import pandas as pd
from conftest import TEST_DATA_DIR
+
from deeplabcut.utils import conversioncode
diff --git a/tests/test_crossvalutils.py b/tests/test_crossvalutils.py
index 61c5af043f..5c821bd03e 100644
--- a/tests/test_crossvalutils.py
+++ b/tests/test_crossvalutils.py
@@ -8,10 +8,11 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
+import pickle
+
import numpy as np
-import pandas as pd
-from deeplabcut.pose_estimation_tensorflow.lib import crossvalutils
+from deeplabcut.core import crossvalutils
BEST_GRAPH = [14, 15, 16, 11, 22, 31, 61, 7, 59, 62, 64]
BEST_GRAPH_MONTBLANC = [1, 0, 2, 5, 4, 3]
@@ -21,9 +22,7 @@ def test_get_n_best_paf_graphs(evaluation_data_and_metadata):
data, metadata = evaluation_data_and_metadata
params = crossvalutils._set_up_evaluation(data)
n_graphs = 5
- paf_inds, dict_ = crossvalutils._get_n_best_paf_graphs(
- data, metadata, params["paf_graph"], n_graphs=n_graphs
- )
+ paf_inds, dict_ = crossvalutils._get_n_best_paf_graphs(data, metadata, params["paf_graph"], n_graphs=n_graphs)
assert len(paf_inds) == n_graphs
assert len(dict_) == len(params["paf_graph"])
assert len(paf_inds[0]) == 11
@@ -67,9 +66,7 @@ def test_benchmark_paf_graphs(evaluation_data_and_metadata):
],
}
inference_cfg = {"topktoretain": 3, "pcutoff": 0.1, "pafthreshold": 0.1}
- results = crossvalutils._benchmark_paf_graphs(
- cfg, inference_cfg, data, [BEST_GRAPH]
- )
+ results = crossvalutils._benchmark_paf_graphs(cfg, inference_cfg, data, [BEST_GRAPH])
all_scores = results[0]
assert len(all_scores) == 1
assert all_scores[0][1] == BEST_GRAPH
@@ -98,10 +95,14 @@ def test_benchmark_paf_graphs_montblanc(evaluation_data_and_metadata_montblanc):
[BEST_GRAPH_MONTBLANC],
split_inds=[metadata["data"]["trainIndices"], metadata["data"]["testIndices"]],
)
- results_gt = pd.read_pickle("tests/data/montblanc_map.pickle")
+ with open("tests/data/montblanc_map.pickle", "rb") as f:
+ results_gt = pickle.load(f)
np.testing.assert_equal(
results[1].loc["purity"].to_numpy().squeeze(),
- results_gt[0].loc["purity", 6].to_numpy(),
+ [
+ results_gt[0][6][("purity", "mean")],
+ results_gt[0][6][("purity", "std")],
+ ],
)
vals = [
results[2][0][0]["mAP"],
@@ -111,5 +112,10 @@ def test_benchmark_paf_graphs_montblanc(evaluation_data_and_metadata_montblanc):
]
np.testing.assert_equal(
vals,
- results_gt[0].iloc[-4:, -1].to_numpy(),
+ [
+ results_gt[0][6][("mAP_train", "mean")],
+ results_gt[0][6][("mAR_train", "mean")],
+ results_gt[0][6][("mAP_test", "mean")],
+ results_gt[0][6][("mAR_test", "mean")],
+ ],
)
diff --git a/tests/test_dataset_augmentation.py b/tests/test_dataset_augmentation.py
index 963bf2f541..9e935c7259 100644
--- a/tests/test_dataset_augmentation.py
+++ b/tests/test_dataset_augmentation.py
@@ -11,8 +11,14 @@
import imgaug.augmenters as iaa
import numpy as np
import pytest
+
from deeplabcut.pose_estimation_tensorflow.datasets import augmentation
+tf = pytest.importorskip(
+ "tensorflow",
+ reason="TensorFlow not installed (use a project extra such as .[tf])",
+)
+
@pytest.mark.parametrize(
"width, height",
@@ -101,9 +107,31 @@ def test_keypoint_horizontal_flip(
keypoints_aug = aug(
images=[sample_image],
keypoints=[sample_keypoints],
- )[
- 1
- ][0]
+ )[1][0]
+ temp = keypoints_aug.reshape((3, 12, 2))
+ for pair in pairs:
+ temp[:, pair] = temp[:, pair[::-1]]
+ keypoints_unaug = temp.reshape((-1, 2))
+ np.testing.assert_allclose(keypoints_unaug, keypoints_flipped)
+
+
+def test_keypoint_horizontal_flip_with_nans(
+ sample_image,
+ sample_keypoints,
+):
+ sample_keypoints[::12] = np.nan
+ sample_keypoints[2::12] = np.nan
+ keypoints_flipped = sample_keypoints.copy()
+ keypoints_flipped[:, 0] = sample_image.shape[1] - keypoints_flipped[:, 0]
+ pairs = [(0, 1), (2, 3)]
+ aug = augmentation.KeypointFliplr(
+ keypoints=list(map(str, range(12))),
+ symmetric_pairs=pairs,
+ )
+ keypoints_aug = aug(
+ images=[sample_image],
+ keypoints=[sample_keypoints],
+ )[1][0]
temp = keypoints_aug.reshape((3, 12, 2))
for pair in pairs:
temp[:, pair] = temp[:, pair[::-1]]
diff --git a/tests/test_evaluate.py b/tests/test_evaluate.py
new file mode 100644
index 0000000000..69ea13d7c6
--- /dev/null
+++ b/tests/test_evaluate.py
@@ -0,0 +1,233 @@
+#
+# 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/master/AUTHORS
+#
+# Licensed under GNU Lesser General Public License v3.0
+#
+import numpy as np
+import pandas as pd
+import pytest
+
+import deeplabcut.pose_estimation_tensorflow as pet
+from deeplabcut.pose_estimation_tensorflow.core.evaluate import (
+ get_available_requested_snapshots,
+ get_snapshots_by_index,
+)
+
+tf = pytest.importorskip(
+ "tensorflow",
+ reason="TensorFlow not installed (use a project extra such as .[tf])",
+)
+
+
+def make_single_animal_rmse_df(
+ bodyparts,
+ train_indices,
+ test_indices,
+ error_data=None,
+) -> pd.DataFrame:
+ if error_data is None:
+ error_data = np.ones((len(train_indices) + len(test_indices), len(bodyparts)))
+ return pd.DataFrame(error_data, columns=bodyparts)
+
+
+def make_multi_animal_rmse_df(
+ scorer,
+ individuals,
+ bodyparts,
+ train_indices,
+ test_indices,
+ error_data=None,
+) -> pd.DataFrame:
+ columns = pd.MultiIndex.from_product(
+ [[scorer], individuals, bodyparts],
+ names=["scorer", "individuals", "bodyparts"],
+ )
+ if error_data is None:
+ error_data = np.ones((len(train_indices) + len(test_indices), len(individuals) * len(bodyparts)))
+ return pd.DataFrame(error_data, columns=columns)
+
+
+KEYPOINT_ERROR_NAMES = [
+ "Train error (px)",
+ "Test error (px)",
+ "Train error (px) with p-cutoff",
+ "Test error (px) with p-cutoff",
+]
+
+KEYPOINT_ERROR_TEST_DATA = [
+ (
+ {
+ "df_error": make_single_animal_rmse_df(
+ bodyparts=["leg", "arm", "head"],
+ train_indices=[0, 1, 3],
+ test_indices=[2, 4],
+ ),
+ "train_indices": [0, 1, 3],
+ "test_indices": [2, 4],
+ },
+ {
+ "leg": [1.0, 1.0], # train, test
+ "arm": [1.0, 1.0], # train, test
+ "head": [1.0, 1.0], # train, test
+ },
+ ),
+ (
+ {
+ "df_error": make_single_animal_rmse_df(
+ bodyparts=["leftHand", "rightHand"],
+ train_indices=[0, 2],
+ test_indices=[1, 3],
+ error_data=[
+ [1.0, np.nan],
+ [1.0, 0.0],
+ [0.0, 10.0],
+ [5.0, 5.0],
+ ],
+ ),
+ "train_indices": [0, 2],
+ "test_indices": [1, 3],
+ },
+ {
+ "leftHand": [0.5, 3.0], # train, test
+ "rightHand": [10.0, 2.5], # train, test
+ },
+ ),
+ (
+ {
+ "df_error": make_single_animal_rmse_df(
+ bodyparts=["leg", "arm", "head"],
+ train_indices=[0, 1, 3],
+ test_indices=[2, 4],
+ ),
+ "train_indices": [0, 1, 3],
+ "test_indices": [2, 4],
+ },
+ {
+ "leg": [1.0, 1.0], # train, test
+ "arm": [1.0, 1.0], # train, test
+ "head": [1.0, 1.0], # train, test
+ },
+ ),
+ (
+ {
+ "df_error": make_multi_animal_rmse_df(
+ scorer="john",
+ individuals=["individual_1", "individual_2"],
+ bodyparts=["leftArm", "rightArm"],
+ train_indices=[0, 1, 3],
+ test_indices=[2],
+ error_data=[
+ # individual_1, individual2
+ # leftArm, rightArm, leftArm, rightArm
+ [1.0, np.nan, 1.0, 2.0],
+ [2.0, 0.0, 1.0, np.nan],
+ [3.0, 10.0, 1.0, np.nan],
+ [10.0, 4.0, np.nan, np.nan],
+ ],
+ ),
+ "train_indices": [0, 1, 3],
+ "test_indices": [2],
+ },
+ {
+ "leftArm": [3.0, 2.0], # train, test
+ "rightArm": [2.0, 10.0], # train, test
+ },
+ ),
+]
+
+
+@pytest.mark.parametrize("inputs, expected_values", KEYPOINT_ERROR_TEST_DATA)
+def test_evaluate_keypoint_error(inputs, expected_values):
+ keypoint_error = pet.keypoint_error(
+ inputs["df_error"],
+ inputs["df_error"],
+ inputs["train_indices"],
+ inputs["test_indices"],
+ )
+ print(inputs["df_error"])
+ print(keypoint_error)
+ for bodypart, mean_errors in expected_values.items():
+ for error_name in KEYPOINT_ERROR_NAMES:
+ if "train" in error_name.lower():
+ mean_error = mean_errors[0]
+ else:
+ mean_error = mean_errors[1]
+
+ assert keypoint_error.loc[error_name, bodypart] == mean_error
+
+
+def test_get_available_requested_snapshots_ok():
+ """Test that the correct snapshots are returned."""
+ available = ["snapshot-1", "snapshot-2"]
+ requested = ["snapshot-2", "snapshot-3"]
+
+ snapshots = get_available_requested_snapshots(
+ requested_snapshots=requested,
+ available_snapshots=available,
+ )
+ assert snapshots == ["snapshot-2"]
+
+
+def test_get_available_requested_snapshots_error():
+ """Test that a ValueError is raised when requested snapshots are not available."""
+ with pytest.raises(ValueError):
+ get_available_requested_snapshots(
+ requested_snapshots=["snapshot-2"],
+ available_snapshots=["snapshot-1", "snapshot-3"],
+ )
+
+
+def test_get_snapshots_by_index_int_ok():
+ """Test that the correct snapshots are returned."""
+ available = ["snapshot-1", "snapshot-2", "snapshot-3"]
+
+ # positive int
+ snapshots = get_snapshots_by_index(
+ idx=2,
+ available_snapshots=available,
+ )
+ assert snapshots == ["snapshot-3"]
+
+ # negative int
+ snapshots = get_snapshots_by_index(
+ idx=-2,
+ available_snapshots=available,
+ )
+ assert snapshots == ["snapshot-2"]
+
+ # all snapshots
+ snapshots = get_snapshots_by_index(
+ idx="all",
+ available_snapshots=available,
+ )
+ assert snapshots == ["snapshot-1", "snapshot-2", "snapshot-3"]
+
+
+def test_get_snapshots_by_index_error():
+ """Test that a ValueError is raised when the index is out of range or invalid
+ str."""
+ available = ["snapshot-1", "snapshot-2", "snapshot-3"]
+
+ # positive int
+ with pytest.raises(IndexError):
+ get_snapshots_by_index(
+ idx=5,
+ available_snapshots=available,
+ )
+ # negative int
+ with pytest.raises(IndexError):
+ get_snapshots_by_index(
+ idx=-4,
+ available_snapshots=available,
+ )
+ # invalid str
+ with pytest.raises(IndexError):
+ get_snapshots_by_index(
+ idx="1",
+ available_snapshots=available,
+ )
diff --git a/tests/test_frame_selection_tools.py b/tests/test_frame_selection_tools.py
index ddee2346c7..17b615ad61 100644
--- a/tests/test_frame_selection_tools.py
+++ b/tests/test_frame_selection_tools.py
@@ -8,10 +8,13 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
-""" Tests for frame selection tools """
+"""Tests for frame selection tools."""
+
import math
from unittest.mock import Mock
+
import pytest
+
import deeplabcut.utils.frameselectiontools as fst
diff --git a/tests/test_inferenceutils.py b/tests/test_inferenceutils.py
index 2736939f6a..cd52c8cd36 100644
--- a/tests/test_inferenceutils.py
+++ b/tests/test_inferenceutils.py
@@ -8,15 +8,17 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
-import numpy as np
import os
import pickle
+from copy import deepcopy
+
+import numpy as np
import pytest
from conftest import TEST_DATA_DIR
-from copy import deepcopy
-from deeplabcut.pose_estimation_tensorflow.lib import inferenceutils
from scipy.spatial.distance import squareform
+from deeplabcut.core import inferenceutils
+
def test_conv_square_to_condensed_indices():
n = 5
@@ -25,7 +27,7 @@ def test_conv_square_to_condensed_indices():
mat[rows, cols] = mat[cols, rows] = np.arange(1, len(rows) + 1)
vec = squareform(mat)
vals = []
- for i, j in zip(rows, cols):
+ for i, j in zip(rows, cols, strict=False):
ind = inferenceutils._conv_square_to_condensed_indices(i, j, n)
vals.append(vec[ind])
np.testing.assert_equal(vec, vals)
@@ -36,9 +38,7 @@ def test_calc_object_keypoint_similarity(real_assemblies):
xy1 = real_assemblies[0][0].xy
xy2 = real_assemblies[0][1].xy
assert inferenceutils.calc_object_keypoint_similarity(xy1, xy1, sigma) == 1
- assert np.isclose(
- inferenceutils.calc_object_keypoint_similarity(xy1, xy2, sigma), 0
- )
+ assert np.isclose(inferenceutils.calc_object_keypoint_similarity(xy1, xy2, sigma), 0)
xy3 = xy1.copy()
xy3[: len(xy3) // 2] = np.nan
assert inferenceutils.calc_object_keypoint_similarity(xy3, xy1, sigma) == 0.5
@@ -51,36 +51,27 @@ def test_calc_object_keypoint_similarity(real_assemblies):
symmetric_pair = [0, 11]
xy4[symmetric_pair] = xy4[symmetric_pair[::-1]]
assert inferenceutils.calc_object_keypoint_similarity(xy1, xy4, sigma) != 1
- assert (
- inferenceutils.calc_object_keypoint_similarity(
- xy1, xy4, sigma, symmetric_kpts=[symmetric_pair]
- )
- == 1
- )
+ assert inferenceutils.calc_object_keypoint_similarity(xy1, xy4, sigma, symmetric_kpts=[symmetric_pair]) == 1
def test_match_assemblies(real_assemblies):
assemblies = real_assemblies[0]
- matched, unmatched = inferenceutils.match_assemblies(
- assemblies, assemblies[::-1], 0.01
- )
- assert not unmatched
- for ass1, ass2, oks in matched:
- assert ass1 is ass2
- assert oks == 1
+ num_gt, matches = inferenceutils.match_assemblies(assemblies, assemblies[::-1], 0.01)
+ assert len(assemblies) == len(matches)
+ for m in matches:
+ assert m.prediction is m.ground_truth
+ assert m.oks == 1
- matched, unmatched = inferenceutils.match_assemblies([], assemblies, 0.01)
- assert not matched
- assert all(ass1 is ass2 for ass1, ass2 in zip(unmatched, assemblies))
+ num_gt, matches = inferenceutils.match_assemblies([], assemblies, 0.01)
+ assert len(matches) == 0
+ assert num_gt == len(assemblies)
def test_evaluate_assemblies(real_assemblies):
assemblies = {i: real_assemblies[i] for i in range(3)}
n_thresholds = 5
thresholds = np.linspace(0.5, 0.95, n_thresholds)
- dict_ = inferenceutils.evaluate_assembly(
- assemblies, assemblies, oks_thresholds=thresholds
- )
+ dict_ = inferenceutils.evaluate_assembly(assemblies, assemblies, oks_thresholds=thresholds)
assert dict_["mAP"] == dict_["mAR"] == 1
assert len(dict_["precisions"]) == len(dict_["recalls"]) == n_thresholds
assert dict_["precisions"].shape[1] == 101
@@ -172,13 +163,11 @@ def test_assembler(tmpdir_factory, real_assemblies):
ass.assemble()
assert not ass.unique
assert len(ass.assemblies) == len(real_assemblies)
- assert sum(1 for a in ass.assemblies.values() for _ in a) == sum(
- 1 for a in real_assemblies.values() for _ in a
- )
+ assert sum(1 for a in ass.assemblies.values() for _ in a) == sum(1 for a in real_assemblies.values() for _ in a)
- output_name = tmpdir_factory.mktemp("data").join("fake.h5")
- ass.to_h5(output_name)
- ass.to_pickle(str(output_name).replace("h5", "pickle"))
+ output_dir = tmpdir_factory.mktemp("data")
+ ass.to_h5(output_dir.join("fake.h5"))
+ ass.to_pickle(output_dir.join("fake.pickle"))
def test_assembler_with_single_bodypart(real_assemblies):
@@ -223,15 +212,9 @@ def test_assembler_with_unique_bodypart(real_assemblies_montblanc):
ass.assemble(chunk_size=0)
assert len(ass.assemblies) == len(real_assemblies_montblanc[0])
assert len(ass.unique) == len(real_assemblies_montblanc[1])
- assemblies = np.concatenate(
- [ass.xy for assemblies in ass.assemblies.values() for ass in assemblies]
- )
+ assemblies = np.concatenate([ass.xy for assemblies in ass.assemblies.values() for ass in assemblies])
assemblies_gt = np.concatenate(
- [
- ass.xy
- for assemblies in real_assemblies_montblanc[0].values()
- for ass in assemblies
- ]
+ [ass.xy for assemblies in real_assemblies_montblanc[0].values() for ass in assemblies]
)
np.testing.assert_equal(assemblies, assemblies_gt)
@@ -270,9 +253,7 @@ def test_assembler_with_identity(tmpdir_factory, real_assemblies):
ass.assemble()
assert not ass.unique
assert len(ass.assemblies) == len(real_assemblies)
- assert sum(1 for a in ass.assemblies.values() for _ in a) == sum(
- 1 for a in real_assemblies.values() for _ in a
- )
+ assert sum(1 for a in ass.assemblies.values() for _ in a) == sum(1 for a in real_assemblies.values() for _ in a)
assert all(np.all(_.data[:, -1] != -1) for a in ass.assemblies.values() for _ in a)
# Test now with identity only and ensure assemblies
@@ -288,9 +269,9 @@ def test_assembler_with_identity(tmpdir_factory, real_assemblies):
eq.append(np.all(ids == ids[0]))
assert all(eq)
- output_name = tmpdir_factory.mktemp("data").join("fake.h5")
- ass.to_h5(output_name)
- ass.to_pickle(str(output_name).replace("h5", "pickle"))
+ output_dir = tmpdir_factory.mktemp("data")
+ ass.to_h5(output_dir.join("fake.h5"))
+ ass.to_pickle(output_dir.join("fake.pickle"))
def test_assembler_calibration(real_assemblies):
diff --git a/tests/test_pose_multianimal_imgaug.py b/tests/test_pose_multianimal_imgaug.py
index 0f19133875..d0f1debab8 100644
--- a/tests/test_pose_multianimal_imgaug.py
+++ b/tests/test_pose_multianimal_imgaug.py
@@ -8,17 +8,24 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
-import numpy as np
import os
+
+import numpy as np
import pytest
from conftest import TEST_DATA_DIR
+
from deeplabcut.pose_estimation_tensorflow.datasets import (
Batch,
- pose_multianimal_imgaug,
PoseDatasetFactory,
+ pose_multianimal_imgaug,
)
from deeplabcut.utils import read_plainconfig
+tf = pytest.importorskip(
+ "tensorflow",
+ reason="TensorFlow not installed (use a project extra such as .[tf])",
+)
+
def mock_imread(path, mode):
return (np.random.rand(400, 400, 3) * 255).astype(np.uint8)
@@ -66,18 +73,19 @@ def test_calc_target_and_scoremap_sizes(
def test_get_batch(ma_dataset):
for batch_size in 1, 4, 8, 16:
ma_dataset.batch_size = batch_size
- batch_images, joint_ids, batch_joints, _, data_items = ma_dataset.get_batch()
- assert (
- len(batch_images)
- == len(joint_ids)
- == len(batch_joints)
- == len(data_items)
- == batch_size
- )
- for data_item, joint_id in zip(data_items, joint_ids):
+ batch_images, joint_ids, batch_joints, data_items = ma_dataset.get_batch()
+ assert len(batch_images) == len(joint_ids) == len(batch_joints) == len(data_items) == batch_size
+ for data_item, joint_id, batch_joint in zip(data_items, joint_ids, batch_joints, strict=False):
assert len(data_item.joints) == len(joint_id)
- for joints, id_ in zip(data_item.joints.values(), joint_id):
- np.testing.assert_equal(joints[:, 0], id_)
+ assert len(batch_joint) == len(np.concatenate(joint_id))
+ start = 0
+ mask = ~np.isnan(batch_joint).any(axis=1)
+ for joints, id_ in zip(data_item.joints.values(), joint_id, strict=False):
+ inds = id_ + start
+ mask_ = mask[inds]
+ np.testing.assert_equal(joints[:, 0], id_[mask_])
+ np.testing.assert_equal(joints[:, 1:], batch_joint[inds][mask_])
+ start += id_.size
def test_build_augmentation_pipeline(ma_dataset):
@@ -88,28 +96,19 @@ def test_build_augmentation_pipeline(ma_dataset):
@pytest.mark.parametrize("num_idchannel", range(4))
def test_get_targetmaps(ma_dataset, num_idchannel):
ma_dataset.cfg["num_idchannel"] = num_idchannel
- batch = list(ma_dataset.get_batch()[1:])
- batch.pop(2)
+ batch = ma_dataset.get_batch()[1:]
target_size, sm_size = ma_dataset.calc_target_and_scoremap_sizes()
scale = np.mean(target_size / ma_dataset.default_size)
maps = ma_dataset.get_targetmaps_update(*batch, sm_size, scale)
assert all(len(map_) == ma_dataset.batch_size for map_ in maps.values())
- assert (
- maps[Batch.part_score_targets][0].shape
- == maps[Batch.part_score_weights][0].shape
- )
- assert (
- maps[Batch.part_score_targets][0].shape[2]
- == ma_dataset.cfg["num_joints"] + num_idchannel
- )
+ assert maps[Batch.part_score_targets][0].shape == maps[Batch.part_score_weights][0].shape
+ assert maps[Batch.part_score_targets][0].shape[2] == ma_dataset.cfg["num_joints"] + num_idchannel
assert maps[Batch.locref_targets][0].shape == maps[Batch.locref_mask][0].shape
assert maps[Batch.locref_targets][0].shape[2] == 2 * ma_dataset.cfg["num_joints"]
- assert (
- maps[Batch.pairwise_targets][0].shape == maps[Batch.pairwise_targets][0].shape
- )
+ assert maps[Batch.pairwise_targets][0].shape == maps[Batch.pairwise_targets][0].shape
assert maps[Batch.pairwise_targets][0].shape[2] == 2 * ma_dataset.cfg["num_limbs"]
def test_batching(ma_dataset):
for _ in range(10):
- batch = ma_dataset.next_batch()
+ ma_dataset.next_batch()
diff --git a/tests/test_predict_multianimal.py b/tests/test_predict_multianimal.py
index eb9bbd7d16..4646a6ca93 100644
--- a/tests/test_predict_multianimal.py
+++ b/tests/test_predict_multianimal.py
@@ -9,9 +9,14 @@
# Licensed under GNU Lesser General Public License v3.0
#
import numpy as np
-import tensorflow as tf
+import pytest
+
from deeplabcut.pose_estimation_tensorflow.core import predict_multianimal
+tf = pytest.importorskip(
+ "tensorflow",
+ reason="TensorFlow not installed (use a project extra such as .[tf])",
+)
RADIUS = 5
THRESHOLD = 0.01
@@ -66,14 +71,10 @@ def test_association_costs(model_outputs, ground_truth_detections):
costs_pred = preds["costs"]
assert len(costs_pred) == len(costs_gt)
eq = [
- np.array_equal(np.argmax(v["m1"], axis=0), np.argmax(costs_gt[k]["m1"], axis=0))
- for k, v in costs_pred.items()
+ np.array_equal(np.argmax(v["m1"], axis=0), np.argmax(costs_gt[k]["m1"], axis=0)) for k, v in costs_pred.items()
]
assert sum(eq) == 60 # 6 arrays are unequal as cost computation was corrected
- assert all(
- np.allclose(v["distance"], costs_gt[k]["distance"], atol=1.5)
- for k, v in costs_pred.items()
- )
+ assert all(np.allclose(v["distance"], costs_gt[k]["distance"], atol=1.5) for k, v in costs_pred.items())
def test_compute_peaks_and_costs_no_graph(model_outputs):
diff --git a/tests/test_predict_supermodel.py b/tests/test_predict_supermodel.py
index 767e2739a8..1453984620 100644
--- a/tests/test_predict_supermodel.py
+++ b/tests/test_predict_supermodel.py
@@ -10,7 +10,8 @@
#
import numpy as np
import pytest
-from deeplabcut.modelzoo.api import superanimal_inference
+
+from deeplabcut.pose_estimation_tensorflow.modelzoo.api import superanimal_inference
def test_get_multi_scale_frames():
@@ -22,7 +23,7 @@ def test_get_multi_scale_frames():
heights,
)
assert len(frames) == len(shapes) == len(heights)
- assert all(shape[0] == h for shape, h in zip(shapes, heights))
+ assert all(shape[0] == h for shape, h in zip(shapes, heights, strict=False))
assert all(round(shape[0] * ar) == shape[1] for shape in shapes)
@@ -44,4 +45,4 @@ def test_project_pred_to_original_size(scale):
)
coords_orig = preds_orig["coordinates"][0]
assert len(coords_orig) == len(xs)
- assert all([round(x * scale) == round(xy[0]) for xy, x in zip(coords_orig, xs)])
+ assert all([round(x * scale) == round(xy[0]) for xy, x in zip(coords_orig, xs, strict=False)])
diff --git a/tests/test_refine_train_dataset/test_outlierframes.py b/tests/test_refine_train_dataset/test_outlierframes.py
new file mode 100644
index 0000000000..a0d5f229ba
--- /dev/null
+++ b/tests/test_refine_train_dataset/test_outlierframes.py
@@ -0,0 +1,255 @@
+from unittest.mock import MagicMock
+
+import numpy as np
+import pandas as pd
+import pytest
+
+from deeplabcut.refine_training_dataset import outlier_frames
+
+# ----------------------------
+# Helpers / fixtures
+# ----------------------------
+
+STATS = [
+ "distance",
+ "sig",
+ "meanx",
+ "meany",
+ "lowerCIx",
+ "higherCIx",
+ "lowerCIy",
+ "higherCIy",
+]
+
+
+@pytest.fixture
+def patch_hdf_write(monkeypatch):
+ """
+ Avoid filesystem / pytables dependency when storeoutput='full' is used.
+ Also lets us assert that the write path is still exercised.
+ """
+ mock = MagicMock()
+ monkeypatch.setattr(pd.DataFrame, "to_hdf", mock)
+ return mock
+
+
+@pytest.fixture
+def patch_fit_sarimax(monkeypatch):
+ def fake_fit_sarimax_model(x, p, p_bound, alpha, ARdegree, MAdegree):
+ x = np.asarray(x, dtype=float)
+ mean = x.copy()
+ ci = np.c_[mean - 1.0, mean + 1.0]
+ return mean, ci
+
+ mock = MagicMock(side_effect=fake_fit_sarimax_model)
+ monkeypatch.setattr(outlier_frames, "FitSARIMAXModel", mock)
+ return mock
+
+
+@pytest.fixture
+def sparse_multianimal_df():
+ """
+ maDLC-like sparse layout:
+ - 2 individuals with shared bodyparts
+ - unique bodyparts present only under a special 'single' bucket
+ This breaks if reconstructed with the full Cartesian product of the non-'coords' levels,
+ e.g. multi-animal projects with unique bodyparts were previously
+ producing many extra columns for the non-existent combinations of individual x unique bodypart.
+ """
+ n_frames = 7
+ scorer = "DLC_scorer"
+ individuals = ["ind1", "ind2"]
+ shared_bodyparts = [f"shared_{i}" for i in range(18)]
+ unique_bodyparts = [f"unique_{i}" for i in range(4)]
+ coords = ["x", "y", "likelihood"]
+
+ tuples = []
+
+ # Shared bodyparts for each real individual
+ for ind in individuals:
+ for bp in shared_bodyparts:
+ for c in coords:
+ tuples.append((scorer, ind, bp, c))
+
+ # Unique bodyparts only under a special bucket
+ for bp in unique_bodyparts:
+ for c in coords:
+ tuples.append((scorer, "single", bp, c))
+
+ columns = pd.MultiIndex.from_tuples(tuples, names=["scorer", "individuals", "bodyparts", "coords"])
+
+ # 18 shared * 2 + 4 unique = 40 streams, each with x/y/likelihood
+ assert len(columns) == 40 * 3
+
+ rng = np.random.default_rng(42)
+ values = rng.normal(size=(n_frames, len(columns)))
+
+ # Keep likelihood valid / boring
+ likelihood_mask = columns.get_level_values("coords") == "likelihood"
+ values[:, likelihood_mask] = 0.9
+
+ df = pd.DataFrame(values, columns=columns)
+ return df
+
+
+@pytest.fixture
+def dense_multianimal_df():
+ """
+ Dense/full-combination layout:
+ every individual x bodypart combination exists.
+ For this topology, the old from_product(...) logic and the new "preserve
+ actual tuples" logic should produce the same output columns (assuming the
+ dataframe is created in canonical product order, which we do here).
+ """
+ n_frames = 5
+ scorer = "DLC_scorer"
+ individuals = ["ind1", "ind2"]
+ bodyparts = ["nose", "tail", "paw"]
+ coords = ["x", "y", "likelihood"]
+
+ tuples = [(scorer, ind, bp, c) for ind in individuals for bp in bodyparts for c in coords]
+
+ columns = pd.MultiIndex.from_tuples(tuples, names=["scorer", "individuals", "bodyparts", "coords"])
+
+ rng = np.random.default_rng(42)
+ values = rng.normal(size=(n_frames, len(columns)))
+ likelihood_mask = columns.get_level_values("coords") == "likelihood"
+ values[:, likelihood_mask] = 0.95
+
+ df = pd.DataFrame(values, columns=columns)
+ return df
+
+
+def _expected_output_columns_from_actual_streams(df):
+ """
+ Expected output columns preserve actual non-'coords' tuples and append the 8 derived stats.
+ """
+ base_cols = df.xs("x", axis=1, level="coords", drop_level=True).columns
+ return pd.MultiIndex.from_tuples(
+ [(tuple(col) if isinstance(col, tuple) else (col,)) + (stat,) for col in base_cols for stat in STATS],
+ names=df.columns.names,
+ )
+
+
+def _expected_output_columns_from_dense_product(df):
+ """
+ Expected output columns for the previous implementation:
+ full Cartesian product of all non-'coords' levels, then the 8 derived stats.
+ This is only correct / behavior-preserving for dense layouts.
+ """
+ columns = df.columns
+ prod = []
+ for i in range(columns.nlevels - 1):
+ prod.append(columns.get_level_values(i).unique())
+ prod.append(STATS)
+ return pd.MultiIndex.from_product(prod, names=columns.names)
+
+
+# ----------------------------
+# Tests
+# ----------------------------
+
+
+def test_compute_deviations_regression_sparse_unique_bodyparts(
+ sparse_multianimal_df,
+ patch_fit_sarimax,
+ patch_hdf_write,
+):
+ """
+ Regression test for the following maDLC unique-bodypart bug:
+ output columns must match the actual sparse stream layout rather than an
+ inflated Cartesian product of all non-'coords' level values.
+ """
+ df = sparse_multianimal_df
+ n_frames = len(df)
+
+ d, o, data = outlier_frames.compute_deviations(
+ df,
+ dataname="dummy.h5",
+ p_bound=0.01,
+ alpha=0.01,
+ ARdegree=3,
+ MAdegree=1,
+ storeoutput="full",
+ )
+
+ # There are 40 real streams in the sparse fixture
+ n_streams = 40
+
+ # Shape sanity checks
+ assert d.shape == (n_frames,)
+ assert o.shape == (n_frames,)
+ assert data.shape == (n_frames, n_streams * 8)
+
+ # Column layout must preserve only the actual streams
+ expected_columns = _expected_output_columns_from_actual_streams(df)
+ assert data.columns.equals(expected_columns)
+
+ # xs(...) on the last level should still work exactly as before
+ distance = data.xs("distance", axis=1, level=-1)
+ sig = data.xs("sig", axis=1, level=-1)
+ assert distance.shape == (n_frames, n_streams)
+ assert sig.shape == (n_frames, n_streams)
+
+ # With the fake fitter, predictions equal observations => zero distances and sig
+ np.testing.assert_allclose(d, 0.0)
+ np.testing.assert_allclose(o, 0.0)
+
+ # FitSARIMAXModel should be called twice per stream (x and y)
+ assert patch_fit_sarimax.call_count == 2 * n_streams
+
+ # "full" path should still try to persist the result
+ patch_hdf_write.assert_called_once()
+
+
+def test_compute_deviations_behavior_preserved_for_dense_layout(
+ dense_multianimal_df,
+ patch_fit_sarimax,
+ patch_hdf_write,
+):
+ """
+ Behavior-preserved check:
+ for a dense layout where every combination exists, the fixed implementation
+ should produce the same columns that the old from_product(...) logic would
+ have produced.
+ """
+ df = dense_multianimal_df
+ n_frames = len(df)
+ n_streams = len(df.xs("x", axis=1, level="coords", drop_level=True).columns)
+
+ d, o, data = outlier_frames.compute_deviations(
+ df,
+ dataname="dummy.h5",
+ p_bound=0.01,
+ alpha=0.01,
+ ARdegree=3,
+ MAdegree=1,
+ storeoutput="full",
+ )
+
+ # Basic shape / output checks
+ assert d.shape == (n_frames,)
+ assert o.shape == (n_frames,)
+ assert data.shape == (n_frames, n_streams * 8)
+
+ # For dense data, new behavior should match old dense-product behavior exactly
+ expected_old_dense_columns = _expected_output_columns_from_dense_product(df)
+ expected_new_columns = _expected_output_columns_from_actual_streams(df)
+
+ # Sanity check of the fixture assumption:
+ # in the dense case, these should indeed be identical.
+ assert expected_new_columns.equals(expected_old_dense_columns)
+
+ # Actual output should match that shared expected index
+ assert data.columns.equals(expected_old_dense_columns)
+
+ # Still selectable by derived-stat level
+ assert data.xs("distance", axis=1, level=-1).shape == (n_frames, n_streams)
+ assert data.xs("sig", axis=1, level=-1).shape == (n_frames, n_streams)
+
+ # Deterministic fake fitter
+ np.testing.assert_allclose(d, 0.0)
+ np.testing.assert_allclose(o, 0.0)
+
+ assert patch_fit_sarimax.call_count == 2 * n_streams
+ patch_hdf_write.assert_called_once()
diff --git a/tests/test_stitcher.py b/tests/test_stitcher.py
index 033552e54c..699ba96d7f 100644
--- a/tests/test_stitcher.py
+++ b/tests/test_stitcher.py
@@ -11,8 +11,8 @@
import numpy as np
import pandas as pd
import pytest
-from deeplabcut.refine_training_dataset.stitch import Tracklet, TrackletStitcher
+from deeplabcut.refine_training_dataset.stitch import Tracklet, TrackletStitcher
TRACKLET_LEN = 1000
TRACKLET_START = 50
@@ -100,7 +100,7 @@ def test_tracklet_data_access(tracklet):
@pytest.mark.parametrize(
"tracklet, where, norm",
- list(zip(make_fake_tracklets(), ("head", "tail"), (False, True))),
+ list(zip(make_fake_tracklets(), ("head", "tail"), (False, True), strict=False)),
)
def test_tracklet_calc_velocity(tracklet, where, norm):
_ = tracklet.calc_velocity(where, norm)
diff --git a/tests/test_tf_install_smoke.py b/tests/test_tf_install_smoke.py
new file mode 100644
index 0000000000..af19b8c8a3
--- /dev/null
+++ b/tests/test_tf_install_smoke.py
@@ -0,0 +1,53 @@
+#
+# 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
+#
+
+"""Smoke test for TensorFlow when optional TF extras are installed."""
+
+import pytest
+
+tf = pytest.importorskip(
+ "tensorflow",
+ reason="TensorFlow not installed (use a project extra such as .[tf])",
+)
+
+
+def test_tensorflow_imports_and_has_matmul() -> None:
+ assert tf.__version__
+ a = tf.constant([[1.0, 2.0]])
+ b = tf.constant([[3.0], [4.0]])
+ c = tf.matmul(a, b)
+
+ if tf.executing_eagerly():
+ result = c.numpy()
+ else:
+ with tf.compat.v1.Session() as sess:
+ result = sess.run(c)
+
+ assert (result == [[11.0]]).all()
+
+
+def test_tf_slim_imports_and_has_conv2d() -> None:
+ try:
+ import tf_slim as slim
+ except ImportError as e:
+ raise AssertionError("tf_slim is not installed or not importable") from e
+
+ assert slim.conv2d(tf.constant([[[[1.0]]]]), 1, kernel_size=[1, 1], stride=1).shape == (1, 1, 1, 1)
+
+
+def test_tf_keras_imports_and_has_regularizers() -> None:
+ try:
+ import tf_keras as keras
+ except ImportError as e:
+ raise AssertionError("tf_keras is not installed or not importable") from e
+ import numpy as np
+
+ assert keras.regularizers.l2(0.01).l2 == np.array(0.01, dtype="float32")
diff --git a/tests/test_trackingutils.py b/tests/test_trackingutils.py
index a489bc31a0..b3dac6d263 100644
--- a/tests/test_trackingutils.py
+++ b/tests/test_trackingutils.py
@@ -10,7 +10,8 @@
#
import numpy as np
import pytest
-from deeplabcut.pose_estimation_tensorflow.lib import trackingutils
+
+from deeplabcut.core import trackingutils
@pytest.fixture()
@@ -21,9 +22,7 @@ def ellipse():
def test_ellipse(ellipse):
assert ellipse.aspect_ratio == 2
- np.testing.assert_equal(
- ellipse.contains_points(np.asarray([[0, 0], [10, 10]])), [True, False]
- )
+ np.testing.assert_equal(ellipse.contains_points(np.asarray([[0, 0], [10, 10]])), [True, False])
def test_ellipse_similarity(ellipse):
@@ -33,7 +32,7 @@ def test_ellipse_similarity(ellipse):
def test_ellipse_fitter():
fitter = trackingutils.EllipseFitter()
assert fitter.fit(np.random.rand(2, 2)) is None
- xy = np.asarray([[-2, 0], [2, 0], [0, 1], [0, -1]], dtype=np.float)
+ xy = np.asarray([[-2, 0], [2, 0], [0, 1], [0, -1]], dtype=float)
assert fitter.fit(xy) is not None
fitter.sd = 0
el = fitter.fit(xy)
@@ -73,12 +72,8 @@ def test_tracking_ellipse(real_assemblies, real_tracklets):
trackers = mot_tracker.track(animals[..., :2])
trackingutils.fill_tracklets(tracklets, trackers, animals, ind)
assert len(tracklets) == len(tracklets_ref)
- assert [len(tracklet) for tracklet in tracklets.values()] == [
- len(tracklet) for tracklet in tracklets_ref.values()
- ]
- assert all(
- t.shape[1] == 4 for tracklet in tracklets.values() for t in tracklet.values()
- )
+ assert [len(tracklet) for tracklet in tracklets.values()] == [len(tracklet) for tracklet in tracklets_ref.values()]
+ assert all(t.shape[1] == 4 for tracklet in tracklets.values() for t in tracklet.values())
def test_box_tracker():
@@ -105,12 +100,8 @@ def test_tracking_box(real_assemblies, real_tracklets):
trackers = mot_tracker.track(bboxes)
trackingutils.fill_tracklets(tracklets, trackers, animals, ind)
assert len(tracklets) == len(tracklets_ref)
- assert [len(tracklet) for tracklet in tracklets.values()] == [
- len(tracklet) for tracklet in tracklets_ref.values()
- ]
- assert all(
- t.shape[1] == 4 for tracklet in tracklets.values() for t in tracklet.values()
- )
+ assert [len(tracklet) for tracklet in tracklets.values()] == [len(tracklet) for tracklet in tracklets_ref.values()]
+ assert all(t.shape[1] == 4 for tracklet in tracklets.values() for t in tracklet.values())
def test_tracking_montblanc(
@@ -127,9 +118,7 @@ def test_tracking_montblanc(
trackers = mot_tracker.track(animals[..., :2])
trackingutils.fill_tracklets(tracklets, trackers, animals, ind)
assert len(tracklets) == len(tracklets_ref)
- assert [len(tracklet) for tracklet in tracklets.values()] == [
- len(tracklet) for tracklet in tracklets_ref.values()
- ]
+ assert [len(tracklet) for tracklet in tracklets.values()] == [len(tracklet) for tracklet in tracklets_ref.values()]
for k, assemblies in tracklets.items():
ref = tracklets_ref[k]
for ind, data in assemblies.items():
@@ -140,12 +129,8 @@ def test_tracking_montblanc(
def test_calc_bboxes_from_keypoints():
# Test bounding box from a single keypoint
xy = np.asarray([[[0, 0, 1]]])
- np.testing.assert_equal(
- trackingutils.calc_bboxes_from_keypoints(xy, 10), [[-10, -10, 10, 10, 1]]
- )
- np.testing.assert_equal(
- trackingutils.calc_bboxes_from_keypoints(xy, 20, 10), [[-10, -20, 30, 20, 1]]
- )
+ np.testing.assert_equal(trackingutils.calc_bboxes_from_keypoints(xy, 10), [[-10, -10, 10, 10, 1]])
+ np.testing.assert_equal(trackingutils.calc_bboxes_from_keypoints(xy, 20, 10), [[-10, -20, 30, 20, 1]])
width = 200
height = width * 2
@@ -160,9 +145,7 @@ def test_calc_bboxes_from_keypoints():
slack = 20
bboxes = trackingutils.calc_bboxes_from_keypoints(xyp, slack=slack)
- np.testing.assert_equal(
- bboxes, [[-slack, -slack, width + slack, height + slack, 0.5]]
- )
+ np.testing.assert_equal(bboxes, [[-slack, -slack, width + slack, height + slack, 0.5]])
offset = 50
bboxes = trackingutils.calc_bboxes_from_keypoints(xyp, offset=offset)
diff --git a/tests/test_trainingsetmanipulation.py b/tests/test_trainingsetmanipulation.py
index d49093dc9a..97749c52d1 100644
--- a/tests/test_trainingsetmanipulation.py
+++ b/tests/test_trainingsetmanipulation.py
@@ -8,22 +8,25 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
-import numpy as np
import os
+
+import numpy as np
import pandas as pd
+import pytest
from conftest import TEST_DATA_DIR
+from skimage import color, io
+
from deeplabcut.generate_training_dataset import (
- read_image_shape_fast,
SplitTrials,
- format_training_data,
format_multianimal_training_data,
- trainingsetmanipulation,
+ format_training_data,
multiple_individuals_trainingsetmanipulation,
+ parse_video_filenames,
+ read_image_shape_fast,
+ trainingsetmanipulation,
)
-
from deeplabcut.utils.auxfun_videos import imread
from deeplabcut.utils.conversioncode import guarantee_multiindex_rows
-from skimage import color, io
def test_read_image_shape_fast(tmp_path):
@@ -58,23 +61,16 @@ def test_format_training_data(monkeypatch):
"read_image_shape_fast",
lambda _: fake_shape,
)
- df = pd.read_hdf(os.path.join(TEST_DATA_DIR, "trimouse_calib.h5")).xs(
- "mus1", level="individuals", axis=1
- )
+ df = pd.read_hdf(os.path.join(TEST_DATA_DIR, "trimouse_calib.h5")).xs("mus1", level="individuals", axis=1)
guarantee_multiindex_rows(df)
train_inds = list(range(10))
_, data = format_training_data(df, train_inds, 12, "")
assert len(data) == len(train_inds)
# Check data comprise path, shape, and xy coordinates
assert all(len(d) == 3 for d in data)
- assert all(
- (d[0].size == 3 and d[0].dtype.char == "U" and d[0][0, -1].endswith(".png"))
- for d in data
- )
+ assert all((d[0].size == 3 and d[0].dtype.char == "U" and d[0][0, -1].endswith(".png")) for d in data)
assert all(np.all(d[1] == np.array(fake_shape)[None]) for d in data)
- assert all(
- (d[2][0, 0].shape[1] == 3 and d[2][0, 0].dtype == np.int64) for d in data
- )
+ assert all((d[2][0, 0].shape[1] == 3 and d[2][0, 0].dtype == np.int64) for d in data)
def test_format_multianimal_training_data(monkeypatch):
@@ -93,8 +89,142 @@ def test_format_multianimal_training_data(monkeypatch):
assert all(isinstance(d, dict) for d in data)
assert all(len(d["image"]) == 3 for d in data)
assert all(np.all(d["size"] == np.array(fake_shape)) for d in data)
- assert all(
- (xy.shape[1] == 3 and np.isfinite(xy).all())
- for d in data
- for xy in d["joints"].values()
+ assert all((xy.shape[1] == 3 and np.isfinite(xy).all()) for d in data for xy in d["joints"].values())
+
+
+@pytest.mark.parametrize(
+ "videos, expected_filenames",
+ [
+ ([], []),
+ (["/data/my-video.mov"], ["my-video"]),
+ (["/data/my-video.mp4", "/data2/my-video.mov"], ["my-video"]),
+ (["/data/my-video.mov", "/data/video2.mov"], ["my-video", "video2"]),
+ (["/a/v1.mov", "/a/v2.mp4", "/b/v1.mov"], ["v1", "v2"]),
+ (["v1.mov", "v2.mov", "v1.mov"], ["v1", "v2"]),
+ (["/a/v1.mp4", "/a/v2.mov", "/b/v2.mov"], ["v1", "v2"]),
+ (["/a/v1.mp4", "/a/v2.mov", "/b/v2.mov", "/b/v3.mp4"], ["v1", "v2", "v3"]),
+ ],
+)
+def test_parse_video_filenames(videos: list[str], expected_filenames: list[str]):
+ filenames = parse_video_filenames(videos)
+ assert filenames == expected_filenames
+
+
+def test_format_training_data_ignores_likelihood_columns(monkeypatch):
+ fake_shape = 3, 480, 640
+ monkeypatch.setattr(
+ trainingsetmanipulation,
+ "read_image_shape_fast",
+ lambda _: fake_shape,
)
+
+ # Base single-animal dataframe (x/y only)
+ df = pd.read_hdf(os.path.join(TEST_DATA_DIR, "trimouse_calib.h5")).xs(
+ "mus1",
+ level="individuals",
+ axis=1,
+ )
+ guarantee_multiindex_rows(df)
+
+ # Add a likelihood column so the layout becomes:
+ # x, y, likelihood, x, y, likelihood, ...
+ new_cols = []
+ new_arrays = []
+
+ coord_level = df.columns.names.index("coords")
+
+ for col in df.columns:
+ new_cols.append(col)
+ new_arrays.append(df[col].to_numpy())
+
+ if col[coord_level] == "y":
+ lik_col = list(col)
+ lik_col[coord_level] = "likelihood"
+ new_cols.append(tuple(lik_col))
+ new_arrays.append(np.ones(len(df), dtype=float))
+
+ df_with_likelihood = pd.DataFrame(
+ np.column_stack(new_arrays),
+ index=df.index,
+ columns=pd.MultiIndex.from_tuples(new_cols, names=df.columns.names),
+ )
+
+ train_inds = list(range(10))
+
+ baseline_train_data, baseline_matlab_data = format_training_data(df, train_inds, 12, "")
+ train_data, matlab_data = format_training_data(df_with_likelihood, train_inds, 12, "")
+
+ # The presence of likelihood columns should not change the formatted result
+ assert len(train_data) == len(baseline_train_data)
+ assert len(matlab_data) == len(baseline_matlab_data)
+
+ for got, expected in zip(train_data, baseline_train_data, strict=False):
+ assert got["image"] == expected["image"]
+ assert got["size"] == expected["size"]
+ assert np.array_equal(got["joints"], expected["joints"])
+
+ for got, expected in zip(matlab_data, baseline_matlab_data, strict=False):
+ assert np.array_equal(got["image"], expected["image"])
+ assert np.array_equal(got["size"], expected["size"])
+ assert np.array_equal(got["joints"][0, 0], expected["joints"][0, 0])
+
+
+def test_merge_annotateddatasets_drops_likelihood_columns(tmp_path):
+ scorer = "testscorer"
+ video_name = "video1"
+ bodyparts = ["nose", "tail"]
+
+ project_path = tmp_path
+ labeled_data_dir = project_path / "labeled-data" / video_name
+ labeled_data_dir.mkdir(parents=True)
+
+ trainingsetfolder_full = project_path / "training-datasets" / "iteration-0"
+ trainingsetfolder_full.mkdir(parents=True)
+
+ # Build a single-animal annotation dataframe with x/y/likelihood columns
+ columns = pd.MultiIndex.from_product(
+ [[scorer], bodyparts, ["x", "y", "likelihood"]],
+ names=["scorer", "bodyparts", "coords"],
+ )
+
+ index = pd.MultiIndex.from_tuples(
+ [("labeled-data", video_name, "img0001.png")],
+ )
+
+ data = np.array([[10.0, 20.0, 0.9, 30.0, 40.0, 0.8]])
+ df = pd.DataFrame(data, index=index, columns=columns)
+
+ input_h5 = labeled_data_dir / f"CollectedData_{scorer}.h5"
+ df.to_hdf(input_h5, key="df_with_missing", mode="w")
+
+ cfg = {
+ "project_path": str(project_path),
+ "video_sets": {str(project_path / "videos" / f"{video_name}.mp4"): {}},
+ "scorer": scorer,
+ "bodyparts": bodyparts,
+ "multianimalproject": False,
+ }
+
+ merged = trainingsetmanipulation.merge_annotateddatasets(
+ cfg,
+ trainingsetfolder_full,
+ )
+
+ # Returned dataframe should not contain likelihood anymore
+ coord_level = "coords" if "coords" in merged.columns.names else merged.columns.names[-1]
+ assert "likelihood" not in merged.columns.get_level_values(coord_level)
+
+ # Saved merged h5 should also not contain likelihood
+ output_h5 = trainingsetfolder_full / f"CollectedData_{scorer}.h5"
+ saved = pd.read_hdf(output_h5)
+
+ coord_level = "coords" if "coords" in saved.columns.names else saved.columns.names[-1]
+ assert "likelihood" not in saved.columns.get_level_values(coord_level)
+
+ # Sanity check: x/y are preserved
+ assert set(saved.columns.get_level_values(coord_level)) == {"x", "y"}
+ output_csv = trainingsetfolder_full / f"CollectedData_{scorer}.csv"
+ saved_csv = pd.read_csv(output_csv, header=[0, 1, 2], index_col=[0, 1, 2])
+
+ coord_level = "coords" if "coords" in saved_csv.columns.names else saved_csv.columns.names[-1]
+ assert "likelihood" not in saved_csv.columns.get_level_values(coord_level)
diff --git a/tests/test_triangulation.py b/tests/test_triangulation.py
index 0fcf2b058c..a1b2fe382c 100644
--- a/tests/test_triangulation.py
+++ b/tests/test_triangulation.py
@@ -11,6 +11,7 @@
import numpy as np
import pandas as pd
import pytest
+
from deeplabcut.pose_estimation_3d import triangulation
@@ -48,9 +49,7 @@ def test_undistort_views(n_view_pairs, is_multi, stereo_params):
df = df.xs("bird1", level="individuals", axis=1)
view_pairs = [(df, df) for _ in range(n_view_pairs)]
- cam_params = {
- f"camera-1-camera-{i}": stereo_params for i in range(2, n_view_pairs + 2)
- }
+ cam_params = {f"camera-1-camera-{i}": stereo_params for i in range(2, n_view_pairs + 2)}
dfs = triangulation._undistort_views(view_pairs, cam_params)
assert len(dfs) == n_view_pairs
assert all(len(pair) == 2 for pair in dfs)
diff --git a/tests/test_video.py b/tests/test_video.py
index 02b70e828f..915933cf6c 100644
--- a/tests/test_video.py
+++ b/tests/test_video.py
@@ -9,10 +9,11 @@
# Licensed under GNU Lesser General Public License v3.0
#
import os
+
import pytest
from conftest import TEST_DATA_DIR
-from deeplabcut.utils.auxfun_videos import VideoWriter
+from deeplabcut.utils.auxfun_videos import VideoWriter
POS_FRAMES = 1 # Equivalent to cv2.CAP_PROP_POS_FRAMES
@@ -57,9 +58,7 @@ def test_reader_wrong_fps(video_clip):
def test_reader_duration(video_clip):
- assert video_clip.calc_duration() == pytest.approx(
- video_clip.calc_duration(robust=False), abs=0.01
- )
+ assert video_clip.calc_duration() == pytest.approx(video_clip.calc_duration(robust=False), abs=0.01)
def test_reader_set_frame(video_clip):
@@ -93,9 +92,7 @@ def test_writer_bbox(video_clip):
assert video_clip.get_bbox(relative=True) == (0, 1, 0, 1)
-@pytest.mark.parametrize(
- "start, end", [(0, 10), ("0:0", "0:10"), ("00:00:00", "00:00:10")]
-)
+@pytest.mark.parametrize("start, end", [(0, 10), ("0:0", "0:10"), ("00:00:00", "00:00:10")])
def test_writer_shorten_invalid_timestamps(video_clip, start, end):
with pytest.raises(ValueError):
video_clip.shorten(start, end)
diff --git a/tests/tools/conftest.py b/tests/tools/conftest.py
new file mode 100644
index 0000000000..ef9b94e067
--- /dev/null
+++ b/tests/tools/conftest.py
@@ -0,0 +1,39 @@
+from __future__ import annotations
+
+import importlib
+import sys
+from pathlib import Path
+from types import ModuleType
+
+import pytest
+
+
+def _repo_root() -> Path:
+ # tests/tools/conftest.py -> repo root is 2 levels up
+ return Path(__file__).resolve().parents[2]
+
+
+def load_selector_module() -> ModuleType:
+ root = _repo_root()
+ tools_dir = root / "tools"
+ init_file = tools_dir / "__init__.py"
+ selector_path = tools_dir / "test_selector.py"
+
+ if not selector_path.exists():
+ raise FileNotFoundError(f"Selector script not found: {selector_path}")
+
+ if not init_file.exists():
+ raise FileNotFoundError(f"tools package marker not found: {init_file}")
+
+ # Ensure repo root is importable so `tools.test_selector` resolves as a package import.
+ root_str = str(root)
+ if root_str not in sys.path:
+ sys.path.insert(0, root_str)
+
+ return importlib.import_module("tools.test_selector")
+
+
+@pytest.fixture(scope="session")
+def selector():
+ """Imported selector module (tools.test_selector)."""
+ return load_selector_module()
diff --git a/tests/tools/docs_and_notebooks_checks/test_check_contracts.py b/tests/tools/docs_and_notebooks_checks/test_check_contracts.py
new file mode 100644
index 0000000000..1abe6c47cb
--- /dev/null
+++ b/tests/tools/docs_and_notebooks_checks/test_check_contracts.py
@@ -0,0 +1,559 @@
+from __future__ import annotations
+
+import importlib.util
+import json
+import os
+import subprocess
+from collections.abc import Callable
+from datetime import date, datetime, timezone
+from pathlib import Path
+from types import ModuleType
+
+import pytest
+
+
+@pytest.fixture(autouse=True)
+def no_github_step_summary(monkeypatch):
+ monkeypatch.delenv("GITHUB_STEP_SUMMARY", raising=False)
+
+
+# -----------------------------
+# Module loader (tools/ is not necessarily a package)
+# -----------------------------
+def load_tool_module() -> ModuleType:
+ repo_root = Path(__file__).resolve().parents[3]
+ tool_path = repo_root / "tools" / "docs_and_notebooks_check.py"
+ assert tool_path.exists(), f"Missing tool: {tool_path}"
+
+ spec = importlib.util.spec_from_file_location("docs_and_notebooks_check", tool_path)
+ assert spec and spec.loader
+ mod = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(mod) # type: ignore[attr-defined]
+ return mod
+
+
+@pytest.fixture(scope="session")
+def tool() -> ModuleType:
+ return load_tool_module()
+
+
+def _write_default_cfg(repo: Path, include: list[str]) -> Path:
+ cfg_path = repo / "tools" / "docs_and_notebooks_report_config.yml"
+ cfg_path.parent.mkdir(parents=True, exist_ok=True)
+ cfg_path.write_text(
+ "version: 1\n"
+ "scan:\n"
+ " include:\n" + "".join(f" - {pat}\n" for pat in include) + " exclude: []\n"
+ "policy:\n"
+ " warn_if_content_older_than_days: 365\n"
+ " warn_if_verified_older_than_days: 365\n"
+ " missing_last_verified_is_warning: true\n"
+ " fail_on_scan_errors: false\n"
+ " require_metadata: []\n"
+ " require_recent_verification: []\n"
+ " require_notebook_normalized: []\n",
+ encoding="utf-8",
+ )
+ return cfg_path
+
+
+# -----------------------------
+# Git helpers for a temp repo
+# -----------------------------
+def _run(cmd: list[str], cwd: Path, env: dict | None = None) -> subprocess.CompletedProcess:
+ return subprocess.run(cmd, cwd=str(cwd), env=env, capture_output=True, text=True, check=True)
+
+
+def _git_init(repo: Path) -> None:
+ _run(["git", "init"], repo)
+ _run(["git", "config", "user.email", "ci@example.com"], repo)
+ _run(["git", "config", "user.name", "CI"], repo)
+
+
+def _git_commit(repo: Path, message: str, when_iso: str) -> None:
+ env = os.environ.copy()
+ env["GIT_AUTHOR_DATE"] = when_iso
+ env["GIT_COMMITTER_DATE"] = when_iso
+ _run(["git", "add", "-A"], repo, env=env)
+ _run(["git", "commit", "-m", message], repo, env=env)
+
+
+def _write(repo: Path, rel: str, content: str) -> None:
+ p = repo / rel
+ p.parent.mkdir(parents=True, exist_ok=True)
+ p.write_text(content, encoding="utf-8")
+
+
+# -----------------------------
+# Shared fixtures
+# -----------------------------
+@pytest.fixture
+def repo(tmp_path: Path) -> Path:
+ repo = tmp_path / "repo"
+ repo.mkdir()
+ _git_init(repo)
+ return repo
+
+
+@pytest.fixture
+def cfg(tool) -> Callable[..., object]:
+ def _make_cfg(
+ include: list[str],
+ exclude: list[str] | None = None,
+ **policy_overrides,
+ ):
+ policy = tool.PolicyConfig(**policy_overrides)
+ return tool.ToolConfig(
+ version=1,
+ scan=tool.ScanConfig(include=include, exclude=exclude or []),
+ policy=policy,
+ )
+
+ return _make_cfg
+
+
+# -----------------------------
+# Contract tests
+# -----------------------------
+def test_marker_constants_exist(tool):
+ assert hasattr(tool, "META_COMMIT_MARKER")
+ assert hasattr(tool, "SUGGESTED_TAGGED_COMMIT")
+ assert tool.META_COMMIT_MARKER in tool.SUGGESTED_TAGGED_COMMIT
+
+
+def test_schema_contract_fields(tool):
+ # DLCMeta must have new fields and must NOT have old last_git_updated
+ meta = tool.DLCMeta()
+ assert hasattr(meta, "last_content_updated")
+ assert hasattr(meta, "last_metadata_updated")
+ assert hasattr(meta, "last_verified")
+ assert hasattr(meta, "verified_for")
+ assert not hasattr(meta, "last_git_updated")
+
+
+def test_git_content_date_skips_meta_commits(tool, repo: Path):
+ """
+ Contract: last_content_updated is computed from git history excluding metadata commits.
+ """
+ rel = "docs/page.md"
+ _write(repo, rel, "# hello\n")
+ _git_commit(repo, "docs: initial content", "2020-01-01T12:00:00+00:00")
+
+ # meta-only rewrite (simulated) committed with marker
+ _write(
+ repo,
+ rel,
+ "---\ndeeplabcut:\n last_metadata_updated: 2026-03-01\n---\n# hello\n",
+ )
+ _git_commit(
+ repo,
+ f"chore(meta): update {tool.META_COMMIT_MARKER}",
+ "2026-03-01T12:00:00+00:00",
+ )
+
+ # raw touched date = 2026-03-01
+ touched = tool.git_last_touched(repo, rel)
+ assert touched == date(2026, 3, 1)
+
+ # content updated date should skip marker commit => 2020-01-01
+ content_date, used_fallback = tool.git_last_content_updated(repo, rel)
+ assert content_date == date(2020, 1, 1)
+ assert used_fallback is False
+
+
+def test_git_content_date_fallback_when_only_meta_commits(tool, repo: Path):
+ """
+ If all commits touching the file are meta-marker commits, we fall back to git_last_touched
+ and flag used_fallback=True.
+ """
+ rel = "docs/page.md"
+ _write(repo, rel, "---\ndeeplabcut:\n notes: hi\n---\n")
+ _git_commit(
+ repo,
+ f"chore(meta): init {tool.META_COMMIT_MARKER}",
+ "2026-03-01T12:00:00+00:00",
+ )
+
+ content_date, used_fallback = tool.git_last_content_updated(repo, rel)
+ assert content_date == date(2026, 3, 1)
+ assert used_fallback is True
+
+
+def test_scan_is_read_only(tool, repo: Path, cfg):
+ """
+ Contract: report/check (scan_files) must be read-only.
+ We validate by asserting file content does not change.
+ """
+ rel = "docs/page.md"
+ orig = "---\ndeeplabcut:\n last_verified: 2020-01-01\n---\n# hello\n"
+ _write(repo, rel, orig)
+ _git_commit(repo, "docs: add page", "2020-01-01T12:00:00+00:00")
+
+ tool_cfg = cfg(include=[rel])
+
+ before = (repo / rel).read_text(encoding="utf-8")
+ records = tool.scan_files(repo, tool_cfg, targets=[r".\docs\page.md"])
+ after = (repo / rel).read_text(encoding="utf-8")
+
+ assert before == after
+ assert len(records) == 1
+ assert records[0].path == rel
+ assert records[0].kind == "md"
+
+
+def test_scan_targets_support_directory_and_glob(tool, repo: Path, cfg):
+ rel_a = "docs/gui/napari/basic_usage.md"
+ rel_b = "docs/gui/napari/advanced_usage.md"
+ rel_c = "docs/other/overview.md"
+
+ _write(repo, rel_a, "# a\n")
+ _write(repo, rel_b, "# b\n")
+ _write(repo, rel_c, "# c\n")
+ _git_commit(repo, "docs: add pages", "2020-01-01T12:00:00+00:00")
+
+ tool_cfg = cfg(include=["docs/**/*.md"])
+
+ # Directory selector
+ recs_dir = tool.scan_files(repo, tool_cfg, targets=["docs/gui/napari/"])
+ paths_dir = sorted(r.path for r in recs_dir)
+ assert set(paths_dir) == {rel_a, rel_b}
+
+ # Glob selector
+ recs_glob = tool.scan_files(repo, tool_cfg, targets=["docs/gui/napari/*.md"])
+ paths_glob = sorted(r.path for r in recs_glob)
+ assert set(paths_glob) == {rel_a, rel_b}
+
+ # Recursive glob selector
+ recs_recursive = tool.scan_files(repo, tool_cfg, targets=["docs/**/*.md"])
+ paths_recursive = sorted(r.path for r in recs_recursive)
+ assert set(paths_recursive) == {rel_a, rel_b, rel_c}
+
+
+def test_validate_requested_targets_treats_dot_slash_as_unmatched(tool, repo: Path, cfg):
+ _write(repo, "docs/page.md", "# hello\n")
+ tool_cfg = cfg(include=["docs/**/*.md"])
+
+ matched, unmatched = tool.validate_requested_targets(repo, tool_cfg, ["./"])
+ assert matched == []
+ assert unmatched == ["./"]
+
+
+def test_validate_requested_targets_treats_empty_like_unmatched(tool, repo: Path, cfg):
+ _write(repo, "docs/page.md", "# hello\n")
+ tool_cfg = cfg(include=["docs/**/*.md"])
+
+ matched, unmatched = tool.validate_requested_targets(repo, tool_cfg, ["", " "])
+ assert matched == []
+ assert unmatched == ["", " "]
+
+
+def test_validate_requested_targets_reports_mixed_valid_and_invalid_targets(tool, repo: Path, cfg):
+ rel = "docs/page.md"
+ _write(repo, rel, "# hello\n")
+ tool_cfg = cfg(include=["docs/**/*.md"])
+
+ matched, unmatched = tool.validate_requested_targets(repo, tool_cfg, [rel, "./"])
+ assert rel in matched
+ assert unmatched == ["./"]
+
+
+def test_scan_files_with_invalid_only_targets_matches_nothing(tool, repo: Path, cfg):
+ tool_cfg = cfg(include=["docs/**/*.md"])
+ records = tool.scan_files(repo, tool_cfg, targets=["./"])
+ assert records == []
+
+
+def test_validate_requested_targets_reports_unmatched(tool, repo: Path, cfg):
+ rel_a = "docs/gui/napari/basic_usage.md"
+ rel_b = "docs/gui/napari/advanced_usage.md"
+
+ _write(repo, rel_a, "# a\n")
+ _write(repo, rel_b, "# b\n")
+ _git_commit(repo, "docs: add pages", "2020-01-01T12:00:00+00:00")
+
+ tool_cfg = cfg(include=["docs/**/*.md"])
+
+ matched, unmatched = tool.validate_requested_targets(
+ repo,
+ tool_cfg,
+ targets=[
+ r".\docs\gui\napari\basic_usage.md",
+ "docs/gui/napari/",
+ "docs/**/*.md",
+ "docs/missing/",
+ "examples/**/*.ipynb",
+ ],
+ )
+
+ assert matched == sorted([rel_a, rel_b])
+ assert unmatched == ["docs/missing/", "examples/**/*.ipynb"]
+
+
+def test_update_requires_ack_when_write(tool, repo: Path, cfg):
+ """
+ Contract: write mode should refuse unless --ack-meta-commit-marker is provided.
+ """
+ rel = "docs/page.md"
+ _write(repo, rel, "# hello\n")
+ _git_commit(repo, "docs: initial content", "2020-01-01T12:00:00+00:00")
+
+ tool_cfg = cfg(include=[rel])
+
+ # should refuse to write without ack
+ with pytest.raises(SystemExit):
+ tool.update_files(
+ repo_root=repo,
+ cfg=tool_cfg,
+ targets=[rel],
+ write=True,
+ set_content_date_from_git=True,
+ set_last_verified=None,
+ set_verified_for=None,
+ ack_meta_commit_marker=False,
+ )
+
+
+def test_update_set_content_date_from_git_only_changes_that_field(tool, repo: Path, cfg):
+ """
+ Contract: update --set-content-date-from-git only sets last_content_updated
+ (plus last_metadata_updated when writing),
+ does NOT override last_verified/verified_for unless explicitly provided.
+ """
+ rel = "docs/page.md"
+ initial = "---\ndeeplabcut:\n last_verified: 2020-02-02\n verified_for: 3.0.0rc1\n---\n# hello\n"
+ _write(repo, rel, initial)
+ _git_commit(repo, "docs: initial content", "2020-01-01T12:00:00+00:00")
+
+ tool_cfg = cfg(include=[rel])
+
+ records = tool.update_files(
+ repo_root=repo,
+ cfg=tool_cfg,
+ targets=[rel],
+ write=True,
+ set_content_date_from_git=True,
+ set_last_verified=None,
+ set_verified_for=None,
+ ack_meta_commit_marker=True,
+ )
+ assert len(records) == 1
+
+ # Read back and confirm verified fields unchanged
+ text = (repo / rel).read_text(encoding="utf-8")
+ fm, _body, _ = tool.read_md_frontmatter(text)
+ assert isinstance(fm, dict) and tool.DLC_NAMESPACE in fm
+ meta = fm[tool.DLC_NAMESPACE]
+
+ assert meta["last_verified"] == "2020-02-02"
+ assert meta["verified_for"] == "3.0.0rc1"
+
+ # last_content_updated should reflect git content date (2020-01-01)
+ assert meta["last_content_updated"] == "2020-01-01"
+
+ # last_metadata_updated should exist because we wrote
+ assert "last_metadata_updated" in meta
+
+
+def test_update_set_verified_fields_only_changes_verified(tool, repo: Path, cfg):
+ """
+ Contract: update with --set-last-verified / --set-verified-for changes only those fields
+ (plus last_metadata_updated if writing), and does not set last_content_updated unless requested.
+ """
+ rel = "docs/page.md"
+ initial = "---\ndeeplabcut:\n last_content_updated: 2000-01-01\n---\n# hello\n"
+ _write(repo, rel, initial)
+ _git_commit(repo, "docs: initial content", "2020-01-01T12:00:00+00:00")
+
+ tool_cfg = cfg(include=[rel])
+
+ records = tool.update_files(
+ repo_root=repo,
+ cfg=tool_cfg,
+ targets=[rel],
+ write=True,
+ set_content_date_from_git=False,
+ set_last_verified=date(2026, 3, 5),
+ set_verified_for="3.0.0rc13",
+ ack_meta_commit_marker=True,
+ )
+ assert len(records) == 1
+
+ text = (repo / rel).read_text(encoding="utf-8")
+ fm, _body, _ = tool.read_md_frontmatter(text)
+ meta = fm[tool.DLC_NAMESPACE]
+
+ # Verified fields updated
+ assert meta["last_verified"] == "2026-03-05"
+ assert meta["verified_for"] == "3.0.0rc13"
+
+ # last_content_updated remains whatever it was (not overwritten)
+ assert meta["last_content_updated"] == "2000-01-01"
+
+
+def test_normalize_is_explicit_and_marks_would_change(tool, repo: Path, cfg):
+ """
+ Contract: normalize is separate and explicit; in dry-run it should mark would_change
+ if notebook is not already in canonical nbformat output.
+ """
+ rel = "docs/nbs/nb.ipynb"
+ # Minimal notebook JSON but not in nbformat canonical formatting (indent/newline differences)
+ raw = '{\n "cells": [],\n "metadata": {},\n "nbformat": 4,\n "nbformat_minor": 5\n}\n'
+ _write(repo, rel, raw)
+ _git_commit(repo, "docs: add notebook", "2020-01-01T12:00:00+00:00")
+
+ tool_cfg = cfg(include=[rel])
+
+ # Dry-run normalize: should set would_change True if not normalized
+ records = tool.normalize_notebooks(
+ repo_root=repo,
+ cfg=tool_cfg,
+ targets=[rel],
+ write=False,
+ ack_meta_commit_marker=True,
+ )
+ assert len(records) == 1
+ assert records[0].kind == "ipynb"
+ # may be True depending on canonical formatting differences
+ assert records[0].would_change
+
+
+def test_write_outputs_contract(tool, repo: Path, cfg, tmp_path: Path):
+ """
+ Contract: write_outputs creates both JSON and Markdown files and JSON is schema-valid.
+ """
+ rel = "docs/page.md"
+ _write(repo, rel, "# hello\n")
+ _git_commit(repo, "docs: initial content", "2020-01-01T12:00:00+00:00")
+
+ tool_cfg = cfg(include=[rel])
+ records = tool.scan_files(repo, tool_cfg, targets=[rel])
+
+ report = tool.Report(
+ generated_at=datetime.now(timezone.utc),
+ repo_root=str(repo),
+ config_path="in-memory",
+ totals=tool.summarize(records),
+ records=records,
+ )
+
+ out_dir = tmp_path / "out"
+ json_path, md_path = tool.write_outputs(report, tool_cfg, out_dir)
+
+ assert json_path.exists()
+ assert md_path.exists()
+
+ payload = json.loads(json_path.read_text(encoding="utf-8"))
+ assert payload["schema_version"] == tool.SCHEMA_VERSION
+ assert "records" in payload and isinstance(payload["records"], list)
+ assert md_path.read_text(encoding="utf-8").startswith("#")
+
+
+def test_notebook_missing_dlc_namespace_warns_missing_metadata(tool, repo: Path, cfg):
+ rel = "docs/nbs/nb.ipynb"
+ # Valid minimal notebook, but no "deeplabcut" namespace under metadata
+ nb = '{\n "cells": [],\n "metadata": {},\n "nbformat": 4,\n "nbformat_minor": 5\n}\n'
+ _write(repo, rel, nb)
+ _git_commit(repo, "docs: add notebook", "2020-01-01T12:00:00+00:00")
+
+ tool_cfg = cfg(include=[rel])
+
+ records = tool.scan_files(repo, tool_cfg, targets=[rel])
+ assert len(records) == 1
+ r = records[0]
+ assert r.kind == "ipynb"
+ assert "missing_metadata" in r.warnings
+ assert r.meta is None
+
+
+def test_notebook_invalid_dlc_namespace_warns_invalid_metadata(tool, repo: Path, cfg):
+ rel = "docs/nbs/nb.ipynb"
+ # deeplabcut namespace exists but is invalid: last_verified must be a date
+ nb = (
+ "{\n"
+ ' "cells": [],\n'
+ ' "metadata": {\n'
+ ' "deeplabcut": {\n'
+ ' "last_verified": "not-a-date"\n'
+ " }\n"
+ " },\n"
+ ' "nbformat": 4,\n'
+ ' "nbformat_minor": 5\n'
+ "}\n"
+ )
+ _write(repo, rel, nb)
+ _git_commit(repo, "docs: add notebook with bad meta", "2020-01-01T12:00:00+00:00")
+
+ tool_cfg = cfg(include=[rel])
+
+ records = tool.scan_files(repo, tool_cfg, targets=[rel])
+ assert len(records) == 1
+ r = records[0]
+ assert r.kind == "ipynb"
+ assert "invalid_metadata" in r.warnings
+ assert r.meta is None
+
+
+def test_main_prints_matched_files_and_fails_on_unmatched_targets(tool, repo: Path, monkeypatch, capsys):
+ rel = "docs/gui/napari/basic_usage.md"
+ _write(repo, rel, "# hello\n")
+ _git_commit(repo, "docs: add page", "2020-01-01T12:00:00+00:00")
+
+ cfg_path = _write_default_cfg(repo, include=["docs/**/*.md"])
+
+ monkeypatch.chdir(repo)
+
+ rc = tool.main(
+ [
+ "--config",
+ str(cfg_path),
+ "--no-step-summary",
+ "report",
+ "--targets",
+ r".\docs\gui\napari\basic_usage.md",
+ "docs/missing/",
+ ]
+ )
+
+ out = capsys.readouterr().out
+ assert rc == 2
+ assert "Matched 1 file(s) from --targets:" in out
+ assert f"- {rel}" in out
+ assert "Unmatched --targets:" in out
+ assert "- docs/missing/" in out
+
+
+def test_main_prints_matched_files_for_valid_targets(tool, repo: Path, monkeypatch, capsys):
+ rel = "docs/gui/napari/basic_usage.md"
+ _write(repo, rel, "# hello\n")
+ _git_commit(repo, "docs: add page", "2020-01-01T12:00:00+00:00")
+ cfg_path = _write_default_cfg(repo, include=["docs/**/*.md"])
+
+ monkeypatch.chdir(repo)
+
+ rc = tool.main(
+ [
+ "--config",
+ str(cfg_path),
+ "--no-step-summary",
+ "report",
+ "--targets",
+ "docs/gui/napari/",
+ ]
+ )
+
+ out = capsys.readouterr().out
+ assert rc == 0
+ assert "Matched 1 file(s) from --targets:" in out
+ assert f"- {rel}" in out
+ assert "Report generated:" in out
+
+
+def test_main_returns_2_for_invalid_target_selector(tool, repo: Path, monkeypatch):
+ _write(repo, "docs/page.md", "# hello\n")
+ _git_commit(repo, "docs: add page", "2020-01-01T12:00:00+00:00")
+ cfg_path = _write_default_cfg(repo, include=["docs/**/*.md"])
+
+ monkeypatch.chdir(repo)
+
+ rc = tool.main(["--config", str(cfg_path), "--no-step-summary", "report", "--targets", "./"])
+ assert rc == 2
diff --git a/tests/tools/test_selector/test_selector_decision.py b/tests/tools/test_selector/test_selector_decision.py
new file mode 100644
index 0000000000..c45ce63d7b
--- /dev/null
+++ b/tests/tools/test_selector/test_selector_decision.py
@@ -0,0 +1,341 @@
+# tests/tools/test_selector/test_selector_decision.py
+from __future__ import annotations
+
+from pathlib import Path
+
+import pytest
+from pydantic import ValidationError
+
+from tools.test_selector_config import (
+ CATEGORY_RULES,
+ CategoryRule,
+ prefix,
+ validate_category_rules,
+)
+
+
+def assert_lanes(res, *, skip=False, docs=False, fast=False, full=False):
+ assert res.lanes.skip is skip
+ assert res.lanes.docs is docs
+ assert res.lanes.fast is fast
+ assert res.lanes.full is full
+
+
+def test_fail_safe_on_empty_changes(selector):
+ res = selector.decide([])
+
+ assert_lanes(res, full=True)
+ assert "no_changed_files_or_diff_unavailable" in res.reasons
+ assert res.lane_reasons["full"] == ["no_changed_files_or_diff_unavailable"]
+
+
+def test_docs_only(selector):
+ files = ["docs/index.md", "docs/guide/intro.md", "_config.yml"]
+ res = selector.decide(files)
+
+ assert_lanes(res, docs=True)
+ assert res.pytest_paths == []
+ assert res.functional_scripts == []
+ assert "category:docs" in res.reasons
+ assert res.lane_reasons["docs"] == ["category:docs"]
+
+
+def test_full_suite_trigger_pyproject_preserves_docs_lane(selector):
+ files = ["pyproject.toml", "docs/index.md"]
+ res = selector.decide(files)
+
+ assert_lanes(res, full=True, docs=True)
+ assert "full_suite_trigger" in res.reasons
+ assert "category:docs" in res.reasons
+ assert res.lane_reasons["full"] == [
+ "full_suite_trigger",
+ "full_suite_trigger_count:1",
+ ]
+
+
+def test_full_suite_trigger_tests_folder(selector):
+ files = ["tests/test_something.py"]
+ res = selector.decide(files)
+
+ assert_lanes(res, full=True)
+ assert "full_suite_trigger" in res.reasons
+ assert res.lane_reasons["full"] == [
+ "full_suite_trigger",
+ "full_suite_trigger_count:1",
+ ]
+
+
+def test_fast_core(selector):
+ files = ["deeplabcut/core/some_module.py"]
+ res = selector.decide(files)
+
+ assert_lanes(res, fast=True)
+
+ # core rule should include these paths (subset check)
+ assert "tests/core/" in res.pytest_paths
+ assert "tests/utils/" in res.pytest_paths
+ # assert res.functional_scripts == [] # not empty, but we don't need to specify exact scripts here
+
+ assert "category:core" in res.reasons
+ assert res.lane_reasons["fast"] == ["category:core"]
+
+ # Provenance should attribute selected pytest roots to the core category.
+ assert "tests/core/" in res.provenance.pytest
+ assert res.provenance.pytest["tests/core/"] == ["core"]
+
+
+def test_fast_multianimal_includes_functional(selector):
+ files = ["deeplabcut/pose_estimation_pytorch/multianimal/foo.py"]
+ res = selector.decide(files)
+
+ assert_lanes(res, fast=True)
+
+ assert "tests/test_predict_multianimal.py" in res.pytest_paths
+ assert "examples/testscript_tensorflow_multi_animal.py" in res.functional_scripts
+
+ assert "multianimal" in res.provenance.pytest["tests/test_predict_multianimal.py"]
+ assert "multianimal" in res.provenance.scripts["examples/testscript_tensorflow_multi_animal.py"]
+
+
+def test_fast_ci_workflows_uses_full_suite(selector):
+ files = [".github/workflows/ci.yml"]
+ res = selector.decide(files)
+
+ assert_lanes(res, full=True)
+
+
+def test_no_category_matched_is_full(selector):
+ files = ["some/unknown/place/file.xyz"]
+ res = selector.decide(files)
+
+ assert_lanes(res, full=True)
+ assert "no_category_matched" in res.reasons
+ assert res.lane_reasons["full"] == ["no_category_matched"]
+
+
+def test_docs_and_core_run_both_lanes(selector):
+ files = ["docs/index.md", "deeplabcut/core/a.py"]
+ res = selector.decide(files)
+
+ assert_lanes(res, docs=True, fast=True)
+ assert "category:docs" in res.reasons
+ assert "category:core" in res.reasons
+
+ assert res.lane_reasons["docs"] == ["category:docs"]
+ assert res.lane_reasons["fast"] == ["category:core"]
+
+ assert "tests/core/" in res.pytest_paths
+ assert "tests/utils/" in res.pytest_paths
+
+
+def test_dedup_and_sorted_outputs(selector):
+ # Force overlap: core includes tests/test_auxiliaryfunctions.py and
+ # ci_tools contributes tests/tools/. Outputs should stay deduped and sorted.
+ files = [
+ "deeplabcut/core/a.py",
+ "tools/whatever.py",
+ ]
+ res = selector.decide(files)
+
+ assert_lanes(res, fast=True)
+
+ # No duplicates
+ assert len(res.pytest_paths) == len(set(res.pytest_paths))
+ assert len(res.functional_scripts) == len(set(res.functional_scripts))
+
+ # Sorted
+ assert res.pytest_paths == sorted(res.pytest_paths)
+ assert res.functional_scripts == sorted(res.functional_scripts)
+
+
+# ----------------------------
+# Validation of category rules
+# ----------------------------
+def test_category_rule_rejects_empty_name():
+ with pytest.raises(ValidationError, match="Rule name must not be empty"):
+ CategoryRule(
+ name="",
+ match_any=[prefix("docs/")],
+ )
+
+
+def test_category_rule_rejects_invalid_name():
+ with pytest.raises(ValidationError, match=r"Rule name must match"):
+ CategoryRule(
+ name="docs-rule",
+ match_any=[prefix("docs/")],
+ )
+
+
+def test_category_rule_requires_non_empty_match_any():
+ with pytest.raises(ValidationError, match="at least 1 item|at least one predicate"):
+ CategoryRule(
+ name="docs",
+ match_any=[],
+ )
+
+
+def test_category_rule_rejects_non_callable_match_any():
+ with pytest.raises(ValidationError, match="callable"):
+ CategoryRule(
+ name="docs",
+ match_any=[123], # type: ignore[list-item]
+ )
+
+
+@pytest.mark.parametrize(
+ "field_name,bad_value",
+ [
+ ("pytest_paths", "/absolute/path.py"),
+ ("pytest_paths", "../escape.py"),
+ ("functional_scripts", "/absolute/script.py"),
+ ("functional_scripts", "../escape_script.py"),
+ ],
+)
+def test_category_rule_rejects_invalid_repo_relative_paths(field_name, bad_value):
+ kwargs = {
+ "name": "docs",
+ "match_any": [prefix("docs/")],
+ "pytest_paths": [],
+ "functional_scripts": [],
+ }
+ kwargs[field_name] = [bad_value]
+
+ with pytest.raises(ValidationError, match="repo-relative|path traversal|absolute path"):
+ CategoryRule(**kwargs)
+
+
+def test_validate_category_rules_rejects_duplicate_names():
+ rules = [
+ CategoryRule(name="docs", match_any=[prefix("docs/")]),
+ CategoryRule(name="docs", match_any=[prefix("more-docs/")]),
+ ]
+
+ with pytest.raises(ValueError, match="Duplicate CategoryRule name"):
+ validate_category_rules(rules)
+
+
+def test_lint_only_changes_select_skip_lane(selector):
+ files = [".pre-commit-config.yaml"]
+ res = selector.decide(files)
+
+ assert_lanes(res, skip=True)
+ assert res.pytest_paths == []
+ assert res.functional_scripts == []
+
+ assert "lint_only" in res.reasons
+ assert "skip" in res.lane_reasons
+ assert "lint_only" in res.lane_reasons["skip"]
+
+
+def test_validate_selected_paths_escalates_to_full_on_missing(selector, tmp_path: Path):
+ # Build a minimal repo dir with none of the selected paths present.
+ repo = tmp_path / "repo"
+ repo.mkdir()
+
+ res = selector.SelectorResult(
+ lanes=selector.LaneSelection(fast=True),
+ pytest_paths=["tests/does_not_exist.py"],
+ functional_scripts=["examples/missing_script.py"],
+ provenance=selector.SelectionProvenance(
+ pytest={"tests/does_not_exist.py": ["core"]},
+ scripts={"examples/missing_script.py": ["core"]},
+ ),
+ reasons=["category:core"],
+ changed_files=["deeplabcut/core/foo.py"],
+ lane_reasons={"fast": ["category:core"]},
+ )
+
+ out = selector.validate_selected_paths(res, repo)
+
+ assert out.lanes.fast is False
+ assert out.lanes.full is True
+
+ assert out.pytest_paths == []
+ assert out.functional_scripts == []
+ assert out.provenance.pytest == {}
+ assert out.provenance.scripts == {}
+
+ assert "missing_selected_paths" in out.reasons
+ assert any(r.startswith("pytest:tests/does_not_exist.py") for r in out.reasons)
+ assert any(r.startswith("script:examples/missing_script.py") for r in out.reasons)
+
+ assert "full" in out.lane_reasons
+
+
+def test_validate_selected_paths_keeps_fast_when_paths_exist(selector, tmp_path: Path):
+ repo = tmp_path / "repo"
+ (repo / "tests").mkdir(parents=True)
+ (repo / "examples").mkdir(parents=True)
+
+ (repo / "tests" / "test_ok.py").write_text("def test_ok(): pass\n")
+ (repo / "examples" / "script_ok.py").write_text("print('ok')\n")
+
+ res = selector.SelectorResult(
+ lanes=selector.LaneSelection(fast=True),
+ pytest_paths=["tests/test_ok.py"],
+ functional_scripts=["examples/script_ok.py"],
+ provenance=selector.SelectionProvenance(
+ pytest={"tests/test_ok.py": ["core"]},
+ scripts={"examples/script_ok.py": ["core"]},
+ ),
+ reasons=["category:core"],
+ changed_files=["deeplabcut/core/foo.py"],
+ lane_reasons={"fast": ["category:core"]},
+ )
+
+ out = selector.validate_selected_paths(res, repo)
+
+ assert out.lanes.fast is True
+ assert out.lanes.full is False
+ assert out.pytest_paths == ["tests/test_ok.py"]
+ assert out.functional_scripts == ["examples/script_ok.py"]
+
+
+# --------------------------------------
+# Current config validity & sanity checks
+# --------------------------------------
+
+
+def test_current_category_rules_are_typed_models():
+ assert CATEGORY_RULES
+ assert all(isinstance(rule, CategoryRule) for rule in CATEGORY_RULES)
+
+
+def test_current_category_rules_pass_cross_rule_validation():
+ validate_category_rules(CATEGORY_RULES)
+
+
+def test_current_category_rule_names_are_unique():
+ names = [rule.name for rule in CATEGORY_RULES]
+ assert len(names) == len(set(names))
+
+
+def test_current_category_rules_have_matchers():
+ assert all(rule.match_any for rule in CATEGORY_RULES)
+
+
+def test_required_category_rules_exist():
+ names = {rule.name for rule in CATEGORY_RULES}
+ assert "docs" in names
+ assert "core" in names
+
+
+def test_docs_rule_exists_once():
+ docs_rules = [rule for rule in CATEGORY_RULES if rule.name == "docs"]
+ assert len(docs_rules) == 1
+
+
+def test_current_selected_paths_exist():
+ repo_root = Path(__file__).resolve().parents[3]
+ missing = []
+
+ for rule in CATEGORY_RULES:
+ for path in rule.pytest_paths:
+ if not (repo_root / path).exists():
+ missing.append((rule.name, "pytest", path))
+ for path in rule.functional_scripts:
+ if not (repo_root / path).exists():
+ missing.append((rule.name, "script", path))
+
+ assert missing == []
diff --git a/tests/tools/test_selector/test_selector_validation.py b/tests/tools/test_selector/test_selector_validation.py
new file mode 100644
index 0000000000..41d2bfe912
--- /dev/null
+++ b/tests/tools/test_selector/test_selector_validation.py
@@ -0,0 +1,172 @@
+from __future__ import annotations
+
+import json
+import subprocess
+from pathlib import Path
+
+import pytest
+
+
+# -----------------
+# Git helpers
+# -----------------
+def _git(repo: Path, *args: str) -> str:
+ proc = subprocess.run(
+ ["git", *args],
+ cwd=repo,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ if proc.returncode != 0:
+ raise RuntimeError(f"git {' '.join(args)} failed: {proc.stderr.strip()}")
+ return proc.stdout.strip()
+
+
+def _init_repo(tmp_path: Path) -> Path:
+ repo = tmp_path / "repo"
+ repo.mkdir()
+ _git(repo, "init")
+ _git(repo, "config", "user.name", "Test User")
+ _git(repo, "config", "user.email", "test@example.com")
+ return repo
+
+
+def _commit_file(repo: Path, relpath: str, content: str, message: str) -> str:
+ path = repo / relpath
+ path.parent.mkdir(parents=True, exist_ok=True)
+ path.write_text(content, encoding="utf-8")
+ _git(repo, "add", relpath)
+ _git(repo, "commit", "-m", message)
+ return _git(repo, "rev-parse", "HEAD")
+
+
+def _write_event(tmp_path: Path, payload: dict) -> Path:
+ event_path = tmp_path / "event.json"
+ event_path.write_text(json.dumps(payload), encoding="utf-8")
+ return event_path
+
+
+# --------------
+# SHA validation & diff range parsing
+# --------------
+def test_validate_sha_accepts(selector):
+ assert selector._validate_sha("x", "abc1234") == "abc1234"
+ assert selector._validate_sha("x", "a" * 40) == "a" * 40
+
+
+@pytest.mark.parametrize(
+ "bad",
+ [
+ "", # empty
+ "notasha", # non-hex
+ "123", # too short
+ "g" * 40, # non-hex
+ " " * 8, # whitespace
+ ],
+)
+def test_validate_sha_rejects(selector, bad):
+ with pytest.raises(ValueError):
+ selector._validate_sha("x", bad)
+
+
+def test_determine_diff_range_pr_uses_merge_base(selector, tmp_path, monkeypatch):
+ repo = _init_repo(tmp_path)
+
+ merge_base = _commit_file(repo, "shared.txt", "base", "base commit")
+ base_sha = _commit_file(repo, "main.txt", "main", "main branch commit")
+
+ _git(repo, "checkout", "-b", "feature", merge_base)
+ head_sha = _commit_file(repo, "feature.txt", "feature", "feature branch commit")
+
+ event_path = _write_event(
+ tmp_path,
+ {
+ "pull_request": {
+ "base": {"sha": base_sha},
+ "head": {"sha": head_sha},
+ }
+ },
+ )
+ monkeypatch.setenv("GITHUB_EVENT_NAME", "pull_request")
+ monkeypatch.setenv("GITHUB_EVENT_PATH", str(event_path))
+
+ base, head, mode = selector.determine_diff_range(repo, None, None)
+
+ assert base == merge_base
+ assert head == head_sha
+ assert mode == selector.DiffMode.PR
+
+
+def test_determine_diff_range_push_uses_before_after(selector, tmp_path, monkeypatch):
+ repo = _init_repo(tmp_path)
+
+ before = _commit_file(repo, "a.txt", "one", "first commit")
+ after = _commit_file(repo, "a.txt", "two", "second commit")
+
+ event_path = _write_event(tmp_path, {"before": before, "after": after})
+ monkeypatch.setenv("GITHUB_EVENT_NAME", "push")
+ monkeypatch.setenv("GITHUB_EVENT_PATH", str(event_path))
+
+ base, head, mode = selector.determine_diff_range(repo, None, None)
+
+ assert base == before
+ assert head == after
+ assert mode == selector.DiffMode.PUSH
+
+
+def test_determine_diff_range_push_zero_sha_uses_empty_tree(selector, tmp_path, monkeypatch):
+ repo = _init_repo(tmp_path)
+
+ after = _commit_file(repo, "initial.txt", "hello", "initial commit")
+ zero_sha = "0" * 40
+ event_path = _write_event(tmp_path, {"before": zero_sha, "after": after})
+ monkeypatch.setenv("GITHUB_EVENT_NAME", "push")
+ monkeypatch.setenv("GITHUB_EVENT_PATH", str(event_path))
+
+ base, head, mode = selector.determine_diff_range(repo, None, None)
+
+ assert base == selector._empty_tree(repo)
+ assert head == after
+ assert mode == selector.DiffMode.INITIAL
+
+
+def test_determine_diff_range_fallback_uses_head_parent(selector, tmp_path, monkeypatch):
+ repo = _init_repo(tmp_path)
+
+ prev = _commit_file(repo, "a.txt", "one", "first commit")
+ head_sha = _commit_file(repo, "a.txt", "two", "second commit")
+
+ monkeypatch.delenv("GITHUB_EVENT_NAME", raising=False)
+ monkeypatch.delenv("GITHUB_EVENT_PATH", raising=False)
+
+ base, head, mode = selector.determine_diff_range(repo, None, None)
+
+ assert base == prev
+ assert head == head_sha
+ assert mode == selector.DiffMode.FALLBACK
+
+
+# -----------------
+# Paths
+# -----------------
+def test_normalize_relpath_basic(selector):
+ assert selector._normalize_relpath("docs/index.md") == "docs/index.md"
+ assert selector._normalize_relpath("docs\\index.md") == "docs/index.md"
+
+
+@pytest.mark.parametrize(
+ "bad",
+ [
+ "", # empty
+ " ", # whitespace
+ "/etc/passwd", # absolute unix
+ "C:/Windows/x", # absolute windows
+ "../secret.txt", # traversal
+ "docs/../../x", # traversal inside
+ "a\x00b", # NUL
+ ],
+)
+def test_normalize_relpath_rejects_bad(selector, bad):
+ with pytest.raises(ValueError):
+ selector._normalize_relpath(bad)
diff --git a/tests/utils/test_collect_video_paths.py b/tests/utils/test_collect_video_paths.py
new file mode 100644
index 0000000000..251778c141
--- /dev/null
+++ b/tests/utils/test_collect_video_paths.py
@@ -0,0 +1,207 @@
+#
+# 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
+#
+"""Tests for ``collect_video_paths``.
+
+These tests pin down the rule:
+
+* When ``video_type`` is not set, directory enumeration filters by
+ ``SUPPORTED_VIDEOS`` but explicitly-supplied files are trusted (returned
+ as-is, even if they have no suffix).
+* When ``video_type`` is set, it is honoured everywhere — both for files
+ pulled from directories and for files supplied by the caller.
+"""
+
+from __future__ import annotations
+
+from pathlib import Path
+
+import pytest
+
+from deeplabcut.utils.auxfun_videos import SUPPORTED_VIDEOS, collect_video_paths
+from deeplabcut.utils.deprecation import DLCDeprecationWarning
+
+
+def _touch(path: Path) -> Path:
+ path.parent.mkdir(parents=True, exist_ok=True)
+ path.write_bytes(b"")
+ return path
+
+
+def test_keeps_suffixless_files_when_explicitly_listed(tmp_path):
+ """Regression test: a caller-supplied file without an extension (e.g.
+ a content-addressed cache entry) must not be silently dropped."""
+ suffixed = _touch(tmp_path / "video.mp4")
+ hashed = _touch(tmp_path / "abcd1234")
+
+ result = collect_video_paths([suffixed, hashed], extensions=None)
+
+ assert {p.name for p in result} == {"video.mp4", "abcd1234"}
+
+
+def test_accepts_path_objects_and_strings(tmp_path):
+ suffixed = _touch(tmp_path / "video.mp4")
+ hashed = _touch(tmp_path / "abcd1234")
+
+ result = collect_video_paths([str(suffixed), hashed], extensions=None)
+
+ assert {p.name for p in result} == {"video.mp4", "abcd1234"}
+
+
+def test_accepts_single_path_argument(tmp_path):
+ """A single path (not wrapped in a list) is also valid input."""
+ hashed = _touch(tmp_path / "abcd1234")
+
+ result = collect_video_paths(hashed, extensions=None)
+
+ assert [p.name for p in result] == ["abcd1234"]
+
+
+def test_explicit_video_type_filters_listed_files(tmp_path):
+ """When ``extensions`` is set, it filters explicitly-supplied files too."""
+ mp4 = _touch(tmp_path / "video.mp4")
+ avi = _touch(tmp_path / "video.avi")
+
+ result = collect_video_paths([mp4, avi], extensions="mp4")
+
+ assert {p.name for p in result} == {"video.mp4"}
+
+
+def test_explicit_video_type_accepts_leading_dot(tmp_path):
+ mp4 = _touch(tmp_path / "video.mp4")
+ avi = _touch(tmp_path / "video.avi")
+
+ result = collect_video_paths([mp4, avi], extensions=".mp4")
+
+ assert {p.name for p in result} == {"video.mp4"}
+
+
+def test_explicit_video_type_case_insensitive(tmp_path):
+ """Extension matching must be case-insensitive."""
+ mp4 = _touch(tmp_path / "video.mp4")
+ avi = _touch(tmp_path / "video.avi")
+
+ result = collect_video_paths([mp4, avi], extensions="MP4")
+
+ assert {p.name for p in result} == {"video.mp4"}
+
+
+def test_multiple_extensions_filter_directory(tmp_path):
+ """A sequence of extensions filters directory contents to only matching files."""
+ mp4 = _touch(tmp_path / "video.mp4")
+ avi = _touch(tmp_path / "video.avi")
+ _touch(tmp_path / "video.mkv")
+
+ result = collect_video_paths(tmp_path, extensions=["mp4", "avi"])
+
+ assert {p.name for p in result} == {mp4.name, avi.name}
+
+
+def test_directory_enumeration_filters_by_supported_videos(tmp_path):
+ """Directory scans must continue to discriminate videos from non-videos."""
+ mp4 = _touch(tmp_path / "video.mp4")
+ _touch(tmp_path / "notes.txt")
+ _touch(tmp_path / "results.h5")
+ _touch(tmp_path / "abcd1234") # suffix-less file in a directory: not a video
+
+ result = collect_video_paths(tmp_path, extensions=None)
+
+ assert [p.name for p in result] == [mp4.name]
+
+
+def test_directory_enumeration_skips_dlc_artifacts(tmp_path):
+ """``*_labeled.*`` and ``*_full.*`` are DLC outputs, not inputs."""
+ mp4 = _touch(tmp_path / "video.mp4")
+ _touch(tmp_path / "video_labeled.mp4")
+ _touch(tmp_path / "video_full.mp4")
+
+ result = collect_video_paths(tmp_path, extensions=None)
+
+ assert {p.name for p in result} == {mp4.name}
+
+
+def test_disable_exclude_patterns_includes_dlc_artifacts(tmp_path):
+ """Setting ``exclude_patterns=[]`` disables all pattern exclusion."""
+ mp4 = _touch(tmp_path / "video.mp4")
+ labeled = _touch(tmp_path / "video_labeled.mp4")
+ full = _touch(tmp_path / "video_full.mp4")
+
+ result = collect_video_paths(tmp_path, extensions=None, exclude_patterns=[])
+
+ assert {p.name for p in result} == {mp4.name, labeled.name, full.name}
+
+
+def test_mixed_files_and_directories(tmp_path):
+ """The function handles a mix of explicit files and directories."""
+ folder = tmp_path / "folder"
+ in_folder = _touch(folder / "from_dir.mp4")
+ _touch(folder / "ignored.txt")
+
+ explicit_mp4 = _touch(tmp_path / "explicit.mp4")
+ explicit_hashed = _touch(tmp_path / "abcd1234")
+
+ result = collect_video_paths(
+ [folder, explicit_mp4, explicit_hashed],
+ extensions=None,
+ )
+
+ assert {p.name for p in result} == {
+ in_folder.name,
+ explicit_mp4.name,
+ explicit_hashed.name,
+ }
+
+
+def test_duplicates_are_removed(tmp_path):
+ mp4 = _touch(tmp_path / "video.mp4")
+
+ result = collect_video_paths([mp4, mp4, str(mp4)], extensions=None)
+
+ assert len(result) == 1
+ assert result[0].name == "video.mp4"
+
+
+def test_missing_path_raises(tmp_path):
+ with pytest.raises(FileNotFoundError):
+ collect_video_paths([tmp_path / "does_not_exist.mp4"], extensions=None)
+
+
+@pytest.mark.parametrize("ext", SUPPORTED_VIDEOS)
+def test_each_supported_extension_picked_up_in_directory(tmp_path, ext):
+ expected = _touch(tmp_path / f"clip.{ext}")
+
+ result = collect_video_paths(tmp_path, extensions=None)
+
+ assert [p.name for p in result] == [expected.name]
+
+
+def test_sorted_by_default_when_not_shuffled(tmp_path):
+ a = _touch(tmp_path / "a.mp4")
+ b = _touch(tmp_path / "b.mp4")
+ c = _touch(tmp_path / "c.mp4")
+
+ result = collect_video_paths([c, a, b], extensions=None, shuffle=False)
+
+ assert [p.name for p in result] == ["a.mp4", "b.mp4", "c.mp4"]
+
+
+@pytest.mark.parametrize("deprecated_value", ["", [""], ("",), {""}])
+def test_deprecated_empty_extensions_warns(tmp_path, deprecated_value):
+ """Empty / blank extension values are deprecated and should emit a warning."""
+ _touch(tmp_path / "video.mp4")
+
+ with pytest.warns(DLCDeprecationWarning):
+ collect_video_paths(tmp_path, extensions=deprecated_value)
+
+
+def test_empty_sequence_raises(tmp_path):
+ """An empty sequence is not a valid filter; callers must pass None instead."""
+ with pytest.raises(ValueError):
+ collect_video_paths(tmp_path, extensions=[])
diff --git a/tests/utils/test_deprecation.py b/tests/utils/test_deprecation.py
new file mode 100644
index 0000000000..7f5a73fd5a
--- /dev/null
+++ b/tests/utils/test_deprecation.py
@@ -0,0 +1,291 @@
+#
+# 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 warnings
+
+import pytest
+from packaging.version import Version
+
+from deeplabcut.utils.deprecation import (
+ DLCDeprecationWarning,
+ deprecated,
+ renamed_parameter,
+)
+
+# ---------------------------------------------------------------------------
+# @deprecated
+# ---------------------------------------------------------------------------
+
+
+def test_deprecated_emits_deprecation_warning():
+ @deprecated()
+ def old_fn():
+ return 42
+
+ with pytest.warns(DLCDeprecationWarning):
+ result = old_fn()
+
+ assert result == 42
+
+
+def test_deprecated_warning_contains_function_name():
+ @deprecated()
+ def my_old_function():
+ pass
+
+ with pytest.warns(DLCDeprecationWarning, match="my_old_function"):
+ my_old_function()
+
+
+def test_deprecated_warning_contains_replacement():
+ @deprecated(replacement="new_module.new_fn")
+ def old_fn():
+ pass
+
+ with pytest.warns(DLCDeprecationWarning, match="new_module.new_fn"):
+ old_fn()
+
+
+def test_deprecated_warning_contains_since_and_removed_in():
+ @deprecated(since="3.1", removed_in="4.0")
+ def old_fn():
+ pass
+
+ with pytest.warns(DLCDeprecationWarning, match="3.1") as record:
+ old_fn()
+
+ assert "4.0" in str(record[0].message)
+
+
+def test_deprecated_preserves_return_value_and_args():
+ @deprecated()
+ def add(a, b):
+ return a + b
+
+ with pytest.warns(DLCDeprecationWarning):
+ assert add(2, 3) == 5
+
+
+def test_deprecated_preserves_name_and_docstring():
+ @deprecated(replacement="new_fn")
+ def documented_fn():
+ """Original docstring."""
+
+ assert documented_fn.__name__ == "documented_fn"
+ assert "Original docstring." in documented_fn.__doc__
+ assert "Deprecated." in documented_fn.__doc__
+ assert "new_fn" in documented_fn.__doc__
+
+
+def test_deprecated_attaches_metadata():
+ @deprecated(replacement="new_fn", since="3.1", removed_in="4.0")
+ def old_fn():
+ pass
+
+ info = old_fn.__deprecated_info__
+ assert info.kind == "callable"
+ assert info.target.endswith("old_fn")
+ assert info.replacement == "new_fn"
+ assert info.since == Version("3.1")
+ assert info.removed_in == Version("4.0")
+
+
+def test_deprecated_invalid_since_raises():
+ with pytest.raises(ValueError, match="Invalid version"):
+
+ @deprecated(since="not-a-version")
+ def old_fn():
+ pass
+
+
+def test_deprecated_invalid_removed_in_raises():
+ with pytest.raises(ValueError, match="Invalid version"):
+
+ @deprecated(removed_in="definitely-not-a-version")
+ def old_fn():
+ pass
+
+
+def test_deprecated_removed_in_must_be_greater_than_since():
+ with pytest.raises(ValueError, match="must be greater than"):
+
+ @deprecated(since="4.0", removed_in="4.0")
+ def old_fn():
+ pass
+
+
+# ---------------------------------------------------------------------------
+# @renamed_parameter
+# ---------------------------------------------------------------------------
+
+
+def test_renamed_parameter_old_name_emits_warning():
+ @renamed_parameter(old="in_random_order", new="shuffle")
+ def fn(shuffle=False):
+ return shuffle
+
+ with pytest.warns(DLCDeprecationWarning):
+ fn(in_random_order=True)
+
+
+def test_renamed_parameter_old_name_is_forwarded():
+ @renamed_parameter(old="in_random_order", new="shuffle")
+ def fn(shuffle=False):
+ return shuffle
+
+ with pytest.warns(DLCDeprecationWarning):
+ result = fn(in_random_order=True)
+
+ assert result is True
+
+
+def test_renamed_parameter_new_name_no_warning():
+ @renamed_parameter(old="in_random_order", new="shuffle")
+ def fn(shuffle=False):
+ return shuffle
+
+ # No warning should be emitted when using the current name.
+ with warnings.catch_warnings():
+ warnings.simplefilter("error", DLCDeprecationWarning)
+ result = fn(shuffle=True)
+
+ assert result is True
+
+
+def test_renamed_parameter_warning_contains_names():
+ @renamed_parameter(old="videotype", new="video_extensions", since="3.2")
+ def fn(video_extensions=None):
+ return video_extensions
+
+ with pytest.warns(DLCDeprecationWarning, match="videotype") as record:
+ fn(videotype="mp4")
+
+ message = str(record[0].message)
+ assert "video_extensions" in message
+ assert "3.2" in message
+
+
+def test_renamed_parameter_preserves_name():
+ @renamed_parameter(old="foo", new="bar")
+ def my_fn(bar=None):
+ """Docstring."""
+
+ assert my_fn.__name__ == "my_fn"
+
+
+def test_renamed_parameter_old_and_new_together_raise():
+ @renamed_parameter(old="videotype", new="video_extensions")
+ def fn(video_extensions=None):
+ return video_extensions
+
+ with pytest.raises(TypeError, match="both 'videotype' and 'video_extensions'"):
+ fn(videotype="mp4", video_extensions="avi")
+
+
+def test_renamed_parameter_attaches_metadata():
+ @renamed_parameter(old="videotype", new="video_extensions", since="3.2")
+ def fn(video_extensions=None):
+ return video_extensions
+
+ params = fn.__deprecated_params__
+ assert len(params) == 1
+
+ info = params[0]
+ assert info.kind == "parameter"
+ assert info.target.endswith("fn")
+ assert info.old_parameter == "videotype"
+ assert info.new_parameter == "video_extensions"
+ assert info.since == Version("3.2")
+
+
+def test_renamed_parameter_invalid_since_raises():
+ with pytest.raises(ValueError, match="Invalid version"):
+
+ @renamed_parameter(old="videotype", new="video_extensions", since="invalid-version")
+ def fn(video_extensions=None):
+ return video_extensions
+
+
+def test_renamed_parameter_new_not_in_signature_raises():
+ with pytest.raises(ValueError, match="not a parameter"):
+
+ @renamed_parameter(old="foo", new="nonexistent")
+ def fn(bar=None):
+ return bar
+
+
+def test_new_not_in_signature_raises():
+ """Applying a rename whose 'new' is not in the signature raises an error."""
+ with pytest.raises(ValueError, match="not a parameter"):
+
+ @renamed_parameter(old="old_name", new="new_name")
+ def fn(not_new_name=None):
+ return not_new_name
+
+
+def test_old_still_in_signature_raises():
+ """Applying a rename when the old name is still in the signature raises an error."""
+ with pytest.raises(ValueError, match="still a parameter"):
+
+ @renamed_parameter(old="old_name", new="new_name")
+ def fn(old_name=None, new_name=None):
+ return new_name
+
+
+def test_renamed_parameter_chaining_raises():
+ """Chaining renames A→B→C raises an error."""
+ with pytest.raises(ValueError, match="chaining renames is not allowed"):
+
+ @renamed_parameter(old="A", new="B") # outer: A→B, but B is already deprecated to C
+ @renamed_parameter(old="B", new="C") # inner: B→C
+ def fn(C=None):
+ return C
+
+
+def test_renamed_parameter_multiple_independent_renames():
+ @renamed_parameter(old="batchsize", new="batch_size")
+ @renamed_parameter(old="videotype", new="video_extensions")
+ def fn(video_extensions=None, batch_size=None):
+ return video_extensions, batch_size
+
+ with pytest.warns(DLCDeprecationWarning):
+ result = fn(videotype="mp4")
+ assert result == ("mp4", None)
+
+ with pytest.warns(DLCDeprecationWarning):
+ result = fn(batchsize=4)
+ assert result == (None, 4)
+
+
+def test_renamed_parameter_positional_arg_unaffected():
+ @renamed_parameter(old="in_random_order", new="shuffle")
+ def fn(shuffle=False):
+ return shuffle
+
+ with warnings.catch_warnings():
+ warnings.simplefilter("error", DLCDeprecationWarning)
+ result = fn(True)
+
+ assert result is True
+
+
+def test_multiple_subsequent_renames_allowed():
+ @renamed_parameter(old="oldestname", new="newest", since="3.0.0")
+ @renamed_parameter(old="older_name", new="newest", since="4.0.0")
+ def fn(*, newest):
+ return newest
+
+ with pytest.warns(DLCDeprecationWarning):
+ result = fn(oldestname=1)
+ assert result == 1
+
+ with pytest.warns(DLCDeprecationWarning):
+ result = fn(older_name=2)
+ assert result == 2
diff --git a/tests/utils/test_multiprocessing.py b/tests/utils/test_multiprocessing.py
new file mode 100644
index 0000000000..a5ab47dd5f
--- /dev/null
+++ b/tests/utils/test_multiprocessing.py
@@ -0,0 +1,40 @@
+#
+# 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/master/AUTHORS
+#
+# Licensed under GNU Lesser General Public License v3.0
+#
+import time
+
+import pytest
+
+from deeplabcut.utils.multiprocessing import call_with_timeout
+
+
+def _succeeding_method(parameter):
+ return parameter
+
+
+def _failing_method():
+ raise ValueError("Raise value error on purpose")
+
+
+def _hanging_method():
+ while True:
+ time.sleep(5)
+
+
+@pytest.mark.skip(reason="Flaky on CI - imports that can exceed timeout on resource-constrained systems")
+def test_call_with_timeout():
+ parameter = (10, "Hello test")
+ assert call_with_timeout(_succeeding_method, 30, parameter) == parameter
+
+ with pytest.raises(ValueError):
+ call_with_timeout(_failing_method, timeout=30)
+
+ with pytest.raises(TimeoutError):
+ call_with_timeout(_hanging_method, timeout=1)
diff --git a/tests/utils/test_skeleton.py b/tests/utils/test_skeleton.py
new file mode 100644
index 0000000000..c83d5060d1
--- /dev/null
+++ b/tests/utils/test_skeleton.py
@@ -0,0 +1,374 @@
+import warnings
+from types import SimpleNamespace
+
+import matplotlib
+
+matplotlib.use("Agg", force=True)
+
+import numpy as np
+import pandas as pd
+import pytest
+from matplotlib.collections import LineCollection
+from matplotlib.figure import Figure
+from scipy.spatial import KDTree
+
+from deeplabcut.utils import skeleton as skeleton_mod
+from deeplabcut.utils.skeleton import SkeletonBuilder, write_config
+
+# ---------------------------------------------------------------------
+# Helpers
+# ---------------------------------------------------------------------
+
+
+def make_config(project_path, scorer="TestScorer", skeleton=None):
+ return {
+ "project_path": str(project_path),
+ "scorer": scorer,
+ "skeleton": skeleton or [],
+ "skeleton_color": "red",
+ "dotsize": 4,
+ }
+
+
+def make_test_builder():
+ """
+ Construct a SkeletonBuilder instance without calling __init__,
+ so individual methods can be unit-tested in isolation.
+ """
+ builder = SkeletonBuilder.__new__(SkeletonBuilder)
+ return builder
+
+
+def attach_fake_canvas(builder):
+ builder.fig = Figure()
+ builder.fig.canvas.draw_idle = lambda: None
+
+
+# ---------------------------------------------------------------------
+# pick_labeled_frame
+# ---------------------------------------------------------------------
+
+
+def test_pick_labeled_frame_multi_animal_drops_single(monkeypatch):
+ builder = make_test_builder()
+
+ index = pd.MultiIndex.from_tuples(
+ [("labeled-data/session1", "img001.png")],
+ names=["folder", "image"],
+ )
+ columns = pd.MultiIndex.from_product(
+ [["TestScorer"], ["single", "mouseA"], ["nose", "tail"], ["x", "y"]],
+ names=["scorer", "individuals", "bodyparts", "coords"],
+ )
+
+ # "single" is fully labeled too, but should be dropped before choosing.
+ row = [
+ 1.0,
+ 2.0,
+ 3.0,
+ 4.0, # single
+ 10.0,
+ 20.0,
+ 30.0,
+ 40.0, # mouseA
+ ]
+ builder.df = pd.DataFrame([row], index=index, columns=columns)
+
+ monkeypatch.setattr(np.random, "shuffle", lambda x: None)
+
+ picked_row, picked_col = builder.pick_labeled_frame()
+
+ assert picked_row == ("labeled-data/session1", "img001.png")
+ assert picked_col == "mouseA"
+
+
+def test_pick_labeled_frame_without_individuals(monkeypatch):
+ builder = make_test_builder()
+
+ index = pd.MultiIndex.from_tuples(
+ [("labeled-data/session1", "img001.png")],
+ names=["folder", "image"],
+ )
+ columns = pd.MultiIndex.from_product(
+ [["TestScorer"], ["nose", "tail"], ["x", "y"]],
+ names=["scorer", "bodyparts", "coords"],
+ )
+
+ builder.df = pd.DataFrame(
+ [[1.0, 2.0, 3.0, 4.0]],
+ index=index,
+ columns=columns,
+ )
+
+ monkeypatch.setattr(np.random, "shuffle", lambda x: None)
+
+ picked_row, picked_col = builder.pick_labeled_frame()
+
+ assert picked_row == ("labeled-data/session1", "img001.png")
+ # fallback path uses count(...).to_frame(), so the single column is usually 0
+ assert picked_col == 0
+
+
+# ---------------------------------------------------------------------
+# clear
+# ---------------------------------------------------------------------
+
+
+def test_clear_resets_indices_segments_and_linecollection():
+ builder = make_test_builder()
+ builder.inds = {(0, 1), (1, 2)}
+ builder.segs = {
+ ((0.0, 0.0), (10.0, 0.0)),
+ ((10.0, 0.0), (20.0, 0.0)),
+ }
+ builder.lines = LineCollection([np.array([[0.0, 0.0], [10.0, 0.0]]), np.array([[10.0, 0.0], [20.0, 0.0]])])
+ attach_fake_canvas(builder)
+
+ builder.clear()
+
+ assert builder.inds == set()
+ assert builder.segs == set()
+ assert list(builder.lines.get_segments()) == []
+
+
+# ---------------------------------------------------------------------
+# export
+# ---------------------------------------------------------------------
+
+
+def test_export_sorts_pairs_and_warns_for_unconnected(monkeypatch):
+ builder = make_test_builder()
+ builder.config_path = "dummy_config.yaml"
+ builder.xy = np.array(
+ [
+ [0.0, 0.0],
+ [10.0, 0.0],
+ [20.0, 0.0],
+ [30.0, 0.0], # intentionally left unconnected
+ ]
+ )
+ builder.bpts = pd.Index(["nose", "tail", "paw", "ear"], name="bodyparts")
+ builder.inds = {(1, 2), (0, 1)} # intentionally unordered
+ builder.cfg = {"skeleton": []}
+
+ captured = {}
+
+ def fake_write_config(path, cfg):
+ captured["path"] = path
+ captured["cfg"] = cfg.copy()
+
+ monkeypatch.setattr(skeleton_mod, "write_config", fake_write_config)
+
+ with pytest.warns(UserWarning, match="didn't connect all the bodyparts"):
+ builder.export()
+
+ assert captured["path"] == "dummy_config.yaml"
+ assert captured["cfg"]["skeleton"] == [
+ ("nose", "tail"),
+ ("tail", "paw"),
+ ]
+
+
+def test_export_without_warning_when_all_bodyparts_connected(monkeypatch):
+ builder = make_test_builder()
+ builder.config_path = "dummy_config.yaml"
+ builder.xy = np.array(
+ [
+ [0.0, 0.0],
+ [10.0, 0.0],
+ [20.0, 0.0],
+ ]
+ )
+ builder.bpts = pd.Index(["nose", "tail", "paw"], name="bodyparts")
+ builder.inds = {(0, 1), (1, 2)}
+ builder.cfg = {"skeleton": []}
+
+ monkeypatch.setattr(skeleton_mod, "write_config", lambda path, cfg: None)
+
+ with warnings.catch_warnings(record=True) as record:
+ warnings.simplefilter("always")
+ builder.export()
+
+ assert not any("didn't connect all the bodyparts" in str(w.message) for w in record)
+ assert builder.cfg["skeleton"] == [
+ ("nose", "tail"),
+ ("tail", "paw"),
+ ]
+
+
+# ---------------------------------------------------------------------
+# on_select
+# ---------------------------------------------------------------------
+
+
+def test_on_select_adds_pairs_segments_and_updates_canvas():
+ builder = make_test_builder()
+ builder.xy = np.array(
+ [
+ [0.0, 0.0],
+ [10.0, 0.0],
+ [20.0, 0.0],
+ ]
+ )
+ builder.tree = KDTree(builder.xy)
+ builder.inds = set()
+ builder.segs = set()
+ builder.lines = LineCollection([])
+ attach_fake_canvas(builder)
+
+ verts = [(0.0, 0.0), (10.0, 0.0), (20.0, 0.0)]
+ builder.on_select(verts)
+
+ assert builder.inds == {(0, 1), (1, 2)}
+ assert ((0.0, 0.0), (10.0, 0.0)) in builder.segs
+ assert ((10.0, 0.0), (20.0, 0.0)) in builder.segs
+ assert len(builder.lines.get_segments()) == 2
+
+
+def test_on_select_ignores_duplicate_hits():
+ builder = make_test_builder()
+ builder.xy = np.array(
+ [
+ [0.0, 0.0],
+ [10.0, 0.0],
+ [20.0, 0.0],
+ ]
+ )
+ builder.tree = KDTree(builder.xy)
+ builder.inds = set()
+ builder.segs = set()
+ builder.lines = LineCollection([])
+ attach_fake_canvas(builder)
+
+ # Repeated nearby vertices should not create duplicate pairs
+ verts = [(0.0, 0.0), (0.1, 0.0), (10.0, 0.0), (10.1, 0.0), (20.0, 0.0)]
+ builder.on_select(verts)
+
+ assert builder.inds == {(0, 1), (1, 2)}
+ assert len(builder.segs) == 2
+
+
+# ---------------------------------------------------------------------
+# on_pick
+# ---------------------------------------------------------------------
+
+
+def test_on_pick_right_click_removes_segment_and_pair():
+ builder = make_test_builder()
+ builder.xy = np.array(
+ [
+ [0.0, 0.0],
+ [10.0, 0.0],
+ ]
+ )
+ builder.tree = KDTree(builder.xy)
+ builder.inds = {(0, 1)}
+ builder.segs = {((0.0, 0.0), (10.0, 0.0))}
+ builder.lines = LineCollection([np.array([[0.0, 0.0], [10.0, 0.0]])])
+ attach_fake_canvas(builder)
+
+ event = SimpleNamespace(
+ mouseevent=SimpleNamespace(button=3),
+ artist=builder.lines,
+ ind=[0],
+ )
+
+ builder.on_pick(event)
+
+ assert builder.inds == set()
+ assert builder.segs == set()
+ assert list(builder.lines.get_segments()) == []
+
+
+def test_on_pick_non_right_click_does_nothing():
+ builder = make_test_builder()
+ builder.xy = np.array(
+ [
+ [0.0, 0.0],
+ [10.0, 0.0],
+ ]
+ )
+ builder.tree = KDTree(builder.xy)
+ builder.inds = {(0, 1)}
+ builder.segs = {((0.0, 0.0), (10.0, 0.0))}
+ builder.lines = LineCollection([np.array([[0.0, 0.0], [10.0, 0.0]])])
+ attach_fake_canvas(builder)
+
+ event = SimpleNamespace(
+ mouseevent=SimpleNamespace(button=1),
+ artist=builder.lines,
+ ind=[0],
+ )
+
+ builder.on_pick(event)
+
+ assert builder.inds == {(0, 1)}
+ assert builder.segs == {((0.0, 0.0), (10.0, 0.0))}
+ assert len(builder.lines.get_segments()) == 1
+
+
+# ---------------------------------------------------------------------
+# __init__ lightweight integration
+# ---------------------------------------------------------------------
+
+
+def test_init_loads_dataframe_image_and_existing_skeleton(tmp_path, monkeypatch):
+ project_path = tmp_path / "project"
+ labeled_data = project_path / "labeled-data" / "session1"
+ labeled_data.mkdir(parents=True)
+
+ cfg_path = project_path / "config.yaml"
+ cfg = make_config(
+ project_path=project_path,
+ scorer="TestScorer",
+ skeleton=[
+ ["nose", "tail"],
+ ["missing", "nose"],
+ ], # second pair should be ignored
+ )
+ write_config(cfg_path, cfg)
+
+ index = pd.MultiIndex.from_tuples(
+ [("labeled-data/session1", "img001.png")],
+ names=["folder", "image"],
+ )
+ columns = pd.MultiIndex.from_product(
+ [["TestScorer"], ["nose", "tail"], ["x", "y"]],
+ names=["scorer", "bodyparts", "coords"],
+ )
+ df = pd.DataFrame(
+ [[0.0, 0.0, 10.0, 0.0]],
+ index=index,
+ columns=columns,
+ )
+ h5_path = labeled_data / "CollectedData_TestScorer.h5"
+ df.to_hdf(h5_path, key="df", mode="w")
+
+ monkeypatch.setattr(skeleton_mod.io, "imread", lambda path: np.zeros((5, 5, 3), dtype=np.uint8))
+ monkeypatch.setattr(SkeletonBuilder, "build_ui", lambda self: None)
+ monkeypatch.setattr(SkeletonBuilder, "display", lambda self: None)
+ monkeypatch.setattr(np.random, "shuffle", lambda x: None)
+
+ builder = SkeletonBuilder(str(cfg_path))
+
+ assert builder.config_path == str(cfg_path)
+ assert list(builder.bpts) == ["nose", "tail"]
+ assert builder.xy.shape == (2, 2)
+ assert builder.image.shape == (5, 5, 3)
+ assert builder.inds == {(0, 1)}
+ assert ((0.0, 0.0), (10.0, 0.0)) in builder.segs
+
+
+def test_init_raises_if_no_labeled_data_found(tmp_path, monkeypatch):
+ project_path = tmp_path / "project"
+ (project_path / "labeled-data").mkdir(parents=True)
+
+ cfg_path = project_path / "config.yaml"
+ cfg = make_config(project_path=project_path, scorer="TestScorer")
+ write_config(cfg_path, cfg)
+
+ monkeypatch.setattr(SkeletonBuilder, "build_ui", lambda self: None)
+ monkeypatch.setattr(SkeletonBuilder, "display", lambda self: None)
+
+ with pytest.raises(IOError, match="No labeled data were found"):
+ SkeletonBuilder(str(cfg_path))
diff --git a/testscript_cli.py b/testscript_cli.py
index 7b74543cdc..295a68f729 100644
--- a/testscript_cli.py
+++ b/testscript_cli.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
"""
modified from: https://github.com/DeepLabCut/DeepLabCut-core/testscript_cli.py
by Mackenzie.
@@ -9,25 +8,24 @@
It produces nothing of interest scientifically.
"""
-task = "Testcore" # Enter the name of your experiment Task
-scorer = "Mackenzie" # Enter the name of the experimenter/labeler
+import os
+import platform
+
+import numpy as np
+import pandas as pd
-import os, subprocess, sys
+import deeplabcut as dlc
+from deeplabcut.core.engine import Engine
+task = "Testcore" # Enter the name of your experiment Task
+scorer = "Mackenzie" # Enter the name of the experimenter/labeler
+print("Imported DLC!")
+engine = Engine.PYTORCH
# def install(package):
# subprocess.check_call([sys.executable, "-m", "pip", "install", package])
# install("tensorflow==1.13.1")
-import deeplabcut as dlc
-
-from pathlib import Path
-import pandas as pd
-import numpy as np
-import platform
-
-print("Imported DLC!")
-
basepath = os.path.dirname(os.path.abspath("testscript_cli.py"))
videoname = "reachingvideo1"
video = [
@@ -105,7 +103,7 @@
videoname,
"CollectedData_" + scorer + ".h5",
),
- "df_with_missing",
+ key="df_with_missing",
format="table",
mode="w",
)
@@ -116,33 +114,14 @@
print("CREATING TRAININGSET")
dlc.create_training_dataset(
- path_config_file, net_type=net_type, augmenter_type=augmenter_type
+ path_config_file,
+ net_type=net_type,
+ augmenter_type=augmenter_type,
+ engine=engine,
)
-posefile = os.path.join(
- cfg["project_path"],
- "dlc-models/iteration-"
- + str(cfg["iteration"])
- + "/"
- + cfg["Task"]
- + cfg["date"]
- + "-trainset"
- + str(int(cfg["TrainingFraction"][0] * 100))
- + "shuffle"
- + str(1),
- "train/pose_cfg.yaml",
-)
-
-DLC_config = dlc.auxiliaryfunctions.read_plainconfig(posefile)
-DLC_config["save_iters"] = numiter
-DLC_config["display_iters"] = 2
-DLC_config["multi_step"] = [[0.001, numiter]]
-
-print("CHANGING training parameters to end quickly!")
-dlc.auxiliaryfunctions.write_plainconfig(posefile, DLC_config)
-
print("TRAIN")
-dlc.train_network(path_config_file)
+dlc.train_network(path_config_file, epochs=numiter, displayiters=2)
print("EVALUATE")
dlc.evaluate_network(path_config_file, plotting=True)
@@ -166,7 +145,8 @@
dlc.create_training_dataset(path_config_file, Shuffles=[2],net_type=net_type,augmenter_type=augmenter_type2)
cfg=dlc.auxiliaryfunctions.read_config(path_config_file)
-posefile=os.path.join(cfg['project_path'],'dlc-models/iteration-'+str(cfg['iteration'])+'/'+ cfg['Task'] + cfg['date'] + '-trainset' + str(int(cfg['TrainingFraction'][0] * 100)) + 'shuffle' + str(2),'train/pose_cfg.yaml')
+posefile=os.path.join(cfg['project_path'],'dlc-models/iteration-'+str(cfg['iteration'])+'/'+ cfg['Task'] + cfg['date'] +
+'-trainset' + str(int(cfg['TrainingFraction'][0] * 100)) + 'shuffle' + str(2),'train/pose_cfg.yaml')
DLC_config=dlc.auxiliaryfunctions.read_plainconfig(posefile)
DLC_config['save_iters']=numiter
DLC_config['display_iters']=1
@@ -190,5 +170,6 @@
dlc.export_model(path_config_file, shuffle=1, make_tar=False)
print(
- "ALL DONE!!! - default/imgaug cases of DLCcore training and evaluation are functional (no extract outlier or refinement tested)."
+ "ALL DONE!!! - default/imgaug cases of DLCcore training and evaluation are functional (no extract outlier or"
+ "refinement tested)."
)
diff --git a/tools/README.md b/tools/README.md
index 32389da346..20d1b29f8b 100644
--- a/tools/README.md
+++ b/tools/README.md
@@ -1,13 +1,209 @@
# Developer tools useful for maintaining the repository
-## Code headers
+This document summarizes the developer tooling and workflows used in this repo.
-The code headers can be standardized by running
+```bash
+pip install -e . --group dev
+```
+
+---
+
+## 1) Pre-commit (recommended)
+
+Enable the repository hooks locally:
+
+```bash
+pre-commit install
+```
+
+Run on all files:
+
+Steering committee members may edit the `NOTICE.yml` to update the header.
+
+## 2) Ruff cleanup helpers
+
+For **local Ruff backlog work** (not a substitute for CI or pre-commit), see [Ruff cleanup helpers](ruff_cleanup_helpers.md). It documents `generate_ruff_report.py` (Markdown report from Ruff JSON) and `fix_e501_with_autopep8.py` (targeted long-line cleanup plus Ruff fix/format).
+
+---
+
+## 3) License headers
+
+Code headers can be standardized by running:
+
+Please follow the instructions in `CONTRIBUTING.md` for contributing to the codebase, including running tests and pre-commit checks before opening a pull request.
+
+Run from the repository root. Update `NOTICE.yml` to change header content.
+
+---
+
+## 4) Running tests locally
+
+### Run the full test suite
+
+```bash
+pytest
+```
+
+### Run a specific test module or folder
+
+```bash
+coverage run -m pytest
+coverage report
+```
+
+## 5) Intelligent test selection (local + CI)
+
+The repository includes a deterministic test-selection tool to reduce CI runtime by running only the relevant workflows and tests based on changed files.
+
+### What it outputs
+
+The selector emits **orthogonal workflow lanes** plus structured selections:
+
+- `lanes`: which workflow lanes should run
+ - `skip`: skip test execution entirely (for lint-only changes)
+ - `docs`: run docs checks
+ - `fast`: run targeted pytest paths and optional functional scripts
+ - `full`: delegate to the full test workflow / matrix
+- `pytest_paths`: list of pytest path arguments (JSON)
+- `functional_scripts`: list of Python scripts to run (JSON)
+- `provenance`: mapping from each selected test/script to the category rule(s) that selected it
+
+It also emits audit metadata:
+
+- `selected_workflows`: ordered list of enabled lanes (`skip`, `docs`, `fast`, `full`)
+- `lane_reasons`: reasons for each enabled lane
+- `diff_mode`: how the diff range was determined
+- `reasons`: aggregate machine-readable reasons for the decision
+- `changed_files`: files considered for the decision
+- `schema_version`: output schema version
+
+### Rule configuration
+
+Routing rules are defined in `tools/test_selector_config.py`.
+
+That file contains:
+
+- reusable path predicate helpers such as `prefix(...)`, `suffix(...)`, `equals(...)`, `case_insensitive_match(...)`, and `all_of(...)`
+- conservative `FULL_SUITE_TRIGGERS`
+- `LINT_ONLY_FILES`
+- validated `CATEGORY_RULES` built from the `CategoryRule` schema
+- `CATEGORY_RULE_BY_NAME` for stable lookup of named rules such as `docs`
+
+The current refactor keeps the rule predicates simple and location-based while validating the rule structure at import time.
+
+### Run locally (no CI env required)
+
+> [!IMPORTANT]
+> Requires `pydantic>=2,<3`
+
+Print the decision as JSON:
-``` bash
-python tools/update_license_headers.py
+```bash
+python tools/test_selector.py --json
```
-from the repository root.
+Write the decision report (`selection.json` and `decision.md`) under `tmp/test-selection/`:
+
+```bash
+python tools/test_selector.py --report-dir tmp/test-selection --json
+```
+
+Write a GitHub job-summary-compatible Markdown report when `GITHUB_STEP_SUMMARY` is available:
+
+```bash
+python tools/test_selector.py --report-dir tmp/test-selection --write-summary
+```
+
+Override the diff range manually:
+
+```bash
+python tools/test_selector.py --base-sha --head-sha --json
+```
+
+In GitHub Actions, the workflow typically adds `--write-github-output` and `--write-summary`.
+
+### Diff modes
+
+The selector records how the diff was determined in `diff_mode`:
+
+- `pr`: pull request diff using `merge-base(base, head)..head`
+- `push`: push diff using `before..after`
+- `manual`: explicit `--base-sha` / `--head-sha`
+- `fallback`: fallback to `HEAD^..HEAD`
+- `initial`: initial commit (`empty-tree..HEAD`)
+- `fallback_no_head`: could not resolve `HEAD`
+
+### Report files
+
+The selector always writes report artifacts for transparency:
+
+- `tmp/test-selection/selection.json`: machine-readable output
+- `tmp/test-selection/decision.md`: human-readable summary with workflow lanes, reasons, explained changed files, selected tests, and provenance
+
+These reports are especially useful when a change unexpectedly routes to `full`.
+
+### Notes
+
+- The selector can enable more than one lane at once. For example, a PR can legitimately enable both `docs` and `fast`, or `docs` and `full`.
+- Docs changes are **orthogonal** to test routing: docs changes can enable the docs lane while still contributing selected tests/scripts if such rules are configured.
+- `LINT_ONLY_FILES` are ignored for routing. If *only* lint-only files changed, the selector enables the `skip` lane.
+- If category rules match changed files but do not contribute explicit tests/scripts, the selector can fall back to the minimal pytest set defined by `MINIMAL_PYTEST`.
+
+### Troubleshooting the selector
+
+If a workflow run is unexpectedly selecting `full`, check:
+
+- `tmp/test-selection/decision.md`
+- `tmp/test-selection/selection.json`
+- `lane_reasons`
+- `diff_mode`
+- `changed_files`
+
+Common causes include:
+
+- a file matched a conservative full-suite trigger
+- no category rule matched the routed files
+- selected paths configured by a rule no longer exist in the repository
+- diff resolution fell back because CI checkout history was incomplete
+
+---
+
+## 6) Docs: Jupyter Book build (local)
+
+The repo uses Jupyter Book for docs:
+
+```bash
+python -m pip install -U pip
+python -m pip install .[docs]
+jupyter-book build .
+```
+
+`.github/workflows/build-book.yml` is the canonical CI implementation.
+
+---
+
+## 7) Testing the test selector
+
+The selector has dedicated tests covering:
+
+- decision behavior for docs / fast / full / skip routing
+- provenance and deduplicated selections
+- `CategoryRule` schema validation
+- integrity checks for the currently defined rules
+
+Run the selector-focused tests with:
+
+```bash
+pytest tests/tools/test_selector/
+```
+
+---
+
+## 8) Troubleshooting tips
-You can edit the `NOTICE.yml` to update the header.
+- If a workflow run is unexpectedly selecting `full`, inspect the selector reports first.
+- If targeted tests fail due to missing dependencies, either:
+ - broaden the fast-lane install (for example by installing required extras), or
+ - adjust selection rules so that the fast lane only selects tests that run in the minimal environment.
+- If manual diff selection is used, always pass both `--base-sha` and `--head-sha` together.
+- In CI, ensure checkout history is deep enough for `merge-base` / `diff` operations (`fetch-depth: 0` is typically safest).
diff --git a/tools/__init__.py b/tools/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tools/docs_and_notebooks_check.py b/tools/docs_and_notebooks_check.py
new file mode 100644
index 0000000000..0d720814d8
--- /dev/null
+++ b/tools/docs_and_notebooks_check.py
@@ -0,0 +1,1259 @@
+"""DeepLabCut docs & notebooks automated checks tool.
+
+Goals
+-----
+- SAFE by default: read-only operations in CI (report/check).
+- Idempotent updates (update mode) that only touch:
+ * Notebook-level metadata for .ipynb (never cells/outputs)
+ * YAML frontmatter for .md docs (optional)
+- Uses pydantic schemas with explicit schema_version for validation.
+- Aims to be contributor-friendly by default: check mode enforces configured policy, while
+ surfacing scan/parsing issues without failing unless strict mode is enabled.
+
+Terminology
+-----------
+last_content_updated
+ Computed from git history, excluding metadata-only commits.
+ (Metadata commits must include META_COMMIT_MARKER in the commit message.)
+
+last_verified
+ Human-controlled date indicating the file was verified to work/be accurate.
+
+verified_for
+ Human-controlled string, typically the project version (e.g. 3.0.0rc13).
+
+tier
+ Optional classification (left unset by default; do not auto-populate).
+
+Usage modes
+-----------
+Report (read-only):
+ python tools/docs_and_notebooks_check.py report
+
+Check (read-only; policy enforcement):
+ python tools/docs_and_notebooks_check.py check
+
+ Runs scans and evaluates configured policy rules.
+ Exits non-zero for policy violations.
+ Scan/parsing errors are always reported in console / JSON / Markdown output,
+ but are non-fatal by default unless strict mode is enabled or they imply a
+ policy violation.
+
+Update content-date field from git (write mode; requires --write):
+ python tools/docs_and_notebooks_check.py update --write --set-content-date-from-git
+
+Update verification fields for selected targets (write mode):
+ python tools/docs_and_notebooks_check.py update --write --targets docs/page.md \
+ --set-last-verified today --set-verified-for 3.0.0rc13
+
+Normalize notebooks deterministically (explicit churn; write mode):
+ python tools/docs_and_notebooks_check.py normalize --write --targets docs/notebook.ipynb
+
+
+Configuration
+-------------
+Uses tools/docs_and_notebooks_report_config.yml by default.
+
+Outputs
+-------
+- docs_nb_checks.json: machine-readable report
+- docs_nb_checks.md: human-readable summary
+
+Notes for CI
+------------
+- Ensure actions/checkout uses fetch-depth: 0 (or sufficiently deep),
+ otherwise git log may not see history.
+- Requires:
+ - pydantic>=2,<3
+ - PyYAML
+ - nbformat>=5
+ to be installed in the environment.
+ Recommended : install in CI job directly (pip install pydantic pyyaml nbformat)
+ rather than adding to requirements, since these are only needed for this tool.
+"""
+
+# tools/docs_and_notebooks_check.py
+from __future__ import annotations
+
+import argparse
+import fnmatch
+import json
+import os
+import re
+import subprocess
+from collections.abc import Sequence
+from datetime import date, datetime, timezone
+from pathlib import Path
+from typing import Any, Literal, TypedDict
+
+import nbformat
+import yaml
+from nbformat.validator import NotebookValidationError
+from pydantic import BaseModel, ConfigDict, Field, ValidationError
+
+SCHEMA_VERSION = 1
+GLOB_CHARS = set("*?[")
+DLC_NAMESPACE = "deeplabcut"
+OUTPUT_FILENAME = "docs_nb_checks"
+SCRIPT_DIR = Path(__file__).resolve().parent
+DEFAULT_CFG = SCRIPT_DIR / "docs_and_notebooks_report_config.yml"
+
+
+# -----------------------------
+# Metadata commit marker / guidance
+# -----------------------------
+# IMPORTANT:
+# Metadata-only updates and notebook normalization rewrite files and will change
+# "git last touched" timestamps. To preserve meaningful "content age", all such
+# commits must include this marker in the commit message.
+META_COMMIT_MARKER = "chore(metadata)"
+SUGGESTED_TAGGED_COMMIT = f"{META_COMMIT_MARKER}: update docs/notebooks metadata"
+
+
+# -----------------------------
+# Pydantic schemas
+# -----------------------------
+
+
+class DLCMeta(BaseModel):
+ """Metadata embedded in files under the `deeplabcut` namespace."""
+
+ model_config = ConfigDict(extra="allow")
+
+ # Tool-managed: last meaningful content update date (excluding metadata commits)
+ last_content_updated: date | None = None
+
+ # Optional tool-managed: last time metadata/normalization was performed
+ last_metadata_updated: date | None = None
+ # Optional human-managed verification fields
+ last_verified: date | None = None
+ # Version or other string indicating what this file was verified for (e.g. "3.0.0rc13")
+ verified_for: str | None = None
+ # Extra metadata fields for later usage (e.g. allowlist tier classification), but not currently used by the tool
+ tier: str | None = None
+ ignore: bool = False
+ notes: str | None = None
+
+
+class ScanConfig(BaseModel):
+ include: list[str] = Field(default_factory=list)
+ exclude: list[str] = Field(default_factory=list)
+
+
+class PolicyConfig(BaseModel):
+ warn_if_content_older_than_days: int = 365
+ warn_if_verified_older_than_days: int = 365
+ missing_last_verified_is_warning: bool = True
+
+ # Strict-mode toggle: if true, scan/parsing errors also fail `check`
+ fail_on_scan_errors: bool = False
+
+ # Allowlists for strict checks (start empty; ratchet later)
+ require_metadata: list[str] = Field(default_factory=list)
+ require_recent_verification: list[str] = Field(default_factory=list)
+
+ require_notebook_normalized: list[str] = Field(default_factory=list)
+
+
+class ToolConfig(BaseModel):
+ version: int = 1
+ scan: ScanConfig
+ policy: PolicyConfig
+
+
+FileKind = Literal["ipynb", "md", "other"]
+
+
+class FileRecord(BaseModel):
+ path: str
+ kind: FileKind
+
+ # Computed from git (excluding metadata-only commits)
+ last_content_updated: date | None = None
+ # Debug-only: raw git last touched (may be metadata commit)
+ last_git_touched: date | None = None
+
+ # Read from file metadata/frontmatter
+ meta: DLCMeta | None = None
+
+ # Derived
+ days_since_content_update: int | None = None
+ days_since_verified: int | None = None
+
+ warnings: list[str] = Field(default_factory=list)
+ errors: list[str] = Field(default_factory=list)
+
+ # If update mode would change file
+ would_change: bool = False
+
+
+class Report(BaseModel):
+ schema_version: int = SCHEMA_VERSION
+ generated_at: datetime
+ repo_root: str
+ config_path: str
+
+ totals: dict[str, int]
+ records: list[FileRecord]
+
+
+# Rebuild models due to __future__ annotations
+DLCMeta.model_rebuild()
+ScanConfig.model_rebuild()
+PolicyConfig.model_rebuild()
+ToolConfig.model_rebuild()
+FileRecord.model_rebuild()
+Report.model_rebuild()
+
+TargetKind = Literal["invalid", "file", "dir", "glob"]
+
+
+class TargetSpec(TypedDict):
+ raw: str
+ normalized: str
+ kind: TargetKind
+
+
+# -----------------------------
+# Helpers
+# -----------------------------
+def normalize_target_spec(spec: str, repo_root: Path) -> str:
+ """
+ Normalize a CLI target into a repo-relative POSIX-style path/pattern.
+
+ Examples
+ --------
+ .\\docs\\a.md -> docs/a.md
+ ./docs/a.md -> docs/a.md
+ docs\\gui\\ -> docs/gui
+ /abs/path/in/repo/a.md -> docs/a.md (if inside repo)
+ """
+ s = spec.strip()
+ if not s:
+ return s
+
+ # Normalize slashes first so Windows-style input works everywhere
+ s = s.replace("\\", "/")
+
+ # Strip leading ./ repeatedly
+ while s.startswith("./"):
+ s = s[2:]
+
+ # If absolute and inside repo, make it repo-relative
+ p = Path(s)
+ if p.is_absolute():
+ try:
+ s = str(p.resolve().relative_to(repo_root)).replace(os.sep, "/")
+ except ValueError:
+ # Outside repo: keep normalized absolute text so it can fail validation cleanly
+ s = str(p).replace(os.sep, "/")
+
+ # Collapse repeated slashes
+ s = re.sub(r"/+", "/", s)
+
+ # Remove trailing slash for canonical matching
+ if len(s) > 1:
+ s = s.rstrip("/")
+
+ return s
+
+
+def compile_target_specs(targets: list[str] | None, repo_root: Path) -> list[TargetSpec] | None:
+ """
+ Convert raw CLI targets into normalized selector specs.
+
+ Each spec is a dict with:
+ - raw: original user input
+ - normalized: normalized repo-relative selector
+ - kind: file | dir | glob | invalid
+ """
+ if not targets:
+ return None
+
+ specs: list[TargetSpec] = []
+
+ for raw in targets:
+ normalized = normalize_target_spec(raw, repo_root)
+ if not normalized:
+ specs.append({"raw": raw, "normalized": "", "kind": "invalid"})
+ continue
+
+ if any(ch in normalized for ch in GLOB_CHARS):
+ specs.append({"raw": raw, "normalized": normalized, "kind": "glob"})
+ continue
+
+ # Treat explicit trailing slash/backslash as directory intent
+ if raw.endswith(("/", "\\")):
+ specs.append({"raw": raw, "normalized": normalized, "kind": "dir"})
+ continue
+
+ candidate = Path(normalized)
+ abs_candidate = candidate if candidate.is_absolute() else (repo_root / candidate)
+ if abs_candidate.exists() and abs_candidate.is_dir():
+ specs.append({"raw": raw, "normalized": normalized, "kind": "dir"})
+ else:
+ specs.append({"raw": raw, "normalized": normalized, "kind": "file"})
+
+ return specs
+
+
+def target_spec_matches_path(rel_path: str, spec: TargetSpec) -> bool:
+ rel_path = rel_path.replace("\\", "/")
+
+ kind = spec["kind"]
+ normalized = spec["normalized"]
+
+ if kind == "invalid":
+ return False
+
+ if kind == "file":
+ return rel_path == normalized
+
+ if kind == "dir":
+ return rel_path == normalized or rel_path.startswith(normalized + "/")
+
+ if kind == "glob":
+ # Intentionally use simple shell-style matching here so patterns like
+ # docs/**/*.md behave the way users generally expect across platforms.
+ return fnmatch.fnmatchcase(rel_path, normalized)
+
+ return False
+
+
+def target_matches(rel_path: str, specs: list[TargetSpec] | None) -> bool:
+ if specs is None:
+ return True
+ return any(target_spec_matches_path(rel_path, spec) for spec in specs)
+
+
+def iter_scan_candidate_paths(repo_root: Path, cfg: ToolConfig) -> list[str]:
+ """
+ Return all repo-relative paths that are in scope for scanning, before applying --targets.
+ """
+ rels: list[str] = []
+ for p in glob_paths(repo_root, cfg.scan.include):
+ rel = str(p.resolve().relative_to(repo_root)).replace(os.sep, "/")
+ if is_excluded(rel, cfg.scan.exclude):
+ continue
+ rels.append(rel)
+ return sorted(set(rels))
+
+
+def validate_requested_targets(
+ repo_root: Path,
+ cfg: ToolConfig,
+ targets: list[str] | None,
+) -> tuple[list[str], list[str]]:
+ """
+ Validate CLI target selectors against the current scan universe.
+
+ Returns:
+ matched_paths: repo-relative file paths matched by any selector
+ unmatched_targets: raw target strings that matched nothing
+ """
+ if not targets:
+ return [], []
+
+ specs = compile_target_specs(targets, repo_root)
+ candidates = iter_scan_candidate_paths(repo_root, cfg)
+
+ matched_paths = sorted({rel for rel in candidates if target_matches(rel, specs)})
+
+ unmatched_targets: list[str] = []
+ for spec in specs or []:
+ if spec["kind"] == "invalid":
+ unmatched_targets.append(spec["raw"])
+ elif not any(target_spec_matches_path(rel, spec) for rel in candidates):
+ unmatched_targets.append(spec["raw"])
+
+ return matched_paths, unmatched_targets
+
+
+def print_target_match_summary(matched_paths: list[str], requested_targets: list[str] | None) -> None:
+ """
+ Print a small CLI summary whenever --targets is used.
+ """
+ if not requested_targets:
+ return
+
+ print(f"\nMatched {len(matched_paths)} file(s) from --targets:")
+ preview_limit = 50
+ for rel in matched_paths[:preview_limit]:
+ print(f"- {rel}")
+ if len(matched_paths) > preview_limit:
+ print(f"... and {len(matched_paths) - preview_limit} more")
+
+
+def _iso_today() -> date:
+ return datetime.now(timezone.utc).date()
+
+
+def _run_git(args: Sequence[str], cwd: Path) -> tuple[int, str, str]:
+ p = subprocess.run(
+ ["git", *args],
+ cwd=str(cwd),
+ capture_output=True,
+ text=True,
+ )
+ return p.returncode, p.stdout.strip(), p.stderr.strip()
+
+
+def find_repo_root(start: Path) -> Path:
+ cur = start.resolve()
+ for _ in range(50):
+ if (cur / ".git").exists():
+ return cur
+ if cur.parent == cur:
+ break
+ cur = cur.parent
+ code, out, _err = _run_git(["rev-parse", "--show-toplevel"], cwd=start)
+ if code == 0 and out:
+ return Path(out).resolve()
+ raise RuntimeError("Could not locate repository root")
+
+
+def glob_paths(repo_root: Path, patterns: list[str]) -> list[Path]:
+ results: list[Path] = []
+ for pat in patterns:
+ results.extend(repo_root.glob(pat))
+ return sorted({p.resolve() for p in results if p.is_file()})
+
+
+def is_excluded(rel_path: str, exclude_patterns: list[str]) -> bool:
+ return any(fnmatch.fnmatch(rel_path, pat) for pat in exclude_patterns)
+
+
+def file_kind(path: Path) -> str:
+ s = path.suffix.lower()
+ if s == ".ipynb":
+ return "ipynb"
+ if s in {".md", ".markdown"}:
+ return "md"
+ return "other"
+
+
+def _parse_git_iso_date(out: str) -> date | None:
+ out = (out or "").strip()
+ if not out:
+ return None
+
+ try:
+ return date.fromisoformat(out)
+ except Exception:
+ pass
+
+ try:
+ if out.endswith("Z"):
+ out = out[:-1] + "+00:00"
+ return datetime.fromisoformat(out).date()
+ except Exception:
+ return None
+
+
+def _git_log_date(repo_root: Path, rel_path: str, extra_args: Sequence[str] = ()) -> date | None:
+ args = [
+ "log",
+ "-1",
+ "--date=short",
+ "--format=%cd",
+ *extra_args,
+ "--",
+ rel_path,
+ ]
+ code, out, _err = _run_git(args, cwd=repo_root)
+ if code != 0:
+ return None
+ return _parse_git_iso_date(out)
+
+
+def git_last_touched(repo_root: Path, rel_path: str) -> date | None:
+ return _git_log_date(repo_root, rel_path)
+
+
+def git_last_content_updated(repo_root: Path, rel_path: str) -> tuple[date | None, bool]:
+ d = _git_log_date(
+ repo_root,
+ rel_path,
+ extra_args=[
+ "--fixed-strings",
+ "--invert-grep",
+ "--grep",
+ META_COMMIT_MARKER,
+ ],
+ )
+ if d is not None:
+ return d, False
+ return git_last_touched(repo_root, rel_path), True
+
+
+FRONTMATTER_RE = re.compile(r"^---\s*$")
+
+
+def read_md_frontmatter(text: str) -> tuple[dict | None, str, str | None]:
+ lines = text.splitlines(keepends=True)
+ if not lines or not FRONTMATTER_RE.match(lines[0]):
+ return None, text, None
+
+ end_idx = None
+ for i in range(1, min(len(lines), 5000)):
+ if FRONTMATTER_RE.match(lines[i]):
+ end_idx = i
+ break
+
+ if end_idx is None:
+ return None, text, "unterminated_markdown_frontmatter"
+
+ fm_text = "".join(lines[1:end_idx])
+ body = "".join(lines[end_idx + 1 :])
+
+ if yaml is None:
+ raise RuntimeError("PyYAML is required to parse Markdown frontmatter")
+
+ fm = yaml.safe_load(fm_text) if fm_text.strip() else {}
+ if not isinstance(fm, dict):
+ return None, text, "markdown_frontmatter_not_mapping"
+
+ return fm, body, None
+
+
+def dump_md_frontmatter(frontmatter: dict, body: str) -> str:
+ if yaml is None:
+ raise RuntimeError("PyYAML is required to write Markdown frontmatter")
+ fm_text = yaml.safe_dump(frontmatter, sort_keys=False, allow_unicode=True)
+ body_to_write = body
+ if body_to_write.startswith("\n"):
+ body_to_write = body_to_write[1:]
+ return "---\n" + fm_text + "---\n" + body_to_write
+
+
+def read_ipynb_meta(path: Path) -> tuple[Any, dict, bool]:
+ """
+ Read a notebook using nbformat.
+ Returns (notebook_node, deeplabcut_meta_dict, has_dlc_namespace).
+ """
+ nb = nbformat.read(str(path), as_version=4)
+
+ meta = getattr(nb, "metadata", {}) or {}
+ has_dlc = DLC_NAMESPACE in meta
+
+ raw_dlc_meta = meta.get(DLC_NAMESPACE)
+ return nb, raw_dlc_meta, has_dlc
+
+
+def notebook_is_normalized(path: Path, nb: Any) -> bool:
+ original = path.read_text(encoding="utf-8")
+ # Normalize newline style so CRLF vs LF differences do not cause false mismatches
+ original_normalized = original.replace("\r\n", "\n").replace("\r", "\n")
+ normalized = nbformat.writes(nb, version=4, indent=2, ensure_ascii=False) + "\n"
+ return original_normalized == normalized
+
+
+def write_ipynb_meta(path: Path, nb: Any) -> None:
+ """
+ Write a notebook using nbformat.
+
+ Note: nbformat writes JSON in a canonical form; it *will* rewrite the file,
+ so expect diffs if the notebook wasn't previously normalized to the same style.
+ """
+ # Validate before writing (optional but recommended)
+ nbformat.validate(nb)
+
+ # Use a stable indentation to reduce churn (choose 2 if your repo tends that way)
+ text = nbformat.writes(nb, version=4, indent=2, ensure_ascii=False)
+
+ path.write_text(text + "\n", encoding="utf-8")
+
+
+def parse_dlc_meta(raw: Any) -> tuple[DLCMeta | None, bool]:
+ # returns (meta, valid)
+ if raw is None or not isinstance(raw, dict):
+ return None, False
+ try:
+ return DLCMeta.model_validate(raw), True
+ except ValidationError:
+ return None, False
+
+
+def meta_to_jsonable(meta: DLCMeta) -> dict:
+ """
+ Return JSON-serializable metadata (dates become ISO strings).
+ This prevents json.dumps() from failing when writing .ipynb files.
+ """
+ return meta.model_dump(mode="json", exclude_none=True)
+
+
+def compute_days_since(d: date | None, today: date) -> int | None:
+ return None if d is None else (today - d).days
+
+
+def match_allowlist(rel_path: str, allowlist: list[str]) -> bool:
+ # Support exact matches or glob patterns
+ return any(pat == rel_path or fnmatch.fnmatch(rel_path, pat) for pat in allowlist)
+
+
+# -----------------------------
+# Core scanning
+# -----------------------------
+
+
+def load_config(config_path: Path) -> ToolConfig:
+ if yaml is None:
+ raise RuntimeError("PyYAML is required (pip install pyyaml)")
+ raw = yaml.safe_load(config_path.read_text(encoding="utf-8"))
+ return ToolConfig.model_validate(raw)
+
+
+def scan_files(repo_root: Path, cfg: ToolConfig, targets: list[str] | None = None) -> list[FileRecord]:
+ today = _iso_today()
+ paths = glob_paths(repo_root, cfg.scan.include)
+ records: list[FileRecord] = []
+ target_specs = compile_target_specs(targets, repo_root)
+
+ for p in paths:
+ rel = str(p.resolve().relative_to(repo_root)).replace(os.sep, "/")
+ if is_excluded(rel, cfg.scan.exclude):
+ continue
+ if not target_matches(rel, target_specs):
+ continue
+ kind = file_kind(p)
+ rec = FileRecord(path=rel, kind=kind)
+
+ rec.last_git_touched = git_last_touched(repo_root, rel)
+ rec.last_content_updated, used_fallback = git_last_content_updated(repo_root, rel)
+ rec.days_since_content_update = compute_days_since(rec.last_content_updated, today)
+ if used_fallback:
+ rec.warnings.append("content_date_fallback_to_git_touched")
+
+ try:
+ if kind == "ipynb":
+ nb, raw_meta, has_dlc = read_ipynb_meta(p)
+
+ try:
+ nbformat.validate(nb)
+ except NotebookValidationError as e:
+ rec.errors.append(f"nbformat_invalid: {e}")
+
+ try:
+ if not notebook_is_normalized(p, nb):
+ rec.warnings.append("notebook_not_normalized")
+ except Exception as e:
+ rec.errors.append(f"notebook_normalization_check_failed: {e}")
+
+ if not has_dlc:
+ rec.meta = None
+ rec.warnings.append("missing_metadata")
+ else:
+ rec.meta, valid = parse_dlc_meta(raw_meta)
+ if not valid:
+ rec.meta = None
+ rec.warnings.append("invalid_metadata")
+
+ elif kind == "md":
+ text = p.read_text(encoding="utf-8")
+ fm, _body, fm_error = read_md_frontmatter(text)
+
+ if fm_error:
+ rec.meta = None
+ rec.warnings.append("invalid_metadata")
+ rec.errors.append(f"markdown_frontmatter_invalid: {fm_error}")
+ else:
+ fm = fm or {}
+ has_dlc = DLC_NAMESPACE in fm
+ raw = fm.get(DLC_NAMESPACE)
+
+ if not has_dlc:
+ rec.meta = None
+ rec.warnings.append("missing_metadata")
+ else:
+ rec.meta, valid = parse_dlc_meta(raw)
+ if not valid:
+ rec.meta = None
+ rec.warnings.append("invalid_metadata")
+
+ else:
+ rec.meta = None
+
+ except Exception as e:
+ rec.errors.append(f"metadata_read_failed: {e}")
+
+ # ignore=True means: keep reporting diagnostics, but skip freshness/policy logic
+ if rec.meta and rec.meta.ignore:
+ records.append(rec)
+ continue
+
+ last_verified = rec.meta.last_verified if rec.meta else None
+ rec.days_since_verified = compute_days_since(last_verified, today)
+
+ # Future dates are data errors
+ if rec.last_content_updated is not None and rec.last_content_updated > today:
+ rec.errors.append("future_last_content_updated")
+ if rec.meta and rec.meta.last_metadata_updated is not None:
+ if rec.meta.last_metadata_updated > today:
+ rec.errors.append("future_last_metadata_updated")
+ if last_verified is not None and last_verified > today:
+ rec.errors.append("future_last_verified")
+
+ pol = cfg.policy
+
+ if (
+ rec.days_since_content_update is not None
+ and rec.days_since_content_update > pol.warn_if_content_older_than_days
+ ):
+ rec.warnings.append(f"content_stale>{pol.warn_if_content_older_than_days}d")
+
+ if last_verified is None and pol.missing_last_verified_is_warning:
+ rec.warnings.append("missing_last_verified")
+ elif rec.days_since_verified is not None and rec.days_since_verified > pol.warn_if_verified_older_than_days:
+ rec.warnings.append(f"verified_stale>{pol.warn_if_verified_older_than_days}d")
+
+ records.append(rec)
+
+ return records
+
+
+# -----------------------------
+# Update mode
+# -----------------------------
+def _require_meta_marker_ack(write: bool, ack_marker: bool) -> None:
+ """
+ Guardrail: writing metadata/normalization without the marker convention will
+ destroy the meaning of content freshness signals. Require an explicit ack.
+ """
+ if not write:
+ return
+ if ack_marker:
+ return
+ raise SystemExit(
+ "Refusing to write without acknowledging metadata-commit convention.\n"
+ "Re-run with --ack-meta-commit-marker and commit with:\n"
+ f" {SUGGESTED_TAGGED_COMMIT}\n"
+ )
+
+
+def update_files(
+ repo_root: Path,
+ cfg: ToolConfig,
+ targets: list[str] | None,
+ write: bool,
+ set_content_date_from_git: bool,
+ set_last_verified: date | None,
+ set_verified_for: str | None,
+ ack_meta_commit_marker: bool,
+) -> list[FileRecord]:
+ today = _iso_today()
+ records = scan_files(repo_root, cfg, targets=targets)
+
+ for rec in records:
+ if rec.kind not in {"ipynb", "md"}:
+ continue
+ if rec.meta and rec.meta.ignore:
+ continue
+
+ meta = rec.meta or DLCMeta()
+
+ # Build the desired metadata WITHOUT touching last_metadata_updated.
+ if set_content_date_from_git and rec.last_content_updated is not None:
+ meta.last_content_updated = rec.last_content_updated
+
+ if set_last_verified is not None:
+ meta.last_verified = set_last_verified
+ if set_verified_for is not None:
+ meta.verified_for = set_verified_for
+
+ desired_base = meta_to_jsonable(meta)
+ abs_path = repo_root / rec.path
+ changed = False
+
+ if rec.kind == "ipynb":
+ nb, _raw, _has_dlc = read_ipynb_meta(abs_path)
+ nb_meta = nb.setdefault("metadata", {})
+ prev = nb_meta.get(DLC_NAMESPACE, {})
+ if not isinstance(prev, dict):
+ prev = {}
+
+ merged_base = dict(prev)
+ merged_base.update(desired_base)
+
+ if merged_base != prev:
+ changed = True
+ if write:
+ _require_meta_marker_ack(write=True, ack_marker=ack_meta_commit_marker)
+
+ meta.last_metadata_updated = today
+ desired_final = meta_to_jsonable(meta)
+
+ merged_final = dict(prev)
+ merged_final.update(desired_final)
+ nb_meta[DLC_NAMESPACE] = merged_final
+ write_ipynb_meta(abs_path, nb)
+
+ elif rec.kind == "md":
+ text = abs_path.read_text(encoding="utf-8")
+ fm, body, fm_error = read_md_frontmatter(text)
+ if fm_error:
+ msg = f"markdown_frontmatter_invalid: {fm_error}"
+ if msg not in rec.errors:
+ rec.errors.append(msg)
+ continue
+
+ fm = fm or {}
+
+ prev = fm.get(DLC_NAMESPACE, {})
+ if not isinstance(prev, dict):
+ prev = {}
+
+ merged_base = dict(prev)
+ merged_base.update(desired_base)
+
+ if merged_base != prev:
+ changed = True
+ if write:
+ _require_meta_marker_ack(write=True, ack_marker=ack_meta_commit_marker)
+
+ meta.last_metadata_updated = today
+ desired_final = meta_to_jsonable(meta)
+
+ merged_final = dict(prev)
+ merged_final.update(desired_final)
+ fm[DLC_NAMESPACE] = merged_final
+ abs_path.write_text(dump_md_frontmatter(fm, body), encoding="utf-8")
+
+ rec.would_change = changed
+ rec.meta = meta
+ rec.days_since_verified = compute_days_since(meta.last_verified, today)
+
+ return records
+
+
+# -----------------------------
+# Notebook formatting
+# -----------------------------
+def normalize_notebooks(
+ repo_root: Path,
+ cfg: ToolConfig,
+ targets: list[str] | None,
+ write: bool,
+ ack_meta_commit_marker: bool,
+) -> list[FileRecord]:
+ """
+ Normalize notebooks deterministically (canonical nbformat JSON).
+ This is intentionally separated from update() because it causes churn.
+ """
+ _require_meta_marker_ack(write=write, ack_marker=ack_meta_commit_marker)
+ records = scan_files(repo_root, cfg, targets=targets)
+ today = _iso_today()
+
+ for rec in records:
+ if rec.kind != "ipynb":
+ continue
+ if rec.meta and rec.meta.ignore:
+ continue
+
+ abs_path = repo_root / rec.path
+ try:
+ nb, _raw, _has_dlc = read_ipynb_meta(abs_path)
+ nbformat.validate(nb)
+
+ if not notebook_is_normalized(abs_path, nb):
+ rec.would_change = True
+ if write:
+ # Update embedded maintenance timestamp
+ meta = rec.meta or DLCMeta()
+ meta.last_metadata_updated = today
+
+ nb_meta = nb.setdefault("metadata", {})
+ prev = nb_meta.get(DLC_NAMESPACE, {})
+ if not isinstance(prev, dict):
+ prev = {}
+ merged = dict(prev)
+ merged.update(meta_to_jsonable(meta))
+ nb_meta[DLC_NAMESPACE] = merged
+
+ # Write to persist metadata update (still canonical)
+ write_ipynb_meta(abs_path, nb)
+ rec.meta = meta
+
+ except Exception as e:
+ rec.errors.append(f"normalize_failed: {e}")
+
+ return records
+
+
+# -----------------------------
+# Output formatting
+# -----------------------------
+
+
+def summarize(records: list[FileRecord]) -> dict[str, int]:
+ return {
+ "files": len(records),
+ "warnings": sum(1 for r in records if r.warnings),
+ "errors": sum(1 for r in records if r.errors),
+ "missing_metadata": sum(1 for r in records if "missing_metadata" in r.warnings),
+ "missing_last_verified": sum(1 for r in records if "missing_last_verified" in r.warnings),
+ "content_stale": sum(1 for r in records if any(w.startswith("content_stale") for w in r.warnings)),
+ "verified_stale": sum(1 for r in records if any(w.startswith("verified_stale") for w in r.warnings)),
+ }
+
+
+def to_markdown(report: Report, cfg: ToolConfig) -> str:
+ pol = cfg.policy
+ t = report.totals
+ lines: list[str] = []
+
+ lines.append("# 🌡️ DeepLabCut freshness report\n")
+ lines.append(f"Generated: {report.generated_at.isoformat()}\n")
+ lines.append(f"Schema: v{report.schema_version}\n\n")
+
+ lines.append("## Summary\n")
+ lines.append(f"- Files scanned: **{t['files']}**\n")
+ lines.append(f"- Files with warnings: **{t['warnings']}**\n")
+ lines.append(f"- Files with scanning errors: **{t['errors']}**\n")
+ lines.append(f"- Missing metadata: **{t['missing_metadata']}**\n")
+ lines.append(f"- Missing last_verified: **{t['missing_last_verified']}**\n")
+ lines.append(f"- Content-stale (> {pol.warn_if_content_older_than_days}d): **{t['content_stale']}**\n")
+ lines.append(f"- Verification-stale (> {pol.warn_if_verified_older_than_days}d): **{t['verified_stale']}**\n\n")
+
+ def fmt_date(d: date | None) -> str:
+ return d.isoformat() if d else "-"
+
+ warn_recs = [r for r in report.records if r.warnings and not (r.meta and r.meta.ignore)]
+ warn_recs.sort(
+ key=lambda r: (
+ -(r.days_since_verified or -1),
+ -(r.days_since_content_update or -1),
+ r.path,
+ )
+ )
+
+ if warn_recs:
+ lines.append("## Warnings\n")
+ for r in warn_recs:
+ meta = r.meta
+ lines.append(f"- **{r.path}** ({r.kind})\n")
+ lines.append(
+ f" - last_content_updated: {fmt_date(r.last_content_updated)} "
+ f"(days: {r.days_since_content_update if r.days_since_content_update is not None else '-'})\n"
+ )
+ if r.last_git_touched:
+ lines.append(f" - last_git_touched: {fmt_date(r.last_git_touched)}\n")
+ if meta and meta.last_metadata_updated:
+ lines.append(f" - last_metadata_updated: {fmt_date(meta.last_metadata_updated)}\n")
+ lv = meta.last_verified if meta else None
+ lines.append(
+ f" - last_verified: {fmt_date(lv)} "
+ f"(days: {r.days_since_verified if r.days_since_verified is not None else '-'})\n"
+ )
+ if meta and meta.verified_for:
+ lines.append(f" - verified_for: {meta.verified_for}\n")
+ if meta and meta.tier:
+ lines.append(f" - tier: {meta.tier}\n")
+ lines.append(f" - warnings: {', '.join(r.warnings)}\n")
+ if r.errors:
+ lines.append(f" - errors: {', '.join(r.errors)}\n")
+ lines.append("\n")
+
+ err_recs = [r for r in report.records if r.errors]
+ if err_recs:
+ lines.append("## Scan errors\n")
+ for r in err_recs:
+ lines.append(f"- **{r.path}**: {', '.join(r.errors)}\n")
+ lines.append("\n")
+
+ lines.append("## Notes\n")
+ lines.append("- 'Out of date' does not necessarily mean 'broken'. Use this as a triage signal.\n")
+ lines.append(
+ "- last_git_touched / last_content_updated are computed from git history. "
+ "last_verified is human-controlled.\n\n"
+ )
+ lines.append(
+ "- In `check` mode, scan/parsing errors are reported for visibility but do not "
+ "fail by default unless strict mode is enabled or they trigger an enforced policy rule.\n"
+ )
+ return "".join(lines)
+
+
+def write_outputs(report: Report, cfg: ToolConfig, out_dir: Path) -> tuple[Path, Path]:
+ out_dir.mkdir(parents=True, exist_ok=True)
+ json_path = out_dir / f"{OUTPUT_FILENAME}.json"
+ md_path = out_dir / f"{OUTPUT_FILENAME}.md"
+
+ payload = report.model_dump(mode="json")
+
+ json_path.write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
+ md_path.write_text(to_markdown(report, cfg), encoding="utf-8")
+ return json_path, md_path
+
+
+# -----------------------------
+# Check enforcement
+# -----------------------------
+def enforce(cfg: ToolConfig, records: list[FileRecord]) -> list[str]:
+ pol = cfg.policy
+ violations: list[str] = []
+ today = _iso_today()
+
+ for r in records:
+ if r.meta and r.meta.ignore:
+ continue
+ if r.kind not in {"ipynb", "md"}:
+ continue
+
+ has_invalid_metadata = "invalid_metadata" in (r.warnings or [])
+
+ if match_allowlist(r.path, pol.require_metadata):
+ if has_invalid_metadata:
+ violations.append(f"{r.path}: invalid metadata")
+ elif r.meta is None:
+ violations.append(f"{r.path}: missing metadata")
+
+ if match_allowlist(r.path, pol.require_recent_verification):
+ if has_invalid_metadata:
+ violations.append(f"{r.path}: invalid metadata")
+ else:
+ lv = r.meta.last_verified if r.meta else None
+ if lv is None:
+ violations.append(f"{r.path}: missing last_verified")
+ else:
+ days = (today - lv).days
+ if days > pol.warn_if_verified_older_than_days:
+ violations.append(
+ f"{r.path}: last_verified is {days}d old (> {pol.warn_if_verified_older_than_days}d)"
+ )
+
+ if r.kind == "ipynb" and match_allowlist(r.path, pol.require_notebook_normalized):
+ if "notebook_not_normalized" in (r.warnings or []):
+ violations.append(f"{r.path}: notebook is not normalized (run update/format)")
+
+ return violations
+
+
+# -----------------------------
+# CLI
+# -----------------------------
+
+
+def parse_date_token(token: str) -> date:
+ token = token.strip().lower()
+ if token in {"today", "now"}:
+ return _iso_today()
+ return date.fromisoformat(token)
+
+
+def collect_scan_issues(records: list[FileRecord], target: Literal["errors", "warnings"]) -> list[str]:
+ items: list[str] = []
+ for r in records:
+ for e in getattr(r, target, []):
+ items.append(f"{r.path}: {e}")
+ return items
+
+
+def main(argv: Sequence[str] | None = None) -> int:
+ parser = argparse.ArgumentParser(description="DeepLabCut checks tool (docs + notebooks)")
+ parser.add_argument("--config", default=str(DEFAULT_CFG), help="Path to YAML config file")
+ parser.add_argument(
+ "--no-step-summary",
+ action="store_true",
+ help="Do not write to GITHUB_STEP_SUMMARY",
+ )
+ parser.add_argument("--out-dir", default=f"tmp/{OUTPUT_FILENAME}", help="Directory to write outputs")
+
+ sub = parser.add_subparsers(dest="cmd", required=True)
+ rep = sub.add_parser("report", help="Generate staleness report (read-only)")
+ rep.add_argument(
+ "--targets",
+ nargs="*",
+ help=(
+ "Optional repo-relative targets to limit the operation. "
+ "Supports exact files, directories, and glob patterns "
+ "(e.g. docs/page.md, docs/gui/, 'docs/**/*.md'). "
+ "Both '/' and '\\' are accepted."
+ ),
+ )
+
+ chk = sub.add_parser(
+ "check",
+ help=(
+ "Run scans + policy checks (read-only). "
+ "Fails on enforced policy violations; scan errors are non-fatal by default."
+ ),
+ )
+ chk.add_argument(
+ "--targets",
+ nargs="*",
+ help=(
+ "Optional list of relative file paths to scan (limits scan to these files). "
+ "Supports exact files, directories, and glob patterns (e.g. docs/page.md, docs/gui/, 'docs/**/*.md'). "
+ "Both '/' and '\\' are accepted."
+ ),
+ )
+ chk.add_argument(
+ "--strict-mode",
+ action="store_true",
+ help="Enable failure on scan/parsing errors (overrides config for this run)",
+ )
+
+ up = sub.add_parser("update", help="Update metadata/frontmatter (write mode requires --write)")
+ up.add_argument(
+ "--write",
+ action="store_true",
+ help="Actually write changes (otherwise dry-run)",
+ )
+ up.add_argument(
+ "--set-content-date-from-git",
+ action="store_true",
+ help="Set embedded last_content_updated from computed git content date",
+ )
+ up.add_argument(
+ "--targets",
+ nargs="*",
+ help=(
+ "Optional list of relative file paths to update. "
+ "Supports exact files, directories, and glob patterns (e.g. docs/page.md, docs/gui/, 'docs/**/*.md'). "
+ "Both '/' and '\\' are accepted."
+ ),
+ )
+ up.add_argument("--set-last-verified", default=None, help="YYYY-MM-DD or 'today'")
+ up.add_argument("--set-verified-for", default=None, help="String like 3.0.0rc13")
+ up.add_argument(
+ "--ack-meta-commit-marker",
+ action="store_true",
+ help=f"Acknowledge that you will commit changes using marker: {META_COMMIT_MARKER}",
+ )
+
+ norm = sub.add_parser(
+ "normalize",
+ help="Normalize notebooks deterministically (write mode requires --write)",
+ )
+ norm.add_argument(
+ "--write",
+ action="store_true",
+ help="Actually write changes (otherwise dry-run)",
+ )
+ norm.add_argument(
+ "--targets",
+ nargs="*",
+ help=(
+ "Optional list of relative notebook paths to normalize. "
+ "Supports exact files, directories, and glob patterns "
+ "(e.g. notebooks/example.ipynb, notebooks/, 'notebooks/**/*.ipynb'). "
+ "Both '/' and '\\' are accepted."
+ ),
+ )
+ norm.add_argument(
+ "--ack-meta-commit-marker",
+ action="store_true",
+ help=f"Acknowledge that you will commit changes using marker: {META_COMMIT_MARKER}",
+ )
+
+ args = parser.parse_args(list(argv) if argv is not None else None)
+
+ config_path = Path(args.config)
+ repo_root = find_repo_root(Path.cwd())
+ cfg = load_config(config_path)
+ out_dir = Path(args.out_dir)
+
+ requested_targets = getattr(args, "targets", None)
+
+ if requested_targets:
+ matched_paths, unmatched_targets = validate_requested_targets(repo_root, cfg, requested_targets)
+ print_target_match_summary(matched_paths, requested_targets)
+
+ if unmatched_targets:
+ print("\nUnmatched --targets:")
+ for raw in unmatched_targets:
+ print(f"- {raw}")
+ print(
+ "\nEach --targets selector must match at least one file in the configured scan set. "
+ "Use repo-relative paths, directories, or glob patterns "
+ "(for example: docs/page.md, docs/gui/, 'docs/**/*.md')."
+ )
+ return 2
+
+ if args.cmd in {"report", "check"}:
+ records = scan_files(repo_root, cfg, targets=getattr(args, "targets", None))
+ elif args.cmd == "update":
+ lv = parse_date_token(args.set_last_verified) if args.set_last_verified else None
+ records = update_files(
+ repo_root,
+ cfg,
+ targets=args.targets,
+ write=bool(args.write),
+ set_content_date_from_git=bool(args.set_content_date_from_git),
+ set_last_verified=lv,
+ set_verified_for=args.set_verified_for,
+ ack_meta_commit_marker=bool(args.ack_meta_commit_marker),
+ )
+ if args.write:
+ print(f"\nSuggested commit message:\n {SUGGESTED_TAGGED_COMMIT}\n")
+
+ else: # normalize
+ records = normalize_notebooks(
+ repo_root,
+ cfg,
+ targets=args.targets,
+ write=bool(args.write),
+ ack_meta_commit_marker=bool(args.ack_meta_commit_marker),
+ )
+ if args.write:
+ print(f"\nSuggested commit message:\n {SUGGESTED_TAGGED_COMMIT}\n")
+
+ report = Report(
+ generated_at=datetime.now(timezone.utc),
+ repo_root=str(repo_root),
+ config_path=str(config_path),
+ totals=summarize(records),
+ records=records,
+ )
+
+ json_path, md_path = write_outputs(report, cfg, out_dir)
+
+ # Emit GitHub Actions job summary if available
+ emit_summary = not getattr(args, "no_step_summary", False)
+ step_summary = os.environ.get("GITHUB_STEP_SUMMARY")
+ if emit_summary and step_summary and md_path.exists():
+ try:
+ content = md_path.read_text(encoding="utf-8")
+ # snippet = "\n".join(content.splitlines()[:220]) + "\n"
+ snippet = "\n".join(content.splitlines()[:]) + "\n"
+ Path(step_summary).write_text(snippet, encoding="utf-8")
+ except Exception:
+ pass
+
+ scan_errors = collect_scan_issues(records, target="errors")
+ if scan_errors:
+ print("\nScan errors detected (non-fatal by default):")
+ for item in scan_errors[:20]:
+ print(f"- {item}")
+ if len(scan_errors) > 20:
+ print(f"... and {len(scan_errors) - 20} more (see report for full details)")
+
+ if args.cmd == "check":
+ violations = enforce(cfg, records)
+ if violations:
+ print("Policy violations:")
+ for v in violations:
+ print(f"- {v}")
+ return 2
+ strict_mode = bool((args.strict_mode) or cfg.policy.fail_on_scan_errors)
+ if strict_mode and scan_errors:
+ print("Strict mode enabled: failing due to scan/parsing errors.")
+ return 1
+
+ # Non-zero if metadata parsing errors occurred for non-report/check commands
+ if args.cmd not in {"report", "check"} and any(r.errors for r in records):
+ return 1
+ else:
+ print("\nReport generated:")
+ print(f"- JSON: {json_path}")
+ print(f"- Markdown: {md_path}")
+
+ if any(r.warnings for r in records):
+ print("Warnings detected; see report for details.")
+ if any(r.errors for r in records):
+ print("Scan errors detected; see report for details.")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/tools/docs_and_notebooks_report_config.yml b/tools/docs_and_notebooks_report_config.yml
new file mode 100644
index 0000000000..60dfcb21f8
--- /dev/null
+++ b/tools/docs_and_notebooks_report_config.yml
@@ -0,0 +1,26 @@
+version: 1
+
+scan:
+ include:
+ - "examples/COLAB/**/*.ipynb"
+ - "examples/JUPYTER/**/*.ipynb"
+ - "docs/**/*.md"
+ - "docs/**/*.ipynb" # if notebooks get added to docs (Jupyter Book supports this)
+ exclude:
+ - "**/.ipynb_checkpoints/**"
+ - "**/_build/**"
+ - "**/build/**"
+
+policy:
+ warn_if_content_older_than_days: 365
+ warn_if_verified_older_than_days: 365
+ missing_last_verified_is_warning: true
+
+ # Ratchet lists for tiered verification requirements.
+ # Tiers have to be determined, and crucial targets identified.
+ # Then specific policies can be set for each tier,
+ # e.g. requiring more recent verification for higher tiers,
+ # or requiring verification for more recent versions.
+ require_metadata: []
+ require_recent_verification: []
+ require_notebook_normalized: []
diff --git a/tools/docs_and_notebooks_tool_README.md b/tools/docs_and_notebooks_tool_README.md
new file mode 100644
index 0000000000..fa27c5dc8f
--- /dev/null
+++ b/tools/docs_and_notebooks_tool_README.md
@@ -0,0 +1,190 @@
+# Docs & Notebooks Checks Tool
+
+This tool scans DeepLabCut documentation pages and notebooks and produces **two independent signals**:
+
+- **`last_content_updated`**: computed from git history as the last *meaningful content* update **excluding metadata-only commits**.
+- **`last_verified`**: a human-controlled date indicating the content was verified to work/be accurate.
+
+In addition, the tool can optionally track:
+
+- **`last_metadata_updated`**: when the tool last performed a metadata/normalization write (helps explain “file changed” without implying content changed).
+- **`verified_for`**: a human-controlled string indicating what the content was verified against (e.g. `3.0.0rc13`).
+
+The tool is designed to be:
+
+- **Safe by default**: CI should run **read-only** modes (`report` / `check`).
+- **Deterministic**: stable outputs and normalized notebook formatting when explicitly requested.
+- **Future-proof**: versioned Pydantic schemas (`schema_version`).
+
+---
+
+## What gets scanned
+
+Default include patterns are defined in `tools/docs_and_notebooks_report_config.yml`.
+Typical patterns include:
+
+- `examples/COLAB/**/*.ipynb`
+- `examples/JUPYTER/**/*.ipynb`
+- `docs/**/*.md`
+- `docs/**/*.ipynb` (if notebooks are added under docs)
+
+You can further restrict the scan via `--targets`.
+
+---
+
+## Metadata storage locations
+
+### Notebooks (`.ipynb`)
+
+The tool **only** reads/writes **top-level notebook metadata** under the `deeplabcut` namespace.
+
+> [!IMPORTANT]
+> It never edits notebook cells, outputs, or execution counts.
+
+Example (excerpt):
+
+```json
+{
+ "metadata": {
+ "deeplabcut": {
+ "last_content_updated": "2020-01-01",
+ "last_metadata_updated": "2026-03-05",
+ "last_verified": "2026-02-20",
+ "verified_for": "3.0.0rc13",
+ "ignore": false
+ }
+ }
+}
+```
+
+> [!NOTE]
+> `tier` is intentionally optional and is not auto-populated.
+
+### Markdown (`.md`)
+
+The tool reads/writes YAML frontmatter at the top of the file (if present):
+
+```yaml
+---
+deeplabcut:
+ last_content_updated: 2020-01-01
+ last_metadata_updated: 2026-03-05
+ last_verified: 2026-02-20
+ verified_for: 3.0.0rc13
+ ignore: false
+---
+```
+
+If a doc page has **no** frontmatter, the tool can still report staleness (read-only), and `update` can add/modify metadata when explicitly requested.
+
+---
+
+## The metadata-commit marker (critical)
+
+Because metadata updates and notebook normalization can rewrite files, they would normally make git (correctly) report that the file was “updated now”.
+
+To preserve a meaningful **`last_content_updated`**, **all metadata-only / normalization commits must include the marker**:
+
+- **Marker**: `META_COMMIT_MARKER` (see `tools/docs_and_notebooks_check.py`)
+- **Suggested commit message**: `SUGGESTED_META_COMMIT_MESSAGE`
+
+When you run `update --write` or `normalize --write`, the tool will:
+
+- Require `--ack-meta-commit-marker` (guardrail)
+- Print a suggested commit message
+
+> [!WARNING]
+> If the marker changes in the future, previous iterations still HAVE to be acknowledged to avoid false positives.
+
+---
+
+## Commands
+
+### 1) Report (read-only)
+
+Generate a report (does not modify files):
+
+```bash
+python tools/docs_and_notebooks_check.py report
+```
+
+Writes (by default):
+
+- `tmp/docs_nb_checks/docs_nb_checks.json`
+- `tmp/docs_nb_checks/docs_nb_checks.md`
+
+### 2) Check (read-only; may fail)
+
+Run policy checks. By default, CI will not fail unless allowlists are configured.
+
+```bash
+python tools/docs_and_notebooks_check.py check
+```
+
+The allowlists live in `tools/docs_and_notebooks_report_config.yml`.
+They are currently empty, but can help enforce stricter policies once populated (start empty; "ratchet" later).
+
+### 3) Update metadata (write mode; explicit intent)
+
+> [!WARNING]
+> `update --write` modifies tracked files. Intended for maintainers (manual), not CI.
+
+#### 3a) Set `last_content_updated` from git (excluding meta commits)
+
+```bash
+python tools/docs_and_notebooks_check.py update --write --set-content-date-from-git --ack-meta-commit-marker
+```
+
+#### 3b) Set verification fields (human-controlled)
+
+```bash
+python tools/docs_and_notebooks_check.py update --write --targets docs/page.md examples/JUPYTER/foo.ipynb --set-last-verified today --set-verified-for 3.0.0rc13 --ack-meta-commit-marker
+```
+
+> Tip: omit `--targets` to operate on all scanned files.
+
+### 4) Normalize notebooks (explicit churn)
+
+> [!IMPORTANT]
+> Notebook normalization rewrites the notebook JSON into a canonical form.
+> As such, it is provided as a separate command.
+
+Dry-run (shows which files *would* change):
+
+```bash
+python tools/docs_and_notebooks_check.py normalize --targets docs/notebook.ipynb
+```
+
+Write:
+
+```bash
+python tools/docs_and_notebooks_check.py normalize --write --targets docs/notebook.ipynb --ack-meta-commit-marker
+```
+
+---
+
+## CI integration
+
+Recommended CI usage:
+
+- Run `report` on PRs and upload the outputs as artifacts.
+- Run `check` once allowlists are populated (start empty to avoid failures).
+
+> [!IMPORTANT]
+> Use `actions/checkout` with `fetch-depth: 0` (or sufficiently deep) so `git log` sees history; shallow clones can cause missing or fallback timestamps.
+
+Dependencies required for this tool (install in the CI job):
+
+```bash
+pip install pydantic pyyaml nbformat
+```
+
+---
+
+## Troubleshooting
+
+- If you see `content_date_fallback_to_git_touched`, it usually means one of:
+ - The checkout history is too shallow, or
+ - *All* commits touching the file are metadata commits with the marker.
+
+- If Pydantic raises `class-not-fully-defined` errors, ensure the tool calls `.model_rebuild()` for its models (this is already done in the tool).
diff --git a/tools/find_import_cycles.py b/tools/find_import_cycles.py
new file mode 100644
index 0000000000..743392dc15
--- /dev/null
+++ b/tools/find_import_cycles.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python3
+from __future__ import annotations
+
+import ast
+from collections import defaultdict
+from pathlib import Path
+
+
+def path_to_module(root: Path, file: Path) -> str:
+ rel = file.relative_to(root)
+ parts = rel.with_suffix("").parts
+ if parts[-1] == "__init__":
+ parts = parts[:-1]
+ return ".".join((root.name, *parts)) if parts else root.name
+
+
+def module_to_file_map(root: Path) -> dict[str, Path]:
+ mapping = {}
+ for file in root.rglob("*.py"):
+ mod = path_to_module(root, file)
+ mapping[mod] = file
+ return mapping
+
+
+def resolve_relative_import(current_module: str, module: str | None, level: int) -> str | None:
+ parts = current_module.split(".")
+ if level > len(parts):
+ return None
+ base = parts[:-level]
+ if module:
+ return ".".join(base + module.split("."))
+ return ".".join(base)
+
+
+def extract_imports(file: Path, current_module: str) -> set[str]:
+ source = file.read_text(encoding="utf-8")
+ tree = ast.parse(source, filename=str(file))
+ imports: set[str] = set()
+
+ for node in ast.walk(tree):
+ if isinstance(node, ast.Import):
+ for alias in node.names:
+ imports.add(alias.name)
+ elif isinstance(node, ast.ImportFrom):
+ if node.level and current_module:
+ resolved = resolve_relative_import(current_module, node.module, node.level)
+ if resolved:
+ imports.add(resolved)
+ elif node.module:
+ imports.add(node.module)
+
+ return imports
+
+
+def internal_edges(root: Path) -> dict[str, set[str]]:
+ mod_to_file = module_to_file_map(root)
+ internal = set(mod_to_file)
+ edges: dict[str, set[str]] = defaultdict(set)
+
+ for mod, file in mod_to_file.items():
+ for imported in extract_imports(file, mod):
+ # Keep only imports that are inside the package
+ for candidate in internal:
+ if imported == candidate or imported.startswith(candidate + "."):
+ edges[mod].add(candidate)
+ break
+
+ return edges
+
+
+def find_cycles(edges: dict[str, set[str]]) -> list[list[str]]:
+ visited = set()
+ stack = []
+ on_stack = set()
+ cycles = []
+
+ def dfs(node: str):
+ visited.add(node)
+ stack.append(node)
+ on_stack.add(node)
+
+ for neighbor in edges.get(node, ()):
+ if neighbor not in visited:
+ dfs(neighbor)
+ elif neighbor in on_stack:
+ idx = stack.index(neighbor)
+ cycle = stack[idx:] + [neighbor]
+ cycles.append(cycle)
+
+ stack.pop()
+ on_stack.remove(node)
+
+ for node in edges:
+ if node not in visited:
+ dfs(node)
+
+ # Deduplicate roughly
+ seen = set()
+ unique = []
+ for cyc in cycles:
+ key = tuple(cyc)
+ if key not in seen:
+ seen.add(key)
+ unique.append(cyc)
+ return unique
+
+
+def main():
+ root = Path("deeplabcut") # change if needed
+ edges = internal_edges(root)
+ cycles = find_cycles(edges)
+
+ if not cycles:
+ print("No cycles found.")
+ return
+
+ print("Import cycles found:\n")
+ for cyc in cycles:
+ print(" -> ".join(cyc))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/ruff_cleanup_helpers.md b/tools/ruff_cleanup_helpers.md
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tools/ruff_report.py b/tools/ruff_report.py
new file mode 100644
index 0000000000..bfa7e6f206
--- /dev/null
+++ b/tools/ruff_report.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python3
+"""Generate a readable Markdown report from Ruff JSON output.
+
+Usage:
+ python ruff_report.py . --output ruff-report.md
+ python ruff_report.py src tests --output lint/ruff-report.md
+"""
+
+from __future__ import annotations
+
+import argparse
+import collections
+import json
+import os
+import subprocess
+import sys
+from collections.abc import Iterable
+from pathlib import Path
+
+RULE_NOTES = {
+ "F401": "Unused import. Usually safe to delete; verify imports with side effects.",
+ "E501": "Line too long. Prefer wrapping expressions, splitting long strings/comments, or extracting variables.",
+ "E402": "Module import not at top of file. Move imports above executable code if possible.",
+ "F403": "`from x import *` makes names unclear. Replace with explicit imports.",
+ "F405": "Likely consequence of `import *`. Import the name explicitly.",
+ "F821": "Undefined name. Usually a real bug or missing import.",
+ "E722": "Bare `except:`. Catch `Exception` or a narrower exception type.",
+ "B904": "Inside `except`, use `raise ... from e` to preserve exception chaining.",
+ "B007": "Unused loop variable. Rename to `_` or use it.",
+ "UP031": "Old `%` formatting. Convert to f-strings or `.format()` where appropriate.",
+ "E721": "Avoid direct `type(x) == Y`; prefer `isinstance(x, Y)`.",
+ "B008": "Function call in default arg. Use `None` + initialize inside the function.",
+ "B023": "Function closes over loop variable. Bind it via default arg or helper.",
+ "B024": "ABC without abstract method. Add `@abstractmethod` or remove ABC intent.",
+ "F811": "Redefined while unused. Remove duplicate or rename.",
+ "B012": "Jump statement in `finally` can swallow exceptions. Restructure flow.",
+ "B016": "Raise an exception instance/class, not a literal.",
+ "B017": "Use a more specific exception with `assertRaises`.",
+ "B020": "Loop variable overrides iterator. Rename loop variables.",
+ "B027": "Empty method in ABC without abstract decorator. Add `@abstractmethod` or implement it.",
+}
+
+
+def run_ruff(paths: Iterable[str]) -> list[dict]:
+ cmd = [sys.executable, "-m", "ruff", "check", *paths, "--output-format=json", "--exit-zero"]
+ proc = subprocess.run(cmd, capture_output=True, text=True)
+ if proc.returncode not in (0, 1):
+ print(proc.stdout)
+ print(proc.stderr, file=sys.stderr)
+ raise SystemExit(f"Failed to run Ruff: {' '.join(cmd)}")
+ data = json.loads(proc.stdout or "[]")
+ if not isinstance(data, list):
+ raise SystemExit("Unexpected Ruff JSON output")
+ return data
+
+
+def relpath(path: str) -> str:
+ try:
+ return os.path.relpath(path)
+ except Exception:
+ return path
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("paths", nargs="*", default=["."], help="Files/directories to scan")
+ parser.add_argument("--output", default="tmp/ruff-report.md", help="Markdown output path")
+ args = parser.parse_args()
+
+ issues = run_ruff(args.paths)
+
+ by_rule: dict[str, list[dict]] = collections.defaultdict(list)
+ for item in issues:
+ by_rule[item.get("code", "UNKNOWN")].append(item)
+
+ out = Path(args.output)
+ out.parent.mkdir(parents=True, exist_ok=True)
+
+ lines: list[str] = []
+ lines.append("# Ruff manual-fix report\n")
+ lines.append(f"Generated from: `{', '.join(args.paths)}`\n")
+ lines.append(f"Total remaining issues: **{len(issues)}**\n")
+
+ lines.append("## Summary\n")
+ lines.append("| Rule | Count | Note |")
+ lines.append("|---|---:|---|")
+ for rule, items in sorted(by_rule.items(), key=lambda kv: (-len(kv[1]), kv[0])):
+ note = RULE_NOTES.get(rule, "")
+ lines.append(f"| `{rule}` | {len(items)} | {note} |")
+ lines.append("")
+
+ lines.append("## Suggested triage order\n")
+ preferred = ["F403", "F405", "F821", "E722", "B904", "E402", "F401", "E501"]
+ present = [r for r in preferred if r in by_rule]
+ if present:
+ for idx, rule in enumerate(present, 1):
+ lines.append(f"{idx}. `{rule}` — {RULE_NOTES.get(rule, '')}")
+ lines.append("")
+
+ lines.append("## Table of contents by rule\n")
+ for rule, items in sorted(by_rule.items(), key=lambda kv: (-len(kv[1]), kv[0])):
+ anchor = rule.lower()
+ lines.append(f"- [{rule} ({len(items)})](#{anchor})")
+ lines.append("")
+
+ for rule, items in sorted(by_rule.items(), key=lambda kv: (-len(kv[1]), kv[0])):
+ lines.append(f"## {rule}\n")
+ lines.append(f"Count: **{len(items)}** ")
+ if rule in RULE_NOTES:
+ lines.append(f"Hint: {RULE_NOTES[rule]} ")
+ lines.append("")
+
+ file_groups: dict[str, list[dict]] = collections.defaultdict(list)
+ for item in items:
+ file_groups[relpath(item["filename"])].append(item)
+
+ lines.append("### Files affected\n")
+ lines.append("| File | Count |")
+ lines.append("|---|---:|")
+ for filename, entries in sorted(file_groups.items(), key=lambda kv: (-len(kv[1]), kv[0])):
+ lines.append(f"| `{filename}` | {len(entries)} |")
+ lines.append("")
+
+ lines.append("### Details\n")
+ for filename, entries in sorted(file_groups.items(), key=lambda kv: (-len(kv[1]), kv[0])):
+ lines.append(f"#### `{filename}` ({len(entries)})\n")
+ lines.append("| Line | Col | Message |")
+ lines.append("|---:|---:|---|")
+ for e in sorted(
+ entries, key=lambda x: (x.get("location", {}).get("row", 0), x.get("location", {}).get("column", 0))
+ ):
+ loc = e.get("location", {})
+ line = loc.get("row", "")
+ col = loc.get("column", "")
+ msg = (e.get("message", "") or "").replace("|", "\\|")
+ lines.append(f"| {line} | {col} | {msg} |")
+ lines.append("")
+ lines.append("Quick open commands:")
+ lines.append("")
+ lines.append("```powershell")
+ lines.append(f'code -g "{filename}:{entries[0].get("location", {}).get("row", 1)}"')
+ lines.append("```")
+ lines.append("")
+
+ out.write_text("\n".join(lines), encoding="utf-8")
+ print(f"Wrote {out}")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/tools/test_selector.py b/tools/test_selector.py
new file mode 100755
index 0000000000..7e50c17dec
--- /dev/null
+++ b/tools/test_selector.py
@@ -0,0 +1,950 @@
+#!/usr/bin/env python3
+"""Deterministic, strictly validated test selector for DeepLabCut.
+
+Outputs orthogonal workflow mode selections plus structured test selections:
+
+ - lanes: which workflow lanes should run (skip, docs, fast, full)
+ - pytest_paths: JSON list of pytest path arguments
+ - functional_scripts: JSON list of python script paths
+ - provenance: JSON mapping each selected test/script to the category rules that selected it
+
+Safety principles
+-----------------
+- Fail-safe: if changes cannot be determined or are ambiguous, the "full" lane is always selected.
+- Deterministic: derives diff range from GitHub Actions event payload when available.
+ * pull_request: uses merge-base(base.sha, head.sha) .. head.sha
+ * push: uses before .. after
+ * manual override: uses exactly --base-sha .. --head-sha
+ * fallback: attempts HEAD~1 .. HEAD
+- Secure: never emits shell command strings; only structured data.
+- Strict: Pydantic schema validation (extra=forbid), SHA validation, path sanitization.
+
+Intended usage in GitHub Actions
+-------------------------------
+- Checkout with sufficient history for merge-base/diff (typically fetch-depth: 0).
+- Run:
+ python tools/test_selector.py --write-github-output --json
+
+This will write the following keys to $GITHUB_OUTPUT:
+ - run_skip (bool): whether to run the skip mode
+ - run_docs (bool): whether to run the docs workflow
+ - run_fast (bool): whether to run targeted test execution
+ - run_full (bool): whether to run the full matrix/full suite workflow
+ - selected_workflows (list): list of selected workflow lanes
+ - lane_reasons (dict): reasons for selecting each workflow lane
+ - diff_mode (str): how the diff was determined
+ - pytest_paths (list): list of pytest path arguments
+ - functional_scripts (list): list of python script paths
+ - reasons (list): aggregate machine-readable reasons for the selection
+ - changed_files (list): list of changed files
+ - provenance (dict): mapping each selected test/script to the category rules that selected it
+
+Notes
+-----
+- This script intentionally keeps the routing rules simple and location-based.
+- Extend CATEGORY_RULES and FULL_SUITE_TRIGGERS as needed, keeping rules auditable.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+import os
+import re
+import subprocess
+from collections import defaultdict
+from collections.abc import Callable, Sequence
+from enum import Enum
+from pathlib import Path
+from typing import Any
+
+from pydantic import BaseModel, ConfigDict, Field, ValidationError
+
+try:
+ from .test_selector_config import (
+ CATEGORY_RULE_BY_NAME,
+ CATEGORY_RULES,
+ FULL_SUITE_TRIGGERS,
+ LINT_ONLY_FILES,
+ MINIMAL_PYTEST,
+ )
+# Allows to run as "python tools/test_selector.py" without installing as a package,
+# but still import the config from the same location.
+except ImportError: # pragma: no cover
+ from test_selector_config import (
+ CATEGORY_RULE_BY_NAME,
+ CATEGORY_RULES,
+ FULL_SUITE_TRIGGERS,
+ LINT_ONLY_FILES,
+ MINIMAL_PYTEST,
+ )
+
+
+SHA_RE = re.compile(r"^[0-9a-f]{7,40}$", re.IGNORECASE)
+# DLC_NAMESPACE = "deeplabcut"
+
+
+class DiffMode(str, Enum):
+ """How the diff was determined, for auditing and reporting."""
+
+ PR = "pr" # merge-base(base, head) .. head
+ PUSH = "push" # before .. after
+ MANUAL = "manual" # explicit base...head from CLI args
+ FALLBACK = "fallback" # HEAD^ .. HEAD
+ INITIAL = "initial" # empty tree .. HEAD
+ FALLBACK_NO_HEAD = "fallback_no_head" # couldn't resolve HEAD
+
+
+MODE_LABELS = {
+ DiffMode.PR: "Pull request (merge-base..HEAD)",
+ DiffMode.PUSH: "Push (before..after)",
+ DiffMode.MANUAL: "Manual override",
+ DiffMode.FALLBACK: "Fallback (HEAD^..HEAD)",
+ DiffMode.INITIAL: "Initial commit (empty tree..HEAD)",
+ DiffMode.FALLBACK_NO_HEAD: "Fallback (couldn't resolve HEAD)",
+}
+
+
+class LaneSelection(BaseModel):
+ """Which workflow lanes should run."""
+
+ model_config = ConfigDict(extra="forbid")
+
+ skip: bool = False # Skip all tests (e.g. lint-only changes only)
+ docs: bool = False # Run docs build checks
+ fast: bool = False # Run targeted pytest + optional functional scripts in test workflow
+ full: bool = False # Delegate to full test workflow/matrix
+
+
+class SelectionProvenance(BaseModel):
+ """Why each selected test/script path was included."""
+
+ model_config = ConfigDict(extra="forbid")
+
+ pytest: dict[str, list[str]] = Field(default_factory=dict)
+ scripts: dict[str, list[str]] = Field(default_factory=dict)
+
+
+class SelectorResult(BaseModel):
+ """Strict output schema."""
+
+ model_config = ConfigDict(extra="forbid")
+
+ schema_version: int = 2
+ diff_mode: DiffMode = DiffMode.FALLBACK_NO_HEAD
+
+ lanes: LaneSelection = Field(default_factory=LaneSelection)
+
+ pytest_paths: list[str] = Field(default_factory=list)
+ functional_scripts: list[str] = Field(default_factory=list)
+ provenance: SelectionProvenance = Field(default_factory=SelectionProvenance)
+
+ reasons: list[str] = Field(default_factory=list)
+ changed_files: list[str] = Field(default_factory=list)
+ lane_reasons: dict[str, list[str]] = Field(default_factory=dict)
+
+
+SelectorResult.model_rebuild() # Ensure model is fully built at import time for validation in main()
+
+
+# -----------------------------
+# Git helpers
+# -----------------------------
+def _run_git(args: Sequence[str], cwd: Path) -> str:
+ proc = subprocess.run(
+ ["git", *args],
+ cwd=str(cwd),
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ if proc.returncode != 0:
+ raise RuntimeError(f"git {' '.join(args)} failed: {proc.stderr.strip()}")
+ return proc.stdout.strip()
+
+
+def find_repo_root() -> Path:
+ out = _run_git(["rev-parse", "--show-toplevel"], Path.cwd())
+ return Path(out).resolve()
+
+
+def _validate_sha(label: str, sha: str) -> str:
+ if not sha or not SHA_RE.match(sha):
+ raise ValueError(f"Invalid {label} SHA: {sha!r}")
+ return sha
+
+
+def _ensure_commit_exists(sha: str, cwd: Path) -> None:
+ _run_git(["cat-file", "-e", f"{sha}^{{commit}}"], cwd)
+
+
+def _load_github_event() -> dict[str, Any]:
+ path = os.environ.get("GITHUB_EVENT_PATH")
+ if not path:
+ return {}
+ try:
+ return json.loads(Path(path).read_text(encoding="utf-8"))
+ except Exception:
+ return {}
+
+
+def _normalize_relpath(p: str) -> str:
+ """Normalize and validate a repo-relative path from git output."""
+ if "\x00" in p:
+ raise ValueError("NUL byte in path")
+ p = p.strip().replace("\\", "/")
+ if not p:
+ raise ValueError("Empty path")
+ if p.startswith("/") or re.match(r"^[A-Za-z]:/", p):
+ raise ValueError(f"Absolute path not allowed: {p}")
+ parts = [x for x in p.split("/") if x not in ("", ".")]
+ if any(x == ".." for x in parts):
+ raise ValueError(f"Path traversal not allowed: {p}")
+ return "/".join(parts)
+
+
+def _empty_tree(repo: Path) -> str:
+ # Avoid hardcoding; derive the empty tree hash deterministically.
+ empty = _run_git(["hash-object", "-t", "tree", os.devnull], repo)
+ return _validate_sha("empty-tree", empty)
+
+
+def determine_diff_range(repo: Path, override_base: str | None, override_head: str | None) -> tuple[str, str, DiffMode]:
+ """Return (base_commit, head_commit, mode)."""
+ zero_sha = "0" * 40
+ event_name = os.environ.get("GITHUB_EVENT_NAME", "")
+ event = _load_github_event()
+
+ if override_base and override_head:
+ base = _validate_sha("base", override_base)
+ head = _validate_sha("head", override_head)
+ _ensure_commit_exists(base, repo)
+ _ensure_commit_exists(head, repo)
+ return base, head, DiffMode.MANUAL
+
+ if event_name == "pull_request" and "pull_request" in event:
+ base_sha = _validate_sha("base", event["pull_request"]["base"]["sha"])
+ head_sha = _validate_sha("head", event["pull_request"]["head"]["sha"])
+ _ensure_commit_exists(base_sha, repo)
+ _ensure_commit_exists(head_sha, repo)
+ # Use merge-base to approximate the PR triple-dot diff base deterministically.
+ merge_base = _run_git(["merge-base", head_sha, base_sha], repo)
+ merge_base = _validate_sha("merge-base", merge_base)
+ _ensure_commit_exists(merge_base, repo)
+ return merge_base, head_sha, DiffMode.PR
+
+ if event_name == "push" and "before" in event and "after" in event:
+ before = _validate_sha("before", event["before"])
+ after = _validate_sha("after", event["after"])
+ _ensure_commit_exists(after, repo)
+
+ if before == zero_sha:
+ empty = _empty_tree(repo)
+ return empty, after, DiffMode.INITIAL
+ try:
+ _ensure_commit_exists(before, repo)
+ return before, after, DiffMode.PUSH
+ except Exception:
+ empty = _empty_tree(repo)
+ return empty, after, DiffMode.INITIAL
+
+ # Fallback: try parent..HEAD; if no parent (initial commit), diff empty-tree..HEAD
+ try:
+ head = _validate_sha("HEAD", _run_git(["rev-parse", "HEAD"], repo))
+ _ensure_commit_exists(head, repo)
+
+ try:
+ prev = _validate_sha("HEAD^", _run_git(["rev-parse", "--verify", "HEAD^"], repo))
+ _ensure_commit_exists(prev, repo)
+ return prev, head, DiffMode.FALLBACK
+ except Exception:
+ # Initial commit (no parent): treat as "everything added"
+ empty = _empty_tree(repo)
+ return empty, head, DiffMode.INITIAL
+ except Exception:
+ return "", "", DiffMode.FALLBACK_NO_HEAD
+
+
+def changed_files(repo: Path, base: str, head: str) -> list[str]:
+ if not base or not head:
+ return []
+ out = _run_git(["diff", "--name-only", "--diff-filter=ACMRTD", base, head], repo)
+ files = [_normalize_relpath(line) for line in out.splitlines() if line.strip()]
+ return sorted(set(files))
+
+
+def _is_safe_relpath(p: str) -> bool:
+ """Safety check for a git-relative path: no absolute, no traversal, no NUL."""
+ return (
+ p
+ and "\x00" not in p
+ and not p.startswith("/")
+ and not re.match(r"^[A-Za-z]:/", p)
+ and ".." not in Path(p).parts
+ )
+
+
+def validate_selected_paths(res: SelectorResult, repo: Path) -> SelectorResult:
+ missing: list[str] = []
+
+ # validate pytest paths (files/dirs)
+ for p in res.pytest_paths:
+ if not _is_safe_relpath(p) or not (repo / p).exists():
+ missing.append(f"pytest:{p}")
+
+ # validate functional scripts (files)
+ for s in res.functional_scripts:
+ if not _is_safe_relpath(s) or not (repo / s).exists():
+ missing.append(f"script:{s}")
+
+ if missing:
+ # Fail-safe escalation: disable fast lane selection, enable full lane.
+ # Preserve docs lane if it was independently selected.
+ res.lanes.fast = False
+ res.lanes.full = True
+ res.pytest_paths = []
+ res.functional_scripts = []
+ res.provenance = SelectionProvenance()
+ res.reasons = res.reasons + ["missing_selected_paths"] + missing
+
+ lane_reasons = dict(res.lane_reasons)
+ lane_reasons.pop("fast", None)
+ full_reasons = list(lane_reasons.get("full", []))
+ full_reasons.extend(["missing_selected_paths", *missing])
+ lane_reasons["full"] = full_reasons
+ res.lane_reasons = lane_reasons
+
+ return res
+
+
+# -----------------------------
+# Decision logic
+# -----------------------------
+
+
+def _matches_any(path: str, preds: Sequence[Callable[[str], bool]]) -> bool:
+ for pred in preds:
+ try:
+ if pred(path):
+ return True
+ except Exception:
+ continue
+ return False
+
+
+def decide(files: list[str]) -> SelectorResult:
+ reasons: list[str] = []
+ lane_reasons: dict[str, list[str]] = {}
+ lanes = LaneSelection()
+
+ if not files:
+ lanes.full = True
+ full_reasons = ["no_changed_files_or_diff_unavailable"]
+ return SelectorResult(
+ lanes=lanes,
+ pytest_paths=[],
+ functional_scripts=[],
+ provenance=SelectionProvenance(),
+ reasons=full_reasons,
+ changed_files=[],
+ lane_reasons={"full": full_reasons},
+ )
+
+ # Lint-only filtering (routing should ignore these files)
+ lint_only = [f for f in files if f in LINT_ONLY_FILES]
+ routed_files = [f for f in files if f not in LINT_ONLY_FILES]
+
+ if lint_only:
+ reasons.append(f"lint_only_count:{len(lint_only)}")
+
+ # If *only* lint-only files changed, skip all lanes
+ if not routed_files:
+ lanes.skip = True
+ skip_reasons = [*reasons, "lint_only"]
+ return SelectorResult(
+ lanes=lanes,
+ pytest_paths=[],
+ functional_scripts=[],
+ provenance=SelectionProvenance(),
+ reasons=skip_reasons,
+ changed_files=files,
+ lane_reasons={"skip": skip_reasons},
+ )
+
+ # Docs lane is orthogonal: if any routed file matches docs, enable docs lane.
+ docs_rule = CATEGORY_RULE_BY_NAME.get("docs")
+ docs_touched = bool(docs_rule and any(_matches_any(f, docs_rule.match_any) for f in routed_files))
+ docs_matched_files = {f for f in routed_files if docs_rule and _matches_any(f, docs_rule.match_any)}
+ non_docs_routed_files = [f for f in routed_files if f not in docs_matched_files]
+
+ docs_pytests_sorted: list[str] = []
+ docs_scripts_sorted: list[str] = []
+
+ if docs_touched:
+ lanes.docs = True
+ reasons.append("category:docs")
+ lane_reasons["docs"] = ["category:docs"]
+ docs_pytests_sorted = sorted(set(docs_rule.pytest_paths)) if docs_rule else []
+ docs_scripts_sorted = sorted(set(docs_rule.functional_scripts)) if docs_rule else []
+
+ # Full-suite triggers always win over fast, but docs lane can still remain enabled.
+ triggered: list[tuple[str, str]] = []
+ for f in routed_files:
+ for name, pred in FULL_SUITE_TRIGGERS:
+ if _matches_any(f, [pred]):
+ triggered.append((f, name))
+
+ if triggered:
+ lanes.full = True
+ full_reasons = [
+ "full_suite_trigger",
+ f"full_suite_trigger_count:{len(triggered)}",
+ ]
+ reasons.extend(full_reasons)
+ lane_reasons["full"] = full_reasons
+ return SelectorResult(
+ lanes=lanes,
+ pytest_paths=[],
+ functional_scripts=[],
+ provenance=SelectionProvenance(),
+ reasons=reasons,
+ changed_files=files,
+ lane_reasons=lane_reasons,
+ )
+
+ # Match NON-doc categories only for test-routing / escalation logic.
+ matched_non_docs = []
+ for rule in CATEGORY_RULES:
+ if rule.name == "docs":
+ continue
+ if any(_matches_any(f, rule.match_any) for f in routed_files):
+ matched_non_docs.append(rule)
+
+ matched_non_docs = sorted(matched_non_docs, key=lambda r: r.name)
+
+ for rule in matched_non_docs:
+ reasons.append(f"category:{rule.name}")
+
+ pytest_paths_set: set[str] = set()
+ functional_set: set[str] = set()
+ pytest_sources: dict[str, set[str]] = defaultdict(set)
+ script_sources: dict[str, set[str]] = defaultdict(set)
+
+ # Docs rules may contribute tests/scripts to the fast lane.
+ if docs_touched:
+ for p in docs_pytests_sorted:
+ pytest_paths_set.add(p)
+ pytest_sources[p].add("docs")
+ for s in docs_scripts_sorted:
+ functional_set.add(s)
+ script_sources[s].add("docs")
+
+ # Non-doc matched categories contribute to fast lane.
+ for rule in matched_non_docs:
+ cat = rule.name
+ for p in rule.pytest_paths or []:
+ pytest_paths_set.add(p)
+ pytest_sources[p].add(cat)
+ for s in rule.functional_scripts or []:
+ functional_set.add(s)
+ script_sources[s].add(cat)
+
+ # If we matched non-doc categories but none provided explicit tests/scripts,
+ # fall back to the minimal pytest lane.
+ fallback_used = False
+ if not pytest_paths_set and not functional_set and matched_non_docs:
+ for p in MINIMAL_PYTEST:
+ pytest_paths_set.add(p)
+ pytest_sources[p].add("fallback_minimal_pytest")
+ reasons.append("fallback_minimal_pytest")
+ fallback_used = True
+
+ # If the routed changes are truly docs-only (no non-doc files remain) and no
+ # tests were selected, return docs lane only.
+ if not pytest_paths_set and not functional_set:
+ if lanes.docs and not non_docs_routed_files:
+ return SelectorResult(
+ lanes=lanes,
+ pytest_paths=[],
+ functional_scripts=[],
+ provenance=SelectionProvenance(),
+ reasons=reasons,
+ changed_files=files,
+ lane_reasons=lane_reasons,
+ )
+
+ # Otherwise fail-safe to full when nothing matched at all.
+ lanes.full = True
+ full_reasons = ["no_category_matched"]
+ reasons.extend(full_reasons)
+ lane_reasons["full"] = full_reasons
+ return SelectorResult(
+ lanes=lanes,
+ pytest_paths=[],
+ functional_scripts=[],
+ provenance=SelectionProvenance(),
+ reasons=reasons,
+ changed_files=files,
+ lane_reasons=lane_reasons,
+ )
+
+ # Fast lane selected
+ lanes.fast = True
+
+ fast_reasons: list[str] = []
+ if docs_touched and (docs_pytests_sorted or docs_scripts_sorted):
+ fast_reasons.append("category:docs")
+ fast_reasons.extend(f"category:{rule.name}" for rule in matched_non_docs)
+ if fallback_used:
+ fast_reasons.append("fallback_minimal_pytest")
+ lane_reasons["fast"] = fast_reasons
+
+ return SelectorResult(
+ lanes=lanes,
+ pytest_paths=sorted(pytest_paths_set),
+ functional_scripts=sorted(functional_set),
+ provenance=SelectionProvenance(
+ pytest={k: sorted(v) for k, v in sorted(pytest_sources.items())},
+ scripts={k: sorted(v) for k, v in sorted(script_sources.items())},
+ ),
+ reasons=reasons,
+ changed_files=files,
+ lane_reasons=lane_reasons,
+ )
+
+
+# -----------------------------
+# Outputs
+# -----------------------------
+def explain_changed_files(files: list[str]) -> dict[str, Any]:
+ """
+ Build an explanation structure for reporting:
+ - per-file: full_trigger_matches, category_matches
+ - grouped: full_triggers, by_category, uncategorized
+ """
+ per_file: dict[str, dict[str, Any]] = {}
+ by_category: dict[str, list[str]] = defaultdict(list)
+ full_trigger_files: dict[str, list[str]] = defaultdict(list)
+ lint_only_files: list[str] = []
+ uncategorized: list[str] = []
+
+ # Prep category predicates
+ categories = [(r.name, r.match_any) for r in CATEGORY_RULES]
+
+ for f in files:
+ # Which full-suite triggers does this file match?
+ ft = []
+ for trig_name, pred in FULL_SUITE_TRIGGERS:
+ try:
+ if pred(f):
+ ft.append(trig_name)
+ except Exception:
+ continue
+
+ # Which categories does it match?
+ cats = []
+ for cat_name, preds in categories:
+ if _matches_any(f, preds):
+ cats.append(cat_name)
+ is_lint_only = f in LINT_ONLY_FILES
+
+ per_file[f] = {
+ "full_triggers": ft,
+ "categories": cats,
+ "lint_only": is_lint_only,
+ }
+
+ if ft:
+ for t in ft:
+ full_trigger_files[t].append(f)
+
+ if cats:
+ for c in cats:
+ by_category[c].append(f)
+
+ if is_lint_only:
+ lint_only_files.append(f)
+ else:
+ # Only uncategorized if it matched no categories AND no full-suite triggers
+ if not ft and not cats:
+ uncategorized.append(f)
+
+ # Deterministic ordering
+ for t in full_trigger_files:
+ full_trigger_files[t] = sorted(set(full_trigger_files[t]))
+ for c in by_category:
+ by_category[c] = sorted(set(by_category[c]))
+
+ return {
+ "per_file": per_file,
+ "full_trigger_files": dict(full_trigger_files),
+ "by_category": dict(by_category),
+ "lint_only": sorted(set(lint_only_files)),
+ "uncategorized": sorted(set(uncategorized)),
+ }
+
+
+def _render_file_line(
+ f: str,
+ info: dict[str, Any],
+ emoji: bool = False,
+ add_tag: bool = True,
+ add_marker: bool = False,
+ category_only: bool = True,
+) -> str:
+ # Optional, single marker only
+ marker = ""
+ if add_marker:
+ if info.get("full_triggers"):
+ marker = "⚠️ " if emoji else ""
+ elif info.get("lint_only"):
+ marker = "🧹 " if emoji else ""
+ elif not info.get("categories"):
+ marker = "❓ " if emoji else ""
+
+ tags = []
+ if add_tag:
+ if info.get("categories"):
+ header = "🏷️ " if emoji else "Category match :"
+ tags.append(f"{header} " + ", ".join(info["categories"]))
+ if info.get("full_triggers") and not category_only:
+ header = "🚨 " if emoji else "Full triggers"
+ tags.append(f"{header} " + ", ".join(info["full_triggers"]))
+ if info.get("lint_only") and not category_only:
+ header = "🧹 " if emoji else "Lint-only :"
+ tags.append(f"{header}")
+
+ tag_str = (" — " + " | ".join(tags)) if tags else ""
+ return f"- {marker}`{f}`{tag_str}"
+
+
+def _enabled_lane_names(res: SelectorResult) -> list[str]:
+ order = ("skip", "docs", "fast", "full")
+ return [name for name in order if getattr(res.lanes, name)]
+
+
+def _lane_label(name: str, emoji: bool = False) -> str:
+ if not emoji:
+ return name
+ return {
+ "skip": "⏩ skip",
+ "docs": "📚 docs",
+ "fast": "⚡ fast",
+ "full": "🧪 full",
+ }.get(name, name)
+
+
+def _compact_reasons(reasons: list[str]) -> list[str]:
+ cats = sorted({r.split(":", 1)[1] for r in reasons if r.startswith("category:")})
+ other = [r for r in reasons if not r.startswith("category:")]
+ out = []
+ if cats:
+ out.append("categories: " + ", ".join(cats))
+ out.extend(other)
+ return out
+
+
+def _details_open(summary: str, add_blank: bool = True) -> str:
+ s = f"{summary} \n"
+ if add_blank:
+ s += "\n"
+ return s
+
+
+def _details_close() -> str:
+ return "\n \n"
+
+
+def _render_decision_markdown(
+ res: SelectorResult,
+ limit: int = 40,
+ style: str = "minimal",
+ emoji: bool = False,
+) -> str:
+ def bullet(items: list[str], limit_: int = limit) -> str:
+ if not items:
+ return "_(none)_"
+ shown = items[:limit_]
+ s = "\n".join(f"- `{x}`" for x in shown)
+ if len(items) > limit_:
+ s += f"\n- … and {len(items) - limit_} more"
+ return s
+
+ # Selection line (minimal, no emoji by default)
+ selected_lanes = _enabled_lane_names(res)
+ if emoji:
+ selected_lanes_label = ", ".join(_lane_label(name, emoji=True) for name in selected_lanes)
+ else:
+ selected_lanes_label = ", ".join(f"`{name}`" for name in selected_lanes)
+
+ if not selected_lanes_label:
+ selected_lanes_label = "_(none)_"
+
+ diff_mode = f"{MODE_LABELS.get(res.diff_mode, res.diff_mode.value)}"
+
+ md: list[str] = []
+ md.append("# Test selection\n")
+ md.append(f"**Selected workflows:** {selected_lanes_label}\n")
+ md.append(f"**Diff mode:** `{diff_mode}`\n")
+
+ # Reasons (compacted)
+ md.append("## Why\n")
+ for r in _compact_reasons(res.reasons):
+ md.append(f"- `{r}`")
+ md.append("")
+
+ if style == "detailed" and res.lane_reasons:
+ md.append("## Workflow lanes\n")
+ for lane in _enabled_lane_names(res):
+ lane_rs = res.lane_reasons.get(lane, [])
+ md.append(f"### `{lane}`")
+ if lane_rs:
+ for r in lane_rs:
+ md.append(f"- `{r}`")
+ else:
+ md.append("_(none)_")
+ md.append("")
+
+ # Explain changed files
+ exp = explain_changed_files(res.changed_files)
+
+ md.append("## Changed files (explained)\n")
+
+ # 1) Collapsible: Files that match full-suite triggers
+ # (Always collapsible if present; otherwise omit section.)
+ if exp["full_trigger_files"]:
+ total_triggered = sum(len(v) for v in exp["full_trigger_files"].values())
+ md.append(_details_open(f"Files that match full-suite triggers ({total_triggered})"))
+ for trig_name in sorted(exp["full_trigger_files"].keys()):
+ files_for_trigger = exp["full_trigger_files"][trig_name]
+ md.append(f"**{trig_name}** ({len(files_for_trigger)})")
+ for f in files_for_trigger[:limit]:
+ md.append(_render_file_line(f, exp["per_file"][f], emoji=emoji))
+ if len(files_for_trigger) > limit:
+ md.append(f"- … and {len(files_for_trigger) - limit} more")
+ md.append("")
+ md.append(_details_close())
+
+ # 2) Files grouped by category (includes uncategorized and lint-only as collapsible lists)
+ md.append("### Files grouped by category\n")
+
+ if exp["by_category"]:
+ for cat in sorted(exp["by_category"].keys()):
+ files = exp["by_category"][cat]
+
+ # Determine if this category has any explicit selection rules attached
+ rule = CATEGORY_RULE_BY_NAME.get(cat)
+ has_rules = bool(rule and (rule.pytest_paths or rule.functional_scripts))
+ note = "" if has_rules else " — no specific testing rules attached"
+
+ md.append(_details_open(f"{cat} ({len(files)}){note}"))
+ for f in files[:limit]:
+ # Already grouped by category; keep lines clean
+ md.append(f"- `{f}`")
+ if len(files) > limit:
+ md.append(f"- … and {len(files) - limit} more")
+ md.append(_details_close())
+ else:
+ md.append("_(none)_\n")
+
+ # Lint-only as collapsible
+ if exp.get("lint_only"):
+ lint_files = exp["lint_only"]
+ md.append(_details_open(f"Lint-only ({len(lint_files)}) — ignored for test selection"))
+ md.append("")
+ for f in lint_files[:limit]:
+ md.append(f"- `{f}`")
+ if len(lint_files) > limit:
+ md.append(f"- … and {len(lint_files) - limit} more")
+ md.append(_details_close())
+
+ # Uncategorized as collapsible — and clarify what it means
+ # IMPORTANT: explain_changed_files() already ensures that files that match ANY category
+ # never land here. This section is only for truly unmatched files.
+ if exp["uncategorized"]:
+ unc_files = exp["uncategorized"]
+ md.append(
+ _details_open(
+ f"Uncategorized ({len(unc_files)}) — no matching category (no specific testing rules attached)"
+ )
+ )
+ md.append("")
+ for f in unc_files[:limit]:
+ md.append(_render_file_line(f, exp["per_file"][f], emoji=emoji))
+ if len(unc_files) > limit:
+ md.append(f"- … and {len(unc_files) - limit} more")
+ md.append(_details_close())
+
+ if style == "detailed":
+ md.append("## Changed files (raw)\n")
+ md.append(bullet(res.changed_files))
+ md.append("")
+
+ # Selected tests
+ md.append("## Selected tests\n")
+ md.append(_details_open("Pytest paths"))
+ md.append(bullet(res.pytest_paths))
+ md.append(_details_close())
+ md.append(_details_open("Functional scripts"))
+ md.append(bullet(res.functional_scripts))
+ md.append(_details_close())
+
+ # Provenance collapsed by default, only if detailed
+ if style == "detailed":
+ md.append("## Provenance\n")
+ md.append(_details_open("Why these tests"))
+ md.append("")
+
+ if res.provenance.pytest:
+ md.append("### Pytest\n")
+ for p, srcs in res.provenance.pytest.items():
+ md.append(f"- `{p}` ← {', '.join(f'`{s}`' for s in srcs)}")
+ else:
+ md.append("### Pytest\n_(none)_")
+
+ if res.provenance.scripts:
+ md.append("\n### Scripts\n")
+ for s, srcs in res.provenance.scripts.items():
+ md.append(f"- `{s}` ← {', '.join(f'`{x}`' for x in srcs)}")
+ else:
+ md.append("\n### Scripts\n_(none)_")
+
+ md.append(_details_close())
+
+ return "\n".join(md)
+
+
+def write_report_files(
+ res: SelectorResult,
+ out_dir: Path,
+ report_style: str = "minimal",
+ no_emoji: bool = False,
+) -> tuple[Path, Path]:
+ out_dir.mkdir(parents=True, exist_ok=True)
+ json_path = out_dir / "selection.json"
+ md_path = out_dir / "decision.md"
+
+ json_path.write_text(res.model_dump_json(indent=2), encoding="utf-8")
+ md_path.write_text(
+ _render_decision_markdown(res, style=report_style, emoji=not no_emoji),
+ encoding="utf-8",
+ )
+ return json_path, md_path
+
+
+def create_job_summary(md_path: Path, overwrite: bool = True) -> None:
+ summary_path = os.environ.get("GITHUB_STEP_SUMMARY")
+ if not summary_path:
+ return
+ # Append markdown to the GitHub Actions Job Summary
+ mode = "w" if overwrite else "a"
+ with open(summary_path, mode, encoding="utf-8") as f:
+ f.write(md_path.read_text(encoding="utf-8"))
+ f.write("\n")
+
+
+def write_github_output(res: SelectorResult) -> None:
+ out_path = os.environ.get("GITHUB_OUTPUT")
+ if not out_path:
+ raise RuntimeError("GITHUB_OUTPUT is not set")
+
+ def j(v) -> str:
+ return json.dumps(v, separators=(",", ":"), ensure_ascii=False)
+
+ selected_workflows = _enabled_lane_names(res)
+
+ with open(out_path, "a", encoding="utf-8") as f:
+ f.write(f"run_skip={str(res.lanes.skip).lower()}\n")
+ f.write(f"run_docs={str(res.lanes.docs).lower()}\n")
+ f.write(f"run_fast={str(res.lanes.fast).lower()}\n")
+ f.write(f"run_full={str(res.lanes.full).lower()}\n")
+ f.write(f"selected_workflows={j(selected_workflows)}\n")
+ f.write(f"lane_reasons={j(res.lane_reasons)}\n")
+ f.write(f"diff_mode={res.diff_mode.value}\n")
+ f.write(f"pytest_paths={j(res.pytest_paths)}\n")
+ f.write(f"functional_scripts={j(res.functional_scripts)}\n")
+ f.write(f"reasons={j(res.reasons)}\n")
+ f.write(f"changed_files={j(res.changed_files)}\n")
+ f.write(f"provenance={j(res.provenance.model_dump())}\n")
+
+
+def main(argv: Sequence[str] | None = None) -> int:
+ ap = argparse.ArgumentParser(description="Deterministic DeepLabCut test selector")
+ ap.add_argument("--json", action="store_true", help="Print JSON result to stdout")
+ ap.add_argument(
+ "--write-github-output",
+ action="store_true",
+ help="Write outputs to $GITHUB_OUTPUT",
+ )
+ ap.add_argument(
+ "--base-sha",
+ default=None,
+ help="Override base commit SHA for manual diff selection (must be used with --head-sha)",
+ )
+ ap.add_argument(
+ "--head-sha",
+ default=None,
+ help="Override head commit SHA for manual diff selection (must be used with --base-sha)",
+ )
+
+ ap.add_argument(
+ "--report-dir",
+ default="tmp/test-selection",
+ help="Directory to write decision report files (selection.json, decision.md)",
+ )
+ ap.add_argument(
+ "--write-summary",
+ action="store_true",
+ help="Append decision.md to GitHub Actions Job Summary if available",
+ )
+ ap.add_argument(
+ "--report-style",
+ choices=["minimal", "detailed"],
+ default="detailed",
+ help="Decision markdown verbosity: minimal or detailed (default: detailed)",
+ )
+ ap.add_argument(
+ "--no-emoji",
+ action="store_true",
+ help="Disable emojis in markdown report (default: off)",
+ )
+
+ args = ap.parse_args(list(argv) if argv is not None else None)
+ if bool(args.base_sha) != bool(args.head_sha):
+ ap.error("Both --base-sha and --head-sha must be provided together")
+
+ repo = find_repo_root()
+
+ base, head, diff_mode = determine_diff_range(repo, args.base_sha, args.head_sha)
+ files = changed_files(repo, base, head)
+
+ res = decide(files)
+ res.diff_mode = diff_mode
+ res.changed_files = files
+ res = validate_selected_paths(res, repo)
+
+ # Strict validation
+ try:
+ res = SelectorResult.model_validate(res.model_dump())
+ except ValidationError as e:
+ raise RuntimeError(f"Output validation failed: {e}") from e
+
+ # Always write report files for transparency
+ report_dir = Path(args.report_dir)
+ json_path, md_path = write_report_files(res, report_dir, report_style=args.report_style, no_emoji=args.no_emoji)
+
+ if args.json:
+ print(res.model_dump_json(indent=2))
+
+ if args.write_github_output:
+ write_github_output(res)
+
+ # Write Job Summary (GitHub renders markdown from $GITHUB_STEP_SUMMARY)
+ if args.write_summary:
+ create_job_summary(md_path)
+
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/tools/test_selector_config.py b/tools/test_selector_config.py
new file mode 100644
index 0000000000..20b5b2c1c8
--- /dev/null
+++ b/tools/test_selector_config.py
@@ -0,0 +1,341 @@
+"""Test selector configuration."""
+
+from __future__ import annotations
+
+import re
+from collections.abc import Callable
+from pathlib import PurePosixPath
+
+from pydantic import BaseModel, ConfigDict, Field, field_validator
+
+PathPred = Callable[[str], bool]
+
+
+def prefix(*values: str) -> PathPred:
+ """Match if path starts with any of the given prefixes."""
+ prefixes = tuple(values)
+ return lambda p: p.startswith(prefixes)
+
+
+def suffix(*values: str) -> PathPred:
+ """Match if path ends with any of the given suffixes."""
+ suffixes = tuple(values)
+ return lambda p: p.endswith(suffixes)
+
+
+def equals(*values: str) -> PathPred:
+ """Match if path equals any of the given exact values."""
+ allowed = frozenset(values)
+ return lambda p: p in allowed
+
+
+def case_insensitive_match(*values: str) -> PathPred:
+ """Case-insensitive substring match against any of the given values."""
+ needles = tuple(v.lower() for v in values)
+ return lambda p: any(n in p.lower() for n in needles)
+
+
+def all_of(*preds: PathPred) -> PathPred:
+ """Logical AND over predicates."""
+ return lambda p: all(pred(p) for pred in preds)
+
+
+# -----------------------------
+# Rules validation models
+# -----------------------------
+_RULE_NAME_RE = re.compile(r"^[a-z0-9_]+$")
+
+
+def _validate_relpath_string(value: str, field_name: str) -> str:
+ """Validate a repo-relative path string used in config."""
+ if not isinstance(value, str):
+ raise TypeError(f"{field_name} entries must be strings")
+
+ value = value.strip()
+ if not value:
+ raise ValueError(f"{field_name} entries must not be empty")
+
+ value = value.replace("\\", "/")
+
+ if value.startswith("/"):
+ raise ValueError(f"{field_name} must be repo-relative, got absolute path: {value!r}")
+
+ if re.match(r"^[A-Za-z]:/", value):
+ raise ValueError(f"{field_name} must not be a Windows absolute path: {value!r}")
+
+ parts = PurePosixPath(value).parts
+ if ".." in parts:
+ raise ValueError(f"{field_name} must not contain path traversal: {value!r}")
+
+ if "\x00" in value:
+ raise ValueError(f"{field_name} must not contain NUL bytes: {value!r}")
+
+ return value
+
+
+class CategoryRule(BaseModel):
+ """Validated test-selection category rule."""
+
+ model_config = ConfigDict(extra="forbid")
+
+ name: str
+ match_any: list[PathPred] = Field(
+ min_length=1,
+ description=("List of predicates; if any predicate matches any changed file, the rule is triggered."),
+ )
+ pytest_paths: list[str] = Field(
+ default_factory=list,
+ description="Pytest paths selected if the rule is triggered.",
+ )
+ functional_scripts: list[str] = Field(
+ default_factory=list,
+ description="Functional test scripts selected if the rule is triggered.",
+ )
+
+ @field_validator("name")
+ @classmethod
+ def validate_name(cls, value: str) -> str:
+ value = value.strip()
+ if not value:
+ raise ValueError("Rule name must not be empty")
+ if not _RULE_NAME_RE.match(value):
+ raise ValueError(f"Rule name must match ^[a-z0-9_]+$ (got {value!r})")
+ return value
+
+ @field_validator("match_any")
+ @classmethod
+ def validate_match_any(cls, preds: list[PathPred]) -> list[PathPred]:
+ if not preds:
+ raise ValueError("match_any must contain at least one predicate")
+ for i, pred in enumerate(preds):
+ if not callable(pred):
+ raise TypeError(f"match_any[{i}] must be callable, got {type(pred).__name__}")
+ return preds
+
+ @field_validator("pytest_paths")
+ @classmethod
+ def validate_pytest_paths(cls, values: list[str]) -> list[str]:
+ return [_validate_relpath_string(v, "pytest_paths") for v in values]
+
+ @field_validator("functional_scripts")
+ @classmethod
+ def validate_functional_scripts(cls, values: list[str]) -> list[str]:
+ return [_validate_relpath_string(v, "functional_scripts") for v in values]
+
+
+def validate_category_rules(rules: list[CategoryRule]) -> list[CategoryRule]:
+ """Validate cross-rule invariants."""
+ seen: dict[str, int] = {}
+
+ for idx, rule in enumerate(rules):
+ if rule.name in seen:
+ first_idx = seen[rule.name]
+ raise ValueError(f"Duplicate CategoryRule name {rule.name!r} at indexes {first_idx} and {idx}")
+ seen[rule.name] = idx
+
+ return rules
+
+
+# -----------------------------
+# Configuration
+# -----------------------------
+MINIMAL_PYTEST = ["tests/test_auxiliaryfunctions.py"]
+
+POSE_TF = "deeplabcut/pose_estimation_tensorflow/"
+POSE_PT = "deeplabcut/pose_estimation_pytorch/"
+
+
+# Conservative full-suite triggers: if any changed file matches, plan=FULL.
+FULL_SUITE_TRIGGERS = [
+ ("Tests files changed", prefix("tests/")),
+ ("pyproject.toml changed", equals("pyproject.toml")),
+ ("lockfile changed", suffix(".lock")),
+ ("DEEPLABCUT.yaml changed", suffix("DEEPLABCUT.yaml")),
+ ("CI workflow changed", prefix(".github/workflows/")),
+]
+
+
+# Files that should be enforced by dedicated lint workflows, not by test selection
+LINT_ONLY_FILES = {
+ ".pre-commit-config.yaml",
+ # ".pre-commit-hooks.yaml",
+}
+
+
+# The per-file/folder rules that determine test selection logic. Each rule has:
+# - a name (for auditing/debugging purposes)
+# - a set of path predicates (match_any) that trigger the rule if any predicate
+# matches any changed file
+# - a list of pytest paths to select if the rule is triggered (can be empty)
+# - a list of functional test scripts to select if the rule is triggered (can be empty)
+CATEGORY_RULES = validate_category_rules(
+ [
+ # DOCS & NOTEBOOKS #
+ CategoryRule(
+ name="docs",
+ match_any=[
+ prefix("docs/"),
+ all_of(suffix(".md", ".rst"), case_insensitive_match("docs")),
+ all_of(suffix(".ipynb"), case_insensitive_match("docs")),
+ equals("_config.yml", "_toc.yml"),
+ equals(".github/workflows/build-book.yml"),
+ ],
+ pytest_paths=[
+ # NOTE:
+ # Optional docs-targeted tests may be attached here.
+ # If present, docs changes will still enable the docs lane, and may also
+ # contribute selections added here to the fast lane.
+ ],
+ functional_scripts=[
+ # NOTE:
+ # Optional docs-targeted functional tests may be attached here.
+ # If present, docs changes will still enable the docs lane, and may also
+ # contribute selections added here to the fast lane.
+ ],
+ ),
+ CategoryRule(
+ name="notebooks_examples",
+ match_any=[
+ prefix("examples/JUPYTER/", "examples/COLAB/"),
+ all_of(suffix(".ipynb"), case_insensitive_match("examples")),
+ ],
+ pytest_paths=MINIMAL_PYTEST,
+ functional_scripts=[],
+ ),
+ # CORE FUNCTIONALITY #
+ CategoryRule(
+ name="superanimal_modelzoo",
+ match_any=[
+ prefix("deeplabcut/modelzoo/"),
+ case_insensitive_match("superanimal"),
+ # case_insensitive_match("modelzoo"), # too broad ?
+ ],
+ pytest_paths=[
+ "tests/test_predict_supermodel.py",
+ "tests/pose_estimation_pytorch/modelzoo/",
+ "tests/pose_estimation_pytorch/other/test_modelzoo.py", # (currently all tests are skipped in this file..) # noqa: E501
+ ],
+ functional_scripts=[
+ # TODO: decide which of these functional testscripts are useful and not too heavy # noqa: E501
+ "examples/testscript_superanimal_adaptation.py", # (runs inference + video adaptation training on shortened video) # noqa: E501
+ # "examples/testscript_superanimal_create_pretrained_project.py", # (runs inference on example videos) # noqa: E501
+ # "examples/testscript_superanimal_inference.py", # (runs inference on multiple videos with multiple models) # noqa: E501
+ # "examples/testscript_superanimal_transfer_learning.py", # (runs full standard training pipeline after weight init) # noqa: E501
+ ],
+ ),
+ CategoryRule(
+ name="multianimal",
+ match_any=[
+ case_insensitive_match("multianimal"),
+ all_of(prefix(POSE_TF), case_insensitive_match("multi")),
+ all_of(prefix(POSE_PT), case_insensitive_match("multi")),
+ ],
+ pytest_paths=[
+ "tests/test_auxfun_multianimal.py",
+ "tests/test_pose_multianimal_imgaug.py",
+ "tests/test_predict_multianimal.py",
+ "tests/test_stitcher.py",
+ "tests/test_trackingutils.py",
+ ],
+ functional_scripts=[
+ "examples/testscript_tensorflow_multi_animal.py",
+ "examples/testscript_pytorch_multi_animal.py",
+ ],
+ ),
+ CategoryRule(
+ name="core",
+ match_any=[
+ prefix(
+ "deeplabcut/core/",
+ "deeplabcut/utils/",
+ POSE_TF,
+ POSE_PT,
+ ),
+ equals("deeplabcut/auxiliaryfunctions.py"),
+ ],
+ pytest_paths=[
+ "tests/test_auxiliaryfunctions.py",
+ "tests/core/",
+ "tests/utils/",
+ ],
+ functional_scripts=[
+ "examples/testscript_tensorflow_single_animal.py",
+ "examples/testscript_tensorflow_multi_animal.py",
+ "examples/testscript_pytorch_single_animal.py",
+ "examples/testscript_pytorch_multi_animal.py",
+ ],
+ ),
+ CategoryRule(
+ name="pose_estimation_tensorflow",
+ match_any=[
+ prefix(POSE_TF),
+ ],
+ pytest_paths=[
+ "tests/test_dataset_augmentation.py",
+ "tests/test_pose_multianimal_imgaug.py",
+ "tests/test_predict_multianimal.py",
+ "tests/test_evaluate.py",
+ # "tests/test_inferenceutils.py",
+ # "tests/test_crossvalutils.py",
+ ],
+ functional_scripts=[
+ "examples/testscript_tensorflow_multi_animal.py",
+ "examples/testscript_tensorflow_single_animal.py",
+ ],
+ ),
+ CategoryRule(
+ name="pose_estimation_pytorch",
+ match_any=[
+ prefix(POSE_PT),
+ ],
+ pytest_paths=[
+ "tests/pose_estimation_pytorch/",
+ ],
+ functional_scripts=[
+ "examples/testscript_pytorch_single_animal.py",
+ "examples/testscript_pytorch_multi_animal.py",
+ ],
+ ),
+ CategoryRule(
+ name="3d_pose_estimation",
+ match_any=[
+ prefix("deeplabcut/pose_estimation_3d/"),
+ ],
+ pytest_paths=[
+ "tests/test_triangulation.py",
+ ],
+ functional_scripts=[
+ "examples/testscript_3d.py",
+ ],
+ ),
+ CategoryRule(
+ name="generate_training_dataset",
+ match_any=[
+ prefix("deeplabcut/generate_training_dataset/"),
+ ],
+ pytest_paths=[
+ "tests/generate_training_dataset/",
+ ],
+ functional_scripts=[],
+ ),
+ # CI & TOOLING #
+ # CategoryRule(
+ # name="ci_workflows",
+ # match_any=[
+ # prefix(".github/workflows/"),
+ # ],
+ # pytest_paths=MINIMAL_PYTEST,
+ # functional_scripts=[],
+ # ),
+ CategoryRule(
+ name="ci_tools",
+ match_any=[
+ prefix("tools/"),
+ ],
+ pytest_paths=["tests/tools/"],
+ functional_scripts=[],
+ ),
+ ]
+)
+
+CATEGORY_RULE_BY_NAME = {r.name: r for r in CATEGORY_RULES}
diff --git a/tools/trim_lines.py b/tools/trim_lines.py
new file mode 100644
index 0000000000..f962b609e3
--- /dev/null
+++ b/tools/trim_lines.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+"""Reduce Ruff E501 violations using autopep8, then normalize with Ruff.
+
+Usage:
+ python fix_e501_with_autopep8.py . --line-length 88
+ python fix_e501_with_autopep8.py src tests --line-length 100 --check
+
+NOTE: if this creates broken escaped f-strings :
+f"some string with a {
+ var
+}"
+Use the ^[ \t]*\}"[ \t]*$ regex to find and fix them.
+
+Requirements:
+ - ruff
+ - autopep8
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+import subprocess
+import sys
+
+
+def run(cmd: list[str], check: bool = True) -> subprocess.CompletedProcess:
+ print("+", " ".join(cmd))
+ proc = subprocess.run(cmd, text=True, capture_output=True)
+ if proc.stdout:
+ print(proc.stdout)
+ if proc.stderr:
+ print(proc.stderr, file=sys.stderr)
+ if check and proc.returncode != 0:
+ raise SystemExit(proc.returncode)
+ return proc
+
+
+def ruff_json(paths: list[str]) -> list[dict]:
+ proc = run(["ruff", "check", *paths, "--output-format=json", "--exit-zero"], check=False)
+ try:
+ data = json.loads(proc.stdout or "[]")
+ except json.JSONDecodeError as e:
+ raise SystemExit(f"Could not parse Ruff JSON: {e}") from e
+ if not isinstance(data, list):
+ raise SystemExit("Unexpected Ruff JSON output")
+ return data
+
+
+def unique_e501_files(paths: list[str]) -> list[str]:
+ data = ruff_json(paths)
+ files = sorted({item["filename"] for item in data if item.get("code") == "E501"})
+ return files
+
+
+def chunked(items: list[str], size: int = 50):
+ for i in range(0, len(items), size):
+ yield items[i : i + size]
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("paths", nargs="*", default=["."], help="Files/directories to scan")
+ parser.add_argument("--line-length", type=int, default=88)
+ parser.add_argument("--check", action="store_true", help="Dry run; only show affected files")
+ args = parser.parse_args()
+
+ files = unique_e501_files(args.paths)
+ if not files:
+ print("No E501 files found. Nothing to do.")
+ return 0
+
+ print(f"Found {len(files)} file(s) with E501.")
+ for f in files:
+ print(" -", f)
+
+ if args.check:
+ return 0
+
+ for batch in chunked(files, 50):
+ run(
+ [
+ "autopep8",
+ "--in-place",
+ "--aggressive",
+ f"--max-line-length={args.line_length}",
+ "--select=E501,W291,W292,W391",
+ *batch,
+ ]
+ )
+
+ run(["ruff", "check", *batch, "--fix", "--unsafe-fixes"], check=False)
+ run(["ruff", "format", *batch], check=False)
+
+ after = len(unique_e501_files(args.paths))
+ print(f"Remaining files with E501: {after}")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/tools/update_license_headers.py b/tools/update_license_headers.py
index 4a05350e47..ca5381deb2 100644
--- a/tools/update_license_headers.py
+++ b/tools/update_license_headers.py
@@ -1,24 +1,25 @@
"""Apply copyright headers to all code files in the repository.
-This file can be called as a python script without arguments. For
-configuration, see the instructions in NOTICE.yml.
+This file can be called as a python script without arguments. For configuration, see the
+instructions in NOTICE.yml.
"""
-import tempfile
-import glob
-import yaml
import fnmatch
+import glob
import subprocess
+import tempfile
+
+import yaml
def load_config(filename):
- with open(filename, "r") as fh:
+ with open(filename) as fh:
config = yaml.safe_load(fh)
return config
def walk_directory(entry):
- """Talk the directory"""
+ """Talk the directory."""
if "header" not in entry:
raise ValueError("Current entry does not have a header.")
@@ -28,8 +29,7 @@ def walk_directory(entry):
def _list_include():
"""List all files specified in the include list."""
for include_pattern in entry["include"]:
- for filename in glob.iglob(include_pattern, recursive=True):
- yield filename
+ yield from glob.iglob(include_pattern, recursive=True)
def _filter_exclude(iterable):
"""Filter filenames from an iterator by the exclude patterns."""
diff --git a/uv.lock b/uv.lock
new file mode 100644
index 0000000000..b3d096b3f4
--- /dev/null
+++ b/uv.lock
@@ -0,0 +1,8412 @@
+version = 1
+revision = 3
+requires-python = ">=3.10"
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+conflicts = [[
+ { package = "deeplabcut", extra = "apple-mchips" },
+ { package = "deeplabcut", extra = "tf" },
+ { package = "deeplabcut", extra = "tf-cu11" },
+ { package = "deeplabcut", extra = "tf-cu12" },
+ { package = "deeplabcut", extra = "tf-latest" },
+], [
+ { package = "deeplabcut", extra = "fmpose3d" },
+ { package = "deeplabcut", extra = "tf-cu11" },
+ { package = "deeplabcut", extra = "tf-cu12" },
+]]
+
+[manifest]
+
+[[manifest.dependency-metadata]]
+name = "openvino-dev"
+version = "2022.1.0"
+
+[[package]]
+name = "absl-py"
+version = "2.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/64/c7/8de93764ad66968d19329a7e0c147a2bb3c7054c554d4a119111b8f9440f/absl_py-2.4.0.tar.gz", hash = "sha256:8c6af82722b35cf71e0f4d1d47dcaebfff286e27110a99fc359349b247dfb5d4", size = 116543, upload-time = "2026-01-28T10:17:05.322Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/18/a6/907a406bb7d359e6a63f99c313846d9eec4f7e6f7437809e03aa00fa3074/absl_py-2.4.0-py3-none-any.whl", hash = "sha256:88476fd881ca8aab94ffa78b7b6c632a782ab3ba1cd19c9bd423abc4fb4cd28d", size = 135750, upload-time = "2026-01-28T10:17:04.19Z" },
+]
+
+[[package]]
+name = "accessible-pygments"
+version = "0.0.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899, upload-time = "2024-05-10T11:23:10.216Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903, upload-time = "2024-05-10T11:23:08.421Z" },
+]
+
+[[package]]
+name = "alabaster"
+version = "0.7.16"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c9/3e/13dd8e5ed9094e734ac430b5d0eb4f2bb001708a8b7856cbf8e084e001ba/alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", size = 23776, upload-time = "2024-01-10T00:56:10.189Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/32/34/d4e1c02d3bee589efb5dfa17f88ea08bdb3e3eac12bc475462aec52ed223/alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92", size = 13511, upload-time = "2024-01-10T00:56:08.388Z" },
+]
+
+[[package]]
+name = "albumentations"
+version = "1.4.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+ { name = "opencv-python-headless" },
+ { name = "pyyaml" },
+ { name = "scikit-image", version = "0.25.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scikit-image", version = "0.26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/dc/c1/4bb46e1afe56a4908bfe6fe58a9231ed5d7e7f46e400559d8bce7fc7dedc/albumentations-1.4.3.tar.gz", hash = "sha256:d0a5ca4edb1695693faa39cdf427d53fdbd7d26f7f8e4100c307e5ff5a463d16", size = 171598, upload-time = "2024-04-03T01:51:05.117Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/40/01/4202bd81ab337dca5693d7d1cb25c8e9041d97762aee738a24382ff9af2f/albumentations-1.4.3-py3-none-any.whl", hash = "sha256:386a7a61b6978f90163bf678745674f1d9031fd39cf17a40ae01d88aade72608", size = 137007, upload-time = "2024-04-03T01:51:02.83Z" },
+]
+
+[[package]]
+name = "annotated-doc"
+version = "0.0.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
+]
+
+[[package]]
+name = "annotated-types"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
+]
+
+[[package]]
+name = "anyio"
+version = "4.13.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "idna" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
+]
+
+[[package]]
+name = "app-model"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "in-n-out" },
+ { name = "psygnal" },
+ { name = "pydantic" },
+ { name = "pydantic-compat" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8d/23/a9537fd7021e8b6ea7db96b73e66fe61e809a3b23338de07d1176e69e4ee/app_model-0.4.0.tar.gz", hash = "sha256:ccf667999f6c659e921ca3490b6da176971e67cf2f41abc34e33caa8cfa18573", size = 120529, upload-time = "2025-06-20T17:41:08.943Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8f/fa/d7f4ae02a3b115abdf561d05ba8d5cdf9e3b2b5724b45d91e6cf08c163c3/app_model-0.4.0-py3-none-any.whl", hash = "sha256:5b1d69ee023d955b0c7a525771fd2fc02ff9d82cd2f12a982949423c3a901210", size = 65710, upload-time = "2025-06-20T17:41:07.696Z" },
+]
+
+[[package]]
+name = "appdirs"
+version = "1.4.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" },
+]
+
+[[package]]
+name = "appnope"
+version = "0.1.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" },
+]
+
+[[package]]
+name = "asttokens"
+version = "3.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" },
+]
+
+[[package]]
+name = "astunparse"
+version = "1.6.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wheel", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f3/af/4182184d3c338792894f34a62672919db7ca008c89abee9b564dd34d8029/astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872", size = 18290, upload-time = "2019-12-22T18:12:13.129Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2b/03/13dde6512ad7b4557eb792fbcf0c653af6076b81e5941d36ec61f7ce6028/astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8", size = 12732, upload-time = "2019-12-22T18:12:11.297Z" },
+]
+
+[[package]]
+name = "attrs"
+version = "26.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" },
+]
+
+[[package]]
+name = "babel"
+version = "2.18.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" },
+]
+
+[[package]]
+name = "beautifulsoup4"
+version = "4.14.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "soupsieve" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" },
+]
+
+[[package]]
+name = "blosc2"
+version = "4.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "msgpack" },
+ { name = "ndindex" },
+ { name = "numexpr", marker = "platform_machine != 'wasm32' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy" },
+ { name = "pydantic" },
+ { name = "requests" },
+ { name = "threadpoolctl", marker = "platform_machine != 'wasm32' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/54/fb/c4d83eb40c8b9cb024507dbc4a3a91bfbc7e36ad5f2528c306575a87f42b/blosc2-4.3.1.tar.gz", hash = "sha256:f0c26b1190b24aae8b58ade95abd7c9aca5e7447db0f13c1915fcbe56a0b8167", size = 5354390, upload-time = "2026-05-19T17:34:45.553Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/a9/d952e141cea441a4387bf9c2e8917764569092e148eba2b3209408a0b655/blosc2-4.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c9bcd07ec550960513c6f9c2b901251c15d9bbfe79a554bbb9363162a92f5db", size = 5817410, upload-time = "2026-05-19T17:33:54.765Z" },
+ { url = "https://files.pythonhosted.org/packages/92/ab/fb94743b4511fb27accb4f9e3d5769cfd039d34801a7787b274cb6455c1c/blosc2-4.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8b38dff8a6d3446f08c7b25c8a00d63da32b63a35c05561d6be77bb67cf352de", size = 5026379, upload-time = "2026-05-19T17:33:56.8Z" },
+ { url = "https://files.pythonhosted.org/packages/81/df/a1b9bcb00d028f69fc58e45702d99d86a4eddf78640cd4ad87b2ab7cfa3d/blosc2-4.3.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:50ec0dec76b75411faaa7e79d2fb5adbfdd90b8b90c0637faa706d03336c87d9", size = 6306638, upload-time = "2026-05-19T17:33:59.029Z" },
+ { url = "https://files.pythonhosted.org/packages/87/1c/b2515f08969c0ed5dd55337ca11dcf9034e2b23250b16badbe6538b27021/blosc2-4.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a52b29306ff7e5b49f8365a7f9567c8bda9a1d3b2e042a4ca185268a379e05d", size = 6589405, upload-time = "2026-05-19T17:34:01.047Z" },
+ { url = "https://files.pythonhosted.org/packages/51/87/1874f6b0ffec99ba908e99ec547e4d45f3740b5c03f8ab1f310a3e36e22c/blosc2-4.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:320c9d6ba943aab3570f15d413c4c8b634bbd9d70c00199bf2cf74fe01112f71", size = 4126788, upload-time = "2026-05-19T17:34:02.941Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/29/f685930f492ec787557635b5e0e91b229c793cd3fd3d32083e5f088a15b9/blosc2-4.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7a1d8d56a7bbd942dc9f46b2c78178b16075ff5e88d7fb466c96e6fd0ff7fcd6", size = 5811703, upload-time = "2026-05-19T17:34:04.62Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/24/fc29520a463b96acf64d3fdf5c8bd6fd5d2a11dbda6d08af8fb69de8611b/blosc2-4.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93a02d9f11e4308385f4f556a14573e24310d49042f6d9ea61d74b9c42880980", size = 5021613, upload-time = "2026-05-19T17:34:05.941Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/dd/d80c3e198e056eec0aed87065a68cead2e8c17cffbf8d6aad498d37946cc/blosc2-4.3.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6759c659467dcf28c7e5d72bf90b7e5559d5e17533364461ab69ab85cce3d4f2", size = 6295416, upload-time = "2026-05-19T17:34:07.722Z" },
+ { url = "https://files.pythonhosted.org/packages/17/ba/8a5d0dcfb95126b6601e4f993ee276c9953be4748bd67f45202fee4ee58e/blosc2-4.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:706c27f5b93df7e320b1cdbc446f248d027106f968f762ecb240fde98bbe8af1", size = 6589973, upload-time = "2026-05-19T17:34:09.437Z" },
+ { url = "https://files.pythonhosted.org/packages/47/4b/a6fe2d0a496cf467c205cf83993c605dd38422a86adbe710fa2f8f5bcdf5/blosc2-4.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:95c8466beebef66c24ef2dd1993cda3c1f8c3c00ec3be0dcdc098dc5eb6b259c", size = 4124211, upload-time = "2026-05-19T17:34:11.07Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/2c/c7db23a891a5eb1893c1b3da206810db48d44cc0180e1b737dab1d5f5733/blosc2-4.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9d5aa1e54412f45da894b022fa33387361332884a6689af4121dad1d91c205fa", size = 5857286, upload-time = "2026-05-19T17:34:12.815Z" },
+ { url = "https://files.pythonhosted.org/packages/47/cc/04a147753b4e7fcf227f7a4c6480e3ee545909529d038bbd8a2e0111bd4a/blosc2-4.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bf349b41af997c775a94e02af45a4215e8ff3d8ff5e2c8e29b452d697e2c3a90", size = 5020423, upload-time = "2026-05-19T17:34:14.909Z" },
+ { url = "https://files.pythonhosted.org/packages/44/22/8f53101763841e907456e92ae06e5318f87228e6fb4e7aa28134c3abee2f/blosc2-4.3.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a162b378741233170fee4fee213f33b03799d4be42603a3f8344f5ed0df9f32a", size = 6240864, upload-time = "2026-05-19T17:34:16.522Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/4d/1c8818d8afd691dfbd847fb478bb606042114fdbdb321a8a12d0e707dd01/blosc2-4.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:faa6f6ea348ada734bf6d5c5b6988d47b975ad42fb160952ed92c6b0e8801e86", size = 6539023, upload-time = "2026-05-19T17:34:17.868Z" },
+ { url = "https://files.pythonhosted.org/packages/46/6b/0ecbef3077298fe4f9daf9c6c8ece80a0f98d1c6efc56e71cc45ad158c75/blosc2-4.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:2cedea2ac373b64b00aa956a04f74d885bdef033878e8cf84e53c001bbef6dc8", size = 4121473, upload-time = "2026-05-19T17:34:19.57Z" },
+ { url = "https://files.pythonhosted.org/packages/01/a9/b363f846afb1fec3b49acc4a248aa98bc31c55370effc59c455caa5398ad/blosc2-4.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fae6ed1deb9e79bd703d959e9c4f5a12d6cbfdb2b3713f646ad0f7266c96c114", size = 5855462, upload-time = "2026-05-19T17:34:21.179Z" },
+ { url = "https://files.pythonhosted.org/packages/60/6f/48ad9373d2d7fb82321f177a4525cccece70a996242c0a2205160aa9c87d/blosc2-4.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87c0f387b8d47d4e6e1f27c72d92366ef16ceb1b80629ed5eaab241822c386af", size = 5018781, upload-time = "2026-05-19T17:34:23.04Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/2f/5777d61be67c001d7674e00302350ba7a288cfea706c5181d440b0d2e8d5/blosc2-4.3.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c48881de2fe195084a1c583acb4ab66ee8b2f0b8f83fba00148a9eb06f4826fe", size = 6248194, upload-time = "2026-05-19T17:34:24.748Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/d6/9ecbfbb73daf8b95149e48dbca9f84d6303e6fac77e3e635c09f281f6f79/blosc2-4.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce881214846959a375279ac2f7ab66e2bfa868bfbd01188ae1329c243f6d1275", size = 6541927, upload-time = "2026-05-19T17:34:26.219Z" },
+ { url = "https://files.pythonhosted.org/packages/be/a7/40ead062061388ae6ac4981fa93127a7ed2da2ca66b2a9c09b191f00b7b1/blosc2-4.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:091202d20394e5593a365950cc43b09c49ececdcac36d3b491ddecc3600f36c2", size = 4121405, upload-time = "2026-05-19T17:34:27.615Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/c2/a9b75f9a228e9ac3a202d23d8d8fa23d737064919ac2a3c3866cfaa80b5d/blosc2-4.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0c50a9c64cad0228e382a09e605c4fd6cd9aa2576b76ab16005121a4a57f9698", size = 5857264, upload-time = "2026-05-19T17:34:29.317Z" },
+ { url = "https://files.pythonhosted.org/packages/68/08/3561e51ed30c26b6272675b4528457b44c840af11b5b0741fbd9d9d62287/blosc2-4.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:98f1eb44f6ff517c6e3476a3eb3a85b357e4604bd3274db354bf52f854fb50bf", size = 5021316, upload-time = "2026-05-19T17:34:31.132Z" },
+ { url = "https://files.pythonhosted.org/packages/12/6c/deff0f7314be3af1cf2fc728bae0be7414084b1ff51cb9a61c5a54997742/blosc2-4.3.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a31ae1e372f2f53215bc8bcecfe4aa5af20a8cfe4124dccc8dc334093539b2c9", size = 6260060, upload-time = "2026-05-19T17:34:32.648Z" },
+ { url = "https://files.pythonhosted.org/packages/44/56/f941a447f47e3c997acb3ff1459c8a26e08f383ec60adf80e3ce04de6be3/blosc2-4.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:565e4d038089c495e06b2e3e5a2881d61619460c8448c0291dbd84840437c28d", size = 6543150, upload-time = "2026-05-19T17:34:34.694Z" },
+ { url = "https://files.pythonhosted.org/packages/77/15/8a91f03a84854615dfa0b2b7afd9ee29b0b3bde5e28ebe38f5f49de438e1/blosc2-4.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:7d76a639d7287bb3fec73dd1ae8196923d95e00b95069bce3c495eaa00acdfe8", size = 4204741, upload-time = "2026-05-19T17:34:36.095Z" },
+ { url = "https://files.pythonhosted.org/packages/97/68/a246dd2f7ef538a572f61bfeab53d6ae0bcdf3e8cc5187c284189f7f27dc/blosc2-4.3.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:365235f674f3b70ae2eae3e2e7f0cdbda7b5e02fa50b357ab211dd45b91ad386", size = 5895602, upload-time = "2026-05-19T17:34:37.416Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/d0/40a9ac3634b520a9a9ffe30569b7b1296fd5e337c858883f379a6844f4c8/blosc2-4.3.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbd29c7f7d2fdaf164f81f68afa2580941b9832ace77f61c01c39788a48b67f7", size = 5067186, upload-time = "2026-05-19T17:34:38.824Z" },
+ { url = "https://files.pythonhosted.org/packages/91/6a/4607099251c65db83040686dfb6f1711278b536725552611365967d978e5/blosc2-4.3.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c558251376af56dea742ab60233faa1c52381a5f1b1a3db20dce192d3e8e7fd8", size = 6222994, upload-time = "2026-05-19T17:34:40.628Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/bd/a9f95816a1a680ddd0f18f2d422df98ee4033f59db71960976e66abda361/blosc2-4.3.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cde49a4cf3b207d36abfeb963eccac998e34b930603baff068cd3593b2d9631f", size = 6512973, upload-time = "2026-05-19T17:34:42.135Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/0e/b1669555dbe381344e97056c9c5c4c9bbf2111026d2f2aa660059bbc541f/blosc2-4.3.1-cp314-cp314t-win_amd64.whl", hash = "sha256:3424327e0ae91e98b9d9c3a32cc27e76d82dc0ec193e81e5b6c770bce5767e7c", size = 4264125, upload-time = "2026-05-19T17:34:43.931Z" },
+]
+
+[[package]]
+name = "bottleneck"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/14/d8/6d641573e210768816023a64966d66463f2ce9fc9945fa03290c8a18f87c/bottleneck-1.6.0.tar.gz", hash = "sha256:028d46ee4b025ad9ab4d79924113816f825f62b17b87c9e1d0d8ce144a4a0e31", size = 104311, upload-time = "2025-09-08T16:30:38.617Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9c/38/144fb32c9efb196f651ddb30e7c48f6047a86972e5b350f3f10c9a5f6a16/bottleneck-1.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40de6be68218ba32cd15addbf4ad7bbbf0075b5c5c4347c579aeae110a5c9a96", size = 100393, upload-time = "2025-09-08T16:29:35.466Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/e3/dbbf4b102f4e6aaf49ad3749a6d778f309473a2950c5ce3bb20b94f2ba84/bottleneck-1.6.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ad1882ba8c8da1f404de2610b45b05291e39eec56150270b03b5b25cf2bbb7f", size = 371509, upload-time = "2025-09-08T16:29:37.037Z" },
+ { url = "https://files.pythonhosted.org/packages/66/ea/60fcbddee5fdf32923ba33ce2337a4cf12834b69de4f8e07219b5ef7c931/bottleneck-1.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f29b14b0ba5a816df6ab559add415c88ea8cf2146364e55f5f4c24ff7c85e494", size = 363480, upload-time = "2025-09-08T16:29:38.311Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/9e/a25434dcadf083e05b0c71ece2de71fad5521268f905721e06e0a7efc5db/bottleneck-1.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17c227ed361cf9a2ab3751a727620298faca9a1e33dd76711ae80834cf34b254", size = 357120, upload-time = "2025-09-08T16:29:39.541Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/b9/99580349c827695dfc094ac672eedba6e1ca244b6e745ff7447c0239d6d8/bottleneck-1.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d278b5633cea38bdae6eaf7df23d54ecb5e4db52f2ebc13fe40c0e738842f2a1", size = 367579, upload-time = "2025-09-08T16:29:40.695Z" },
+ { url = "https://files.pythonhosted.org/packages/95/06/6326994249388ceb2400d07c6a96a50941749d2d9ec80da22a99046e3a38/bottleneck-1.6.0-cp310-cp310-win32.whl", hash = "sha256:26c87c2f6364d82b67eab7218f0346e9c42f336088ca4e19d77dc76eecf272fc", size = 107838, upload-time = "2025-09-08T16:29:41.907Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/75/8f0e8e266ea99ffbc69500a927f0c114a07fe465bfbc59871d6fe22d9ee0/bottleneck-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:9d33bcd60a13d0603f5db9d953352a3c098242c46f8f919290fd11c54b42b9e5", size = 113364, upload-time = "2025-09-08T16:29:43.437Z" },
+ { url = "https://files.pythonhosted.org/packages/83/96/9d51012d729f97de1e75aad986f3ba50956742a40fc99cbab4c2aa896c1c/bottleneck-1.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:69ef4514782afe39db2497aaea93b1c167ab7ab3bc5e3930500ef9cf11841db7", size = 100400, upload-time = "2025-09-08T16:29:44.464Z" },
+ { url = "https://files.pythonhosted.org/packages/16/f4/4fcbebcbc42376a77e395a6838575950587e5eb82edf47d103f8daa7ba22/bottleneck-1.6.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:727363f99edc6dc83d52ed28224d4cb858c07a01c336c7499c0c2e5dd4fd3e4a", size = 375920, upload-time = "2025-09-08T16:29:45.52Z" },
+ { url = "https://files.pythonhosted.org/packages/36/13/7fa8cdc41cbf2dfe0540f98e1e0caf9ffbd681b1a0fc679a91c2698adaf9/bottleneck-1.6.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:847671a9e392220d1dfd2ff2524b4d61ec47b2a36ea78e169d2aa357fd9d933a", size = 367922, upload-time = "2025-09-08T16:29:46.743Z" },
+ { url = "https://files.pythonhosted.org/packages/13/7d/dccfa4a2792c1bdc0efdde8267e527727e517df1ff0d4976b84e0268c2f9/bottleneck-1.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:daef2603ab7b4ec4f032bb54facf5fa92dacd3a264c2fd9677c9fc22bcb5a245", size = 361379, upload-time = "2025-09-08T16:29:48.042Z" },
+ { url = "https://files.pythonhosted.org/packages/93/42/21c0fad823b71c3a8904cbb847ad45136d25573a2d001a9cff48d3985fab/bottleneck-1.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fc7f09bda980d967f2e9f1a746eda57479f824f66de0b92b9835c431a8c922d4", size = 371911, upload-time = "2025-09-08T16:29:49.366Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/b0/830ff80f8c74577d53034c494639eac7a0ffc70935c01ceadfbe77f590c2/bottleneck-1.6.0-cp311-cp311-win32.whl", hash = "sha256:1f78bad13ad190180f73cceb92d22f4101bde3d768f4647030089f704ae7cac7", size = 107831, upload-time = "2025-09-08T16:29:51.397Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/42/01d4920b0aa51fba503f112c90714547609bbe17b6ecfc1c7ae1da3183df/bottleneck-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:8f2adef59fdb9edf2983fe3a4c07e5d1b677c43e5669f4711da2c3daad8321ad", size = 113358, upload-time = "2025-09-08T16:29:52.602Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/72/7e3593a2a3dd69ec831a9981a7b1443647acb66a5aec34c1620a5f7f8498/bottleneck-1.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3bb16a16a86a655fdbb34df672109a8a227bb5f9c9cf5bb8ae400a639bc52fa3", size = 100515, upload-time = "2025-09-08T16:29:55.141Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/d4/e7bbea08f4c0f0bab819d38c1a613da5f194fba7b19aae3e2b3a27e78886/bottleneck-1.6.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0fbf5d0787af9aee6cef4db9cdd14975ce24bd02e0cc30155a51411ebe2ff35f", size = 377451, upload-time = "2025-09-08T16:29:56.718Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/80/a6da430e3b1a12fd85f9fe90d3ad8fe9a527ecb046644c37b4b3f4baacfc/bottleneck-1.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d08966f4a22384862258940346a72087a6f7cebb19038fbf3a3f6690ee7fd39f", size = 368303, upload-time = "2025-09-08T16:29:57.834Z" },
+ { url = "https://files.pythonhosted.org/packages/30/11/abd30a49f3251f4538430e5f876df96f2b39dabf49e05c5836820d2c31fe/bottleneck-1.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:604f0b898b43b7bc631c564630e936a8759d2d952641c8b02f71e31dbcd9deaa", size = 361232, upload-time = "2025-09-08T16:29:59.104Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/ac/1c0e09d8d92b9951f675bd42463ce76c3c3657b31c5bf53ca1f6dd9eccff/bottleneck-1.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d33720bad761e642abc18eda5f188ff2841191c9f63f9d0c052245decc0faeb9", size = 373234, upload-time = "2025-09-08T16:30:00.488Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/ea/382c572ae3057ba885d484726bb63629d1f63abedf91c6cd23974eb35a9b/bottleneck-1.6.0-cp312-cp312-win32.whl", hash = "sha256:a1e5907ec2714efbe7075d9207b58c22ab6984a59102e4ecd78dced80dab8374", size = 108020, upload-time = "2025-09-08T16:30:01.773Z" },
+ { url = "https://files.pythonhosted.org/packages/48/ad/d71da675eef85ac153eef5111ca0caa924548c9591da00939bcabba8de8e/bottleneck-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:81e3822499f057a917b7d3972ebc631ac63c6bbcc79ad3542a66c4c40634e3a6", size = 113493, upload-time = "2025-09-08T16:30:02.872Z" },
+ { url = "https://files.pythonhosted.org/packages/97/1a/e117cd5ff7056126d3291deb29ac8066476e60b852555b95beb3fc9d62a0/bottleneck-1.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015de414ca016ebe56440bdf5d3d1204085080527a3c51f5b7b7a3e704fe6fd", size = 100521, upload-time = "2025-09-08T16:30:03.89Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/22/05555a9752357e24caa1cd92324d1a7fdde6386aab162fcc451f8f8eedc2/bottleneck-1.6.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:456757c9525b0b12356f472e38020ed4b76b18375fd76e055f8d33fb62956f5e", size = 377719, upload-time = "2025-09-08T16:30:05.135Z" },
+ { url = "https://files.pythonhosted.org/packages/11/ee/76593af47097d9633109bed04dbcf2170707dd84313ca29f436f9234bc51/bottleneck-1.6.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c65254d51b6063c55f6272f175e867e2078342ae75f74be29d6612e9627b2c0", size = 368577, upload-time = "2025-09-08T16:30:06.387Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/f7/4dcacaf637d2b8d89ea746c74159adda43858d47358978880614c3fa4391/bottleneck-1.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a172322895fbb79c6127474f1b0db0866895f0b804a18d5c6b841fea093927fe", size = 361441, upload-time = "2025-09-08T16:30:07.613Z" },
+ { url = "https://files.pythonhosted.org/packages/05/34/21eb1eb1c42cb7be2872d0647c292fc75768d14e1f0db66bf907b24b2464/bottleneck-1.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5e81b642eb0d5a5bf00312598d7ed142d389728b694322a118c26813f3d1fa9", size = 373416, upload-time = "2025-09-08T16:30:08.899Z" },
+ { url = "https://files.pythonhosted.org/packages/48/cb/7957ff40367a151139b5f1854616bf92e578f10804d226fbcdecfd73aead/bottleneck-1.6.0-cp313-cp313-win32.whl", hash = "sha256:543d3a89d22880cd322e44caff859af6c0489657bf9897977d1f5d3d3f77299c", size = 108029, upload-time = "2025-09-08T16:30:09.909Z" },
+ { url = "https://files.pythonhosted.org/packages/90/a8/735df4156fa5595501d5d96a6ee102f49c13d2ce9e2a287ad51806bc3ba0/bottleneck-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:48a44307d604ceb81e256903e5d57d3adb96a461b1d3c6a69baa2c67e823bd36", size = 113497, upload-time = "2025-09-08T16:30:10.82Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/5c/8c1260df8ade7cebc2a8af513a27082b5e36aa4a5fb762d56ea6d969d893/bottleneck-1.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:547e6715115867c4657c9ae8cc5ddac1fec8fdad66690be3a322a7488721b06b", size = 101606, upload-time = "2025-09-08T16:30:11.935Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/ea/f03e2944e91ee962922c834ed21e5be6d067c8395681f5dc6c67a0a26853/bottleneck-1.6.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5e4a4a6e05b6f014c307969129e10d1a0afd18f3a2c127b085532a4a76677aef", size = 391804, upload-time = "2025-09-08T16:30:13.13Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/58/2b356b8a81eb97637dccee6cf58237198dd828890e38be9afb4e5e58e38e/bottleneck-1.6.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2baae0d1589b4a520b2f9cf03528c0c8b20717b3f05675e212ec2200cf628f12", size = 383443, upload-time = "2025-09-08T16:30:14.318Z" },
+ { url = "https://files.pythonhosted.org/packages/55/52/cf7d09ed3736ad0d50c624787f9b580ae3206494d95cc0f4814b93eef728/bottleneck-1.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2e407139b322f01d8d5b6b2e8091b810f48a25c7fa5c678cfcdc420dfe8aea0a", size = 375458, upload-time = "2025-09-08T16:30:15.379Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/e9/7c87a34a24e339860064f20fac49f6738e94f1717bc8726b9c47705601d8/bottleneck-1.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1adefb89b92aba6de9c6ea871d99bcd29d519f4fb012cc5197917813b4fc2c7f", size = 386384, upload-time = "2025-09-08T16:30:17.012Z" },
+ { url = "https://files.pythonhosted.org/packages/59/57/db51855e18a47671801180be748939b4c9422a0544849af1919116346b5f/bottleneck-1.6.0-cp313-cp313t-win32.whl", hash = "sha256:64b8690393494074923780f6abdf5f5577d844b9d9689725d1575a936e74e5f0", size = 109448, upload-time = "2025-09-08T16:30:18.076Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/1e/683c090b624f13a5bf88a0be2241dc301e98b2fb72a45812a7ae6e456cc4/bottleneck-1.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:cb67247f65dcdf62af947c76c6c8b77d9f0ead442cac0edbaa17850d6da4e48d", size = 115190, upload-time = "2025-09-08T16:30:19.106Z" },
+ { url = "https://files.pythonhosted.org/packages/77/e2/eb7c08964a3f3c4719f98795ccd21807ee9dd3071a0f9ad652a5f19196ff/bottleneck-1.6.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:98f1d789042511a0f042b3bdcd2903e8567e956d3aa3be189cce3746daeb8550", size = 100544, upload-time = "2025-09-08T16:30:20.22Z" },
+ { url = "https://files.pythonhosted.org/packages/99/ec/c6f3be848f37689f481797ce7d9807d5f69a199d7fc0e46044f9b708c468/bottleneck-1.6.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1fad24c99e39ad7623fc2a76d37feb26bd32e4dd170885edf4dbf4bfce2199a3", size = 378315, upload-time = "2025-09-08T16:30:21.409Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/8f/2d6600836e2ea8f14fcefac592dc83497e5b88d381470c958cb9cdf88706/bottleneck-1.6.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643e61e50a6f993debc399b495a1609a55b3bd76b057e433e4089505d9f605c7", size = 368978, upload-time = "2025-09-08T16:30:23.458Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/b5/bf72b49f5040212873b985feef5050015645e0a02204b591e1d265fc522a/bottleneck-1.6.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa668efbe4c6b200524ea0ebd537212da9b9801287138016fdf64119d6fcf201", size = 362074, upload-time = "2025-09-08T16:30:24.71Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/c8/c4891a0604eb680031390182c6e264247e3a9a8d067d654362245396fadf/bottleneck-1.6.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9f7dd35262e89e28fedd79d45022394b1fa1aceb61d2e747c6d6842e50546daa", size = 374019, upload-time = "2025-09-08T16:30:26.438Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/2d/ed096f8d1b9147e84914045dd89bc64e3c32eee49b862d1e20d573a9ab0d/bottleneck-1.6.0-cp314-cp314-win32.whl", hash = "sha256:bd90bec3c470b7fdfafc2fbdcd7a1c55a4e57b5cdad88d40eea5bc9bab759bf1", size = 110173, upload-time = "2025-09-08T16:30:27.521Z" },
+ { url = "https://files.pythonhosted.org/packages/33/70/1414acb6ae378a15063cfb19a0a39d69d1b6baae1120a64d2b069902549b/bottleneck-1.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:b43b6d36a62ffdedc6368cf9a708e4d0a30d98656c2b5f33d88894e1bcfd6857", size = 115899, upload-time = "2025-09-08T16:30:28.524Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/ed/4570b5d8c1c85ce3c54963ebc37472231ed54f0b0d8dbb5dde14303f775f/bottleneck-1.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:53296707a8e195b5dcaa804b714bd222b5e446bd93cd496008122277eb43fa87", size = 101615, upload-time = "2025-09-08T16:30:29.556Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/93/c148faa07ae91f266be1f3fad1fde95aa2449e12937f3f3df2dd720b86e0/bottleneck-1.6.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6df19cc48a83efd70f6d6874332aa31c3f5ca06a98b782449064abbd564cf0e", size = 392411, upload-time = "2025-09-08T16:30:31.186Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/1c/e6ad221d345a059e7efb2ad1d46a22d9fdae0486faef70555766e1123966/bottleneck-1.6.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96bb3a52cb3c0aadfedce3106f93ab940a49c9d35cd4ed612e031f6deb27e80f", size = 384022, upload-time = "2025-09-08T16:30:32.364Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/40/5b15c01eb8c59d59bc84c94d01d3d30797c961f10ec190f53c27e05d62ab/bottleneck-1.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d1db9e831b69d5595b12e79aeb04cb02873db35576467c8dd26cdc1ee6b74581", size = 376004, upload-time = "2025-09-08T16:30:33.731Z" },
+ { url = "https://files.pythonhosted.org/packages/74/f6/cb228f5949553a5c01d1d5a3c933f0216d78540d9e0bf8dd4343bb449681/bottleneck-1.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4dd7ac619570865fcb7a0e8925df418005f076286ad2c702dd0f447231d7a055", size = 386909, upload-time = "2025-09-08T16:30:34.973Z" },
+ { url = "https://files.pythonhosted.org/packages/09/9a/425065c37a67a9120bf53290371579b83d05bf46f3212cce65d8c01d470a/bottleneck-1.6.0-cp314-cp314t-win32.whl", hash = "sha256:7fb694165df95d428fe00b98b9ea7d126ef786c4a4b7d43ae2530248396cadcb", size = 111636, upload-time = "2025-09-08T16:30:36.044Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/23/c41006e42909ec5114a8961818412310aa54646d1eae0495dbff3598a095/bottleneck-1.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:174b80930ce82bd8456c67f1abb28a5975c68db49d254783ce2cb6983b4fea40", size = 117611, upload-time = "2025-09-08T16:30:37.055Z" },
+]
+
+[[package]]
+name = "build"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (os_name != 'nt' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "importlib-metadata", marker = "python_full_version < '3.10.2' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging" },
+ { name = "pyproject-hooks" },
+ { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/78/e0/df5e171f685f82f37b12e1f208064e24244911079d7b767447d1af7e0d70/build-1.5.0.tar.gz", hash = "sha256:302c22c3ba2a0fd5f3911918651341ebb3896176cbdec15bd421f80b1afc7647", size = 89796, upload-time = "2026-04-30T03:18:25.17Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl", hash = "sha256:13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f", size = 26018, upload-time = "2026-04-30T03:18:23.644Z" },
+]
+
+[[package]]
+name = "cachey"
+version = "0.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "heapdict" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c6/9c/e3c959c1601013bf8a72e8bf91ea1ebc6fe8a2305bd2324b039ee0403277/cachey-0.2.1.tar.gz", hash = "sha256:0310ba8afe52729fa7626325c8d8356a8421c434bf887ac851e58dcf7cf056a6", size = 6461, upload-time = "2020-03-11T15:34:08.721Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/57/f0/e24f3e5d5d539abeb783087b87c26cfb99c259f1126700569e000243745a/cachey-0.2.1-py3-none-any.whl", hash = "sha256:49cf8528496ce3f99d47f1bd136b7c88237e55347a15d880f47cefc0615a83c3", size = 6415, upload-time = "2020-03-11T15:34:07.347Z" },
+]
+
+[[package]]
+name = "certifi"
+version = "2026.4.22"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" },
+]
+
+[[package]]
+name = "cffi"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser", marker = "implementation_name != 'PyPy' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" },
+ { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" },
+ { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" },
+ { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" },
+ { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" },
+ { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" },
+ { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" },
+ { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" },
+ { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
+ { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
+ { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
+ { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
+ { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
+ { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
+ { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
+ { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
+ { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
+ { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
+ { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
+ { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
+ { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
+ { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
+ { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
+ { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
+]
+
+[[package]]
+name = "cfgv"
+version = "3.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" },
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.4.7"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" },
+ { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" },
+ { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" },
+ { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" },
+ { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" },
+ { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" },
+ { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" },
+ { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" },
+ { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" },
+ { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" },
+ { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" },
+ { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" },
+ { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" },
+ { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" },
+ { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" },
+ { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" },
+ { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" },
+ { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" },
+ { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" },
+ { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" },
+ { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" },
+ { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" },
+ { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" },
+ { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" },
+ { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" },
+ { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" },
+ { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" },
+ { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" },
+ { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" },
+ { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" },
+ { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" },
+ { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" },
+ { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" },
+ { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" },
+ { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" },
+ { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" },
+ { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" },
+ { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" },
+ { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" },
+ { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" },
+ { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" },
+]
+
+[[package]]
+name = "click"
+version = "8.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/23/e4/796662cd90cf80e3a363c99db2b88e0e394b988a575f60a17e16440cd011/click-8.4.0.tar.gz", hash = "sha256:638f1338fe1235c8f4e008e4a8a254fb5c5fbdcbb40ece3c9142ebb78e792973", size = 350843, upload-time = "2026-05-17T00:47:58.425Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ee/ae/8e92f8058baf87f6c7d86ee7e457668690195cc77efedb8d3797a06e3940/click-8.4.0-py3-none-any.whl", hash = "sha256:40c50b7c6c6adac2823d411041ec84f3f103f1b280d5e9ce0d7f998995832f81", size = 116147, upload-time = "2026-05-17T00:47:56.842Z" },
+]
+
+[[package]]
+name = "cloudpickle"
+version = "3.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" },
+]
+
+[[package]]
+name = "cmake"
+version = "4.3.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/76/07/f1d6f7bcf056a139352cc2972f92a92005ac0ee98103165b1f620873b196/cmake-4.3.2.tar.gz", hash = "sha256:5f47f5f00910c474662d09a0516413c6e9750bde73cdc52dea3988102a274e06", size = 36969, upload-time = "2026-04-23T21:51:35.982Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/82/15/4c8980d5ceef53f0c490425f5b8e47f3ff863348400b0ea5ba4e349119f1/cmake-4.3.2-py3-none-macosx_10_10_universal2.whl", hash = "sha256:f8f570813753ed4564928cf45c4c13c31e46b3e66b1a07fe695cb9f7b7af185e", size = 52883814, upload-time = "2026-04-23T21:50:16.764Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/50/2f336143dbcf5eca7c2d7e86273a84ef60231a0b7cfa33143d964c62f250/cmake-4.3.2-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ca739ab0d8960261fdb1bb6e1e6c16b9cd033ae0a98341483cc233ba7c81b22b", size = 29578008, upload-time = "2026-04-23T21:50:21.965Z" },
+ { url = "https://files.pythonhosted.org/packages/52/e9/4b571a24924b5bdbd5c0b0fbb0b6b1008eb6472ceb9f23bca9e08e09632e/cmake-4.3.2-py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2b81038453a40aed73f8d28185c4c3ef43c3a91a7ff1577ce08e498769ecfe16", size = 30676971, upload-time = "2026-04-23T21:50:25.395Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/d4/87f91ba2030c862a27a5b2df42e4e80d3244910acd10aa4cdcd0111820a9/cmake-4.3.2-py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:8add046a4ef7c606d8e0a444050415054c7da3b8535b2c8ce1f03e265dda098d", size = 30462837, upload-time = "2026-04-23T21:50:28.785Z" },
+ { url = "https://files.pythonhosted.org/packages/21/ee/5475cd861db5e8e22fd6b3fd323a67f8f62df487163f72bc281739309bcf/cmake-4.3.2-py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:7105a5411bbc405242677d29222812028566aafac3f78fda08c1717f03e5ca2a", size = 28377235, upload-time = "2026-04-23T21:50:32.144Z" },
+ { url = "https://files.pythonhosted.org/packages/63/cd/1008be054420fd759b73d3328326d047ea693c3910a55cb2a597d911baee/cmake-4.3.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:339655b93289c1b03c6a72523d46d3b0d19dc51406d3a90f8eefcbec525cb271", size = 29512079, upload-time = "2026-04-23T21:50:36.295Z" },
+ { url = "https://files.pythonhosted.org/packages/13/c4/e7c3649c4941927aff24c464090c4ce7f1f24167077f1b8aff3994def88d/cmake-4.3.2-py3-none-manylinux_2_31_armv7l.whl", hash = "sha256:ea95db137fd27f420d5e149c41e1f7621e786869afa3ca0fe18301df9e066607", size = 26628649, upload-time = "2026-04-23T21:50:40.047Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/05/ee3b002e0e7303a36e1f3c52e18d004fe089076a0e7b38df5e64a2328e88/cmake-4.3.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:3947bc5973ca3846c76486993abd0fba0cb9119300d58ed9173a672d1eef505b", size = 26769012, upload-time = "2026-04-23T21:50:44.237Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/a6/4e42625ce96197ffd72bb6a0e9c02d64506ee3075dd801219bb44fd9dcf7/cmake-4.3.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:bc316be89fa43c265697c3b9ffcebde977bcc4515974372cc460c974f458ff98", size = 38595561, upload-time = "2026-04-23T21:50:48.601Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/5b/80ab6fa7aceff0af186a844b6a53c3022e9775467804a1ce2ec3aafb02b3/cmake-4.3.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d10648929eb3294449ceae7a7c0b9714ca445280ddc25c209f427e7a07c6f3a9", size = 35226159, upload-time = "2026-04-23T21:50:53.968Z" },
+ { url = "https://files.pythonhosted.org/packages/10/e3/73fc202fb943221a7ce78a5374c7a73093d26af85378ddab9c04586dc98b/cmake-4.3.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a69e629e6cd973e1f9b11d247d116acb47b35cdd8e39aee4b04b9040851cd8a0", size = 41262833, upload-time = "2026-04-23T21:50:59.402Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/d3/3ef79820bfbcd5b51ef3b8029fc9c96da363daa11aae02ddd82df08fa446/cmake-4.3.2-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:95e0ff31a692d12130f33d1467d0074f8314cf3a79940694896de9367b6e4fae", size = 40437896, upload-time = "2026-04-23T21:51:04.304Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/a8/8a1147fa26a3c1564bf654b744baeb2b8c0efccfde04190d3f9222c27bde/cmake-4.3.2-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:67775407b963385a7942dd56f0567ef3c75453c6336654aeafe43165d78648bb", size = 35492662, upload-time = "2026-04-23T21:51:08.982Z" },
+ { url = "https://files.pythonhosted.org/packages/78/96/42a744beb4c9f6e459f1a57162f391a9941f31850048fc461e88095f98da/cmake-4.3.2-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:aae70bb95762f6da20131cf72da6322557ff6784968755d201d42a44cd9494e5", size = 37723238, upload-time = "2026-04-23T21:51:13.595Z" },
+ { url = "https://files.pythonhosted.org/packages/06/4c/513a73685feef886c824982b6618bb3cc7e7ac8579b34f1fe043bce11af5/cmake-4.3.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b1c5c292b1189e48d01f0bed01ed800c31eed967afd033c4beabff9cc97209f2", size = 38554155, upload-time = "2026-04-23T21:51:18.153Z" },
+ { url = "https://files.pythonhosted.org/packages/14/aa/35b387f6cc0990247195109b04523a1c05dbb4f0fd0bae28e7f34c4a1e6f/cmake-4.3.2-py3-none-win32.whl", hash = "sha256:3c55f0c61c70642d9e7f6b4fc638622027f045b388e357d74efcca4a7111e4aa", size = 37819702, upload-time = "2026-04-23T21:51:22.933Z" },
+ { url = "https://files.pythonhosted.org/packages/96/8d/7ceb7223d274e88d621ce00f2160ae74aead18a3d36f61b8fb52cbe6b7ca/cmake-4.3.2-py3-none-win_amd64.whl", hash = "sha256:78049aac277aabe376d8e82993f0be234d086d0e8ad4708755d5a209a04e1138", size = 41272507, upload-time = "2026-04-23T21:51:27.93Z" },
+ { url = "https://files.pythonhosted.org/packages/69/4f/fa4da5330b63d5ce7909892005eced21d1fca58d022ed6b40f216f6f6c52/cmake-4.3.2-py3-none-win_arm64.whl", hash = "sha256:b218d636a99fa0eb23713d37d3e3c3a9c0e707e0579b46780ff908acef229386", size = 39613878, upload-time = "2026-04-23T21:51:33.041Z" },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
+]
+
+[[package]]
+name = "comm"
+version = "0.2.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" },
+]
+
+[[package]]
+name = "contourpy"
+version = "1.3.2"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" },
+ { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" },
+ { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" },
+ { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" },
+ { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" },
+ { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload-time = "2025-04-15T17:35:54.473Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload-time = "2025-04-15T17:35:58.283Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload-time = "2025-04-15T17:36:03.235Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload-time = "2025-04-15T17:36:08.275Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload-time = "2025-04-15T17:36:13.29Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload-time = "2025-04-15T17:36:18.329Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload-time = "2025-04-15T17:36:33.878Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload-time = "2025-04-15T17:36:51.295Z" },
+ { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload-time = "2025-04-15T17:36:55.002Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload-time = "2025-04-15T17:36:58.576Z" },
+ { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" },
+ { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" },
+ { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" },
+ { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" },
+ { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/61/5673f7e364b31e4e7ef6f61a4b5121c5f170f941895912f773d95270f3a2/contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb", size = 271630, upload-time = "2025-04-15T17:38:19.142Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/66/a40badddd1223822c95798c55292844b7e871e50f6bfd9f158cb25e0bd39/contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08", size = 255670, upload-time = "2025-04-15T17:38:23.688Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/c7/cf9fdee8200805c9bc3b148f49cb9482a4e3ea2719e772602a425c9b09f8/contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c", size = 306694, upload-time = "2025-04-15T17:38:28.238Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/e7/ccb9bec80e1ba121efbffad7f38021021cda5be87532ec16fd96533bb2e0/contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f", size = 345986, upload-time = "2025-04-15T17:38:33.502Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/49/ca13bb2da90391fa4219fdb23b078d6065ada886658ac7818e5441448b78/contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85", size = 318060, upload-time = "2025-04-15T17:38:38.672Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841", size = 322747, upload-time = "2025-04-15T17:38:43.712Z" },
+ { url = "https://files.pythonhosted.org/packages/72/30/669b8eb48e0a01c660ead3752a25b44fdb2e5ebc13a55782f639170772f9/contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422", size = 1308895, upload-time = "2025-04-15T17:39:00.224Z" },
+ { url = "https://files.pythonhosted.org/packages/05/5a/b569f4250decee6e8d54498be7bdf29021a4c256e77fe8138c8319ef8eb3/contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef", size = 1379098, upload-time = "2025-04-15T17:43:29.649Z" },
+ { url = "https://files.pythonhosted.org/packages/19/ba/b227c3886d120e60e41b28740ac3617b2f2b971b9f601c835661194579f1/contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f", size = 178535, upload-time = "2025-04-15T17:44:44.532Z" },
+ { url = "https://files.pythonhosted.org/packages/12/6e/2fed56cd47ca739b43e892707ae9a13790a486a3173be063681ca67d2262/contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9", size = 223096, upload-time = "2025-04-15T17:44:48.194Z" },
+ { url = "https://files.pythonhosted.org/packages/54/4c/e76fe2a03014a7c767d79ea35c86a747e9325537a8b7627e0e5b3ba266b4/contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f", size = 285090, upload-time = "2025-04-15T17:43:34.084Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/e2/5aba47debd55d668e00baf9651b721e7733975dc9fc27264a62b0dd26eb8/contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739", size = 268643, upload-time = "2025-04-15T17:43:38.626Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/37/cd45f1f051fe6230f751cc5cdd2728bb3a203f5619510ef11e732109593c/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823", size = 310443, upload-time = "2025-04-15T17:43:44.522Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/a2/36ea6140c306c9ff6dd38e3bcec80b3b018474ef4d17eb68ceecd26675f4/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5", size = 349865, upload-time = "2025-04-15T17:43:49.545Z" },
+ { url = "https://files.pythonhosted.org/packages/95/b7/2fc76bc539693180488f7b6cc518da7acbbb9e3b931fd9280504128bf956/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532", size = 321162, upload-time = "2025-04-15T17:43:54.203Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/10/76d4f778458b0aa83f96e59d65ece72a060bacb20cfbee46cf6cd5ceba41/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b", size = 327355, upload-time = "2025-04-15T17:44:01.025Z" },
+ { url = "https://files.pythonhosted.org/packages/43/a3/10cf483ea683f9f8ab096c24bad3cce20e0d1dd9a4baa0e2093c1c962d9d/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52", size = 1307935, upload-time = "2025-04-15T17:44:17.322Z" },
+ { url = "https://files.pythonhosted.org/packages/78/73/69dd9a024444489e22d86108e7b913f3528f56cfc312b5c5727a44188471/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd", size = 1372168, upload-time = "2025-04-15T17:44:33.43Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/1b/96d586ccf1b1a9d2004dd519b25fbf104a11589abfd05484ff12199cca21/contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1", size = 189550, upload-time = "2025-04-15T17:44:37.092Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/e6/6000d0094e8a5e32ad62591c8609e269febb6e4db83a1c75ff8868b42731/contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69", size = 238214, upload-time = "2025-04-15T17:44:40.827Z" },
+ { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload-time = "2025-04-15T17:45:15.535Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload-time = "2025-04-15T17:45:20.166Z" },
+ { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload-time = "2025-04-15T17:45:24.794Z" },
+]
+
+[[package]]
+name = "contourpy"
+version = "1.3.3"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" },
+ { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" },
+ { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" },
+ { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" },
+ { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" },
+ { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" },
+ { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" },
+ { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" },
+ { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" },
+ { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" },
+ { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" },
+ { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" },
+ { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" },
+ { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" },
+ { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" },
+ { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" },
+ { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" },
+ { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" },
+ { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" },
+ { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" },
+ { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" },
+ { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" },
+ { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" },
+ { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" },
+ { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" },
+ { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" },
+ { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" },
+ { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" },
+ { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" },
+ { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" },
+ { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" },
+ { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" },
+ { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" },
+]
+
+[[package]]
+name = "coverage"
+version = "7.14.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/23/7f/d0720730a397a999ffc0fd3f5bebef347338e3a47b727da66fbb228e2ff2/coverage-7.14.0.tar.gz", hash = "sha256:057a6af2f160a85384cde4ab36f0d2777bae1057bae255f95413cdd382aa5c74", size = 919489, upload-time = "2026-05-10T18:02:31.397Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/59/9d/7c83ef51c3eb495f10010094e661833588b7709946da634c8b66520b97c7/coverage-7.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84c32d90bf4537f0e7b4dec9aaa9a938fb8205136b9d2ecf4d7629d5262dc075", size = 219668, upload-time = "2026-05-10T17:59:23.106Z" },
+ { url = "https://files.pythonhosted.org/packages/24/34/898546aefbd28f0af131201d0dc852c9e976f817bd7d5bfb8dc4e02863bb/coverage-7.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7c843572c605ab51cfdb5c6b5f2586e2a8467c0d28eca4bdef4ec70c5fecbd82", size = 220192, upload-time = "2026-05-10T17:59:26.095Z" },
+ { url = "https://files.pythonhosted.org/packages/df/4a/b457c88aca72b0df13a98167ebd5d947135ccd9881ea88ce6a570e13aa9b/coverage-7.14.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0c451757d3fa2603354fdc789b5e58a0e327a117c370a40e3476ba4eabab228c", size = 246932, upload-time = "2026-05-10T17:59:27.806Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/d9/92600e89486fd074c50f0117422b2c9592c3e144e2f25bd5ac0bc62bc7a0/coverage-7.14.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3fd43f0616e765ab78d069cf8358def7363957a45cee446d65c502dcfeea7893", size = 248762, upload-time = "2026-05-10T17:59:29.479Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/e1/9ea1eb9c311da7f15853559dc1d9d82bef88ecd3e59fbeb51f16bc2ffa91/coverage-7.14.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:731e535b1498b27d13594a0527a79b0510867b0ad891532be41cb883f2128e20", size = 250625, upload-time = "2026-05-10T17:59:31.33Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/03/57afca1b8106f8549a5329139315041fe166d6099bd9381346b9430dfbd1/coverage-7.14.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c7492f2d493b976941c7ca050f273cbda2f43c381124f7586a3e3c16d1804fec", size = 252539, upload-time = "2026-05-10T17:59:32.692Z" },
+ { url = "https://files.pythonhosted.org/packages/57/5e/2e9fc63c9928119c1dbae02222be51407d3e7ebac5811ebbda4af3557795/coverage-7.14.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc38367eaa2abb1b766ac333142bce7655335a73537f5c8b75aaa89c2b987757", size = 247636, upload-time = "2026-05-10T17:59:34.599Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/e2/0b7898cda21041cc67546e19b80ba66cbbb47cbece52a76a5904de6a3aaf/coverage-7.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0a951308cde22cf77f953955a754d04dccb57fe3bb8e345d685778ed9fc1632a", size = 248666, upload-time = "2026-05-10T17:59:36.232Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/e3/d33662a2fdaef23229c15921f39c84ec38441f3069ba26e134ed402c833b/coverage-7.14.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fab3877e4ebb06bd9d4d4d00ee53309ee5478e66873c66a382272e3ee33eb7ea", size = 246670, upload-time = "2026-05-10T17:59:38.029Z" },
+ { url = "https://files.pythonhosted.org/packages/99/b2/533942c3bfbf6770b5c32d7f2ff029fe013dba31f3fe8b45cabbb250365e/coverage-7.14.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b812eb847b19876ebf33fb6c4f11819af05ab6050b0bfa1bc53412ae81779adb", size = 250484, upload-time = "2026-05-10T17:59:39.974Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/00/15acbad83a96de13c73831486c7627bfed73dfaec53b04e4a6315edf3fd8/coverage-7.14.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d9c8ef6ed820c433de075657d72dda1f89a2984955e58b8a75feb3f184250218", size = 246942, upload-time = "2026-05-10T17:59:41.659Z" },
+ { url = "https://files.pythonhosted.org/packages/70/db/cef0228de493f2c740c760a9057a61d00c6849480073b70a75b87c7d4bab/coverage-7.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d128b1bba9361fbaaf6a19e179e6cfd6a9103ce0c0555876f72780acc93efd85", size = 247544, upload-time = "2026-05-10T17:59:43.471Z" },
+ { url = "https://files.pythonhosted.org/packages/77/a0/d9ef8e148f3025c2ae8401d77cda1502b6d2a4d8102603a8af31460aedb6/coverage-7.14.0-cp310-cp310-win32.whl", hash = "sha256:65f267ca1370726ec2c1aa38bbe4df9a71a740f22878d2d4bf59d71a4cd8d323", size = 222285, upload-time = "2026-05-10T17:59:44.908Z" },
+ { url = "https://files.pythonhosted.org/packages/85/c0/30c454c7d3cf47b2805d4e06f12443f5eece8a5d030d3b0350e7b74ecb49/coverage-7.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:b34ece8065914f938ed7f2c5872bb865336977a52919149846eac3744327267a", size = 223215, upload-time = "2026-05-10T17:59:46.779Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/e4/649c8d4f7f1709b6dbfc474358aa1bba02f67bcd52e2fec291a5014006cd/coverage-7.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a78e2a9d9c5e3b8d4ab9b9d28c985ea66fced0a7d7c2aec1f216e03a2011480", size = 219795, upload-time = "2026-05-10T17:59:48.198Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/8d/46692d24b3f395d4cbf17bfcc57136b4f2f9c0c0df864b0bddfc1d71a014/coverage-7.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1816c505187592dcd1c5a5f226601a549f70365fbd00930ac88b0c225b76bb4", size = 220299, upload-time = "2026-05-10T17:59:49.683Z" },
+ { url = "https://files.pythonhosted.org/packages/12/c2/a40f5cb295bbcbb697a76947a56081c494c61950366294ee426ffe261099/coverage-7.14.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d8e1762f0e9cbc26ec315471e7b47855218e833cd5a032d706fbf43845d878c7", size = 250721, upload-time = "2026-05-10T17:59:51.494Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/35/202235eb5c3c14c212462cd91d61b7386bf8fc44bc7a77f4742d2a69174b/coverage-7.14.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9336e23e8bb3a3925398261385e2a1533957d3e760e91070dcb0e98bfa514eed", size = 252633, upload-time = "2026-05-10T17:59:53.244Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/80/5f596e8995785124ee191c42535664c5e62c65995b66f4ca21e28ae04c81/coverage-7.14.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd1169b2230f9cbe9c638ba38022ed7a2b1e641cc07f7cea0365e4be2a74980", size = 254743, upload-time = "2026-05-10T17:59:55.021Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/6d/0d178825be2350f0adb27984d0aa7cf84bbdab201f6fb926b535d23a8f5f/coverage-7.14.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d1bb3543b58fea74d2cd1abc4054cc927e4724687cb4560cd2ed88d2c7d820c0", size = 256700, upload-time = "2026-05-10T17:59:56.511Z" },
+ { url = "https://files.pythonhosted.org/packages/19/5b/9e549c2f6e9dfea472adadba06c294e64735dabc2dd19015fac082095013/coverage-7.14.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a93bac2cb577ef60074999ed56d8a1535894398e2ed920d4185c3ec0c8864742", size = 250854, upload-time = "2026-05-10T17:59:57.94Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/1c/b94f9f5f36396021ee2f62c5834b12e6a3d31f0bed5d6fc6d1c3caec087c/coverage-7.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5904abf7e18cddc463219b17552229650c6b79e061d31a1059283051169cf7d5", size = 252433, upload-time = "2026-05-10T17:59:59.688Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/cb/d192cd8e1345eccabc32016f2d39072ecd10cb4f4b983ed8d0ebdeaf00dc/coverage-7.14.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:741f57cddc9004a8c81b084660215f33a6b597dbe62c31386b983ee26310e327", size = 250494, upload-time = "2026-05-10T18:00:01.953Z" },
+ { url = "https://files.pythonhosted.org/packages/53/c5/aac9f460a41d835dbddef1d377f105f6ac2311d0f3c1588e9f51046d8813/coverage-7.14.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:664123feb0929d7affc135717dbd70d61d98688a08ab1e5ba464739620c6252d", size = 254261, upload-time = "2026-05-10T18:00:03.779Z" },
+ { url = "https://files.pythonhosted.org/packages/23/aa/7af7c0081980a9cb3d289c5a435a4b7657dcecbd128e25c580e6a50389b5/coverage-7.14.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c83d2399a51bbec8429266905d33616f04bc5726b1138c35844d5fcd896b2e20", size = 250216, upload-time = "2026-05-10T18:00:05.262Z" },
+ { url = "https://files.pythonhosted.org/packages/35/60/a4257538ce2f6b978aeb51870d6c4208c510928a03db7e0339bb625dccb7/coverage-7.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb2e855b87321259a037429288ae85216d191c74de3e79bf57cd2bc0761992c", size = 251125, upload-time = "2026-05-10T18:00:06.858Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/ab/f91af47642ec1aa53490e835a95847168d9c77fc39aa58527604c051e145/coverage-7.14.0-cp311-cp311-win32.whl", hash = "sha256:731dc15b385ac52289743d476245b61e1a2927e803bef655b52bc3b2a75a21f3", size = 222300, upload-time = "2026-05-10T18:00:08.608Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/f0/a71ddbd874431e7a7cd96071f0c331cfbbad07704833c765d24ffbab8a67/coverage-7.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:bfb0ed8ec5d25e93face268115d7964db9df8b9aae8edcde9ec6b16c726a7cc1", size = 223241, upload-time = "2026-05-10T18:00:10.746Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/6e/d9d312a5151a96cd110efee32efc3fc97b01ebd86203fe618ccb29cf4c92/coverage-7.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:7ebb1c6df9f78046a1b1e0a89674cd4bf73b7c648914eebcf976a57fd99a5627", size = 221908, upload-time = "2026-05-10T18:00:12.242Z" },
+ { url = "https://files.pythonhosted.org/packages/09/1e/2f996b2c8415cbb6f54b0f5ec1ee850c96d7911961afb4fc05f4a89d8c58/coverage-7.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7ffd19fc8aed057fd686a17a4935eef5f9859d69208f96310e893e64b9b6ccf5", size = 219967, upload-time = "2026-05-10T18:00:13.756Z" },
+ { url = "https://files.pythonhosted.org/packages/34/23/35c7aea1274aef7525bdd2dc92f710bdde6d11652239d71d1ec450067939/coverage-7.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:829994cfe1aeb773ca27bf246d4badc1e764893e3bfb98fff820fcecd1ca4662", size = 220329, upload-time = "2026-05-10T18:00:15.264Z" },
+ { url = "https://files.pythonhosted.org/packages/75/cf/a8f4b43a16e194b0261257ad28ded5853ec052570afef4a84e1d81189f3b/coverage-7.14.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b4f07cf7edcb7ec39431a5074d7ea83b29a9f71fcfc494f0f40af4e65180420f", size = 251839, upload-time = "2026-05-10T18:00:17.16Z" },
+ { url = "https://files.pythonhosted.org/packages/69/ff/6699e7b71e60d3049eb2bdcbc95ee3f35707b2b0e48f32e9e63d3ce30c08/coverage-7.14.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ca3d9cf2c32b521bd9518385608787fa86f38daf993695307531822c3430ed67", size = 254576, upload-time = "2026-05-10T18:00:18.829Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ec/c936d495fcd67f48f03a9c4ad3297ff80d1f222a5df3980f15b34c186c21/coverage-7.14.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92af52828e7f29d827346b0294e5a0853fa206db77db0395b282918d41e28db9", size = 255690, upload-time = "2026-05-10T18:00:20.648Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/42/5af63f636cc62a4a2b1b3ba9146f6ee6f53a35a50d5cefc54d5670f60999/coverage-7.14.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7b2bb6c9d7e769360d0f20a0f219603fd64f0c8f97de17ab25853261602be0fb", size = 257949, upload-time = "2026-05-10T18:00:22.28Z" },
+ { url = "https://files.pythonhosted.org/packages/26/d3/a225317bd2012132a27e1176d51660b826f99bb975876463c44ea0d7ee5a/coverage-7.14.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1c9ed6ef99f88fb8c14aa8e2bf8eb0fe55fa2edfea68f8675d78741df1a5ac0e", size = 252242, upload-time = "2026-05-10T18:00:24.076Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/7f/9e65495298c3ea414742998539c37d048b5e81cc818fb1828cc6b51d10bf/coverage-7.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8231ade007f37959fbf58acc677f26b922c02eda6f0428ea307da0fd39681bf3", size = 253608, upload-time = "2026-05-10T18:00:25.588Z" },
+ { url = "https://files.pythonhosted.org/packages/94/46/1522b524a35bdad22b2b8c4f9d32d0a104b524726ec380b2db68db1746f5/coverage-7.14.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d8b013632cc1ce1d09dbe4f32667b4d320ec2f54fc326ebeffcd0b0bcc2bb6c4", size = 251753, upload-time = "2026-05-10T18:00:27.104Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/e9/cdf00d38817742c541ade405e115a3f7bf36e6f2a8b99d4f209861b85a2d/coverage-7.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1733198802d71ec4c524f322e2867ee05c62e9e75df86bdca545407a221827d1", size = 255823, upload-time = "2026-05-10T18:00:29.038Z" },
+ { url = "https://files.pythonhosted.org/packages/38/fc/5e7877cf5f902d08a17ff1c532511476d87e1bea355bd5028cb97f902e79/coverage-7.14.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:72a305291fa8ee01332f1aaf38b348ca34097f6aa0b0ef627eef2837e57bbba5", size = 251323, upload-time = "2026-05-10T18:00:30.647Z" },
+ { url = "https://files.pythonhosted.org/packages/18/9d/50f05a72dff8487464fdd4178dda5daed642a060e60afb644e3d45123559/coverage-7.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fcaba850dd317c65423a9d63d88f9573c53b00354d6dd95724576cc98a131595", size = 253197, upload-time = "2026-05-10T18:00:32.211Z" },
+ { url = "https://files.pythonhosted.org/packages/00/3f/6f61ffe6439df266c3cf60f5c99cfaa21103d0210d706a42fc6c30683ff8/coverage-7.14.0-cp312-cp312-win32.whl", hash = "sha256:5ac83957a80d0701310e96d8bec68cdcf4f90a7674b7d13f15a344315b41ab27", size = 222515, upload-time = "2026-05-10T18:00:33.717Z" },
+ { url = "https://files.pythonhosted.org/packages/85/19/93853133df2cb371083285ef6a93982a0173e7a233b0f61373ba9fd30eb2/coverage-7.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:70390b0da32cb90b501953716302906e8bcce087cb283e70d8c97729f22e92b2", size = 223324, upload-time = "2026-05-10T18:00:35.172Z" },
+ { url = "https://files.pythonhosted.org/packages/74/18/9f7fe62f659f24b7a82a0be56bf94c1bd0a89e0ae7ab4c668f6e82404294/coverage-7.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:91b993743d959b8be85b4abf9d5478216a69329c321efe5be0433c1a841d691d", size = 221944, upload-time = "2026-05-10T18:00:37.014Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/76/b7c66ee3c66e1b0f9d894c8125983aa0c03fb2336f2fd16559f9c966157f/coverage-7.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f2bbb8254370eb4c628ff3d6fa8a7f74ddc40565394d4f7ab791d1fe568e37ef", size = 219990, upload-time = "2026-05-10T18:00:38.887Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/af/e567cbad5ba69c013a50146dfa886dc7193361fda77521f51274ff620e1b/coverage-7.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:23b81107f46d3f21d0cbce30664fcec0f5d9f585638a67081750f99738f6bf66", size = 220365, upload-time = "2026-05-10T18:00:40.864Z" },
+ { url = "https://files.pythonhosted.org/packages/44/6f/9ad575d505b4d805b254febc8a5b338a2efe278f8786e56ff1cb8413f9c3/coverage-7.14.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:22a7e06a5f11a757cdfe79018e9095f9f69ae283c5cd8123774c788deec8717b", size = 251363, upload-time = "2026-05-10T18:00:42.489Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/5f/b5370068b2f57787454592ed7dcd1002f0f1703b7db1fa30f6a325a4ca6e/coverage-7.14.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9d1aa57a1dc8e05bdc42e81c5d671d849577aeedf279f4c449d6d286f9ed88ca", size = 253961, upload-time = "2026-05-10T18:00:44.079Z" },
+ { url = "https://files.pythonhosted.org/packages/29/1e/51adf17738976e8f2b85ddef7b7aa12a0838b056c92f175941d8862767c1/coverage-7.14.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90c1a51bcfddf645b3bb7ec333d9e94393a8e94f55642380fa8a9a5a9e636cb7", size = 255193, upload-time = "2026-05-10T18:00:45.623Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/7b/5bfd7ac1df3b881c2ac7a5cbc99c7609e6296c402f5ef587cd81c6f355b3/coverage-7.14.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a841fae2fadcae4f438d43b6ccc4aac2ad609f47cdb6cfdce60cbb3fe5ca7bc2", size = 257326, upload-time = "2026-05-10T18:00:47.173Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/38/1d37d316b174fad3843a1d76dbdfe4398771c9ecd0515935dd9ece9cd627/coverage-7.14.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c79d2319cabef1fe8e86df73371126931550804738f78ad7d31e3aad85a67367", size = 251582, upload-time = "2026-05-10T18:00:49.152Z" },
+ { url = "https://files.pythonhosted.org/packages/34/46/746704f95980ba220214e1a41e18cec5aea80a898eaa53c51bf2d645ff36/coverage-7.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b23b0c6f0b1db6ad769b7050c8b641c0bf215ded26c1816955b17b7f26edfa9", size = 253325, upload-time = "2026-05-10T18:00:51.252Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/b9/bbe87206d9687b192352f893797825b5f5b15ecd3aa9c68fbff0c074d77b/coverage-7.14.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:55d3089079ce181a4566b1065ab28d2575eb76d8ac8f81f4fcda2bf037fee087", size = 251291, upload-time = "2026-05-10T18:00:52.816Z" },
+ { url = "https://files.pythonhosted.org/packages/46/57/b8cdb12ac0d73ef0243218bd5e22c9df8f92edab8018213a86aec67c5324/coverage-7.14.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:49c005cba1e2f9677fb2845dcdf9a2e72a52a17d63e8231aaaae35d9f50215ef", size = 255448, upload-time = "2026-05-10T18:00:54.548Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/d4/5002019538b2036ce3c84340f54d2fd5100d55b0a6b0894eee56128d03c7/coverage-7.14.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:9117377b823daa28aa8635fbb08cda1cd6be3d7143257345459559aeef852d52", size = 251110, upload-time = "2026-05-10T18:00:56.122Z" },
+ { url = "https://files.pythonhosted.org/packages/37/53/20c5009477660f084e6ed60bc02a91894b8e234e617e86ecfd9aaf78e27b/coverage-7.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7b79d646cf46d5cf9a9f40281d4441df5849e445726e369006d2b117710b33fe", size = 252885, upload-time = "2026-05-10T18:00:57.967Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/ab/3cf6427ac9c1f1db747dbb1ce71dde47984876d4c2cfd018a3fef0a78d4d/coverage-7.14.0-cp313-cp313-win32.whl", hash = "sha256:fb609b3658479e33f9516d46f1a89dbb9b6c261366e3a11844a96ec487533dae", size = 222539, upload-time = "2026-05-10T18:00:59.581Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/b8/9228523e80321c2cb4880d1f589bc0171f2f71432c35118ad04dc01decce/coverage-7.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:0773d8329cf32b6fd222e4b52622c61fe8d503eb966cfc8d3c3c10c96266d50e", size = 223344, upload-time = "2026-05-10T18:01:01.531Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/99/118daa192f95e3a6cb2740100fbf8797cda1734b4134ef0b5d501a7fa8f3/coverage-7.14.0-cp313-cp313-win_arm64.whl", hash = "sha256:b4e26a0f1b696faf283bffe5b8569e44e336c582439df5d53281ab89ee0cba96", size = 221966, upload-time = "2026-05-10T18:01:03.16Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/f1/a46cc0c013be170216253184a32366d7cbdb9252feaec866b05c2d12a894/coverage-7.14.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:953f521ca9445300397e65fda3dca58b2dbd68fee983777420b57ac3c77e9f90", size = 220679, upload-time = "2026-05-10T18:01:05.058Z" },
+ { url = "https://files.pythonhosted.org/packages/64/8c/9c30a3d311a34177fa432995be7fbfc64477d8bac5630bd38055b1c9b424/coverage-7.14.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:98af83fd65ae24b1fdd03aaead967a9f523bcd2f1aab2d4f3ffda65bb568a6f1", size = 221033, upload-time = "2026-05-10T18:01:07.002Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/cd/3fb5e06c3badefd0c1b47e2044fdca67f8220a4ec2e7fcfb476aa0a67c6c/coverage-7.14.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:668b92e6958c4db7cf92e81caac328dfbbdbb215db2850ad28f0cbe1eea0bfbd", size = 262333, upload-time = "2026-05-10T18:01:08.903Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/e6/fbc322325c7294d3e22c1ad6b79e45d0806b25228c8e5842aed6d8169aa7/coverage-7.14.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9fbd898551762dea00d3fef2b1c4f99afd2c6a3ff952ea07d60a9bd5ed4f34bc", size = 264410, upload-time = "2026-05-10T18:01:10.531Z" },
+ { url = "https://files.pythonhosted.org/packages/08/92/c497b264bec1673c47cc77e26f760fcda4654cabf1f39546d1a23a3b8c35/coverage-7.14.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68af363c07ecd8d4b7d4043d85cb376d7d227eceb54e5323ee45da73dbd3e426", size = 266836, upload-time = "2026-05-10T18:01:12.19Z" },
+ { url = "https://files.pythonhosted.org/packages/78/fc/045da320987f401af5d2815d351e8aa799aec859f60e29f445e3089eeedb/coverage-7.14.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6e57054a583da8ac55edf24117ea4c9133032cfc4cf72aa2d48c1e5d4b52f899", size = 267974, upload-time = "2026-05-10T18:01:13.926Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/ae/227b1e379497fb7a4fc3286e620f80c8a1e7cec66d45695a01639eb1af65/coverage-7.14.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cc3499459bbcdd51a65b64c35ab7ed2764eaf3cba826e0df3f1d7fe2e102b70b", size = 261578, upload-time = "2026-05-10T18:01:15.564Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/f5/3570342900f2acea31d33ff1590c5d8bac1a8e1a2e1c6d34a5d5e61de681/coverage-7.14.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:45899ec2138a4346ed34d601dedf5076fb74edf2d1dd9dc76a78e82397edee90", size = 264394, upload-time = "2026-05-10T18:01:17.607Z" },
+ { url = "https://files.pythonhosted.org/packages/16/29/de1bbc01c935b28f89b1dc3db85b011c055e843a8e5e3b83141c3f80af7f/coverage-7.14.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8767486808c436f05b23ab98eb963fb29185e32a9357a166971685cb3459900f", size = 262022, upload-time = "2026-05-10T18:01:19.304Z" },
+ { url = "https://files.pythonhosted.org/packages/35/95/f53890b0bf2fc10ab168e05d38869215e73ca24c4cb521c3bb0eb62fe16b/coverage-7.14.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a3b5ddfd6aa7ddad53ee3edb231e88a2151507a43229b7d71b953916deca127d", size = 265732, upload-time = "2026-05-10T18:01:21.494Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/ea/c919e259081dd2bdf0e43b87209709ba7ec2e4117c2a7f5185379c43463c/coverage-7.14.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:63df0fe568e698e1045792399f8ab6da3a6c2dce3182813fb92afa2641087b47", size = 260921, upload-time = "2026-05-10T18:01:23.533Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/2c/c2831889705a81dc5d1c6ca12e4d8e9b95dfc146d153488a6c0ea685d28e/coverage-7.14.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:827d6397dbd95144939b18f89edf31f63e1f99633e8d5f32f22ba8bdda567477", size = 263109, upload-time = "2026-05-10T18:01:25.165Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/a9/2fcae5003cac3d63fe344d2166243c2756935f48420863c5272b240d550b/coverage-7.14.0-cp313-cp313t-win32.whl", hash = "sha256:7bf43e000d24012599b879791cff41589af90674722421ef11b11a5431920bab", size = 223212, upload-time = "2026-05-10T18:01:27.157Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/bb/18e94d7b14b9b398164197114a587a04ab7c9fdbe1d237eef57311c5e883/coverage-7.14.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3f5549365af25d770e06b1f8f5682d9a5637d06eb494db91c6fa75d3950cc917", size = 224272, upload-time = "2026-05-10T18:01:29.107Z" },
+ { url = "https://files.pythonhosted.org/packages/db/56/4f14fad782b035c81c4ffd09159e7103d42bb1d93ac8496d04b90a11b7da/coverage-7.14.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6d160217ec6fe890f16ad3a9531761589443749e448f91986c972714fad361c8", size = 222530, upload-time = "2026-05-10T18:01:31.151Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/18/b9a6586d73992807c26f9a5f274131be3d76b56b18a82b9392e2a25d2e45/coverage-7.14.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9aed9fa983514ca032790f3fe0d1c0e42ca7e16b42432af1706b50a9a46bef5d", size = 220036, upload-time = "2026-05-10T18:01:33.057Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/9b/4165a1d56ddc302a0e2d518fd9d412a4fd0b57562618c78c5f21c57194f5/coverage-7.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ba3b8390db29296dbbf49e91b6fe08f990743a90c8f447ba4c2ffc29670dfa63", size = 220368, upload-time = "2026-05-10T18:01:34.705Z" },
+ { url = "https://files.pythonhosted.org/packages/69/aa/c12e52a5ba148d9995229d557e3be6e554fe469addc0e9241b2f0956d8ea/coverage-7.14.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3a5d8e876dfa2f102e970b183863d6dedd023d3c0eeca1fe7a9787bc5f28b212", size = 251417, upload-time = "2026-05-10T18:01:36.949Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/51/ec641c26e6dca1b25a7d2035ba6ecb7c884ef1a100a9e42fbe4ce4405139/coverage-7.14.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5ebb8f4614a3787d567e610bbfdf96a4798dd69a1afb1bd8ad228d4111fe6ff3", size = 253924, upload-time = "2026-05-10T18:01:38.985Z" },
+ { url = "https://files.pythonhosted.org/packages/33/c4/59c3de0bd1b538824173fd518fed51c1ce740ca5ed68e74545983f4053a9/coverage-7.14.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b9bf47223dd8db3d4c4b2e443b02bace480d428f0822c3f991600448a176c97", size = 255269, upload-time = "2026-05-10T18:01:40.957Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/a9/36dfa153a62040296f6e7febfdb20a5720622f6ef5a81a41e8237b9a5344/coverage-7.14.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3485a836550b303d006d57cc06e3d5afaabc642c77050b7c985a97b13e3776b8", size = 257583, upload-time = "2026-05-10T18:01:42.607Z" },
+ { url = "https://files.pythonhosted.org/packages/26/7b/cc2c048d4114d9ab1c2409e9ee365e5ae10736df6dffcfc9444effa6c708/coverage-7.14.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3e7e88110bae996d199d1693ca8ec3fd52441d426401ae963437598667b4c5eb", size = 251434, upload-time = "2026-05-10T18:01:44.537Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/df/6770eaa576e604575e9a78055313250faef5faa84bd6f71a39fece519c43/coverage-7.14.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15228a6800ce7bdf1b74800595e56db7138cecb338fdbf044806e10dcf182dfe", size = 253280, upload-time = "2026-05-10T18:01:46.175Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/9e/1c0264514a3f98259a6d64765a397b2c8373e3ba59ee722a4802d3ec0c61/coverage-7.14.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9d26ac7f5398bafc5b57421ad994e8a4749e8a7a0e62d05ec7d53014d5963bfa", size = 251241, upload-time = "2026-05-10T18:01:48.732Z" },
+ { url = "https://files.pythonhosted.org/packages/64/16/4efdf3e3c4079cdbf0ece56a2fea872df9e8a3e15a13a0af4400e1075944/coverage-7.14.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2fb73254ff43c911c967a899e1359bc5049b4b115d6e8fbdde4937d0a2246cd5", size = 255516, upload-time = "2026-05-10T18:01:50.819Z" },
+ { url = "https://files.pythonhosted.org/packages/93/69/b1de96346603881b3d1bc8d6447c83200e1c9700ffbaff926ba01ff5724c/coverage-7.14.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:454a380af72c6adada298ed270d38c7a391288198dbfb8467f786f588751a90c", size = 251059, upload-time = "2026-05-10T18:01:52.773Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/66/2881853e0363a5e0a724d1103e53650795367471b6afb234f8b49e713bc6/coverage-7.14.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:65c86fb646d2bd2972e96bd1a8b45817ed907cee68655d6295fe7ec031d04cca", size = 252716, upload-time = "2026-05-10T18:01:54.506Z" },
+ { url = "https://files.pythonhosted.org/packages/55/5c/0d3305d002c41dcde873dbe456491e663dc55152ca526b630b5c47efd62f/coverage-7.14.0-cp314-cp314-win32.whl", hash = "sha256:6a6516b02a6101398e19a3f44820f69bab2590697f7def4331f668b14adaf828", size = 222788, upload-time = "2026-05-10T18:01:56.487Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/58/6e1b8f52fdc3184b47dc5037f5070d83a3d11042db1594b02d2a44d786c8/coverage-7.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:45e0f79d8351fa76e256716df91eab12890d32678b9590df7ae1042e4bd4cf5d", size = 223600, upload-time = "2026-05-10T18:01:58.497Z" },
+ { url = "https://files.pythonhosted.org/packages/00/70/a18c408e674bc26281cadaedc7351f929bd2094e191e4b15271c30b084cc/coverage-7.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:4b899594a8b2d81e5cc064a0d7f9cac2081fed91049456cae7676787e41549c9", size = 222168, upload-time = "2026-05-10T18:02:00.411Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/89/2681f071d238b62aff8dfc2ab44fc24cfdb38d1c01f391a80522ff5d3a16/coverage-7.14.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f580f8c80acd94ac72e863efe2cab791d8c38d153e0b463b92dfa000d5c84cd1", size = 220766, upload-time = "2026-05-10T18:02:02.313Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/c7/c987babafd9207ffa1995e1ef1f9b26762cf4963aa768a66b6f0501e4616/coverage-7.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a2bd259c442cd43c49b30fbafc51776eb19ea396faf159d26a83e6a0a5f13b0c", size = 221035, upload-time = "2026-05-10T18:02:04.017Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/e9/d6a5ac3b333088143d6fc877d398a9a674dc03124a2f776e131f03864823/coverage-7.14.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a706b908dfa85538863504c624b237a3cc34232bf403c057414ebfdb3b4d9f84", size = 262405, upload-time = "2026-05-10T18:02:05.915Z" },
+ { url = "https://files.pythonhosted.org/packages/38/b1/e70838d29a7c08e22d44398a46db90815bbcbf28de06992bd9210d1a8d8e/coverage-7.14.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7333cd944ee4393b9b3d3c1b598c936d4fc8d70573a4c7dacfec5590dd50e436", size = 264530, upload-time = "2026-05-10T18:02:07.582Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/73/5c31ef97763288d03d9995152b96d5475b527c63d91c84b01caea894b83a/coverage-7.14.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f162bc9a15b82d947b02651b0c7e1609d6f7a8735ca330cfadec8481dd97d5a", size = 266932, upload-time = "2026-05-10T18:02:09.401Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/76/dd56d80f29c5f05b4d76f7e7c6d47cafacae017189c75c5759d24f9ff0cc/coverage-7.14.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:362cb78e01a5dc82009d88004cf60f2e6b6d6fcbfdec05b05af73b0abf40118f", size = 268062, upload-time = "2026-05-10T18:02:11.399Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/c7/27ba85cd5b95614f159ff93ebff1901584a8d192e2e5e24c4943a7453f59/coverage-7.14.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:acebd068fca5512c3a6fde9c045f901613478781a73f0e82b307b214daef23fb", size = 261504, upload-time = "2026-05-10T18:02:13.257Z" },
+ { url = "https://files.pythonhosted.org/packages/13/2e/e8149f60ab5d5684c6eee881bdf34b127115cddbb958b196768dd9d63473/coverage-7.14.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:29fe3da551dface75deb2ccbf87b6b66e2e7ef38f6d89050b428be94afff3490", size = 264398, upload-time = "2026-05-10T18:02:15.063Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/7f/1261b025285323225f4b4abffa5a643649dfd67e25ddca7ebcbdea3b7cb3/coverage-7.14.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b4cc4fce8672fffcb09b0eafc167b396b3ba53c4a7230f54b7aaffbf6c835fa9", size = 262000, upload-time = "2026-05-10T18:02:16.756Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/dc/829c54f60b9d08389439c00f813c752781c496fc5788c78d8006db4b4f2b/coverage-7.14.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5d4a51aad8ba8bdcd2b8bd8f03d4aca19693fa2327a3470e4718a25b03481020", size = 265732, upload-time = "2026-05-10T18:02:18.817Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/b0/70bd1419941652fa062689cba9c3eeafb8f5e6fbb890bce41c3bdda5dbd6/coverage-7.14.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9f323af3e1e4f68b60b7b247e37b8515563a61375518fa59de1af48ba28a3db6", size = 260847, upload-time = "2026-05-10T18:02:20.528Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/73/be40b2390656c654d35ea0015ea7ba3d945769cf80790ad5e0bb2d56d2ba/coverage-7.14.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1a0abc7342ea9711c469dd8b821c6c311e6bc6aac1442e5fbd6b27fae0a8f3db", size = 263166, upload-time = "2026-05-10T18:02:22.337Z" },
+ { url = "https://files.pythonhosted.org/packages/29/55/4a643f712fcf7cf2881f8ec1e0ccb7b164aff3108f69b51801246c8799f2/coverage-7.14.0-cp314-cp314t-win32.whl", hash = "sha256:a9f864ef57b7172e2db87a096642dd51e179e085ab6b2c371c29e885f65c8fb2", size = 223573, upload-time = "2026-05-10T18:02:24.11Z" },
+ { url = "https://files.pythonhosted.org/packages/27/96/3acae5da0953be042c0b4dea6d6789d2f080701c77b88e44d5bd41b9219b/coverage-7.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:29943e552fdc08e082eb51400fb2f58e118a83b5542bd06531214e084399b644", size = 224680, upload-time = "2026-05-10T18:02:25.896Z" },
+ { url = "https://files.pythonhosted.org/packages/93/3d/6ab5d2dd8325d838737c6f8d83d62eb6230e0d70b87b51b57bbfd08fa767/coverage-7.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:742a73ea621953b012f2c4c2219b512180dd84489acf5b1596b0aafc55b9100b", size = 222703, upload-time = "2026-05-10T18:02:27.822Z" },
+ { url = "https://files.pythonhosted.org/packages/61/e8/cb8e80d6f9f55b99588625062822bf946cf03ed06315df4bd8397f5632a1/coverage-7.14.0-py3-none-any.whl", hash = "sha256:8de5b61163aee3d05c8a2beab6f47913df7981dad1baf82c414d99158c286ab1", size = 211764, upload-time = "2026-05-10T18:02:29.538Z" },
+]
+
+[package.optional-dependencies]
+toml = [
+ { name = "tomli", marker = "python_full_version <= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+
+[[package]]
+name = "cryptography"
+version = "48.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi", marker = "(python_full_version < '3.12' and platform_python_implementation != 'PyPy' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and platform_python_implementation != 'PyPy' and extra == 'extra-10-deeplabcut-tf') or (platform_python_implementation != 'PyPy' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" },
+ { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" },
+ { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" },
+ { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" },
+ { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" },
+ { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" },
+ { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" },
+ { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" },
+ { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" },
+ { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" },
+ { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" },
+ { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" },
+ { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" },
+ { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" },
+ { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" },
+ { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" },
+ { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" },
+ { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" },
+ { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" },
+ { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" },
+ { url = "https://files.pythonhosted.org/packages/be/d2/024b5e06be9d44cb021fb0e1a03d34d63989cf56a0fe62f3dfbab695b9b4/cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855", size = 3950391, upload-time = "2026-05-04T22:59:17.415Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126, upload-time = "2026-05-04T22:59:20.197Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270, upload-time = "2026-05-04T22:59:22.647Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797, upload-time = "2026-05-04T22:59:24.912Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800, upload-time = "2026-05-04T22:59:27.474Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/ca/7e8365deec19afb2b2c7be7c1c0aa8f99633b54e90c570999acda93260fc/cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a", size = 3739536, upload-time = "2026-05-04T22:59:29.61Z" },
+]
+
+[[package]]
+name = "cuda-bindings"
+version = "12.9.4"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+]
+dependencies = [
+ { name = "cuda-pathfinder", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/37/31/bfcc870f69c6a017c4ad5c42316207fc7551940db6f3639aa4466ec5faf3/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a022c96b8bd847e8dc0675523431149a4c3e872f440e3002213dbb9e08f0331a", size = 11800959, upload-time = "2025-10-21T14:51:26.458Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/d8/b546104b8da3f562c1ff8ab36d130c8fe1dd6a045ced80b4f6ad74f7d4e1/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5", size = 12148218, upload-time = "2025-10-21T14:51:28.855Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/1e/9c8ed3f3dbed7b7d038805fdc65cbc65fda9983e84437778a9571e7092bc/cuda_bindings-12.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:f69107389e6b9948969bfd0a20c4f571fd1aefcfb1d2e1b72cc8ba5ecb7918ab", size = 11464568, upload-time = "2025-10-21T14:51:31.454Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/2b/ebcbb60aa6dba830474cd360c42e10282f7a343c0a1f58d24fbd3b7c2d77/cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6a429dc6c13148ff1e27c44f40a3dd23203823e637b87fd0854205195988306", size = 11840604, upload-time = "2025-10-21T14:51:34.565Z" },
+ { url = "https://files.pythonhosted.org/packages/45/e7/b47792cc2d01c7e1d37c32402182524774dadd2d26339bd224e0e913832e/cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c912a3d9e6b6651853eed8eed96d6800d69c08e94052c292fec3f282c5a817c9", size = 12210593, upload-time = "2025-10-21T14:51:36.574Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/be/90d32049e06abcfba4b2e7df1dbcb5e16215c8852eef0cd8b25f38a66bd4/cuda_bindings-12.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:443b0875916879c2e4c3722941e25e42d5ab9bcbf34c9e83404fb100fa1f6913", size = 11490933, upload-time = "2025-10-21T14:51:38.792Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/c2/65bfd79292b8ff18be4dd7f7442cea37bcbc1a228c1886f1dea515c45b67/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:694ba35023846625ef471257e6b5a4bc8af690f961d197d77d34b1d1db393f56", size = 11760260, upload-time = "2025-10-21T14:51:40.79Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" },
+ { url = "https://files.pythonhosted.org/packages/df/6b/9c1b1a6c01392bfdd758e9486f52a1a72bc8f49e98f9355774ef98b5fb4e/cuda_bindings-12.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:696ca75d249ddf287d01b9a698b8e2d8a05046495a9c051ca15659dc52d17615", size = 11586961, upload-time = "2025-10-21T14:51:45.394Z" },
+ { url = "https://files.pythonhosted.org/packages/05/8b/b4b2d1c7775fa403b64333e720cfcfccef8dcb9cdeb99947061ca5a77628/cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf8bfaedc238f3b115d957d1fd6562b7e8435ba57f6d0e2f87d0e7149ccb2da5", size = 11570071, upload-time = "2025-10-21T14:51:47.472Z" },
+ { url = "https://files.pythonhosted.org/packages/63/56/e465c31dc9111be3441a9ba7df1941fe98f4aa6e71e8788a3fb4534ce24d/cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f", size = 11906628, upload-time = "2025-10-21T14:51:49.905Z" },
+ { url = "https://files.pythonhosted.org/packages/05/d0/d0e4e2e047d8e899f023fa15ad5e9894ce951253f4c894f1cd68490fdb14/cuda_bindings-12.9.4-cp313-cp313-win_amd64.whl", hash = "sha256:a2e82c8985948f953c2be51df45c3fe11c812a928fca525154fb9503190b3e64", size = 11556719, upload-time = "2025-10-21T14:51:52.248Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/07/6aff13bc1e977e35aaa6b22f52b172e2890c608c6db22438cf7ed2bf43a6/cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3adf4958dcf68ae7801a59b73fb00a8b37f8d0595060d66ceae111b1002de38d", size = 11566797, upload-time = "2025-10-21T14:51:54.581Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/84/1e6be415e37478070aeeee5884c2022713c1ecc735e6d82d744de0252eee/cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb", size = 11925991, upload-time = "2025-10-21T14:51:56.535Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/3c/972edfddb4ae8a9fccd3c3766ed47453b6f805b6026b32f10209dd4b8ad4/cuda_bindings-12.9.4-cp313-cp313t-win_amd64.whl", hash = "sha256:b32d8b685f0e66f5658bcf4601ef034e89fc2843582886f0a58784a4302da06c", size = 11894363, upload-time = "2025-10-21T14:51:58.633Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/b5/96a6696e20c4ffd2b327f54c7d0fde2259bdb998d045c25d5dedbbe30290/cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f53a7f453d4b2643d8663d036bafe29b5ba89eb904c133180f295df6dc151e5", size = 11624530, upload-time = "2025-10-21T14:52:01.539Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/af/6dfd8f2ed90b1d4719bc053ff8940e494640fe4212dc3dd72f383e4992da/cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686", size = 11922703, upload-time = "2025-10-21T14:52:03.585Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/87/652796522cc1a7af559460e1ce59b642e05c1468b9c08522a9a096b4cf04/cuda_bindings-12.9.4-cp314-cp314-win_amd64.whl", hash = "sha256:53a10c71fdbdb743e0268d07964e5a996dd00b4e43831cbfce9804515d97d575", size = 11517716, upload-time = "2025-10-21T14:52:06.013Z" },
+ { url = "https://files.pythonhosted.org/packages/39/73/d2fc40c043bac699c3880bf88d3cebe9d88410cd043795382826c93a89f0/cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20f2699d61d724de3eb3f3369d57e2b245f93085cab44fd37c3bea036cea1a6f", size = 11565056, upload-time = "2025-10-21T14:52:08.338Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/19/90ac264acc00f6df8a49378eedec9fd2db3061bf9263bf9f39fd3d8377c3/cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee", size = 11924658, upload-time = "2025-10-21T14:52:10.411Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/52/a30f46e822bfa6b4a659d1e8de8c4a4adf908ea075dac568b55362541bd8/cuda_bindings-12.9.4-cp314-cp314t-win_amd64.whl", hash = "sha256:53e11991a92ff6f26a0c8a98554cd5d6721c308a6b7bfb08bebac9201e039e43", size = 12055608, upload-time = "2025-10-21T14:52:12.335Z" },
+]
+
+[[package]]
+name = "cuda-bindings"
+version = "13.2.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "cuda-pathfinder", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/ef/184aa775e970fc089942cd9ec6302e6e44679d4c14549c6a7ea45bf7f798/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6f3682ec3c4769326aafc67c2ba669d97d688d0b7e63e659d36d2f8b72f32d6", size = 6329075, upload-time = "2026-03-11T00:12:32.319Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/ea/81999d01375645f34596c76eb046b4b36d58cc6fe2bddb2410f8a7b7a827/cuda_bindings-13.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:845025438a1b9e20718b9fb42add3e0eb72e85458bcab3eeb80bfd8f0a9dab33", size = 5600047, upload-time = "2026-03-11T00:12:34.848Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d", size = 5730273, upload-time = "2026-03-11T00:12:37.18Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1", size = 6313924, upload-time = "2026-03-11T00:12:39.462Z" },
+ { url = "https://files.pythonhosted.org/packages/29/5a/0ce1731c48bcd9f40996a4ef1abbf634f1a7fe4a15c5050b1e75ce3a7acf/cuda_bindings-13.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:debb51b211d246f8326f6b6e982506a5d0d9906672c91bc478b66addc7ecc60a", size = 5631363, upload-time = "2026-03-11T00:12:41.58Z" },
+ { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619, upload-time = "2026-03-11T00:12:45.939Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/a5/d7f01a415e134546248cef612adad8153c9f1eb10ec79505a7cd8294370b/cuda_bindings-13.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:45815daeb595bf3b405c52671a2542b1f8e9329f3b029494acbfcc74aeaa1f2d", size = 5840830, upload-time = "2026-03-11T00:12:48.43Z" },
+ { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610, upload-time = "2026-03-11T00:12:50.337Z" },
+ { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914, upload-time = "2026-03-11T00:12:52.374Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/84/d3b6220b51cbc02ca14db7387e97445126b4ff5125aaa6c5dd7dcb75e679/cuda_bindings-13.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8cebe3ce4aeeca5af9c490e175f76c4b569bbf4a35a62294b777bc77bf7ac4d8", size = 5796512, upload-time = "2026-03-11T00:12:54.483Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673, upload-time = "2026-03-11T00:12:56.371Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386, upload-time = "2026-03-11T00:12:58.965Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/73/98bcb069778fe420226db75aff54b5dd6c3ecfd0912edabab723326e80b7/cuda_bindings-13.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:bd658bb5c0e55b7b3e5dd0ed509c6addb298c665db26a9bfba35e1e626000ba2", size = 5938605, upload-time = "2026-03-11T00:13:01.639Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469, upload-time = "2026-03-11T00:13:04.063Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693, upload-time = "2026-03-11T00:13:06.003Z" },
+ { url = "https://files.pythonhosted.org/packages/52/49/4e01cc06447d39476e138d1b1adec8d35c0d04eccd2c8d69befc08cd66e8/cuda_bindings-13.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6ccf14e0c1def3b7200100aafff3a9f7e210ecb6e409329e92dcf6cd2c00d5c7", size = 6662637, upload-time = "2026-03-11T00:13:07.881Z" },
+]
+
+[[package]]
+name = "cuda-pathfinder"
+version = "1.5.4"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/11/d0/c177e29701cf1d3008d7d2b16b5fc626592ce13bd535f8795c5f57187e0e/cuda_pathfinder-1.5.4-py3-none-any.whl", hash = "sha256:9563d3175ce1828531acf4b94e1c1c7d67208c347ca002493e2654878b26f4b7", size = 51657, upload-time = "2026-04-27T22:42:07.712Z" },
+]
+
+[[package]]
+name = "cuda-toolkit"
+version = "13.0.2"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" },
+]
+
+[package.optional-dependencies]
+cudart = [
+ { name = "nvidia-cuda-runtime", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+cufft = [
+ { name = "nvidia-cufft", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+cufile = [
+ { name = "nvidia-cufile", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+cupti = [
+ { name = "nvidia-cuda-cupti", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+curand = [
+ { name = "nvidia-curand", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+cusolver = [
+ { name = "nvidia-cusolver", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+cusparse = [
+ { name = "nvidia-cusparse", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+nvjitlink = [
+ { name = "nvidia-nvjitlink", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+nvrtc = [
+ { name = "nvidia-cuda-nvrtc", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+nvtx = [
+ { name = "nvidia-nvtx", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+
+[[package]]
+name = "cycler"
+version = "0.12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" },
+]
+
+[[package]]
+name = "dask"
+version = "2026.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "cloudpickle" },
+ { name = "fsspec" },
+ { name = "importlib-metadata", marker = "python_full_version < '3.12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging" },
+ { name = "partd" },
+ { name = "pyyaml" },
+ { name = "toolz" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d7/2a/5d8cc1579590af86576dde890254440e478c7174b93a02095ecfc2e6ba38/dask-2026.3.0.tar.gz", hash = "sha256:f7d96c8274e8a900d217c1ff6ea8d1bbf0b4c2c21e74a409644498d925eb8f85", size = 11000710, upload-time = "2026-03-18T07:10:14.945Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl", hash = "sha256:be614b9242b0b38288060fb2d7696125946469c98a1c30e174883fd199e0428d", size = 1485630, upload-time = "2026-03-18T07:10:12.832Z" },
+]
+
+[package.optional-dependencies]
+array = [
+ { name = "numpy" },
+]
+dataframe = [
+ { name = "numpy" },
+ { name = "pandas" },
+ { name = "pyarrow" },
+]
+
+[[package]]
+name = "dask-image"
+version = "2025.11.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "dask", extra = ["array", "dataframe"] },
+ { name = "numpy" },
+ { name = "pandas" },
+ { name = "pims" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tifffile", version = "2025.5.10", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5c/c4/7b83217443201469384a415687a8b89da8e55fc7a182e9507a69851a78b9/dask_image-2025.11.0.tar.gz", hash = "sha256:45cf1a9c3a8a1c143c75d43f1494e4fe0827564d3ec6efb93618fb04603ba0b3", size = 79561, upload-time = "2025-11-13T01:57:28.093Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6f/4b/817da308fa1170da07ef01259585887a3bbb6ab80700b3e61ce4967301ec/dask_image-2025.11.0-py3-none-any.whl", hash = "sha256:4834ece8d7133f8cd7d4e672f7f5a598c9057e687b20f14f3121360e3e1690b4", size = 61936, upload-time = "2025-11-13T01:57:27.133Z" },
+]
+
+[[package]]
+name = "debugpy"
+version = "1.8.20"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207, upload-time = "2026-01-29T23:03:28.199Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/71/be/8bd693a0b9d53d48c8978fa5d889e06f3b5b03e45fd1ea1e78267b4887cb/debugpy-1.8.20-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:157e96ffb7f80b3ad36d808646198c90acb46fdcfd8bb1999838f0b6f2b59c64", size = 2099192, upload-time = "2026-01-29T23:03:29.707Z" },
+ { url = "https://files.pythonhosted.org/packages/77/1b/85326d07432086a06361d493d2743edd0c4fc2ef62162be7f8618441ac37/debugpy-1.8.20-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:c1178ae571aff42e61801a38b007af504ec8e05fde1c5c12e5a7efef21009642", size = 3088568, upload-time = "2026-01-29T23:03:31.467Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/60/3e08462ee3eccd10998853eb35947c416e446bfe2bc37dbb886b9044586c/debugpy-1.8.20-cp310-cp310-win32.whl", hash = "sha256:c29dd9d656c0fbd77906a6e6a82ae4881514aa3294b94c903ff99303e789b4a2", size = 5284399, upload-time = "2026-01-29T23:03:33.678Z" },
+ { url = "https://files.pythonhosted.org/packages/72/43/09d49106e770fe558ced5e80df2e3c2ebee10e576eda155dcc5670473663/debugpy-1.8.20-cp310-cp310-win_amd64.whl", hash = "sha256:3ca85463f63b5dd0aa7aaa933d97cbc47c174896dcae8431695872969f981893", size = 5316388, upload-time = "2026-01-29T23:03:35.095Z" },
+ { url = "https://files.pythonhosted.org/packages/51/56/c3baf5cbe4dd77427fd9aef99fcdade259ad128feeb8a786c246adb838e5/debugpy-1.8.20-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:eada6042ad88fa1571b74bd5402ee8b86eded7a8f7b827849761700aff171f1b", size = 2208318, upload-time = "2026-01-29T23:03:36.481Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/7d/4fa79a57a8e69fe0d9763e98d1110320f9ecd7f1f362572e3aafd7417c9d/debugpy-1.8.20-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:7de0b7dfeedc504421032afba845ae2a7bcc32ddfb07dae2c3ca5442f821c344", size = 3171493, upload-time = "2026-01-29T23:03:37.775Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/f2/1e8f8affe51e12a26f3a8a8a4277d6e60aa89d0a66512f63b1e799d424a4/debugpy-1.8.20-cp311-cp311-win32.whl", hash = "sha256:773e839380cf459caf73cc533ea45ec2737a5cc184cf1b3b796cd4fd98504fec", size = 5209240, upload-time = "2026-01-29T23:03:39.109Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/92/1cb532e88560cbee973396254b21bece8c5d7c2ece958a67afa08c9f10dc/debugpy-1.8.20-cp311-cp311-win_amd64.whl", hash = "sha256:1f7650546e0eded1902d0f6af28f787fa1f1dbdbc97ddabaf1cd963a405930cb", size = 5233481, upload-time = "2026-01-29T23:03:40.659Z" },
+ { url = "https://files.pythonhosted.org/packages/14/57/7f34f4736bfb6e00f2e4c96351b07805d83c9a7b33d28580ae01374430f7/debugpy-1.8.20-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:4ae3135e2089905a916909ef31922b2d733d756f66d87345b3e5e52b7a55f13d", size = 2550686, upload-time = "2026-01-29T23:03:42.023Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/78/b193a3975ca34458f6f0e24aaf5c3e3da72f5401f6054c0dfd004b41726f/debugpy-1.8.20-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:88f47850a4284b88bd2bfee1f26132147d5d504e4e86c22485dfa44b97e19b4b", size = 4310588, upload-time = "2026-01-29T23:03:43.314Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/55/f14deb95eaf4f30f07ef4b90a8590fc05d9e04df85ee379712f6fb6736d7/debugpy-1.8.20-cp312-cp312-win32.whl", hash = "sha256:4057ac68f892064e5f98209ab582abfee3b543fb55d2e87610ddc133a954d390", size = 5331372, upload-time = "2026-01-29T23:03:45.526Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/39/2bef246368bd42f9bd7cba99844542b74b84dacbdbea0833e610f384fee8/debugpy-1.8.20-cp312-cp312-win_amd64.whl", hash = "sha256:a1a8f851e7cf171330679ef6997e9c579ef6dd33c9098458bd9986a0f4ca52e3", size = 5372835, upload-time = "2026-01-29T23:03:47.245Z" },
+ { url = "https://files.pythonhosted.org/packages/15/e2/fc500524cc6f104a9d049abc85a0a8b3f0d14c0a39b9c140511c61e5b40b/debugpy-1.8.20-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:5dff4bb27027821fdfcc9e8f87309a28988231165147c31730128b1c983e282a", size = 2539560, upload-time = "2026-01-29T23:03:48.738Z" },
+ { url = "https://files.pythonhosted.org/packages/90/83/fb33dcea789ed6018f8da20c5a9bc9d82adc65c0c990faed43f7c955da46/debugpy-1.8.20-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:84562982dd7cf5ebebfdea667ca20a064e096099997b175fe204e86817f64eaf", size = 4293272, upload-time = "2026-01-29T23:03:50.169Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/25/b1e4a01bfb824d79a6af24b99ef291e24189080c93576dfd9b1a2815cd0f/debugpy-1.8.20-cp313-cp313-win32.whl", hash = "sha256:da11dea6447b2cadbf8ce2bec59ecea87cc18d2c574980f643f2d2dfe4862393", size = 5331208, upload-time = "2026-01-29T23:03:51.547Z" },
+ { url = "https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl", hash = "sha256:eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7", size = 5372930, upload-time = "2026-01-29T23:03:53.585Z" },
+ { url = "https://files.pythonhosted.org/packages/33/2e/f6cb9a8a13f5058f0a20fe09711a7b726232cd5a78c6a7c05b2ec726cff9/debugpy-1.8.20-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9c74df62fc064cd5e5eaca1353a3ef5a5d50da5eb8058fcef63106f7bebe6173", size = 2538066, upload-time = "2026-01-29T23:03:54.999Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/56/6ddca50b53624e1ca3ce1d1e49ff22db46c47ea5fb4c0cc5c9b90a616364/debugpy-1.8.20-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad", size = 4269425, upload-time = "2026-01-29T23:03:56.518Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/d9/d64199c14a0d4c476df46c82470a3ce45c8d183a6796cfb5e66533b3663c/debugpy-1.8.20-cp314-cp314-win32.whl", hash = "sha256:352036a99dd35053b37b7803f748efc456076f929c6a895556932eaf2d23b07f", size = 5331407, upload-time = "2026-01-29T23:03:58.481Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/d9/1f07395b54413432624d61524dfd98c1a7c7827d2abfdb8829ac92638205/debugpy-1.8.20-cp314-cp314-win_amd64.whl", hash = "sha256:a98eec61135465b062846112e5ecf2eebb855305acc1dfbae43b72903b8ab5be", size = 5372521, upload-time = "2026-01-29T23:03:59.864Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658, upload-time = "2026-01-29T23:04:17.404Z" },
+]
+
+[[package]]
+name = "decorator"
+version = "5.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/60/8b/32f9823da46cde7df2087faa08cd98d01b908f8dcab982cdba9c84e85355/decorator-5.3.1.tar.gz", hash = "sha256:4cbcdd55a6efadb9dbea26b858f4fb3264567b52d69ca0d25b721b553f60ea82", size = 58084, upload-time = "2026-05-18T06:03:28.057Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl", hash = "sha256:f47fe6fdbd2edd623ecfe36875d37aba411624e2670dd395dddae1358689bb3c", size = 10365, upload-time = "2026-05-18T06:03:26.517Z" },
+]
+
+[[package]]
+name = "deeplabcut"
+version = "3.0.0"
+source = { editable = "." }
+dependencies = [
+ { name = "albumentations" },
+ { name = "dlclibrary" },
+ { name = "einops" },
+ { name = "filelock" },
+ { name = "filterpy" },
+ { name = "h5py", marker = "sys_platform == 'darwin' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "huggingface-hub" },
+ { name = "imageio-ffmpeg" },
+ { name = "imgaug" },
+ { name = "matplotlib" },
+ { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numba" },
+ { name = "numpy" },
+ { name = "packaging" },
+ { name = "pandas", extra = ["hdf5", "performance"] },
+ { name = "pillow" },
+ { name = "pycocotools" },
+ { name = "pydantic" },
+ { name = "pyyaml" },
+ { name = "ruamel-yaml" },
+ { name = "scikit-image", version = "0.25.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scikit-image", version = "0.26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "statsmodels" },
+ { name = "tables", version = "3.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tables", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "timm" },
+ { name = "torch", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "torch", version = "2.10.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "torch", version = "2.12.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "torchvision", version = "0.15.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "torchvision", version = "0.25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "torchvision", version = "0.27.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "tqdm" },
+]
+
+[package.optional-dependencies]
+apple-mchips = [
+ { name = "protobuf", version = "4.25.9", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'darwin'" },
+ { name = "tensorflow", version = "2.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow", version = "2.17.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow-metal", marker = "sys_platform == 'darwin'" },
+ { name = "tensorpack", marker = "sys_platform == 'darwin'" },
+ { name = "tf-keras", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tf-keras", version = "2.17.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tf-slim", marker = "sys_platform == 'darwin'" },
+]
+docs = [
+ { name = "jupyter-book" },
+ { name = "numpydoc" },
+ { name = "sphinxcontrib-mermaid" },
+]
+fmpose3d = [
+ { name = "fmpose3d" },
+]
+gui = [
+ { name = "napari-deeplabcut" },
+ { name = "pyside6" },
+ { name = "qdarkstyle" },
+]
+modelzoo = [
+ { name = "huggingface-hub" },
+]
+openvino = [
+ { name = "openvino-dev" },
+]
+tf = [
+ { name = "protobuf", version = "4.25.9", source = { registry = "https://pypi.org/simple" } },
+ { name = "tensorflow", version = "2.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow", version = "2.17.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow-io-gcs-filesystem", version = "0.31.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' and sys_platform == 'win32'" },
+ { name = "tensorflow-metal", marker = "sys_platform == 'darwin'" },
+ { name = "tensorpack" },
+ { name = "tf-keras", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tf-keras", version = "2.17.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tf-slim" },
+]
+tf-cu11 = [
+ { name = "protobuf", version = "4.25.9", source = { registry = "https://pypi.org/simple" } },
+ { name = "tensorflow", version = "2.14.0", source = { registry = "https://pypi.org/simple" } },
+ { name = "tensorflow-io-gcs-filesystem", version = "0.31.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'win32'" },
+ { name = "tensorflow-metal", marker = "sys_platform == 'darwin'" },
+ { name = "tensorpack" },
+ { name = "tf-keras", version = "2.14.1", source = { registry = "https://pypi.org/simple" } },
+ { name = "tf-slim" },
+ { name = "torch", version = "2.0.1", source = { registry = "https://pypi.org/simple" } },
+ { name = "torchvision", version = "0.15.2", source = { registry = "https://pypi.org/simple" } },
+]
+tf-cu12 = [
+ { name = "protobuf", version = "5.29.6", source = { registry = "https://pypi.org/simple" } },
+ { name = "tensorflow", version = "2.18.0", source = { registry = "https://pypi.org/simple" } },
+ { name = "tensorflow-metal", marker = "sys_platform == 'darwin'" },
+ { name = "tensorpack" },
+ { name = "tf-keras", version = "2.18.0", source = { registry = "https://pypi.org/simple" } },
+ { name = "tf-slim" },
+ { name = "torch", version = "2.10.0", source = { registry = "https://pypi.org/simple" } },
+ { name = "torchvision", version = "0.25.0", source = { registry = "https://pypi.org/simple" } },
+]
+tf-latest = [
+ { name = "protobuf", version = "5.29.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "protobuf", version = "6.33.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.13' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow", version = "2.18.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.13' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow-metal", marker = "sys_platform == 'darwin'" },
+ { name = "tensorpack" },
+ { name = "tf-keras", version = "2.18.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tf-keras", version = "2.20.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.13' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tf-slim" },
+]
+wandb = [
+ { name = "wandb" },
+]
+
+[package.dev-dependencies]
+dev = [
+ { name = "coverage" },
+ { name = "nbformat" },
+ { name = "pre-commit" },
+ { name = "pytest" },
+ { name = "pytest-cov" },
+ { name = "ruff" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "albumentations", specifier = "<=1.4.3" },
+ { name = "dlclibrary", specifier = ">=0.0.12" },
+ { name = "einops" },
+ { name = "filelock", specifier = ">=3.12,<3.16" },
+ { name = "filterpy", specifier = ">=1.4.4" },
+ { name = "fmpose3d", marker = "extra == 'fmpose3d'", specifier = ">=0.0.8" },
+ { name = "h5py", marker = "sys_platform == 'darwin'", specifier = ">=3.15.1" },
+ { name = "huggingface-hub", specifier = ">=0.23" },
+ { name = "huggingface-hub", marker = "extra == 'modelzoo'" },
+ { name = "imageio-ffmpeg" },
+ { name = "imgaug", specifier = ">=0.4" },
+ { name = "jupyter-book", marker = "extra == 'docs'", specifier = "==1.0.4.post1" },
+ { name = "matplotlib", specifier = ">=3.3,!=3.7,!=3.7.1,<3.9" },
+ { name = "napari-deeplabcut", marker = "extra == 'gui'", specifier = ">=0.3.1" },
+ { name = "networkx", specifier = ">=2.6" },
+ { name = "numba", specifier = ">=0.54" },
+ { name = "numpy", specifier = ">=1.18.5,<2" },
+ { name = "numpydoc", marker = "extra == 'docs'" },
+ { name = "openvino-dev", marker = "extra == 'openvino'", specifier = "==2022.1" },
+ { name = "packaging", specifier = ">=26" },
+ { name = "pandas", extras = ["hdf5", "performance"], specifier = ">=2.2,<3" },
+ { name = "pillow", specifier = ">=7.1" },
+ { name = "protobuf", marker = "sys_platform == 'darwin' and extra == 'apple-mchips'", specifier = "<7" },
+ { name = "protobuf", marker = "extra == 'tf'", specifier = "<7" },
+ { name = "protobuf", marker = "extra == 'tf-cu11'", specifier = "<7" },
+ { name = "protobuf", marker = "extra == 'tf-cu12'", specifier = "<7" },
+ { name = "protobuf", marker = "extra == 'tf-latest'", specifier = "<7" },
+ { name = "pycocotools" },
+ { name = "pydantic", specifier = ">=2,<3" },
+ { name = "pyside6", marker = "platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'gui'", specifier = "<6.10" },
+ { name = "pyside6", marker = "(platform_machine != 'x86_64' and extra == 'gui') or (sys_platform != 'linux' and extra == 'gui')" },
+ { name = "pyyaml" },
+ { name = "qdarkstyle", marker = "extra == 'gui'", specifier = "==3.1" },
+ { name = "ruamel-yaml", specifier = ">=0.15" },
+ { name = "scikit-image", specifier = ">=0.17" },
+ { name = "scikit-learn", specifier = ">=1" },
+ { name = "scipy", specifier = ">=1.9" },
+ { name = "sphinxcontrib-mermaid", marker = "extra == 'docs'" },
+ { name = "statsmodels", specifier = ">=0.11" },
+ { name = "tables", specifier = ">3.8" },
+ { name = "tensorflow", marker = "python_full_version >= '3.12' and extra == 'tf'", specifier = ">=2.16.1,<2.18" },
+ { name = "tensorflow", marker = "python_full_version < '3.12' and extra == 'tf'", specifier = ">=2.12,<2.16" },
+ { name = "tensorflow", marker = "python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'apple-mchips'", specifier = ">=2.15,<2.18" },
+ { name = "tensorflow", marker = "python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'apple-mchips'", specifier = ">=2.12,<2.15" },
+ { name = "tensorflow", marker = "extra == 'tf-cu11'", specifier = "==2.14" },
+ { name = "tensorflow", marker = "extra == 'tf-cu12'", specifier = "==2.18" },
+ { name = "tensorflow", marker = "extra == 'tf-latest'", specifier = ">=2.18" },
+ { name = "tensorflow-io-gcs-filesystem", marker = "python_full_version < '3.12' and sys_platform == 'win32' and extra == 'tf'", specifier = "==0.31" },
+ { name = "tensorflow-io-gcs-filesystem", marker = "sys_platform == 'win32' and extra == 'tf-cu11'", specifier = "==0.31" },
+ { name = "tensorflow-metal", marker = "python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'apple-mchips'", specifier = ">=1.2" },
+ { name = "tensorflow-metal", marker = "python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'tf'", specifier = ">=1.2" },
+ { name = "tensorflow-metal", marker = "python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'apple-mchips'", specifier = "==1.2" },
+ { name = "tensorflow-metal", marker = "python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'tf'", specifier = "==1.2" },
+ { name = "tensorflow-metal", marker = "sys_platform == 'darwin' and extra == 'tf-cu11'", specifier = "==1.2" },
+ { name = "tensorflow-metal", marker = "sys_platform == 'darwin' and extra == 'tf-cu12'", specifier = "==1.2" },
+ { name = "tensorflow-metal", marker = "sys_platform == 'darwin' and extra == 'tf-latest'", specifier = ">=1.2" },
+ { name = "tensorpack", marker = "sys_platform == 'darwin' and extra == 'apple-mchips'", specifier = ">=0.11" },
+ { name = "tensorpack", marker = "extra == 'tf'", specifier = ">=0.11" },
+ { name = "tensorpack", marker = "extra == 'tf-cu11'", specifier = "==0.11" },
+ { name = "tensorpack", marker = "extra == 'tf-cu12'", specifier = "==0.11" },
+ { name = "tensorpack", marker = "extra == 'tf-latest'", specifier = ">=0.11" },
+ { name = "tf-keras", marker = "python_full_version >= '3.12' and extra == 'tf'", specifier = ">=2.15,<2.18" },
+ { name = "tf-keras", marker = "python_full_version < '3.12' and extra == 'tf'", specifier = "<2.15" },
+ { name = "tf-keras", marker = "sys_platform == 'darwin' and extra == 'apple-mchips'" },
+ { name = "tf-keras", marker = "extra == 'tf-cu11'", specifier = "==2.14.1" },
+ { name = "tf-keras", marker = "extra == 'tf-cu12'", specifier = "==2.18" },
+ { name = "tf-keras", marker = "extra == 'tf-latest'" },
+ { name = "tf-slim", marker = "sys_platform == 'darwin' and extra == 'apple-mchips'", specifier = ">=1.1" },
+ { name = "tf-slim", marker = "extra == 'tf'", specifier = ">=1.1" },
+ { name = "tf-slim", marker = "extra == 'tf-cu11'", specifier = "==1.1" },
+ { name = "tf-slim", marker = "extra == 'tf-cu12'", specifier = "==1.1" },
+ { name = "tf-slim", marker = "extra == 'tf-latest'", specifier = ">=1.1" },
+ { name = "timm" },
+ { name = "torch", specifier = ">=2" },
+ { name = "torch", marker = "extra == 'tf-cu11'", specifier = "<2.1" },
+ { name = "torch", marker = "extra == 'tf-cu12'", specifier = "<2.11" },
+ { name = "torchvision" },
+ { name = "torchvision", marker = "extra == 'tf-cu11'", specifier = "<0.16" },
+ { name = "torchvision", marker = "extra == 'tf-cu12'", specifier = "<0.26" },
+ { name = "tqdm" },
+ { name = "wandb", marker = "extra == 'wandb'" },
+]
+provides-extras = ["gui", "openvino", "docs", "fmpose3d", "tf", "tf-cu11", "tf-cu12", "tf-latest", "apple-mchips", "modelzoo", "wandb"]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "coverage" },
+ { name = "nbformat", specifier = ">5" },
+ { name = "pre-commit" },
+ { name = "pytest" },
+ { name = "pytest-cov" },
+ { name = "ruff" },
+]
+
+[[package]]
+name = "distlib"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
+]
+
+[[package]]
+name = "dlclibrary"
+version = "0.0.12"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "huggingface-hub" },
+ { name = "ruamel-yaml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0d/bf/14c4a73bfb090642a2f8353ff38a623024ee39f7efb4966962ed2a194c3a/dlclibrary-0.0.12.tar.gz", hash = "sha256:9c1dcb98edcba03f33c31e0c0f9d18ce1c349bef65f8d3cd5bde49d5e76171db", size = 13187, upload-time = "2026-05-19T08:04:33.669Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/49/57/c0ebe1191a2a445e0a671ce9255b149747ff6285aa6610e355b2e4e72cfe/dlclibrary-0.0.12-py3-none-any.whl", hash = "sha256:be8e22d334fd37cfde6e68936b73ee45a2f0f55baedc83832d773e4f2aedb38b", size = 17488, upload-time = "2026-05-19T08:04:32.456Z" },
+]
+
+[[package]]
+name = "docstring-parser"
+version = "0.18.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e0/4d/f332313098c1de1b2d2ff91cf2674415cc7cddab2ca1b01ae29774bd5fdf/docstring_parser-0.18.0.tar.gz", hash = "sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015", size = 29341, upload-time = "2026-04-14T04:09:19.867Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl", hash = "sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b", size = 22484, upload-time = "2026-04-14T04:09:18.638Z" },
+]
+
+[[package]]
+name = "docutils"
+version = "0.21.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" },
+]
+
+[[package]]
+name = "einops"
+version = "0.8.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2c/77/850bef8d72ffb9219f0b1aac23fbc1bf7d038ee6ea666f331fa273031aa2/einops-0.8.2.tar.gz", hash = "sha256:609da665570e5e265e27283aab09e7f279ade90c4f01bcfca111f3d3e13f2827", size = 56261, upload-time = "2026-01-26T04:13:17.638Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/09/f8d8f8f31e4483c10a906437b4ce31bdf3d6d417b73fe33f1a8b59e34228/einops-0.8.2-py3-none-any.whl", hash = "sha256:54058201ac7087911181bfec4af6091bb59380360f069276601256a76af08193", size = 65638, upload-time = "2026-01-26T04:13:18.546Z" },
+]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
+]
+
+[[package]]
+name = "executing"
+version = "2.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" },
+]
+
+[[package]]
+name = "fastjsonschema"
+version = "2.21.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130, upload-time = "2025-08-14T18:49:36.666Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024, upload-time = "2025-08-14T18:49:34.776Z" },
+]
+
+[[package]]
+name = "filelock"
+version = "3.15.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/08/dd/49e06f09b6645156550fb9aee9cc1e59aba7efbc972d665a1bd6ae0435d4/filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb", size = 18007, upload-time = "2024-06-22T15:59:14.749Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ae/f0/48285f0262fe47103a4a45972ed2f9b93e4c80b8fd609fa98da78b2a5706/filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7", size = 16159, upload-time = "2024-06-22T15:59:12.695Z" },
+]
+
+[[package]]
+name = "filterpy"
+version = "1.4.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "matplotlib" },
+ { name = "numpy" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f6/1d/ac8914360460fafa1990890259b7fa5ef7ba4cd59014e782e4ab3ab144d8/filterpy-1.4.5.zip", hash = "sha256:4f2a4d39e4ea601b9ab42b2db08b5918a9538c168cff1c6895ae26646f3d73b1", size = 177985, upload-time = "2018-10-10T22:38:24.63Z" }
+
+[[package]]
+name = "flatbuffers"
+version = "25.12.19"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" },
+]
+
+[[package]]
+name = "flexcache"
+version = "0.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/55/b0/8a21e330561c65653d010ef112bf38f60890051d244ede197ddaa08e50c1/flexcache-0.3.tar.gz", hash = "sha256:18743bd5a0621bfe2cf8d519e4c3bfdf57a269c15d1ced3fb4b64e0ff4600656", size = 15816, upload-time = "2024-03-09T03:21:07.555Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/27/cd/c883e1a7c447479d6e13985565080e3fea88ab5a107c21684c813dba1875/flexcache-0.3-py3-none-any.whl", hash = "sha256:d43c9fea82336af6e0115e308d9d33a185390b8346a017564611f1466dcd2e32", size = 13263, upload-time = "2024-03-09T03:21:05.635Z" },
+]
+
+[[package]]
+name = "flexparser"
+version = "0.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/82/99/b4de7e39e8eaf8207ba1a8fa2241dd98b2ba72ae6e16960d8351736d8702/flexparser-0.4.tar.gz", hash = "sha256:266d98905595be2ccc5da964fe0a2c3526fbbffdc45b65b3146d75db992ef6b2", size = 31799, upload-time = "2024-11-07T02:00:56.249Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fe/5e/3be305568fe5f34448807976dc82fc151d76c3e0e03958f34770286278c1/flexparser-0.4-py3-none-any.whl", hash = "sha256:3738b456192dcb3e15620f324c447721023c0293f6af9955b481e91d00179846", size = 27625, upload-time = "2024-11-07T02:00:54.523Z" },
+]
+
+[[package]]
+name = "fmpose3d"
+version = "0.0.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "einops" },
+ { name = "filterpy" },
+ { name = "huggingface-hub" },
+ { name = "numba" },
+ { name = "numpy" },
+ { name = "opencv-python" },
+ { name = "pandas" },
+ { name = "scikit-image", version = "0.25.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scikit-image", version = "0.26.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-fmpose3d') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-fmpose3d') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "timm" },
+ { name = "torch", version = "2.12.0", source = { registry = "https://pypi.org/simple" } },
+ { name = "torchvision", version = "0.27.0", source = { registry = "https://pypi.org/simple" } },
+ { name = "tqdm" },
+ { name = "yacs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/94/88/df6b75e3267f859c542244a6eca5edba7ecda4660bcddc6680aa2c5d74b0/fmpose3d-0.0.9.tar.gz", hash = "sha256:89e0137ed3525d2d8b6a4a6276a810643c5afd5556b32f3899eac909287d85bd", size = 114115, upload-time = "2026-04-10T12:14:09.729Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bd/1d/fb7a16fafcf437a590462b691372a99e64219e484ebb0e1fa3f849b5eeb9/fmpose3d-0.0.9-py3-none-any.whl", hash = "sha256:9d3a0aea8fdb54234c2dd9ec8d9df5dad3c16350eb7b9aba09f22345165e6d24", size = 131801, upload-time = "2026-04-10T12:14:08.562Z" },
+]
+
+[[package]]
+name = "fonttools"
+version = "4.63.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f2/c9/4141c90a90db20f807c7e10bfd689fe53eb8f7f4caff58ee4d4dfe46919f/fonttools-4.63.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e3297a6a4059b4acc3a1e9a8b04741f240a80044eef08ebd32e8b5bcdddce75b", size = 2884632, upload-time = "2026-05-14T12:02:38.56Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/46/ad12b5c10eae602d7ef814b02afa08aacbf89da917fed5b071282b7eadc2/fonttools-4.63.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b1cd75a03ad8cb5bc40c90bfde68c0c47de423aa19e5c0f362b43520645eea94", size = 2429441, upload-time = "2026-05-14T12:02:41.162Z" },
+ { url = "https://files.pythonhosted.org/packages/90/8f/bdca24a84c81d56fffed052229cdcff368f6e05882e526f4558891481f65/fonttools-4.63.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0425b277a59cff3d80ca42162a8de360f318438a2ac83570842a678d826d579", size = 4946346, upload-time = "2026-05-14T12:02:43.41Z" },
+ { url = "https://files.pythonhosted.org/packages/04/59/a639c0e136441ee91a65b56fdf89e5d075927e7a09c559d1b0f5276577db/fonttools-4.63.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d7e5c9973aa04c95650c96e5f5ad865fbf42d62079163ecfab1e01cbc2504c22", size = 4903184, upload-time = "2026-05-14T12:02:45.742Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/53/91b7e0cb45b536f3da1b29ba8cbab89f27e8b986809e0b1982303a3f4eca/fonttools-4.63.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cb014d58140a38135f16064c74c652ed57aa0b75cbf8bb59cac821f7edb5334e", size = 4922967, upload-time = "2026-05-14T12:02:48.386Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/b7/87439bf44e6b97c5538cd29d0b7e366a5b8ce2cc132a4134fb67fa3f2fa2/fonttools-4.63.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:032038247a96c1690f9f31e377c389383c902531b085aa4e4dabd6f57f870e69", size = 5042799, upload-time = "2026-05-14T12:02:50.424Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/7c/8b96c3263b89ef99cded544c0f0636686f85dbd3c211c4dceef0231fca23/fonttools-4.63.0-cp310-cp310-win32.whl", hash = "sha256:a8b33a82979e0a6a34ff435cc81317be1f95ec1ebb7a3a2d1c8a6a54f02ae44e", size = 1519704, upload-time = "2026-05-14T12:02:52.523Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/4d/2c2f0069970b6907de8fb5b05c5c0193cc22f717df151d1c7aef1c738f58/fonttools-4.63.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c18358a155d75034911c5ee397a5b44cd19dd325dbb8b35fb60bf421d6a72ac", size = 1568666, upload-time = "2026-05-14T12:02:54.917Z" },
+ { url = "https://files.pythonhosted.org/packages/75/2b/a7f1545bdf5da69c4bda0cea2a5781f0ad2a6623e0277267672db43c5fe6/fonttools-4.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b8ae05d9eacf6081414d759c0a352769ac28ce31280d6bb8e77b03f9e3c449f", size = 2881793, upload-time = "2026-05-14T12:02:56.645Z" },
+ { url = "https://files.pythonhosted.org/packages/49/50/965308c703f085f225db2886813b27e015b8b3438c350b22dd65b52c2a2c/fonttools-4.63.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cdc9f567aec74a72918fd060283911406750cbc9fd28c1316023deb6ce31a9", size = 2428130, upload-time = "2026-05-14T12:02:58.891Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/38/6937fbd7f2dc3a6b48725851bc2c15ec949b9af14d9bbcb5fe83cdf9bdf9/fonttools-4.63.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c14b4fd138c4bafcca294765c547914e1aa431ae1ca94ab99d8db08c958bd3b", size = 5111952, upload-time = "2026-05-14T12:03:01.263Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/43/a81f20050a3115b57d62c8e781446949512eac36690dc384ccea65ff4cc1/fonttools-4.63.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76ac49f929aecaf82d83250b8347e099d7aecba0f4726c1d9b6df3b8bb5fe18", size = 5082308, upload-time = "2026-05-14T12:03:03.211Z" },
+ { url = "https://files.pythonhosted.org/packages/67/00/cdd9d4944ca6ae280d01e69cc37bde3bf663630b837a6fc6d2cd65d80e0e/fonttools-4.63.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dcf076a4474fe0d7367e5bbf5b052c7284fa1feca729c04176ce513521afd8a0", size = 5087932, upload-time = "2026-05-14T12:03:05.147Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/f1/0aa0dbea778c75adbef223c42019fd47d22262b905974d62d829545d485f/fonttools-4.63.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7dd683fef0663e9f0f45cf541d788d24caa3ec9db50796b588e1757d8b3bc007", size = 5213271, upload-time = "2026-05-14T12:03:07.238Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/99/253e4056e1f0e67b9390125a154b73b5eb73ad521bece95c004858fdeec2/fonttools-4.63.0-cp311-cp311-win32.whl", hash = "sha256:afefc1ed0a59785a7fb06ea7e1678e849c193e1e387db783579bc7b3056fcfcb", size = 2304473, upload-time = "2026-05-14T12:03:09.271Z" },
+ { url = "https://files.pythonhosted.org/packages/08/60/defa5e69641db890a63be281f41345f4c33b157824eaf0b9fad3e08b0dcb/fonttools-4.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:063e08bd17bd5a90127a14123de0d6a952dbc847695fd98b63c043d58057f90c", size = 2356389, upload-time = "2026-05-14T12:03:11.53Z" },
+ { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131, upload-time = "2026-05-14T12:03:13.386Z" },
+ { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704, upload-time = "2026-05-14T12:03:15.801Z" },
+ { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298, upload-time = "2026-05-14T12:03:18.085Z" },
+ { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800, upload-time = "2026-05-14T12:03:20.161Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666, upload-time = "2026-05-14T12:03:22.87Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598, upload-time = "2026-05-14T12:03:25.168Z" },
+ { url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575, upload-time = "2026-05-14T12:03:27.496Z" },
+ { url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211, upload-time = "2026-05-14T12:03:30.057Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/8d/d8fec3dcde2963f8c908fb315e5ff2cd0ac34f82394bbbf73a2aa5145ce3/fonttools-4.63.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd", size = 2876062, upload-time = "2026-05-14T12:03:32.554Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/71/d935dc54e4ff121bfdd11e08702db63a7e6f25af21d8a3d7b7212df53641/fonttools-4.63.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59", size = 2424594, upload-time = "2026-05-14T12:03:34.86Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/40/e76320afa1df918e146155ef239b1719ee266092e96f5423bfd075affba1/fonttools-4.63.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d", size = 5024840, upload-time = "2026-05-14T12:03:36.745Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/36/0b805d8c485f872f65a509cbe3b58a5d0d17bee855333b54a150c79d3061/fonttools-4.63.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68", size = 4975801, upload-time = "2026-05-14T12:03:38.833Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/26/2cee03d0aa083ab022da5c07aff9ed3f689da1defb81ad6917c9627896da/fonttools-4.63.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be", size = 4965009, upload-time = "2026-05-14T12:03:41.494Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/48/cc4b66d9058c0d0982c833fad10127c4b0e9324606aafa41382295ca4102/fonttools-4.63.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27", size = 5105892, upload-time = "2026-05-14T12:03:43.525Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/1f/a98a30a814b9ddef3a2e706025f90b9e0bc94890e6cb15254bc86547d11a/fonttools-4.63.0-cp313-cp313-win32.whl", hash = "sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380", size = 2291313, upload-time = "2026-05-14T12:03:45.594Z" },
+ { url = "https://files.pythonhosted.org/packages/92/46/5177b01f3b4abfdd4409f31cca4ab279c9343a26efbe9ec78c97fc612e02/fonttools-4.63.0-cp313-cp313-win_amd64.whl", hash = "sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b", size = 2342299, upload-time = "2026-05-14T12:03:47.414Z" },
+ { url = "https://files.pythonhosted.org/packages/27/d2/23d25e3f247b328be58d04a4c9f894178a0d1eda7d42867cfb388adaf416/fonttools-4.63.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fd1e3094f42d806d3d7c79162fc59e5910fcbe3a7360c385b8da969bc4493745", size = 2875338, upload-time = "2026-05-14T12:03:50.052Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/58/7dfa0c761cb3b2964e2a84c4dc986c926a87de0cb9fb60d5b28ded3f2914/fonttools-4.63.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6e528da43bc3791085f8cb6141b1d13e459226790240340fcbb4625649238b03", size = 2422661, upload-time = "2026-05-14T12:03:52.154Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/87/64cfa18a7a1621d17b7f4502b2b0ed8a135a90c3db51ea590ee99043e76b/fonttools-4.63.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b2248c5decb223562f7902ff6325077a073f608ee8e33e88ad88db734eb9f49", size = 5010526, upload-time = "2026-05-14T12:03:54.647Z" },
+ { url = "https://files.pythonhosted.org/packages/36/e1/a8933a72c45a87177fbde2696e0d0755c8c9062f8c077a961c6215fa27b1/fonttools-4.63.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:308f957cdeaf8abe4e5f2f124902ef405448af92c90f80e302a3b771c2e6116b", size = 4923946, upload-time = "2026-05-14T12:03:56.984Z" },
+ { url = "https://files.pythonhosted.org/packages/27/60/872e6e233b8c5e8b41413796ff18b7fe479661bd40147e071b450dfad7a1/fonttools-4.63.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bf00f21eb5fb721dbaf73d1e9da6d02a1af7768f2ebcf9798be98beab8ba90f6", size = 4962489, upload-time = "2026-05-14T12:03:59.443Z" },
+ { url = "https://files.pythonhosted.org/packages/30/c4/83c24f2ec38b90cfda84bf4b1a1f49df80e84a1db4e7ac6e0d41bf23bc39/fonttools-4.63.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c1aaa4b9c75798400ac043ce04d74e7830376c85095a5a6ed7cba2f17a266bf4", size = 5071870, upload-time = "2026-05-14T12:04:02.122Z" },
+ { url = "https://files.pythonhosted.org/packages/de/40/3ae22b60ff1d41ce0bd044b31238cdc72cef99f28b976f1e128ebd618c9b/fonttools-4.63.0-cp314-cp314-win32.whl", hash = "sha256:22693918177bd9ceabec4736d338045f357769416fc6b0b2508eefef75b08616", size = 2295026, upload-time = "2026-05-14T12:04:04.47Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/d4/98078064ccc76b45cb0f6c002452011e93c4bd26f6850344f0951cc1fe89/fonttools-4.63.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d782fac32985914c351556f68ac0855391572bcd87de50e05970d3cd4c96fc5", size = 2347454, upload-time = "2026-05-14T12:04:06.752Z" },
+ { url = "https://files.pythonhosted.org/packages/49/4e/652d1580c5f4e39f7d103b0c793e4773129ad633dce4addd0cf4dfebde02/fonttools-4.63.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6db5140a60a5d731d21ec076745b40a310607731b0a565b50776393188649001", size = 2958152, upload-time = "2026-05-14T12:04:08.706Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/55/ad864c9a9b219f552eb46b32cd7906c466e5a578ba0c3abfcc0fe7413eb6/fonttools-4.63.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d76edbff9014094dbf03bd2d074709dfa6ec7aba13d838c937a2b33d2d6a86e", size = 2460809, upload-time = "2026-05-14T12:04:10.783Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/2b/0aa8db70f18cf52e49b4ed5ecec68547f981160bf5ded3b5aed6faa0a6f9/fonttools-4.63.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0eac00b9118c3c2f87d272e45341871c5b3066baa3c86897fa634a7c3fb59096", size = 5148649, upload-time = "2026-05-14T12:04:12.747Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/63/18e4369c25043096f1048e0c9915951adc4f842bd81c6b18155824d6fa99/fonttools-4.63.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51394295f1a51de8b5f30bdb1e1b9a4231536c7064ef5c6e211eec19fa36036f", size = 4932147, upload-time = "2026-05-14T12:04:14.806Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/3f/67f3eac2ffd8a98446c5022f8ed3864eac878a5ff7af8df4c8286dba16cc/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9e12f105d2b6342c559c298afb674006bb2893afc7102dcf8a1b55b0486b4e40", size = 5027237, upload-time = "2026-05-14T12:04:17.675Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/ba/4e6214cb38a7b04779e97bb7636de9a5c7f20af7018d03dee0b64c08510a/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:796f27556dbe094c4824f75ca85267e4df776c79036c8441469a4df37038c196", size = 5053933, upload-time = "2026-05-14T12:04:20.818Z" },
+ { url = "https://files.pythonhosted.org/packages/34/3b/214dcc19ee31d3d38fb5ad2755c11ef0514e5dc300bbaf41c0b69f393799/fonttools-4.63.0-cp314-cp314t-win32.whl", hash = "sha256:948428a275741f0b64b113c955425a953314f4b9ab9997f73a72c83e68e569c8", size = 2359326, upload-time = "2026-05-14T12:04:24.22Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/1e/3ff1a9b523058c2eeb6a9d50f5574e2a738200d0d94107d5bc4105e8da3f/fonttools-4.63.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6d4741eb179121cab9eea4cb2393d24492373a260d7945006358c08cfbf45419", size = 2425829, upload-time = "2026-05-14T12:04:26.829Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" },
+]
+
+[[package]]
+name = "freetype-py"
+version = "2.5.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d0/9c/61ba17f846b922c2d6d101cc886b0e8fb597c109cedfcb39b8c5d2304b54/freetype-py-2.5.1.zip", hash = "sha256:cfe2686a174d0dd3d71a9d8ee9bf6a2c23f5872385cf8ce9f24af83d076e2fbd", size = 851738, upload-time = "2024-08-29T18:32:26.37Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/38/a8/258dd138ebe60c79cd8cfaa6d021599208a33f0175a5e29b01f60c9ab2c7/freetype_py-2.5.1-py3-none-macosx_10_9_universal2.whl", hash = "sha256:d01ded2557694f06aa0413f3400c0c0b2b5ebcaabeef7aaf3d756be44f51e90b", size = 1747885, upload-time = "2024-08-29T18:32:17.604Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/93/280ad06dc944e40789b0a641492321a2792db82edda485369cbc59d14366/freetype_py-2.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d2f6b3d68496797da23204b3b9c4e77e67559c80390fc0dc8b3f454ae1cd819", size = 1051055, upload-time = "2024-08-29T18:32:19.153Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/36/853cad240ec63e21a37a512ee19c896b655ce1772d803a3dd80fccfe63fe/freetype_py-2.5.1-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:289b443547e03a4f85302e3ac91376838e0d11636050166662a4f75e3087ed0b", size = 1043856, upload-time = "2024-08-29T18:32:20.565Z" },
+ { url = "https://files.pythonhosted.org/packages/93/6f/fcc1789e42b8c6617c3112196d68e87bfe7d957d80812d3c24d639782dcb/freetype_py-2.5.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:cd3bfdbb7e1a84818cfbc8025fca3096f4f2afcd5d4641184bf0a3a2e6f97bbf", size = 1108180, upload-time = "2024-08-29T18:32:21.871Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/1b/161d3a6244b8a820aef188e4397a750d4a8196316809576d015f26594296/freetype_py-2.5.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:3c1aefc4f0d5b7425f014daccc5fdc7c6f914fb7d6a695cc684f1c09cd8c1660", size = 1106792, upload-time = "2024-08-29T18:32:23.134Z" },
+ { url = "https://files.pythonhosted.org/packages/93/6e/bd7fbfacca077bc6f34f1a1109800a2c41ab50f4704d3a0507ba41009915/freetype_py-2.5.1-py3-none-win_amd64.whl", hash = "sha256:0b7f8e0342779f65ca13ef8bc103938366fecade23e6bb37cb671c2b8ad7f124", size = 814608, upload-time = "2024-08-29T18:32:24.648Z" },
+]
+
+[[package]]
+name = "fsspec"
+version = "2026.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760, upload-time = "2026-04-29T20:42:38.635Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" },
+]
+
+[[package]]
+name = "gast"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/91/f6/e73969782a2ecec280f8a176f2476149dd9dba69d5f8779ec6108a7721e6/gast-0.7.0.tar.gz", hash = "sha256:0bb14cd1b806722e91ddbab6fb86bba148c22b40e7ff11e248974e04c8adfdae", size = 33630, upload-time = "2025-11-29T15:30:05.266Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1d/33/f1c6a276de27b7d7339a34749cc33fa87f077f921969c47185d34a887ae2/gast-0.7.0-py3-none-any.whl", hash = "sha256:99cbf1365633a74099f69c59bd650476b96baa5ef196fec88032b00b31ba36f7", size = 22966, upload-time = "2025-11-29T15:30:03.983Z" },
+]
+
+[[package]]
+name = "gitdb"
+version = "4.0.12"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "smmap" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" },
+]
+
+[[package]]
+name = "gitpython"
+version = "3.1.50"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "gitdb" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/33/f6/354ae6491228b5eb40e10d89c4d13c651fe1cf7556e35ebdded50cff57ce/gitpython-3.1.50.tar.gz", hash = "sha256:80da2d12504d52e1f998772dc5baf6e553f8d2fcfe1fcc226c9d9a2ee3372dcc", size = 219798, upload-time = "2026-05-06T04:01:26.571Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl", hash = "sha256:d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9", size = 212507, upload-time = "2026-05-06T04:01:23.799Z" },
+]
+
+[[package]]
+name = "google-auth"
+version = "2.53.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cryptography", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pyasn1-modules", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c6/ad/ff781329bbbdc0974a098d996e89c9e1f7024262f9e3eec442fbb9ad1ac6/google_auth-2.53.0.tar.gz", hash = "sha256:e7e6aa16f6bee7b2b264830fd04f08087a1d5a836df516251a5d15327b246c9c", size = 335844, upload-time = "2026-05-15T20:53:07.928Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4a/c9/db44165ba7c581268c6d46017ef63339110378305062830104fc7fa144cb/google_auth-2.53.0-py3-none-any.whl", hash = "sha256:6e7449917c599b35126a99ec268ec6880301f2fea41dce198fe8fd83ff642b68", size = 246071, upload-time = "2026-05-15T20:53:05.609Z" },
+]
+
+[[package]]
+name = "google-auth-oauthlib"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "google-auth", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "requests-oauthlib", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e3/b4/ef2170c5f6aa5bc2461bab959a84e56d2819ce26662b50038d2d0602223e/google-auth-oauthlib-1.0.0.tar.gz", hash = "sha256:e375064964820b47221a7e1b7ee1fd77051b6323c3f9e3e19785f78ab67ecfc5", size = 20530, upload-time = "2023-02-07T20:53:20.679Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4a/07/8d9a8186e6768b55dfffeb57c719bc03770cf8a970a074616ae6f9e26a57/google_auth_oauthlib-1.0.0-py2.py3-none-any.whl", hash = "sha256:95880ca704928c300f48194d1770cf5b1462835b6e49db61445a520f793fd5fb", size = 18926, upload-time = "2023-02-07T20:53:18.837Z" },
+]
+
+[[package]]
+name = "google-pasta"
+version = "0.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/35/4a/0bd53b36ff0323d10d5f24ebd67af2de10a1117f5cf4d7add90df92756f1/google-pasta-0.2.0.tar.gz", hash = "sha256:c9f2c8dfc8f96d0d5808299920721be30c9eec37f2389f28904f454565c8a16e", size = 40430, upload-time = "2020-03-13T18:57:50.34Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a3/de/c648ef6835192e6e2cc03f40b19eeda4382c49b5bafb43d88b931c4c74ac/google_pasta-0.2.0-py3-none-any.whl", hash = "sha256:b32482794a366b5366a32c92a9a9201b107821889935a02b3e51f6b432ea84ed", size = 57471, upload-time = "2020-03-13T18:57:48.872Z" },
+]
+
+[[package]]
+name = "greenlet"
+version = "3.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3c/3f/dbf99fb14bfeb88c28f16729215478c0e265cacd6dc22270c8f31bb6892f/greenlet-3.5.0.tar.gz", hash = "sha256:d419647372241bc68e957bf38d5c1f98852155e4146bd1e4121adea81f4f01e4", size = 196995, upload-time = "2026-04-27T13:37:15.544Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b0/03/84359833f7e1d49a883e92777637c592306030e30cee5e2b1e6476f95c88/greenlet-3.5.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:29ea813b2e1f45fa9649a17853b2b5465c4072fbcb072e5af6cd3a288216574a", size = 283502, upload-time = "2026-04-27T12:20:55.213Z" },
+ { url = "https://files.pythonhosted.org/packages/25/ce/6f9f008266273aa14a2e011945797ac5802b97b8b40efe7afe1ee6c1afc9/greenlet-3.5.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:804a70b328e706b785c6ef16187051c394a63dd1a906d89be24b6ad77759f13f", size = 600508, upload-time = "2026-04-27T12:52:37.876Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/6d/b0f3272c2368ea2c1aa19a5ad70db0be8f8dff6e6d3d1eb82efa00cbcf19/greenlet-3.5.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:884f649de075b84739713d41dd4dfd41e2b910bfb769c4a3ea02ec1da52cd9bb", size = 613283, upload-time = "2026-04-27T12:59:37.957Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/ae/1db979ff6ae7958d80b288f63d5f6c30df96682700ea9fc340ce994d94a1/greenlet-3.5.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4d0eadc7e4d9ffb2af4247b606cae307be8e448911e5a0d0b16d72fc3d224cfd", size = 619894, upload-time = "2026-04-27T13:02:35.13Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/ac/0b509b6fb93551ce5a01612ee1acda7f7dda4bbb66c99aeb2ab403d205dc/greenlet-3.5.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b28037cb07768933c54d81bfe47a85f9f402f57d7d69743b991a713b63954eb", size = 613418, upload-time = "2026-04-27T12:25:23.852Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/94/b0590e3d1978f02419f30502341c40d72f77eb0a2198119fe27df47714ee/greenlet-3.5.0-cp310-cp310-manylinux_2_39_riscv64.whl", hash = "sha256:f8c30c2225f40dd76c50790f0eb3b5c7c18431efb299e2782083e1981feed243", size = 415681, upload-time = "2026-04-27T13:05:11.494Z" },
+ { url = "https://files.pythonhosted.org/packages/03/03/2b2b680ec87aaa97998fb5b8d76658d4d3560386864f17efab33ba7c2e24/greenlet-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cda05425526240807408156b6960a17a79a0c760b813573b67027823be760977", size = 1572229, upload-time = "2026-04-27T12:53:23.509Z" },
+ { url = "https://files.pythonhosted.org/packages/61/e4/42b259e7a19aff1a270a4bd82caf6353109ed6860c9454e18f37162b83ae/greenlet-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9c615f869163e14bb1ced20322d8038fb680b08236521ac3f30cd4c1288785a0", size = 1639886, upload-time = "2026-04-27T12:25:22.325Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/b4/733ca47b883b67c57f90d3ecb21055c9ec753597d10754ac201644061f9d/greenlet-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:ba8f0bdc2fae6ce915dfd0c16d2d00bca7e4247c1eae4416e06430e522137858", size = 237795, upload-time = "2026-04-27T12:21:40.118Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/0f/a91f143f356523ff682309732b175765a9bc2836fd7c081c2c67fedc1ad4/greenlet-3.5.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8f1cc966c126639cd152fdaa52624d2655f492faa79e013fea161de3e6dda082", size = 284726, upload-time = "2026-04-27T12:20:51.402Z" },
+ { url = "https://files.pythonhosted.org/packages/95/82/800646c7ffc5dbabd75ddd2f6b519bb898c0c9c969e5d0473bfe5d20bcce/greenlet-3.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:362624e6a8e5bca3b8233e45eef33903a100e9539a2b995c364d595dbc4018b3", size = 604264, upload-time = "2026-04-27T12:52:39.494Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/ac/354867c0bba812fc33b15bc55aedafedd0aee3c7dd91dfca22444157dc0c/greenlet-3.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5ecd83806b0f4c2f53b1018e0005cd82269ea01d42befc0368730028d850ed1c", size = 616099, upload-time = "2026-04-27T12:59:39.623Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/ab/192090c4a5b30df148c22bf4b8895457d739a7c7c5a7b9c41e5dd7f537f2/greenlet-3.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa94cb2288681e3a11645958f1871d48ee9211bd2f66628fdace505927d6e564", size = 623976, upload-time = "2026-04-27T13:02:37.363Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/b0/815bece7399e01cadb69014219eebd0042339875c59a59b0820a46ece356/greenlet-3.5.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ff251e9a0279522e62f6176412869395a64ddf2b5c5f782ff609a8216a4e662", size = 615198, upload-time = "2026-04-27T12:25:25.928Z" },
+ { url = "https://files.pythonhosted.org/packages/24/11/05eb2b9b188c6df7d68a89c99134d644a7af616a40b9808e8e6ced315d5d/greenlet-3.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:64d6ac45f7271f48e45f67c95b54ef73534c52ec041fcda8edf520c6d811f4bc", size = 418379, upload-time = "2026-04-27T13:05:12.755Z" },
+ { url = "https://files.pythonhosted.org/packages/10/80/3b2c0a895d6698f6ddb31b07942ebfa982f3e30888bc5546a5b5990de8b2/greenlet-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d874e79afd41a96e11ff4c5d0bc90a80973e476fda1c2c64985667397df432b", size = 1574927, upload-time = "2026-04-27T12:53:25.81Z" },
+ { url = "https://files.pythonhosted.org/packages/44/0e/f354af514a4c61454dbc68e44d47544a5a4d6317e30b77ddfa3a09f4c5f3/greenlet-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0ed006e4b86c59de7467eb2601cd1b77b5a7d657d1ee55e30fe30d76451edba4", size = 1642683, upload-time = "2026-04-27T12:25:23.9Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/6a/87f38255201e993a1915265ebb80cd7c2c78b04a45744995abbf6b259fd8/greenlet-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:703cb211b820dbffbbc55a16bfc6e4583a6e6e990f33a119d2cc8b83211119c8", size = 238115, upload-time = "2026-04-27T12:21:48.845Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/f8/450fe3c5938fa737ea4d22699772e6e34e8e24431a47bf4e8a1ceed4a98e/greenlet-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:6c18dfb59c70f5a94acd271c72e90128c3c776e41e5f07767908c8c1b74ad339", size = 235017, upload-time = "2026-04-27T12:22:26.768Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/32/f2ce6d4cac3e55bc6173f92dbe627e782e1850f89d986c3606feb63aafa7/greenlet-3.5.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:db2910d3c809444e0a20147361f343fe2798e106af8d9d8506f5305302655a9f", size = 286228, upload-time = "2026-04-27T12:20:34.421Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/aa/caed9e5adf742315fc7be2a84196373aab4816e540e38ba0d76cb7584d68/greenlet-3.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ec9ea74e7268ace7f9aab1b1a4e730193fc661b39a993cd91c606c32d4a3628", size = 601775, upload-time = "2026-04-27T12:52:41.045Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/af/90ae08497400a941595d12774447f752d3dfe0fbb012e35b76bc5c0ff37e/greenlet-3.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54d243512da35485fc7a6bf3c178fdda6327a9d6506fcdd62b1abd1e41b2927b", size = 614436, upload-time = "2026-04-27T12:59:41.595Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/e9/4eeadf8cb3403ac274245ba75f07844abc7fa5f6787583fc9156ba741e0f/greenlet-3.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:41353ec2ecedf7aa8f682753a41919f8718031a6edac46b8d3dc7ed9e1ceb136", size = 620610, upload-time = "2026-04-27T13:02:39.194Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/e0/2e13df68f367e2f9960616927d60857dd7e56aaadd59a47c644216b2f920/greenlet-3.5.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d280a7f5c331622c69f97eb167f33577ff2d1df282c41cd15907fc0a3ca198c", size = 611388, upload-time = "2026-04-27T12:25:28.008Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/ef/f913b3c0eb7d26d86a2401c5e1546c9d46b657efee724b06f6f4ac5d8824/greenlet-3.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:58c1c374fe2b3d852f9b6b11a7dff4c85404e51b9a596fd9e89cf904eb09866d", size = 422775, upload-time = "2026-04-27T13:05:14.261Z" },
+ { url = "https://files.pythonhosted.org/packages/82/f7/393c64055132ac0d488ef6be549253b7e6274194863967ddc0bc8f5b87b8/greenlet-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1eb67d5adefb5bd2e182d42678a328979a209e4e82eb93575708185d31d1f588", size = 1570768, upload-time = "2026-04-27T12:53:28.099Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/4b/eaf7735253522cf56d1b74d672a58f54fc114702ceaf05def59aae72f6e1/greenlet-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2628d6c86f6cb0cb45e0c3c54058bbec559f57eaae699447748cb3928150577e", size = 1635983, upload-time = "2026-04-27T12:25:26.903Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/fe/4fb3a0805bd5165da5ebf858da7cc01cce8061674106d2cf5bdab32cbfde/greenlet-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:d4d9f0624c775f2dfc56ba54d515a8c771044346852a918b405914f6b19d7fd8", size = 238840, upload-time = "2026-04-27T12:23:54.806Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/cb/baa584cb00532126ffe12d9787db0a60c5a4f55c27bfe2666df5d4c30a32/greenlet-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:83ed9f27f1680b50e89f40f6df348a290ea234b249a4003d366663a12eab94f2", size = 235615, upload-time = "2026-04-27T12:21:38.57Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/58/fc576f99037ce19c5aa16628e4c3226b6d1419f72a62c79f5f40576e6eb3/greenlet-3.5.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5a5ed18de6a0f6cc7087f1563f6bd93fc7df1c19165ca01e9bde5a5dc281d106", size = 285066, upload-time = "2026-04-27T12:23:05.033Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/ba/b28ddbe6bfad6a8ac196ef0e8cff37bc65b79735995b9e410923fffeeb70/greenlet-3.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a717fbc46d8a354fa675f7c1e813485b6ba3885f9bef0cd56e5ba27d758ff5b", size = 604414, upload-time = "2026-04-27T12:52:42.358Z" },
+ { url = "https://files.pythonhosted.org/packages/09/06/4b69f8f0b67603a8be2790e55107a190b376f2627fe0eaf5695d85ffb3cd/greenlet-3.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ddc090c5c1792b10246a78e8c2163ebbe04cf877f9d785c230a7b27b39ad038e", size = 617349, upload-time = "2026-04-27T12:59:43.32Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/15/a643b4ecd09969e30b8a150d5919960caae0abe4f5af75ab040b1ab85e78/greenlet-3.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4964101b8585c144cbda5532b1aa644255126c08a265dae90c16e7a0e63aaa9d", size = 623234, upload-time = "2026-04-27T13:02:40.611Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/17/a3918541fd0ddefe024a69de6d16aa7b46d36ac19562adaa63c7fa180eff/greenlet-3.5.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2094acd54b272cb6eae8c03dd87b3fa1820a4cef18d6889c378d503500a1dc13", size = 613927, upload-time = "2026-04-27T12:25:30.28Z" },
+ { url = "https://files.pythonhosted.org/packages/77/18/3b13d5ef1275b0ffaf933b05efa21408ac4ca95823c7411d79682e4fdcff/greenlet-3.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:7022615368890680e67b9965d33f5773aade330d5343bbe25560135aaa849eae", size = 425243, upload-time = "2026-04-27T13:05:15.689Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/e1/bd0af6213c7dd33175d8a462d4c1fe1175124ebed4855bc1475a5b5242c2/greenlet-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5e05ba267789ea87b5a155cf0e810b1ab88bf18e9e8740813945ceb8ee4350ba", size = 1570893, upload-time = "2026-04-27T12:53:29.483Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/2a/0789702f864f5382cb476b93d7a9c823c10472658102ccd65f415747d2e2/greenlet-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0ecec963079cd58cbd14723582384f11f166fd58883c15dcbfb342e0bc9b5846", size = 1636060, upload-time = "2026-04-27T12:25:28.845Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/8f/22bf9df92bbff0eb07842b60f7e63bf7675a9742df628437a9f02d09137f/greenlet-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:728d9667d8f2f586644b748dbd9bb67e50d6a9381767d1357714ea6825bb3bf5", size = 238740, upload-time = "2026-04-27T12:24:01.341Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/b7/9c5c3d653bd4ff614277c049ac676422e2c557db47b4fe43e6313fc005dc/greenlet-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:47422135b1d308c14b2c6e758beedb1acd33bb91679f5670edf77bf46244722b", size = 235525, upload-time = "2026-04-27T12:23:12.308Z" },
+ { url = "https://files.pythonhosted.org/packages/94/5e/a70f31e3e8d961c4ce589c15b28e4225d63704e431a23932a3808cbcc867/greenlet-3.5.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:f35807464c4c58c55f0d31dfa83c541a5615d825c2fe3d2b95360cf7c4e3c0a8", size = 285564, upload-time = "2026-04-27T12:23:08.555Z" },
+ { url = "https://files.pythonhosted.org/packages/af/a6/046c0a28e21833e4086918218cfb3d8bed51c075a1b700f20b9d7861c0f4/greenlet-3.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55fa7ea52771be44af0de27d8b80c02cd18c2c3cddde6c847ecebdf72418b6a1", size = 651166, upload-time = "2026-04-27T12:52:43.644Z" },
+ { url = "https://files.pythonhosted.org/packages/47/f8/4af27f71c5ff32a7fbc516adb46370d9c4ae2bc7bd3dc7d066ac542b4b15/greenlet-3.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a97e4821aa710603f94de0da25f25096454d78ffdace5dc77f3a006bc01abba3", size = 663792, upload-time = "2026-04-27T12:59:44.93Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/89/2dadb89793c37ee8b4c237857188293e9060dc085f19845c292e00f8e091/greenlet-3.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bf2d8a80bec89ab46221ae45c5373d5ba0bd36c19aa8508e85c6cd7e5106cd37", size = 668086, upload-time = "2026-04-27T13:02:42.314Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/59/1bd6d7428d6ed9106efbb8c52310c60fd04f6672490f452aeaa3829aa436/greenlet-3.5.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f52a464e4ed91780bdfbbdd2b97197f3accaa629b98c200f4dffada759f3ae7", size = 660933, upload-time = "2026-04-27T12:25:33.276Z" },
+ { url = "https://files.pythonhosted.org/packages/82/35/75722be7e26a2af4cbd2dc35b0ed382dacf9394b7e75551f76ed1abe87f2/greenlet-3.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:1bae92a1dd94c5f9d9493c3a212dd874c202442047cf96446412c862feca83a2", size = 470799, upload-time = "2026-04-27T13:05:17.094Z" },
+ { url = "https://files.pythonhosted.org/packages/83/e4/b903e5a5fae1e8a28cdd32a0cfbfd560b668c25b692f67768822ddc5f40f/greenlet-3.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:762612baf1161ccb8437c0161c668a688223cba28e1bf038f4eb47b13e39ccdf", size = 1618401, upload-time = "2026-04-27T12:53:31.062Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/e3/5ec408a329acb854fb607a122e1ee5fb3ff649f9a97952948a90803c0d8e/greenlet-3.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:57a43c6079a89713522bc4bcb9f75070ecf5d3dbad7792bfe42239362cbf2a16", size = 1682038, upload-time = "2026-04-27T12:25:31.838Z" },
+ { url = "https://files.pythonhosted.org/packages/91/20/6b165108058767ee643c55c5c4904d591a830ee2b3c7dbd359828fbc829f/greenlet-3.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:3bc59be3945ae9750b9e7d45067d01ae3fe90ea5f9ade99239dabdd6e28a5033", size = 239835, upload-time = "2026-04-27T12:24:54.136Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/62/1c498375cee177b55d980c1db319f26470e5309e54698c8f8fc06c0fd539/greenlet-3.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:a96fcee45e03fe30a62669fd16ab5c9d3c172660d3085605cb1e2d1280d3c988", size = 236862, upload-time = "2026-04-27T12:23:24.957Z" },
+ { url = "https://files.pythonhosted.org/packages/78/a8/4522939255bb5409af4e87132f915446bf3622c2c292d14d3c38d128ae82/greenlet-3.5.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:a10a732421ab4fec934783ce3e54763470d0181db6e3468f9103a275c3ed1853", size = 293614, upload-time = "2026-04-27T12:24:12.874Z" },
+ { url = "https://files.pythonhosted.org/packages/15/5e/8744c52e2c027b5a8772a01561934c8835f869733e101f62075c60430340/greenlet-3.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fc391b1566f2907d17aaebe78f8855dc45675159a775fcf9e61f8ee0078e87f", size = 650723, upload-time = "2026-04-27T12:52:45.412Z" },
+ { url = "https://files.pythonhosted.org/packages/00/ef/7b4c39c03cf46ceca512c5d3f914afd85aa30b2cc9a93015b0dd73e4be6c/greenlet-3.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:680bd0e7ad5e8daa8a4aa89f68fd6adc834b8a8036dc256533f7e08f4a4b01f7", size = 656529, upload-time = "2026-04-27T12:59:46.295Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/5c/0602239503b124b70e39355cbdb39361ecfe65b87a5f2f63752c32f5286f/greenlet-3.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1aa4ce8debcd4ea7fb2e150f3036588c41493d1d52c43538924ae1819003f4ce", size = 657015, upload-time = "2026-04-27T13:02:43.973Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/b5/c7768f352f5c010f92064d0063f987e7dc0cd290a6d92a34109015ce4aa1/greenlet-3.5.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddb36c7d6c9c0a65f18c7258634e0c416c6ab59caac8c987b96f80c2ebda0112", size = 654364, upload-time = "2026-04-27T12:25:35.64Z" },
+ { url = "https://files.pythonhosted.org/packages/38/51/8699f865f125dc952384cb432b0f7138aa4d8f2969a7d12d0df5b94d054d/greenlet-3.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:728a73687e39ae9ca34e4694cbf2f049d3fbc7174639468d0f67200a97d8f9e2", size = 488275, upload-time = "2026-04-27T13:05:18.28Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/d0/079ebe12e4b1fc758857ce5be1a5e73f06870f2101e52611d1e71925ce54/greenlet-3.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e5ddf316ced87539144621453c3aef229575825fe60c604e62bedc4003f372b2", size = 1614204, upload-time = "2026-04-27T12:53:32.618Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/89/6c2fb63df3596552d20e58fb4d96669243388cf680cff222758812c7bfaa/greenlet-3.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4a448128607be0de65342dc9b31be7f948ef4cc0bc8832069350abefd310a8f2", size = 1675480, upload-time = "2026-04-27T12:25:34.168Z" },
+ { url = "https://files.pythonhosted.org/packages/15/32/77ee8a6c1564fc345a491a4e85b3bf360e4cf26eac98c4532d2fdb96e01f/greenlet-3.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d60097128cb0a1cab9ea541186ea13cd7b847b8449a7787c2e2350da0cb82d86", size = 245324, upload-time = "2026-04-27T12:24:40.295Z" },
+]
+
+[[package]]
+name = "grpcio"
+version = "1.80.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9d/cd/bb7b7e54084a344c03d68144450da7ddd5564e51a298ae1662de65f48e2d/grpcio-1.80.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c", size = 6050363, upload-time = "2026-03-30T08:46:20.894Z" },
+ { url = "https://files.pythonhosted.org/packages/16/02/1417f5c3460dea65f7a2e3c14e8b31e77f7ffb730e9bfadd89eda7a9f477/grpcio-1.80.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388", size = 12026037, upload-time = "2026-03-30T08:46:25.144Z" },
+ { url = "https://files.pythonhosted.org/packages/43/98/c910254eedf2cae368d78336a2de0678e66a7317d27c02522392f949b5c6/grpcio-1.80.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02", size = 6602306, upload-time = "2026-03-30T08:46:27.593Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/f8/88ca4e78c077b2b2113d95da1e1ab43efd43d723c9a0397d26529c2c1a56/grpcio-1.80.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc", size = 7301535, upload-time = "2026-03-30T08:46:29.556Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/96/f28660fe2fe0f153288bf4a04e4910b7309d442395135c88ed4f5b3b8b40/grpcio-1.80.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a", size = 6808669, upload-time = "2026-03-30T08:46:31.984Z" },
+ { url = "https://files.pythonhosted.org/packages/47/eb/3f68a5e955779c00aeef23850e019c1c1d0e032d90633ba49c01ad5a96e0/grpcio-1.80.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9", size = 7409489, upload-time = "2026-03-30T08:46:34.684Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/a7/d2f681a4bfb881be40659a309771f3bdfbfdb1190619442816c3f0ffc079/grpcio-1.80.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199", size = 8423167, upload-time = "2026-03-30T08:46:36.833Z" },
+ { url = "https://files.pythonhosted.org/packages/97/8a/29b4589c204959aa35ce5708400a05bba72181807c45c47b3ec000c39333/grpcio-1.80.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81", size = 7846761, upload-time = "2026-03-30T08:46:40.091Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/d2/ed143e097230ee121ac5848f6ff14372dba91289b10b536d54fb1b7cbae7/grpcio-1.80.0-cp310-cp310-win32.whl", hash = "sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069", size = 4156534, upload-time = "2026-03-30T08:46:42.026Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/c9/df8279bb49b29409995e95efa85b72973d62f8aeff89abee58c91f393710/grpcio-1.80.0-cp310-cp310-win_amd64.whl", hash = "sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58", size = 4889869, upload-time = "2026-03-30T08:46:44.219Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/db/1d56e5f5823257b291962d6c0ce106146c6447f405b60b234c4f222a7cde/grpcio-1.80.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a", size = 6055009, upload-time = "2026-03-30T08:46:46.265Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/18/c83f3cad64c5ca63bca7e91e5e46b0d026afc5af9d0a9972472ceba294b3/grpcio-1.80.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060", size = 12035295, upload-time = "2026-03-30T08:46:49.099Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/8e/e14966b435be2dda99fbe89db9525ea436edc79780431a1c2875a3582644/grpcio-1.80.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2", size = 6610297, upload-time = "2026-03-30T08:46:52.123Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/26/d5eb38f42ce0e3fdc8174ea4d52036ef8d58cc4426cb800f2610f625dd75/grpcio-1.80.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21", size = 7300208, upload-time = "2026-03-30T08:46:54.859Z" },
+ { url = "https://files.pythonhosted.org/packages/25/51/bd267c989f85a17a5b3eea65a6feb4ff672af41ca614e5a0279cc0ea381c/grpcio-1.80.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab", size = 6813442, upload-time = "2026-03-30T08:46:57.056Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/d9/d80eef735b19e9169e30164bbf889b46f9df9127598a83d174eb13a48b26/grpcio-1.80.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1", size = 7414743, upload-time = "2026-03-30T08:46:59.682Z" },
+ { url = "https://files.pythonhosted.org/packages/de/f2/567f5bd5054398ed6b0509b9a30900376dcf2786bd936812098808b49d8d/grpcio-1.80.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106", size = 8426046, upload-time = "2026-03-30T08:47:02.474Z" },
+ { url = "https://files.pythonhosted.org/packages/62/29/73ef0141b4732ff5eacd68430ff2512a65c004696997f70476a83e548e7e/grpcio-1.80.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6", size = 7851641, upload-time = "2026-03-30T08:47:05.462Z" },
+ { url = "https://files.pythonhosted.org/packages/46/69/abbfa360eb229a8623bab5f5a4f8105e445bd38ce81a89514ba55d281ad0/grpcio-1.80.0-cp311-cp311-win32.whl", hash = "sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440", size = 4154368, upload-time = "2026-03-30T08:47:08.027Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/d4/ae92206d01183b08613e846076115f5ac5991bae358d2a749fa864da5699/grpcio-1.80.0-cp311-cp311-win_amd64.whl", hash = "sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9", size = 4894235, upload-time = "2026-03-30T08:47:10.839Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/e8/a2b749265eb3415abc94f2e619bbd9e9707bebdda787e61c593004ec927a/grpcio-1.80.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0", size = 6015616, upload-time = "2026-03-30T08:47:13.428Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/97/b1282161a15d699d1e90c360df18d19165a045ce1c343c7f313f5e8a0b77/grpcio-1.80.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2", size = 12014204, upload-time = "2026-03-30T08:47:15.873Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/5e/d319c6e997b50c155ac5a8cb12f5173d5b42677510e886d250d50264949d/grpcio-1.80.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de", size = 6563866, upload-time = "2026-03-30T08:47:18.588Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/f6/fdd975a2cb4d78eb67769a7b3b3830970bfa2e919f1decf724ae4445f42c/grpcio-1.80.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921", size = 7273060, upload-time = "2026-03-30T08:47:21.113Z" },
+ { url = "https://files.pythonhosted.org/packages/db/f0/a3deb5feba60d9538a962913e37bd2e69a195f1c3376a3dd44fe0427e996/grpcio-1.80.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411", size = 6782121, upload-time = "2026-03-30T08:47:23.827Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/84/36c6dcfddc093e108141f757c407902a05085e0c328007cb090d56646cdf/grpcio-1.80.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd", size = 7383811, upload-time = "2026-03-30T08:47:26.517Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/ef/f3a77e3dc5b471a0ec86c564c98d6adfa3510d38f8ee99010410858d591e/grpcio-1.80.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f", size = 8393860, upload-time = "2026-03-30T08:47:29.439Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/8d/9d4d27ed7f33d109c50d6b5ce578a9914aa68edab75d65869a17e630a8d1/grpcio-1.80.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f", size = 7830132, upload-time = "2026-03-30T08:47:33.254Z" },
+ { url = "https://files.pythonhosted.org/packages/14/e4/9990b41c6d7a44e1e9dee8ac11d7a9802ba1378b40d77468a7761d1ad288/grpcio-1.80.0-cp312-cp312-win32.whl", hash = "sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193", size = 4140904, upload-time = "2026-03-30T08:47:35.319Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/2c/296f6138caca1f4b92a31ace4ae1b87dab692fc16a7a3417af3bb3c805bf/grpcio-1.80.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff", size = 4880944, upload-time = "2026-03-30T08:47:37.831Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/3a/7c3c25789e3f069e581dc342e03613c5b1cb012c4e8c7d9d5cf960a75856/grpcio-1.80.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad", size = 6017243, upload-time = "2026-03-30T08:47:40.075Z" },
+ { url = "https://files.pythonhosted.org/packages/04/19/21a9806eb8240e174fd1ab0cd5b9aa948bb0e05c2f2f55f9d5d7405e6d08/grpcio-1.80.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0", size = 12010840, upload-time = "2026-03-30T08:47:43.11Z" },
+ { url = "https://files.pythonhosted.org/packages/18/3a/23347d35f76f639e807fb7a36fad3068aed100996849a33809591f26eca6/grpcio-1.80.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f", size = 6567644, upload-time = "2026-03-30T08:47:46.806Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/40/96e07ecb604a6a67ae6ab151e3e35b132875d98bc68ec65f3e5ab3e781d7/grpcio-1.80.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6", size = 7277830, upload-time = "2026-03-30T08:47:49.643Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/e2/da1506ecea1f34a5e365964644b35edef53803052b763ca214ba3870c856/grpcio-1.80.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140", size = 6783216, upload-time = "2026-03-30T08:47:52.817Z" },
+ { url = "https://files.pythonhosted.org/packages/44/83/3b20ff58d0c3b7f6caaa3af9a4174d4023701df40a3f39f7f1c8e7c48f9d/grpcio-1.80.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d", size = 7385866, upload-time = "2026-03-30T08:47:55.687Z" },
+ { url = "https://files.pythonhosted.org/packages/47/45/55c507599c5520416de5eefecc927d6a0d7af55e91cfffb2e410607e5744/grpcio-1.80.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7", size = 8391602, upload-time = "2026-03-30T08:47:58.303Z" },
+ { url = "https://files.pythonhosted.org/packages/10/bb/dd06f4c24c01db9cf11341b547d0a016b2c90ed7dbbb086a5710df7dd1d7/grpcio-1.80.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7", size = 7826752, upload-time = "2026-03-30T08:48:01.311Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/1e/9d67992ba23371fd63d4527096eb8c6b76d74d52b500df992a3343fd7251/grpcio-1.80.0-cp313-cp313-win32.whl", hash = "sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294", size = 4142310, upload-time = "2026-03-30T08:48:04.594Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/e6/283326a27da9e2c3038bc93eeea36fb118ce0b2d03922a9cda6688f53c5b/grpcio-1.80.0-cp313-cp313-win_amd64.whl", hash = "sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50", size = 4882833, upload-time = "2026-03-30T08:48:07.363Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/6d/e65307ce20f5a09244ba9e9d8476e99fb039de7154f37fb85f26978b59c3/grpcio-1.80.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e", size = 6017376, upload-time = "2026-03-30T08:48:10.005Z" },
+ { url = "https://files.pythonhosted.org/packages/69/10/9cef5d9650c72625a699c549940f0abb3c4bfdb5ed45a5ce431f92f31806/grpcio-1.80.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f", size = 12018133, upload-time = "2026-03-30T08:48:12.927Z" },
+ { url = "https://files.pythonhosted.org/packages/04/82/983aabaad82ba26113caceeb9091706a0696b25da004fe3defb5b346e15b/grpcio-1.80.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9", size = 6574748, upload-time = "2026-03-30T08:48:16.386Z" },
+ { url = "https://files.pythonhosted.org/packages/07/d7/031666ef155aa0bf399ed7e19439656c38bbd143779ae0861b038ce82abd/grpcio-1.80.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14", size = 7277711, upload-time = "2026-03-30T08:48:19.627Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/43/f437a78f7f4f1d311804189e8f11fb311a01049b2e08557c1068d470cb2e/grpcio-1.80.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05", size = 6785372, upload-time = "2026-03-30T08:48:22.373Z" },
+ { url = "https://files.pythonhosted.org/packages/93/3d/f6558e9c6296cb4227faa5c43c54a34c68d32654b829f53288313d16a86e/grpcio-1.80.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1", size = 7395268, upload-time = "2026-03-30T08:48:25.638Z" },
+ { url = "https://files.pythonhosted.org/packages/06/21/0fdd77e84720b08843c371a2efa6f2e19dbebf56adc72df73d891f5506f0/grpcio-1.80.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f", size = 8392000, upload-time = "2026-03-30T08:48:28.974Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/68/67f4947ed55d2e69f2cc199ab9fd85e0a0034d813bbeef84df6d2ba4d4b7/grpcio-1.80.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e", size = 7828477, upload-time = "2026-03-30T08:48:32.054Z" },
+ { url = "https://files.pythonhosted.org/packages/44/b6/8d4096691b2e385e8271911a0de4f35f0a6c7d05aff7098e296c3de86939/grpcio-1.80.0-cp314-cp314-win32.whl", hash = "sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae", size = 4218563, upload-time = "2026-03-30T08:48:34.538Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/8c/bbe6baf2557262834f2070cf668515fa308b2d38a4bbf771f8f7872a7036/grpcio-1.80.0-cp314-cp314-win_amd64.whl", hash = "sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f", size = 5019457, upload-time = "2026-03-30T08:48:37.308Z" },
+]
+
+[[package]]
+name = "h11"
+version = "0.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
+]
+
+[[package]]
+name = "h5py"
+version = "3.16.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526, upload-time = "2026-03-06T13:49:08.07Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3a/6b/231413e58a787a89b316bb0d1777da3c62257e4797e09afd8d17ad3549dc/h5py-3.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e06f864bedb2c8e7c1358e6c73af48519e317457c444d6f3d332bb4e8fa6d7d9", size = 3724137, upload-time = "2026-03-06T13:47:35.242Z" },
+ { url = "https://files.pythonhosted.org/packages/74/f9/557ce3aad0fe8471fb5279bab0fc56ea473858a022c4ce8a0b8f303d64e9/h5py-3.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec86d4fffd87a0f4cb3d5796ceb5a50123a2a6d99b43e616e5504e66a953eca3", size = 3090112, upload-time = "2026-03-06T13:47:37.634Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/f5/e15b3d0dc8a18e56409a839e6468d6fb589bc5207c917399c2e0706eeb44/h5py-3.16.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:86385ea895508220b8a7e45efa428aeafaa586bd737c7af9ee04661d8d84a10d", size = 4844847, upload-time = "2026-03-06T13:47:39.811Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/92/a8851d936547efe30cc0ce5245feac01f3ec6171f7899bc3f775c72030b3/h5py-3.16.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8975273c2c5921c25700193b408e28d6bdd0111c37468b2d4e25dcec4cd1d84d", size = 5065352, upload-time = "2026-03-06T13:47:41.489Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/ae/f2adc5d0ca9626db3277a3d87516e124cbc5d0eea0bd79bc085702d04f2c/h5py-3.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1677ad48b703f44efc9ea0c3ab284527f81bc4f318386aaaebc5fede6bbae56f", size = 4839173, upload-time = "2026-03-06T13:47:43.586Z" },
+ { url = "https://files.pythonhosted.org/packages/64/0b/e0c8c69da1d8838da023a50cd3080eae5d475691f7636b35eff20bb6ef20/h5py-3.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c4dd4cf5f0a4e36083f73172f6cfc25a5710789269547f132a20975bfe2434c", size = 5076216, upload-time = "2026-03-06T13:47:45.315Z" },
+ { url = "https://files.pythonhosted.org/packages/66/35/d88fd6718832133c885004c61ceeeb24dbd6397ef877dbed6b3a64d6a286/h5py-3.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:bdef06507725b455fccba9c16529121a5e1fbf56aa375f7d9713d9e8ff42454d", size = 3183639, upload-time = "2026-03-06T13:47:47.041Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:719439d14b83f74eeb080e9650a6c7aa6d0d9ea0ca7f804347b05fac6fbf18af", size = 3721663, upload-time = "2026-03-06T13:47:49.599Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3f0a0e136f2e95dd0b67146abb6668af4f1a69c81ef8651a2d316e8e01de447", size = 3087630, upload-time = "2026-03-06T13:47:51.249Z" },
+ { url = "https://files.pythonhosted.org/packages/98/a8/2594cef906aee761601eff842c7dc598bea2b394a3e1c00966832b8eeb7c/h5py-3.16.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a6fbc5367d4046801f9b7db9191b31895f22f1c6df1f9987d667854cac493538", size = 4823472, upload-time = "2026-03-06T13:47:53.085Z" },
+ { url = "https://files.pythonhosted.org/packages/52/a0/c1f604538ff6db22a0690be2dc44ab59178e115f63c917794e529356ab23/h5py-3.16.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fb1720028d99040792bb2fb31facb8da44a6f29df7697e0b84f0d79aff2e9bd3", size = 5027150, upload-time = "2026-03-06T13:47:55.043Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/fd/301739083c2fc4fd89950f9bcfce75d6e14b40b0ca3d40e48a8993d1722c/h5py-3.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:314b6054fe0b1051c2b0cb2df5cbdab15622fb05e80f202e3b6a5eee0d6fe365", size = 4814544, upload-time = "2026-03-06T13:47:56.893Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/42/2193ed41ccee78baba8fcc0cff2c925b8b9ee3793305b23e1f22c20bf4c7/h5py-3.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ffbab2fedd6581f6aa31cf1639ca2cb86e02779de525667892ebf4cc9fd26434", size = 5034013, upload-time = "2026-03-06T13:47:59.01Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/20/e6c0ff62ca2ad1a396a34f4380bafccaaf8791ff8fccf3d995a1fc12d417/h5py-3.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:17d1f1630f92ad74494a9a7392ab25982ce2b469fc62da6074c0ce48366a2999", size = 3191673, upload-time = "2026-03-06T13:48:00.626Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/48/239cbe352ac4f2b8243a8e620fa1a2034635f633731493a7ff1ed71e8658/h5py-3.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:85b9c49dd58dc44cf70af944784e2c2038b6f799665d0dcbbc812a26e0faa859", size = 2673834, upload-time = "2026-03-06T13:48:02.579Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/c0/5d4119dba94093bbafede500d3defd2f5eab7897732998c04b54021e530b/h5py-3.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5313566f4643121a78503a473f0fb1e6dcc541d5115c44f05e037609c565c4d", size = 3685604, upload-time = "2026-03-06T13:48:04.198Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/42/c84efcc1d4caebafb1ecd8be4643f39c85c47a80fe254d92b8b43b1eadaf/h5py-3.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42b012933a83e1a558c673176676a10ce2fd3759976a0fedee1e672d1e04fc9d", size = 3061940, upload-time = "2026-03-06T13:48:05.783Z" },
+ { url = "https://files.pythonhosted.org/packages/89/84/06281c82d4d1686fde1ac6b0f307c50918f1c0151062445ab3b6fa5a921d/h5py-3.16.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ff24039e2573297787c3063df64b60aab0591980ac898329a08b0320e0cf2527", size = 5198852, upload-time = "2026-03-06T13:48:07.482Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/e9/1a19e42cd43cc1365e127db6aae85e1c671da1d9a5d746f4d34a50edb577/h5py-3.16.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:dfc21898ff025f1e8e67e194965a95a8d4754f452f83454538f98f8a3fcb207e", size = 5405250, upload-time = "2026-03-06T13:48:09.628Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/8e/9790c1655eabeb85b92b1ecab7d7e62a2069e53baefd58c98f0909c7a948/h5py-3.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:698dd69291272642ffda44a0ecd6cd3bda5faf9621452d255f57ce91487b9794", size = 5190108, upload-time = "2026-03-06T13:48:11.26Z" },
+ { url = "https://files.pythonhosted.org/packages/51/d7/ab693274f1bd7e8c5f9fdd6c7003a88d59bedeaf8752716a55f532924fbb/h5py-3.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b2c02b0a160faed5fb33f1ba8a264a37ee240b22e049ecc827345d0d9043074", size = 5419216, upload-time = "2026-03-06T13:48:13.322Z" },
+ { url = "https://files.pythonhosted.org/packages/03/c1/0976b235cf29ead553e22f2fb6385a8252b533715e00d0ae52ed7b900582/h5py-3.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:96b422019a1c8975c2d5dadcf61d4ba6f01c31f92bbde6e4649607885fe502d6", size = 3182868, upload-time = "2026-03-06T13:48:15.759Z" },
+ { url = "https://files.pythonhosted.org/packages/14/d9/866b7e570b39070f92d47b0ff1800f0f8239b6f9e45f02363d7112336c1f/h5py-3.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:39c2838fb1e8d97bcf1755e60ad1f3dd76a7b2a475928dc321672752678b96db", size = 2653286, upload-time = "2026-03-06T13:48:17.279Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/9e/6142ebfda0cb6e9349c091eae73c2e01a770b7659255248d637bec54a88b/h5py-3.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:370a845f432c2c9619db8eed334d1e610c6015796122b0e57aa46312c22617d9", size = 3671808, upload-time = "2026-03-06T13:48:19.737Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42108e93326c50c2810025aade9eac9d6827524cdccc7d4b75a546e5ab308edb", size = 3045837, upload-time = "2026-03-06T13:48:21.854Z" },
+ { url = "https://files.pythonhosted.org/packages/da/1e/6172269e18cc5a484e2913ced33339aad588e02ba407fafd00d369e22ef3/h5py-3.16.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:099f2525c9dcf28de366970a5fb34879aab20491589fa89ce2863a84218bb524", size = 5193860, upload-time = "2026-03-06T13:48:24.071Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9300ad32dea9dfc5171f94d5f6948e159ed93e4701280b0f508773b3f582f402", size = 5400417, upload-time = "2026-03-06T13:48:25.728Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/81/5b62d760039eed64348c98129d17061fdfc7839fc9c04eaaad6dee1004e4/h5py-3.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:171038f23bccddfc23f344cadabdfc9917ff554db6a0d417180d2747fe4c75a7", size = 5185214, upload-time = "2026-03-06T13:48:27.436Z" },
+ { url = "https://files.pythonhosted.org/packages/28/c4/532123bcd9080e250696779c927f2cb906c8bf3447df98f5ceb8dcded539/h5py-3.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e420b539fb6023a259a1b14d4c9f6df8cf50d7268f48e161169987a57b737ff", size = 5414598, upload-time = "2026-03-06T13:48:29.49Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/d9/a27997f84341fc0dfcdd1fe4179b6ba6c32a7aa880fdb8c514d4dad6fba3/h5py-3.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:18f2bbcd545e6991412253b98727374c356d67caa920e68dc79eab36bf5fedad", size = 3175509, upload-time = "2026-03-06T13:48:31.131Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/23/bb8647521d4fd770c30a76cfc6cb6a2f5495868904054e92f2394c5a78ff/h5py-3.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:656f00e4d903199a1d58df06b711cf3ca632b874b4207b7dbec86185b5c8c7d4", size = 2647362, upload-time = "2026-03-06T13:48:33.411Z" },
+ { url = "https://files.pythonhosted.org/packages/48/3c/7fcd9b4c9eed82e91fb15568992561019ae7a829d1f696b2c844355d95dd/h5py-3.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9c9d307c0ef862d1cd5714f72ecfafe0a5d7529c44845afa8de9f46e5ba8bd65", size = 3678608, upload-time = "2026-03-06T13:48:35.183Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/b7/9366ed44ced9b7ef357ab48c94205280276db9d7f064aa3012a97227e966/h5py-3.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8c1eff849cdd53cbc73c214c30ebdb6f1bb8b64790b4b4fc36acdb5e43570210", size = 3054773, upload-time = "2026-03-06T13:48:37.139Z" },
+ { url = "https://files.pythonhosted.org/packages/58/a5/4964bc0e91e86340c2bbda83420225b2f770dcf1eb8a39464871ad769436/h5py-3.16.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:e2c04d129f180019e216ee5f9c40b78a418634091c8782e1f723a6ca3658b965", size = 5198886, upload-time = "2026-03-06T13:48:38.879Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/16/d905e7f53e661ce2c24686c38048d8e2b750ffc4350009d41c4e6c6c9826/h5py-3.16.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e4360f15875a532bc7b98196c7592ed4fc92672a57c0a621355961cafb17a6dd", size = 5404883, upload-time = "2026-03-06T13:48:41.324Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/f2/58f34cb74af46d39f4cd18ea20909a8514960c5a3e5b92fd06a28161e0a8/h5py-3.16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3fae9197390c325e62e0a1aa977f2f62d994aa87aab182abbea85479b791197c", size = 5192039, upload-time = "2026-03-06T13:48:43.117Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/ca/934a39c24ce2e2db017268c08da0537c20fa0be7e1549be3e977313fc8f5/h5py-3.16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:43259303989ac8adacc9986695b31e35dba6fd1e297ff9c6a04b7da5542139cc", size = 5421526, upload-time = "2026-03-06T13:48:44.838Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/14/615a450205e1b56d16c6783f5ccd116cde05550faad70ae077c955654a75/h5py-3.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:fa48993a0b799737ba7fd21e2350fa0a60701e58180fae9f2de834bc39a147ab", size = 3183263, upload-time = "2026-03-06T13:48:47.117Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/48/a6faef5ed632cae0c65ac6b214a6614a0b510c3183532c521bdb0055e117/h5py-3.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:1897a771a7f40d05c262fc8f37376ec37873218544b70216872876c627640f63", size = 2663450, upload-time = "2026-03-06T13:48:48.707Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/32/0c8bb8aedb62c772cf7c1d427c7d1951477e8c2835f872bc0a13d1f85f86/h5py-3.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:15922e485844f77c0b9d275396d435db3baa58292a9c2176a386e072e0cf2491", size = 3760693, upload-time = "2026-03-06T13:48:50.453Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/1f/fcc5977d32d6387c5c9a694afee716a5e20658ac08b3ff24fdec79fb05f2/h5py-3.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:df02dd29bd247f98674634dfe41f89fd7c16ba3d7de8695ec958f58404a4e618", size = 3181305, upload-time = "2026-03-06T13:48:52.221Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/a1/af87f64b9f986889884243643621ebbd4ac72472ba8ec8cec891ac8e2ca1/h5py-3.16.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:0f456f556e4e2cebeebd9d66adf8dc321770a42593494a0b6f0af54a7567b242", size = 5074061, upload-time = "2026-03-06T13:48:54.089Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/d0/146f5eaff3dc246a9c7f6e5e4f42bd45cc613bce16693bcd4d1f7c958bf5/h5py-3.16.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:3e6cb3387c756de6a9492d601553dffea3fe11b5f22b443aac708c69f3f55e16", size = 5279216, upload-time = "2026-03-06T13:48:56.75Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/9d/12a13424f1e604fc7df9497b73c0356fb78c2fb206abd7465ce47226e8fd/h5py-3.16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8389e13a1fd745ad2856873e8187fd10268b2d9677877bb667b41aebd771d8b7", size = 5070068, upload-time = "2026-03-06T13:48:59.169Z" },
+ { url = "https://files.pythonhosted.org/packages/41/8c/bbe98f813722b4873818a8db3e15aa3e625b59278566905ac439725e8070/h5py-3.16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:346df559a0f7dcb31cf8e44805319e2ab24b8957c45e7708ce503b2ec79ba725", size = 5300253, upload-time = "2026-03-06T13:49:02.033Z" },
+ { url = "https://files.pythonhosted.org/packages/32/9e/87e6705b4d6890e7cecdf876e2a7d3e40654a2ae37482d79a6f1b87f7b92/h5py-3.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4c6ab014ab704b4feaa719ae783b86522ed0bf1f82184704ed3c9e4e3228796e", size = 3381671, upload-time = "2026-03-06T13:49:04.351Z" },
+ { url = "https://files.pythonhosted.org/packages/96/91/9fad90cfc5f9b2489c7c26ad897157bce82f0e9534a986a221b99760b23b/h5py-3.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:faca8fb4e4319c09d83337adc80b2ca7d5c5a343c2d6f1b6388f32cfecca13c1", size = 2740706, upload-time = "2026-03-06T13:49:06.347Z" },
+]
+
+[[package]]
+name = "heapdict"
+version = "1.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5a/9b/d8963ae7e388270b695f3b556b6dc9adb70ae9618fba09aa1e7b1886652d/HeapDict-1.0.1.tar.gz", hash = "sha256:8495f57b3e03d8e46d5f1b2cc62ca881aca392fd5cc048dc0aa2e1a6d23ecdb6", size = 4274, upload-time = "2019-09-09T18:57:02.154Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b6/9d/cd4777dbcf3bef9d9627e0fe4bc43d2e294b1baeb01d0422399d5e9de319/HeapDict-1.0.1-py3-none-any.whl", hash = "sha256:6065f90933ab1bb7e50db403b90cab653c853690c5992e69294c2de2b253fc92", size = 3917, upload-time = "2019-09-09T18:57:00.821Z" },
+]
+
+[[package]]
+name = "hf-xet"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/74/d8/5c06fc76461418326a7decf8367480c35be11a41fd938633929c60a9ec6b/hf_xet-1.5.0.tar.gz", hash = "sha256:e0fb0a34d9f406eed88233e829a67ec016bec5af19e480eac65a233ea289a948", size = 837196, upload-time = "2026-05-06T06:18:15.583Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/9b/6912c99070915a4f28119e3c5b52a9abd1eec0ad5cb293b8c967a0c6f5a2/hf_xet-1.5.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:7d70fe2ce97b9db73b9c9b9c81fe3693640aec83416a966c446afea54acfae3c", size = 4023383, upload-time = "2026-05-06T06:17:53.947Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/6d/9563cfde59b5d8128a9c7ec972a087f4c782e4f7bac5a85234edfd5d5e49/hf_xet-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:73a0dae8c71de3b0633a45c73f4a4a5ed09e94b43441d82981a781d4f12baa42", size = 3792751, upload-time = "2026-05-06T06:17:51.791Z" },
+ { url = "https://files.pythonhosted.org/packages/07/a5/ed5a0cf35b49a0571af5a8f53416dad1877a718c021c9937c3a53cb45781/hf_xet-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a60290ec57e9b71767fba7c3645ddafdd0759974b540441510c629c6db6db24a", size = 4456058, upload-time = "2026-05-06T06:17:40.735Z" },
+ { url = "https://files.pythonhosted.org/packages/60/fb/3ae8bf2a7a37a4197d0195d7247fd25b3952e15cb8a599e285dfaa6f52b3/hf_xet-1.5.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e5de0f6deada0dada870bb376a11bcd1f08abf3a968a6d118f33e72d1b1eb480", size = 4250783, upload-time = "2026-05-06T06:17:38.412Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/9b/8bae40d4d91525085137196e84eb0ed49cf65b5e96e5c3ecdadd8bd0fac2/hf_xet-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c799d49f1a5544a0ef7591c0ee75e0d6b93d6f56dc7a4979f59f7518d2872216", size = 4445594, upload-time = "2026-05-06T06:18:04.219Z" },
+ { url = "https://files.pythonhosted.org/packages/13/59/c74efbbd4e8728172b2cc72a2bc014d2947a4b7bdced932fbd3f5da1a4e5/hf_xet-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2baea1b0b989e5c152fe81425f7745ddc8901280ba3d97c98d8cdece7b706c60", size = 4663995, upload-time = "2026-05-06T06:18:06.1Z" },
+ { url = "https://files.pythonhosted.org/packages/73/32/8e1e0410af64cda9b139d1dcebdc993a8ff9c8c7c0e2696ae356d75ccc0d/hf_xet-1.5.0-cp313-cp313t-win_amd64.whl", hash = "sha256:526345b3ed45f374f6317349df489167606736c876241ba984105afe7fd4839d", size = 3966608, upload-time = "2026-05-06T06:18:19.74Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/34/a8febc8f4edbea8b3e21b02ebc8b628679b84ba7e45cde624a7736b51500/hf_xet-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:786d28e2eb8315d5035544b9d137b4a842d600c434bb91bf7d0d953cce906ad4", size = 3796946, upload-time = "2026-05-06T06:18:17.568Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/20/8fc8996afe5815fa1a6be8e9e5c02f24500f409d599e905800d498a4e14d/hf_xet-1.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:872d5601e6deea30d15865ede55d29eac6daf5a534ab417b99b6ef6b076dd96c", size = 4023495, upload-time = "2026-05-06T06:18:01.94Z" },
+ { url = "https://files.pythonhosted.org/packages/32/6a/93d84463c00cecb561a7508aa6303e35ee2894294eac14245526924415fe/hf_xet-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9929561f5abf4581c8ea79587881dfef6b8abb2a0d8a51915936fc2a614f4e73", size = 3792731, upload-time = "2026-05-06T06:18:00.021Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/5a/8ec8e0c863b382d00b3c2e2af6ded6b06371be617144a625903a6d562f4b/hf_xet-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7b7bbae318e583a86fb21e5a4a175d6721d628a2874f4bd022d0e660c32a682", size = 4456738, upload-time = "2026-05-06T06:17:49.574Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/ca/f7effa1a67717da2bcc6b6c28f71c6ca648c77acaec4e2c32f40cbe16d85/hf_xet-1.5.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:cf7b2dc6f31a4ea754bb50f74cde482dcf5d366d184076d8530b9872787f3761", size = 4251622, upload-time = "2026-05-06T06:17:47.096Z" },
+ { url = "https://files.pythonhosted.org/packages/65/f2/19247dba3e231cf77dec59ddfb878f00057635ff773d099c9b59d37812c3/hf_xet-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8dbcbab554c9ef158ef2c991545c3e970ddd8cc7acdcd0a78c5a41095dab4ded", size = 4445667, upload-time = "2026-05-06T06:18:11.983Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/64/6f116801a3bcfb6f59f5c251f48cadc47ea54026441c4a385079286a94fa/hf_xet-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5906bf7718d3636dc13402914736abe723492cb730f744834f5f5b67d3a12702", size = 4664619, upload-time = "2026-05-06T06:18:13.771Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/e8/069542d37946ed08669b127e1496fa99e78196d71de8d41eda5e9f1b7a58/hf_xet-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5f3dc2248fc01cc0a00cd392ab497f1ca373fcbc7e3f2da1f452480b384e839e", size = 3966802, upload-time = "2026-05-06T06:18:28.162Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/91/fc6fdec27b14d04e88c386ac0a0129732b53fa23f7c4a78f4b83a039c567/hf_xet-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b285cea1b5bab46b758772716ba8d6854a1a0310fed1c249d678a8b38601e5a0", size = 3797168, upload-time = "2026-05-06T06:18:26.287Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/fb/69ff198a82cae7eb1a69fb84d93b3a3e4816564d76817fe541ddc96874eb/hf_xet-1.5.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dad0dc84e941b8ba3c860659fe1fdc35c049d47cce293f003287757e971a8f56", size = 4030814, upload-time = "2026-05-06T06:17:57.933Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/ff/edcc2b40162bef3ff78e14ab637e5f3b89243d6aee72f5949d3bb6a5af83/hf_xet-1.5.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fd6e5a9b0fdac4ed03ed45ef79254a655b1aaab514a02202617fbf643f5fdf7a", size = 3798444, upload-time = "2026-05-06T06:17:55.79Z" },
+ { url = "https://files.pythonhosted.org/packages/49/4d/103f76b04310e5e57656696cc184690d20c466af0bca3ca88f8c8ea5d4f3/hf_xet-1.5.0-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3531b1823a0e6d77d80f9ed15ca0e00f0d115094f8ac033d5cae88f4564cc949", size = 4465986, upload-time = "2026-05-06T06:17:44.886Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/a2/546f47f464737b3edbab6f8ddb57f2599b93d2cbb66f06abb475ccb48651/hf_xet-1.5.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9a0ee58cd18d5ea799f7ed11290bbccbe56bdd8b1d97ca74b9cc49a3945d7a3b", size = 4259865, upload-time = "2026-05-06T06:17:42.639Z" },
+ { url = "https://files.pythonhosted.org/packages/95/7f/1be593c1f28613be2e196473481cd81bfc5910795e30a34e8f744f6cac4f/hf_xet-1.5.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e60df5a42e9bed8628b6416af2cba4cba57ae9f02de226a06b020d98e1aab18", size = 4459835, upload-time = "2026-05-06T06:18:08.026Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/b2/703569fc881f3284487e68cda7b42179978480da3c438042a6bbbb4a671c/hf_xet-1.5.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4b35549ce62601b84da4ff9b24d970032ace3d4430f52d91bcbb26c901d6c690", size = 4672414, upload-time = "2026-05-06T06:18:09.864Z" },
+ { url = "https://files.pythonhosted.org/packages/af/37/1b6def445c567286b50aa3b33828158e135b1be44938dde59f11382a500c/hf_xet-1.5.0-cp37-abi3-win_amd64.whl", hash = "sha256:2806c7c17b4d23f8d88f7c4814f838c3b6150773fe339c20af23e1cfaf2797e4", size = 3977238, upload-time = "2026-05-06T06:18:23.621Z" },
+ { url = "https://files.pythonhosted.org/packages/62/94/3b66b148778ee100dcfd69c2ca22b57b41b44d3063ceec934f209e9184ce/hf_xet-1.5.0-cp37-abi3-win_arm64.whl", hash = "sha256:b6c9df403040248c76d808d3e047d64db2d923bae593eb244c41e425cf6cd7be", size = 3806916, upload-time = "2026-05-06T06:18:21.7Z" },
+]
+
+[[package]]
+name = "hsluv"
+version = "5.0.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ac/81/af16607fa045724e515579d312577261b436f36f419e7c677e7e88fcc943/hsluv-5.0.4.tar.gz", hash = "sha256:2281f946427a882010042844a38c7bbe9e0d0aaf9d46babe46366ed6f169b72e", size = 543090, upload-time = "2023-09-11T21:46:52.022Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/96/36/5bddefea3d7adf22a64f9aa9701492f8a9fe6948223f5cf2602c22ec9be7/hsluv-5.0.4-py2.py3-none-any.whl", hash = "sha256:0138bd10038e2ee1b13eecae9a7d49d4ec8c320b1d7eb4f860832c792e3e4567", size = 5252, upload-time = "2023-09-11T21:46:50.407Z" },
+]
+
+[[package]]
+name = "httpcore"
+version = "1.0.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
+]
+
+[[package]]
+name = "httpx"
+version = "0.28.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "certifi" },
+ { name = "httpcore" },
+ { name = "idna" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
+]
+
+[[package]]
+name = "huggingface-hub"
+version = "1.15.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "filelock" },
+ { name = "fsspec" },
+ { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "httpx" },
+ { name = "packaging" },
+ { name = "pyyaml" },
+ { name = "tqdm" },
+ { name = "typer" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bb/b6/e22bd20a25299c34b8c5922c1545a6320825b13906eb0f7298edfd034a0b/huggingface_hub-1.15.0.tar.gz", hash = "sha256:28abfdddda3927fd4de6a63cf26ab012498a2c24dae52baf150c5c6edf98a1d5", size = 784100, upload-time = "2026-05-15T11:42:52.149Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6e/11/0b64cc9024329b76d7547c19a67604a61d21d3ba678a69d1b220c29d5112/huggingface_hub-1.15.0-py3-none-any.whl", hash = "sha256:a4a59af04cbc41a3fe3fec429b171ef994ef8c971eda10136746f408dd4e3744", size = 663602, upload-time = "2026-05-15T11:42:50.487Z" },
+]
+
+[[package]]
+name = "identify"
+version = "2.6.19"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" },
+]
+
+[[package]]
+name = "idna"
+version = "3.15"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" },
+]
+
+[[package]]
+name = "imageio"
+version = "2.37.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+ { name = "pillow" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b1/84/93bcd1300216ea50811cee96873b84a1bebf8d0489ffaf7f2a3756bab866/imageio-2.37.3.tar.gz", hash = "sha256:bbb37efbfc4c400fcd534b367b91fcd66d5da639aaa138034431a1c5e0a41451", size = 389673, upload-time = "2026-03-09T11:31:12.573Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl", hash = "sha256:46f5bb8522cd421c0f5ae104d8268f569d856b29eb1a13b92829d1970f32c9f0", size = 317646, upload-time = "2026-03-09T11:31:10.771Z" },
+]
+
+[[package]]
+name = "imageio-ffmpeg"
+version = "0.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/44/bd/c3343c721f2a1b0c9fc71c1aebf1966a3b7f08c2eea8ed5437a2865611d6/imageio_ffmpeg-0.6.0.tar.gz", hash = "sha256:e2556bed8e005564a9f925bb7afa4002d82770d6b08825078b7697ab88ba1755", size = 25210, upload-time = "2025-01-16T21:34:32.747Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/da/58/87ef68ac83f4c7690961bce288fd8e382bc5f1513860fc7f90a9c1c1c6bf/imageio_ffmpeg-0.6.0-py3-none-macosx_10_9_intel.macosx_10_9_x86_64.whl", hash = "sha256:9d2baaf867088508d4a3458e61eeb30e945c4ad8016025545f66c4b5aaef0a61", size = 24932969, upload-time = "2025-01-16T21:34:20.464Z" },
+ { url = "https://files.pythonhosted.org/packages/40/5c/f3d8a657d362cc93b81aab8feda487317da5b5d31c0e1fdfd5e986e55d17/imageio_ffmpeg-0.6.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b1ae3173414b5fc5f538a726c4e48ea97edc0d2cdc11f103afee655c463fa742", size = 21113891, upload-time = "2025-01-16T21:34:00.277Z" },
+ { url = "https://files.pythonhosted.org/packages/33/e7/1925bfbc563c39c1d2e82501d8372734a5c725e53ac3b31b4c2d081e895b/imageio_ffmpeg-0.6.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1d47bebd83d2c5fc770720d211855f208af8a596c82d17730aa51e815cdee6dc", size = 25632706, upload-time = "2025-01-16T21:33:53.475Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/2d/43c8522a2038e9d0e7dbdf3a61195ecc31ca576fb1527a528c877e87d973/imageio_ffmpeg-0.6.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c7e46fcec401dd990405049d2e2f475e2b397779df2519b544b8aab515195282", size = 29498237, upload-time = "2025-01-16T21:34:13.726Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/13/59da54728351883c3c1d9fca1710ab8eee82c7beba585df8f25ca925f08f/imageio_ffmpeg-0.6.0-py3-none-win32.whl", hash = "sha256:196faa79366b4a82f95c0f4053191d2013f4714a715780f0ad2a68ff37483cc2", size = 19652251, upload-time = "2025-01-16T21:34:06.812Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/c6/fa760e12a2483469e2bf5058c5faff664acf66cadb4df2ad6205b016a73d/imageio_ffmpeg-0.6.0-py3-none-win_amd64.whl", hash = "sha256:02fa47c83703c37df6bfe4896aab339013f62bf02c5ebf2dce6da56af04ffc0a", size = 31246824, upload-time = "2025-01-16T21:34:28.6Z" },
+]
+
+[[package]]
+name = "imagesize"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6c/e6/7bf14eeb8f8b7251141944835abd42eb20a658d89084b7e1f3e5fe394090/imagesize-2.0.0.tar.gz", hash = "sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3", size = 1773045, upload-time = "2026-03-03T14:18:29.941Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5f/53/fb7122b71361a0d121b669dcf3d31244ef75badbbb724af388948de543e2/imagesize-2.0.0-py2.py3-none-any.whl", hash = "sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96", size = 9441, upload-time = "2026-03-03T14:18:27.892Z" },
+]
+
+[[package]]
+name = "imgaug"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "imageio" },
+ { name = "matplotlib" },
+ { name = "numpy" },
+ { name = "opencv-python" },
+ { name = "pillow" },
+ { name = "scikit-image", version = "0.25.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scikit-image", version = "0.26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "shapely" },
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/25/7d/820295b8fdaf06dce9688ef2fdeb5a317896d3276db7723e5a94e85e1253/imgaug-0.4.0.tar.gz", hash = "sha256:46bab63ed38f8980630ff721a09ca2281b7dbd4d8c11258818b6ebcc69ea46c7", size = 937254, upload-time = "2020-02-05T20:54:24.835Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/66/b1/af3142c4a85cba6da9f4ebb5ff4e21e2616309552caca5e8acefe9840622/imgaug-0.4.0-py2.py3-none-any.whl", hash = "sha256:ce61e65b4eb7405fc62c1b0a79d2fa92fd47f763aaecb65152d29243592111f9", size = 948018, upload-time = "2020-02-05T20:54:22.293Z" },
+]
+
+[[package]]
+name = "importlib-metadata"
+version = "9.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "zipp" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl", hash = "sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7", size = 27789, upload-time = "2026-03-20T06:42:55.665Z" },
+]
+
+[[package]]
+name = "in-n-out"
+version = "0.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/af/08/07edfac98a38ab0208557524cbdd94a296f565b0558417ccb2c03d14a6ea/in_n_out-0.2.1.tar.gz", hash = "sha256:43cde2b7de981d41a6d70618a2b7bd989481095922a53ead4dc75f2bbd5dffea", size = 26026, upload-time = "2024-04-22T18:56:50.418Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3b/06/711a4d105ad3d01d3ef351a1039bb5cc517a57dbf377d7da9a0808e34c77/in_n_out-0.2.1-py3-none-any.whl", hash = "sha256:343e81edb27cf41ec946134a92964f408465abdf6a065c6c55fe96f53bc3c8b7", size = 19951, upload-time = "2024-04-22T18:56:48.572Z" },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
+]
+
+[[package]]
+name = "ipykernel"
+version = "6.31.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "appnope", marker = "sys_platform == 'darwin' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "comm" },
+ { name = "debugpy" },
+ { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "ipython", version = "9.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "jupyter-client" },
+ { name = "jupyter-core" },
+ { name = "matplotlib-inline" },
+ { name = "nest-asyncio" },
+ { name = "packaging" },
+ { name = "psutil" },
+ { name = "pyzmq" },
+ { name = "tornado" },
+ { name = "traitlets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a5/1d/d5ba6edbfe6fae4c3105bca3a9c889563cc752c7f2de45e333164c7f4846/ipykernel-6.31.0.tar.gz", hash = "sha256:2372ce8bc1ff4f34e58cafed3a0feb2194b91fc7cad0fc72e79e47b45ee9e8f6", size = 167493, upload-time = "2025-10-20T11:42:39.948Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl", hash = "sha256:abe5386f6ced727a70e0eb0cf1da801fa7c5fa6ff82147747d5a0406cd8c94af", size = 117003, upload-time = "2025-10-20T11:42:37.502Z" },
+]
+
+[[package]]
+name = "ipython"
+version = "8.39.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "colorama", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "decorator", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "jedi", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "matplotlib-inline", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pexpect", marker = "(python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "prompt-toolkit", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pygments", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "stack-data", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "traitlets", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c0/56/4cc7fc9e9e3f38fd324f24f8afe0ad8bb5fa41283f37f1aaf9de0612c968/ipython-8.39.0-py3-none-any.whl", hash = "sha256:bb3c51c4fa8148ab1dea07a79584d1c854e234ea44aa1283bcb37bc75054651f", size = 831849, upload-time = "2026-03-27T10:02:07.846Z" },
+]
+
+[[package]]
+name = "ipython"
+version = "9.13.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "colorama", marker = "(python_full_version >= '3.11' and sys_platform == 'win32') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'win32' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "decorator", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "jedi", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "matplotlib-inline", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pexpect", marker = "(python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'emscripten' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'win32' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "prompt-toolkit", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "psutil", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pygments", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "stack-data", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "traitlets", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "python_full_version == '3.11.*' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549, upload-time = "2026-04-24T12:24:55.221Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b9/86/3060e8029b7cc505cce9a0137431dda81d0a3fde93a8f0f50ee0bf37a795/ipython-9.13.0-py3-none-any.whl", hash = "sha256:57f9d4639e20818d328d287c7b549af3d05f12486ea8f2e7f73e52a36ec4d201", size = 627274, upload-time = "2026-04-24T12:24:53.038Z" },
+]
+
+[[package]]
+name = "ipython-pygments-lexers"
+version = "1.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" },
+]
+
+[[package]]
+name = "jedi"
+version = "0.20.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "parso" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/46/b7/a3635f6a2d7cf5b5dd98064fc1d5fbbafcb25477bcea204a3a92145d158b/jedi-0.20.0.tar.gz", hash = "sha256:c3f4ccbd276696f4b19c54618d4fb18f9fc24b0aef02acf704b23f487daa1011", size = 3119416, upload-time = "2026-05-01T23:38:47.814Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9a/93/242e2eab5fe682ffcb8b0084bde703a41d51e17ee0f3a31ff0d9d813620a/jedi-0.20.0-py2.py3-none-any.whl", hash = "sha256:7bdd9c2634f56713299976f4cbd59cb3fa92165cc5e05ea811fb253480728b67", size = 4884812, upload-time = "2026-05-01T23:38:43.919Z" },
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
+]
+
+[[package]]
+name = "joblib"
+version = "1.5.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" },
+]
+
+[[package]]
+name = "jsonschema"
+version = "4.26.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "jsonschema-specifications" },
+ { name = "referencing" },
+ { name = "rpds-py" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" },
+]
+
+[[package]]
+name = "jsonschema-specifications"
+version = "2025.9.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "referencing" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
+]
+
+[[package]]
+name = "jupyter-book"
+version = "1.0.4.post1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "jinja2" },
+ { name = "jsonschema" },
+ { name = "linkify-it-py" },
+ { name = "myst-nb" },
+ { name = "myst-parser" },
+ { name = "pyyaml" },
+ { name = "sphinx" },
+ { name = "sphinx-book-theme", version = "1.1.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "sphinx-book-theme", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "sphinx-comments" },
+ { name = "sphinx-copybutton" },
+ { name = "sphinx-design", version = "0.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "sphinx-design", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "sphinx-external-toc" },
+ { name = "sphinx-jupyterbook-latex" },
+ { name = "sphinx-multitoc-numbering" },
+ { name = "sphinx-thebe" },
+ { name = "sphinx-togglebutton" },
+ { name = "sphinxcontrib-bibtex" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cf/ee/5d10ce5b161764ad44219853f386e98b535cb3879bcb0d7376961a1e3897/jupyter_book-1.0.4.post1.tar.gz", hash = "sha256:2fe92c49ff74840edc0a86bb034eafdd0f645fca6e48266be367ce4d808b9601", size = 67412, upload-time = "2025-02-28T14:55:48.637Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/37/86/d45756beaeb4b9b06125599b429451f8640b5db6f019d606f33c85743fd4/jupyter_book-1.0.4.post1-py3-none-any.whl", hash = "sha256:3a27a6b2581f1894ffe8f347d1a3432f06fc616997547919c42cd41c54db625d", size = 45005, upload-time = "2025-02-28T14:55:46.561Z" },
+]
+
+[[package]]
+name = "jupyter-cache"
+version = "1.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "click" },
+ { name = "importlib-metadata" },
+ { name = "nbclient" },
+ { name = "nbformat" },
+ { name = "pyyaml" },
+ { name = "sqlalchemy" },
+ { name = "tabulate" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bb/f7/3627358075f183956e8c4974603232b03afd4ddc7baf72c2bc9fff522291/jupyter_cache-1.0.1.tar.gz", hash = "sha256:16e808eb19e3fb67a223db906e131ea6e01f03aa27f49a7214ce6a5fec186fb9", size = 32048, upload-time = "2024-11-15T16:03:55.322Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/64/6b/67b87da9d36bff9df7d0efbd1a325fa372a43be7158effaf43ed7b22341d/jupyter_cache-1.0.1-py3-none-any.whl", hash = "sha256:9c3cafd825ba7da8b5830485343091143dff903e4d8c69db9349b728b140abf6", size = 33907, upload-time = "2024-11-15T16:03:54.021Z" },
+]
+
+[[package]]
+name = "jupyter-client"
+version = "8.8.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jupyter-core" },
+ { name = "python-dateutil" },
+ { name = "pyzmq" },
+ { name = "tornado" },
+ { name = "traitlets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020, upload-time = "2026-01-08T13:55:47.938Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371, upload-time = "2026-01-08T13:55:45.562Z" },
+]
+
+[[package]]
+name = "jupyter-core"
+version = "5.9.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "platformdirs" },
+ { name = "traitlets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" },
+]
+
+[[package]]
+name = "keras"
+version = "2.14.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bf/85/d52a86eb5ae700e1f8694157019249eb33350ae9e477cd03ecdb50939d22/keras-2.14.0.tar.gz", hash = "sha256:22788bdbc86d9988794fe9703bb5205141da797c4faeeb59497c58c3d94d34ed", size = 1251354, upload-time = "2023-09-11T17:21:04.379Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fe/58/34d4d8f1aa11120c2d36d7ad27d0526164b1a8ae45990a2fede31d0e59bf/keras-2.14.0-py3-none-any.whl", hash = "sha256:d7429d1d2131cc7eb1f2ea2ec330227c7d9d38dab3dfdf2e78defee4ecc43fcd", size = 1709236, upload-time = "2023-09-11T17:21:02.164Z" },
+]
+
+[[package]]
+name = "keras"
+version = "3.12.2"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "absl-py", marker = "python_full_version < '3.11'" },
+ { name = "h5py", marker = "python_full_version < '3.11'" },
+ { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "namex", marker = "python_full_version < '3.11'" },
+ { name = "numpy", marker = "python_full_version < '3.11'" },
+ { name = "optree", marker = "python_full_version < '3.11'" },
+ { name = "packaging", marker = "python_full_version < '3.11'" },
+ { name = "rich", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/63/73/19e057f7a2a6d641246bacca21e0bbcb2be341afca98ea461a0f2a9ab92d/keras-3.12.2.tar.gz", hash = "sha256:e19c7c7f8f2a81e44d4f203e567731a15a270d8ef351060982b45a1fafdf3fce", size = 1129833, upload-time = "2026-05-07T21:48:18.396Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/55/ba/1f2daa7940d7c5c65efc85370453e9e67ace0d06b4a7346b53f0e7355453/keras-3.12.2-py3-none-any.whl", hash = "sha256:0433310d7d626d5cbbc58e98223b3a77ce7d7d4398bf7e169d4e8bdcf9ce0296", size = 1476474, upload-time = "2026-05-07T21:48:16.057Z" },
+]
+
+[[package]]
+name = "keras"
+version = "3.14.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "absl-py", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11')" },
+ { name = "h5py", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11')" },
+ { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.12.*' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.13' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-tf') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11')" },
+ { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and python_full_version < '3.13' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.11' and python_full_version < '3.13' and extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "namex", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11')" },
+ { name = "numpy", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11')" },
+ { name = "optree", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11')" },
+ { name = "packaging", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11')" },
+ { name = "rich", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.11.*' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/35/e7/97a7664581b73e4f9ff1d3a767a493b6ac5d3e0ed1926bd2b6b2c8bbccd7/keras-3.14.1.tar.gz", hash = "sha256:ef479173102ad29db89b53c232efdc3fb5ad57c28bc27ead59f3e78a1eecd05b", size = 1263647, upload-time = "2026-05-07T21:43:35.112Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/02/03/184267c1d09783dd070f1ddfd0d4beb7503139dfc7bd75b422867cf282fd/keras-3.14.1-py3-none-any.whl", hash = "sha256:ebd2c14d2af3c9de18083604d408483996407fc7d2f9ebd1d565961f96608c29", size = 1628606, upload-time = "2026-05-07T21:43:32.737Z" },
+]
+
+[[package]]
+name = "kiwisolver"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ac/f8/06549565caa026e540b7e7bab5c5a90eb7ca986015f4c48dace243cd24d9/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32cc0a5365239a6ea0c6ed461e8838d053b57e397443c0ca894dcc8e388d4374", size = 122802, upload-time = "2026-03-09T13:12:37.515Z" },
+ { url = "https://files.pythonhosted.org/packages/84/eb/8476a0818850c563ff343ea7c9c05dcdcbd689a38e01aa31657df01f91fa/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc0b66c1eec9021353a4b4483afb12dfd50e3669ffbb9152d6842eb34c7e29fd", size = 66216, upload-time = "2026-03-09T13:12:38.812Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/c4/f9c8a6b4c21aed4198566e45923512986d6cef530e7263b3a5f823546561/kiwisolver-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86e0287879f75621ae85197b0877ed2f8b7aa57b511c7331dce2eb6f4de7d476", size = 63917, upload-time = "2026-03-09T13:12:40.053Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/0e/ba4ae25d03722f64de8b2c13e80d82ab537a06b30fc7065183c6439357e3/kiwisolver-1.5.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:62f59da443c4f4849f73a51a193b1d9d258dcad0c41bc4d1b8fb2bcc04bfeb22", size = 1628776, upload-time = "2026-03-09T13:12:41.976Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/e4/3f43a011bc8a0860d1c96f84d32fa87439d3feedf66e672fef03bf5e8bac/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9190426b7aa26c5229501fa297b8d0653cfd3f5a36f7990c264e157cbf886b3b", size = 1228164, upload-time = "2026-03-09T13:12:44.002Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/34/3a901559a1e0c218404f9a61a93be82d45cb8f44453ba43088644980f033/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c8277104ded0a51e699c8c3aff63ce2c56d4ed5519a5f73e0fd7057f959a2b9e", size = 1246656, upload-time = "2026-03-09T13:12:45.557Z" },
+ { url = "https://files.pythonhosted.org/packages/87/9e/f78c466ea20527822b95ad38f141f2de1dcd7f23fb8716b002b0d91bbe59/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8f9baf6f0a6e7571c45c8863010b45e837c3ee1c2c77fcd6ef423be91b21fedb", size = 1295562, upload-time = "2026-03-09T13:12:47.562Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/66/fd0e4a612e3a286c24e6d6f3a5428d11258ed1909bc530ba3b59807fd980/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cff8e5383db4989311f99e814feeb90c4723eb4edca425b9d5d9c3fefcdd9537", size = 2178473, upload-time = "2026-03-09T13:12:50.254Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/8e/6cac929e0049539e5ee25c1ee937556f379ba5204840d03008363ced662d/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ebae99ed6764f2b5771c522477b311be313e8841d2e0376db2b10922daebbba4", size = 2274035, upload-time = "2026-03-09T13:12:51.785Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/d3/9d0c18f1b52ea8074b792452cf17f1f5a56bd0302a85191f405cfbf9da16/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:d5cd5189fc2b6a538b75ae45433140c4823463918f7b1617c31e68b085c0022c", size = 2443217, upload-time = "2026-03-09T13:12:53.329Z" },
+ { url = "https://files.pythonhosted.org/packages/45/2a/6e19368803a038b2a90857bf4ee9e3c7b667216d045866bf22d3439fd75e/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f42c23db5d1521218a3276bb08666dcb662896a0be7347cba864eca45ff64ede", size = 2249196, upload-time = "2026-03-09T13:12:55.057Z" },
+ { url = "https://files.pythonhosted.org/packages/75/2b/3f641dfcbe72e222175d626bacf2f72c3b34312afec949dd1c50afa400f5/kiwisolver-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:94eff26096eb5395136634622515b234ecb6c9979824c1f5004c6e3c3c85ccd2", size = 73389, upload-time = "2026-03-09T13:12:56.496Z" },
+ { url = "https://files.pythonhosted.org/packages/da/88/299b137b9e0025d8982e03d2d52c123b0a2b159e84b0ef1501ef446339cf/kiwisolver-1.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:dd952e03bfbb096cfe2dd35cd9e00f269969b67536cb4370994afc20ff2d0875", size = 64782, upload-time = "2026-03-09T13:12:57.609Z" },
+ { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798, upload-time = "2026-03-09T13:12:58.963Z" },
+ { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216, upload-time = "2026-03-09T13:13:00.155Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911, upload-time = "2026-03-09T13:13:01.671Z" },
+ { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209, upload-time = "2026-03-09T13:13:03.385Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888, upload-time = "2026-03-09T13:13:05.231Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304, upload-time = "2026-03-09T13:13:06.743Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650, upload-time = "2026-03-09T13:13:08.607Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949, upload-time = "2026-03-09T13:13:10.299Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125, upload-time = "2026-03-09T13:13:11.841Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783, upload-time = "2026-03-09T13:13:14.399Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726, upload-time = "2026-03-09T13:13:16.047Z" },
+ { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738, upload-time = "2026-03-09T13:13:17.897Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718, upload-time = "2026-03-09T13:13:19.421Z" },
+ { url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480, upload-time = "2026-03-09T13:13:20.818Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930, upload-time = "2026-03-09T13:13:21.997Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" },
+ { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" },
+ { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" },
+ { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" },
+ { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" },
+ { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" },
+ { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" },
+ { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" },
+ { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" },
+ { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" },
+ { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" },
+ { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" },
+ { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" },
+ { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" },
+ { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231, upload-time = "2026-03-09T13:14:41.323Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489, upload-time = "2026-03-09T13:14:42.534Z" },
+ { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063, upload-time = "2026-03-09T13:14:44.759Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913, upload-time = "2026-03-09T13:14:46.247Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782, upload-time = "2026-03-09T13:14:48.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815, upload-time = "2026-03-09T13:14:50.721Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925, upload-time = "2026-03-09T13:14:52.304Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322, upload-time = "2026-03-09T13:14:54.637Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857, upload-time = "2026-03-09T13:14:56.471Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376, upload-time = "2026-03-09T13:14:58.072Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549, upload-time = "2026-03-09T13:14:59.668Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680, upload-time = "2026-03-09T13:15:01.364Z" },
+ { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905, upload-time = "2026-03-09T13:15:03.925Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086, upload-time = "2026-03-09T13:15:07.775Z" },
+ { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577, upload-time = "2026-03-09T13:15:09.139Z" },
+ { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794, upload-time = "2026-03-09T13:15:10.525Z" },
+ { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646, upload-time = "2026-03-09T13:15:12.016Z" },
+ { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511, upload-time = "2026-03-09T13:15:13.311Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858, upload-time = "2026-03-09T13:15:15.103Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539, upload-time = "2026-03-09T13:15:16.661Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310, upload-time = "2026-03-09T13:15:18.229Z" },
+ { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244, upload-time = "2026-03-09T13:15:20.444Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154, upload-time = "2026-03-09T13:15:22.039Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377, upload-time = "2026-03-09T13:15:23.741Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288, upload-time = "2026-03-09T13:15:25.789Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158, upload-time = "2026-03-09T13:15:27.63Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260, upload-time = "2026-03-09T13:15:29.421Z" },
+ { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403, upload-time = "2026-03-09T13:15:31.517Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687, upload-time = "2026-03-09T13:15:33.204Z" },
+ { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" },
+ { url = "https://files.pythonhosted.org/packages/17/6f/6fd4f690a40c2582fa34b97d2678f718acf3706b91d270c65ecb455d0a06/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:295d9ffe712caa9f8a3081de8d32fc60191b4b51c76f02f951fd8407253528f4", size = 59606, upload-time = "2026-03-09T13:15:40.81Z" },
+ { url = "https://files.pythonhosted.org/packages/82/a0/2355d5e3b338f13ce63f361abb181e3b6ea5fffdb73f739b3e80efa76159/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:51e8c4084897de9f05898c2c2a39af6318044ae969d46ff7a34ed3f96274adca", size = 57537, upload-time = "2026-03-09T13:15:42.071Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/b9/1d50e610ecadebe205b71d6728fd224ce0e0ca6aba7b9cbe1da049203ac5/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b83af57bdddef03c01a9138034c6ff03181a3028d9a1003b301eb1a55e161a3f", size = 79888, upload-time = "2026-03-09T13:15:43.317Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/ee/b85ffcd75afed0357d74f0e6fc02a4507da441165de1ca4760b9f496390d/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf4679a3d71012a7c2bf360e5cd878fbd5e4fcac0896b56393dec239d81529ed", size = 77584, upload-time = "2026-03-09T13:15:44.605Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/dd/644d0dde6010a8583b4cd66dd41c5f83f5325464d15c4f490b3340ab73b4/kiwisolver-1.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:41024ed50e44ab1a60d3fe0a9d15a4ccc9f5f2b1d814ff283c8d01134d5b81bc", size = 73390, upload-time = "2026-03-09T13:15:45.832Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532, upload-time = "2026-03-09T13:15:47.047Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420, upload-time = "2026-03-09T13:15:48.199Z" },
+ { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892, upload-time = "2026-03-09T13:15:49.694Z" },
+ { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603, upload-time = "2026-03-09T13:15:50.945Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" },
+]
+
+[[package]]
+name = "latexcodec"
+version = "3.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/27/dd/4270b2c5e2ee49316c3859e62293bd2ea8e382339d63ab7bbe9f39c0ec3b/latexcodec-3.0.1.tar.gz", hash = "sha256:e78a6911cd72f9dec35031c6ec23584de6842bfbc4610a9678868d14cdfb0357", size = 31222, upload-time = "2025-06-17T18:47:34.051Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b5/40/23569737873cc9637fd488606347e9dd92b9fa37ba4fcda1f98ee5219a97/latexcodec-3.0.1-py3-none-any.whl", hash = "sha256:a9eb8200bff693f0437a69581f7579eb6bca25c4193515c09900ce76451e452e", size = 18532, upload-time = "2025-06-17T18:47:30.726Z" },
+]
+
+[[package]]
+name = "lazy-loader"
+version = "0.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/49/ac/21a1f8aa3777f5658576777ea76bfb124b702c520bbe90edf4ae9915eafa/lazy_loader-0.5.tar.gz", hash = "sha256:717f9179a0dbed357012ddad50a5ad3d5e4d9a0b8712680d4e687f5e6e6ed9b3", size = 15294, upload-time = "2026-03-06T15:45:09.054Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl", hash = "sha256:ab0ea149e9c554d4ffeeb21105ac60bed7f3b4fd69b1d2360a4add51b170b005", size = 8044, upload-time = "2026-03-06T15:45:07.668Z" },
+]
+
+[[package]]
+name = "libclang"
+version = "18.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6e/5c/ca35e19a4f142adffa27e3d652196b7362fa612243e2b916845d801454fc/libclang-18.1.1.tar.gz", hash = "sha256:a1214966d08d73d971287fc3ead8dfaf82eb07fb197680d8b3859dbbbbf78250", size = 39612, upload-time = "2024-03-17T16:04:37.434Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4b/49/f5e3e7e1419872b69f6f5e82ba56e33955a74bd537d8a1f5f1eff2f3668a/libclang-18.1.1-1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:0b2e143f0fac830156feb56f9231ff8338c20aecfe72b4ffe96f19e5a1dbb69a", size = 25836045, upload-time = "2024-06-30T17:40:31.646Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/e5/fc61bbded91a8830ccce94c5294ecd6e88e496cc85f6704bf350c0634b70/libclang-18.1.1-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:6f14c3f194704e5d09769108f03185fce7acaf1d1ae4bbb2f30a72c2400cb7c5", size = 26502641, upload-time = "2024-03-18T15:52:26.722Z" },
+ { url = "https://files.pythonhosted.org/packages/db/ed/1df62b44db2583375f6a8a5e2ca5432bbdc3edb477942b9b7c848c720055/libclang-18.1.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:83ce5045d101b669ac38e6da8e58765f12da2d3aafb3b9b98d88b286a60964d8", size = 26420207, upload-time = "2024-03-17T15:00:26.63Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/fc/716c1e62e512ef1c160e7984a73a5fc7df45166f2ff3f254e71c58076f7c/libclang-18.1.1-py2.py3-none-manylinux2010_x86_64.whl", hash = "sha256:c533091d8a3bbf7460a00cb6c1a71da93bffe148f172c7d03b1c31fbf8aa2a0b", size = 24515943, upload-time = "2024-03-17T16:03:45.942Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/3d/f0ac1150280d8d20d059608cf2d5ff61b7c3b7f7bcf9c0f425ab92df769a/libclang-18.1.1-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:54dda940a4a0491a9d1532bf071ea3ef26e6dbaf03b5000ed94dd7174e8f9592", size = 23784972, upload-time = "2024-03-17T16:12:47.677Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/2f/d920822c2b1ce9326a4c78c0c2b4aa3fde610c7ee9f631b600acb5376c26/libclang-18.1.1-py2.py3-none-manylinux2014_armv7l.whl", hash = "sha256:cf4a99b05376513717ab5d82a0db832c56ccea4fd61a69dbb7bccf2dfb207dbe", size = 20259606, upload-time = "2024-03-17T16:17:42.437Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/c2/de1db8c6d413597076a4259cea409b83459b2db997c003578affdd32bf66/libclang-18.1.1-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:69f8eb8f65c279e765ffd28aaa7e9e364c776c17618af8bff22a8df58677ff4f", size = 24921494, upload-time = "2024-03-17T16:14:20.132Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/2d/3f480b1e1d31eb3d6de5e3ef641954e5c67430d5ac93b7fa7e07589576c7/libclang-18.1.1-py2.py3-none-win_amd64.whl", hash = "sha256:4dd2d3b82fab35e2bf9ca717d7b63ac990a3519c7e312f19fa8e86dcc712f7fb", size = 26415083, upload-time = "2024-03-17T16:42:21.703Z" },
+ { url = "https://files.pythonhosted.org/packages/71/cf/e01dc4cc79779cd82d77888a88ae2fa424d93b445ad4f6c02bfc18335b70/libclang-18.1.1-py2.py3-none-win_arm64.whl", hash = "sha256:3f0e1f49f04d3cd198985fea0511576b0aee16f9ff0e0f0cad7f9c57ec3c20e8", size = 22361112, upload-time = "2024-03-17T16:42:59.565Z" },
+]
+
+[[package]]
+name = "linkify-it-py"
+version = "2.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "uc-micro-py" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2e/c9/06ea13676ef354f0af6169587ae292d3e2406e212876a413bf9eece4eb23/linkify_it_py-2.1.0.tar.gz", hash = "sha256:43360231720999c10e9328dc3691160e27a718e280673d444c38d7d3aaa3b98b", size = 29158, upload-time = "2026-03-01T07:48:47.683Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b4/de/88b3be5c31b22333b3ca2f6ff1de4e863d8fe45aaea7485f591970ec1d3e/linkify_it_py-2.1.0-py3-none-any.whl", hash = "sha256:0d252c1594ecba2ecedc444053db5d3a9b7ec1b0dd929c8f1d74dce89f86c05e", size = 19878, upload-time = "2026-03-01T07:48:46.098Z" },
+]
+
+[[package]]
+name = "lit"
+version = "18.1.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/47/b4/d7e210971494db7b9a9ac48ff37dfa59a8b14c773f9cf47e6bda58411c0d/lit-18.1.8.tar.gz", hash = "sha256:47c174a186941ae830f04ded76a3444600be67d5e5fb8282c3783fba671c4edb", size = 161127, upload-time = "2024-06-25T14:33:14.489Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/96/06/b36f150fa7c5bcc96a31a4d19a20fddbd1d965b6f02510b57a3bb8d4b930/lit-18.1.8-py3-none-any.whl", hash = "sha256:a873ff7acd76e746368da32eb7355625e2e55a2baaab884c9cc130f2ee0300f7", size = 96365, upload-time = "2024-06-25T14:33:12.101Z" },
+]
+
+[[package]]
+name = "llvmlite"
+version = "0.47.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/01/88/a8952b6d5c21e74cbf158515b779666f692846502623e9e3c39d8e8ba25f/llvmlite-0.47.0.tar.gz", hash = "sha256:62031ce968ec74e95092184d4b0e857e444f8fdff0b8f9213707699570c33ccc", size = 193614, upload-time = "2026-03-31T18:29:53.497Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/f5/a1bde3aa8c43524b0acaf3f72fb3d80a32dd29dbb42d7dc434f84584cdcc/llvmlite-0.47.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41270b0b1310717f717cf6f2a9c68d3c43bd7905c33f003825aebc361d0d1b17", size = 37232772, upload-time = "2026-03-31T18:28:12.198Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/fb/76d88fc05ee1f9c1a6efe39eb493c4a727e5d1690412469017cd23bcb776/llvmlite-0.47.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f9d118bc1dd7623e0e65ca9ac485ec6dd543c3b77bc9928ddc45ebd34e1e30a7", size = 56275179, upload-time = "2026-03-31T18:28:15.725Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/08/29da7f36217abd56a0c389ef9a18bea47960826e691ced1a36c92c6ce93c/llvmlite-0.47.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ea5cfb04a6ab5b18e46be72b41b015975ba5980c4ddb41f1975b83e19031063", size = 55128632, upload-time = "2026-03-31T18:28:19.946Z" },
+ { url = "https://files.pythonhosted.org/packages/df/f8/5e12e9ed447d65f04acf6fcf2d79cded2355640b5131a46cee4c99a5949d/llvmlite-0.47.0-cp310-cp310-win_amd64.whl", hash = "sha256:166b896a2262a2039d5fc52df5ee1659bd1ccd081183df7a2fba1b74702dd5ea", size = 38138402, upload-time = "2026-03-31T18:28:23.327Z" },
+ { url = "https://files.pythonhosted.org/packages/34/0b/b9d1911cfefa61399821dfb37f486d83e0f42630a8d12f7194270c417002/llvmlite-0.47.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74090f0dcfd6f24ebbef3f21f11e38111c4d7e6919b54c4416e1e357c3446b07", size = 37232770, upload-time = "2026-03-31T18:28:26.765Z" },
+ { url = "https://files.pythonhosted.org/packages/46/27/5799b020e4cdfb25a7c951c06a96397c135efcdc21b78d853bbd9c814c7d/llvmlite-0.47.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ca14f02e29134e837982497959a8e2193d6035235de1cb41a9cb2bd6da4eedbb", size = 56275177, upload-time = "2026-03-31T18:28:31.01Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/51/48a53fedf01cb1f3f43ef200be17ebf83c8d9a04018d3783c1a226c342c2/llvmlite-0.47.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12a69d4bb05f402f30477e21eeabe81911e7c251cecb192bed82cd83c9db10d8", size = 55128631, upload-time = "2026-03-31T18:28:36.046Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/50/59227d06bdc96e23322713c381af4e77420949d8cd8a042c79e0043096cc/llvmlite-0.47.0-cp311-cp311-win_amd64.whl", hash = "sha256:c37d6eb7aaabfa83ab9c2ff5b5cdb95a5e6830403937b2c588b7490724e05327", size = 38138400, upload-time = "2026-03-31T18:28:40.076Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/48/4b7fe0e34c169fa2f12532916133e0b219d2823b540733651b34fdac509a/llvmlite-0.47.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:306a265f408c259067257a732c8e159284334018b4083a9e35f67d19792b164f", size = 37232769, upload-time = "2026-03-31T18:28:43.735Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/4b/e3f2cd17822cf772a4a51a0a8080b0032e6d37b2dbe8cfb724eac4e31c52/llvmlite-0.47.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5853bf26160857c0c2573415ff4efe01c4c651e59e2c55c2a088740acfee51cd", size = 56275178, upload-time = "2026-03-31T18:28:48.342Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/55/a3b4a543185305a9bdf3d9759d53646ed96e55e7dfd43f53e7a421b8fbae/llvmlite-0.47.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:003bcf7fa579e14db59c1a1e113f93ab8a06b56a4be31c7f08264d1d4072d077", size = 55128632, upload-time = "2026-03-31T18:28:52.901Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/f5/d281ae0f79378a5a91f308ea9fdb9f9cc068fddd09629edc0725a5a8fde1/llvmlite-0.47.0-cp312-cp312-win_amd64.whl", hash = "sha256:f3079f25bdc24cd9d27c4b2b5e68f5f60c4fdb7e8ad5ee2b9b006007558f9df7", size = 38138692, upload-time = "2026-03-31T18:28:57.147Z" },
+ { url = "https://files.pythonhosted.org/packages/77/6f/4615353e016799f80fa52ccb270a843c413b22361fadda2589b2922fb9b0/llvmlite-0.47.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a3c6a735d4e1041808434f9d440faa3d78d9b4af2ee64d05a66f351883b6ceec", size = 37232771, upload-time = "2026-03-31T18:29:01.324Z" },
+ { url = "https://files.pythonhosted.org/packages/31/b8/69f5565f1a280d032525878a86511eebed0645818492feeb169dfb20ae8e/llvmlite-0.47.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2699a74321189e812d476a43d6d7f652f51811e7b5aad9d9bba842a1c7927acb", size = 56275178, upload-time = "2026-03-31T18:29:05.748Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/da/b32cafcb926fb0ce2aa25553bf32cb8764af31438f40e2481df08884c947/llvmlite-0.47.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c6951e2b29930227963e53ee152441f0e14be92e9d4231852102d986c761e40", size = 55128632, upload-time = "2026-03-31T18:29:11.235Z" },
+ { url = "https://files.pythonhosted.org/packages/46/9f/4898b44e4042c60fafcb1162dfb7014f6f15b1ec19bf29cfea6bf26df90d/llvmlite-0.47.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2e9adf8698d813a9a5efb2d4370caf344dbc1e145019851fee6a6f319ba760e", size = 38138695, upload-time = "2026-03-31T18:29:15.43Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/d4/33c8af00f0bf6f552d74f3a054f648af2c5bc6bece97972f3bfadce4f5ec/llvmlite-0.47.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:de966c626c35c9dff5ae7bf12db25637738d0df83fc370cf793bc94d43d92d14", size = 37232773, upload-time = "2026-03-31T18:29:19.453Z" },
+ { url = "https://files.pythonhosted.org/packages/64/1d/a760e993e0c0ba6db38d46b9f48f6c7dceb8ac838824997fb9e25f97bc04/llvmlite-0.47.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ddbccff2aeaff8670368340a158abefc032fe9b3ccf7d9c496639263d00151aa", size = 56275176, upload-time = "2026-03-31T18:29:24.149Z" },
+ { url = "https://files.pythonhosted.org/packages/84/3b/e679bc3b29127182a7f4aa2d2e9e5bea42adb93fb840484147d59c236299/llvmlite-0.47.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4a7b778a2e144fc64468fb9bf509ac1226c9813a00b4d7afea5d988c4e22fca", size = 55128631, upload-time = "2026-03-31T18:29:29.536Z" },
+ { url = "https://files.pythonhosted.org/packages/be/f7/19e2a09c62809c9e63bbd14ce71fb92c6ff7b7b3045741bb00c781efc3c9/llvmlite-0.47.0-cp314-cp314-win_amd64.whl", hash = "sha256:694e3c2cdc472ed2bd8bd4555ca002eec4310961dd58ef791d508f57b5cc4c94", size = 39153826, upload-time = "2026-03-31T18:29:33.681Z" },
+ { url = "https://files.pythonhosted.org/packages/40/a1/581a8c707b5e80efdbbe1dd94527404d33fe50bceb71f39d5a7e11bd57b7/llvmlite-0.47.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:92ec8a169a20b473c1c54d4695e371bde36489fc1efa3688e11e99beba0abf9c", size = 37232772, upload-time = "2026-03-31T18:29:37.952Z" },
+ { url = "https://files.pythonhosted.org/packages/11/03/16090dd6f74ba2b8b922276047f15962fbeea0a75d5601607edb301ba945/llvmlite-0.47.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa1cbd800edd3b20bc141521f7fd45a6185a5b84109aa6855134e81397ffe72b", size = 56275178, upload-time = "2026-03-31T18:29:42.58Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/cb/0abf1dd4c5286a95ffe0c1d8c67aec06b515894a0dd2ac97f5e27b82ab0b/llvmlite-0.47.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6725179b89f03b17dabe236ff3422cb8291b4c1bf40af152826dfd34e350ae8", size = 55128632, upload-time = "2026-03-31T18:29:46.939Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/79/d3bbab197e86e0ff4f9c07122895b66a3e0d024247fcff7f12c473cb36d9/llvmlite-0.47.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6842cf6f707ec4be3d985a385ad03f72b2d724439e118fcbe99b2929964f0453", size = 39153839, upload-time = "2026-03-31T18:29:51.004Z" },
+]
+
+[[package]]
+name = "locket"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2f/83/97b29fe05cb6ae28d2dbd30b81e2e402a3eed5f460c26e9eaa5895ceacf5/locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", size = 4350, upload-time = "2022-04-20T22:04:44.312Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3", size = 4398, upload-time = "2022-04-20T22:04:42.23Z" },
+]
+
+[[package]]
+name = "magicgui"
+version = "0.10.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "docstring-parser" },
+ { name = "psygnal" },
+ { name = "qtpy" },
+ { name = "superqt", extra = ["iconify"] },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ba/9c/0d918ee0a9b31f16e9b76c1b86b81b5d4b7bc491354c76030b87790f0cab/magicgui-0.10.2.tar.gz", hash = "sha256:ae7a4cbb7ef2028b827b1877cf0b06743d756074fe6ef849391d62448ab7b65d", size = 20946219, upload-time = "2026-04-10T14:45:28.927Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/56/bb/5ba264d3ccc13294e74c48c3ef0c2e96b8b13765562628515654012cc47e/magicgui-0.10.2-py3-none-any.whl", hash = "sha256:0f304d4da1a4309ad15d38cb809ef73269cea73a3195ac944f38d7c56d8e0fd5", size = 128254, upload-time = "2026-04-10T14:45:27.173Z" },
+]
+
+[[package]]
+name = "markdown"
+version = "3.10.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" },
+]
+
+[[package]]
+name = "markdown-it-py"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mdurl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" },
+]
+
+[[package]]
+name = "markupsafe"
+version = "3.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" },
+ { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" },
+ { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" },
+ { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" },
+ { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" },
+ { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" },
+ { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" },
+ { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" },
+ { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" },
+ { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" },
+ { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" },
+ { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" },
+ { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
+ { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
+ { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
+ { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
+ { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
+ { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
+ { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
+ { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
+ { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
+ { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
+ { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
+ { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
+ { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
+ { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
+ { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
+ { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
+ { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
+ { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
+ { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
+ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
+]
+
+[[package]]
+name = "matplotlib"
+version = "3.8.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "cycler" },
+ { name = "fonttools" },
+ { name = "kiwisolver" },
+ { name = "numpy" },
+ { name = "packaging" },
+ { name = "pillow" },
+ { name = "pyparsing" },
+ { name = "python-dateutil" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/38/4f/8487737a74d8be4ab5fbe6019b0fae305c1604cf7209500969b879b5f462/matplotlib-3.8.4.tar.gz", hash = "sha256:8aac397d5e9ec158960e31c381c5ffc52ddd52bd9a47717e2a694038167dffea", size = 35934425, upload-time = "2024-04-04T01:47:18.594Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/67/c0/1f88491656d21a2fecd90fbfae999b2f87bc44d439ef301ec8e0e4a937a0/matplotlib-3.8.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:abc9d838f93583650c35eca41cfcec65b2e7cb50fd486da6f0c49b5e1ed23014", size = 7603557, upload-time = "2024-04-04T01:47:46.363Z" },
+ { url = "https://files.pythonhosted.org/packages/86/9c/aa059a4fb8154d5875a5ddd33f8d0a42d77c0225fe4325e9b9358f39b0bf/matplotlib-3.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f65c9f002d281a6e904976007b2d46a1ee2bcea3a68a8c12dda24709ddc9106", size = 7497421, upload-time = "2024-04-04T01:47:54.074Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/67/ded5217d42de1532193cd87db925c67997d23c68b20c3eaa9e4c6a0adb67/matplotlib-3.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce1edd9f5383b504dbc26eeea404ed0a00656c526638129028b758fd43fc5f10", size = 11377985, upload-time = "2024-04-04T01:48:04.955Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/07/061f97211f942101070a46fecd813a6b1bd83590ed7b07c473cabd707fe7/matplotlib-3.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd79298550cba13a43c340581a3ec9c707bd895a6a061a78fa2524660482fc0", size = 11608003, upload-time = "2024-04-04T01:48:16.25Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/d3/5d0bb1d905e219543fdfd7ab04e9d641a766367c83a5ffbcea60d2b2cf2d/matplotlib-3.8.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:90df07db7b599fe7035d2f74ab7e438b656528c68ba6bb59b7dc46af39ee48ef", size = 9535368, upload-time = "2024-04-04T01:48:26.265Z" },
+ { url = "https://files.pythonhosted.org/packages/62/5a/a5108ae3db37f35f8a2be8a57d62da327af239214c9661464ce09ee32d7d/matplotlib-3.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:ac24233e8f2939ac4fd2919eed1e9c0871eac8057666070e94cbf0b33dd9c338", size = 7656037, upload-time = "2024-04-04T01:48:34.761Z" },
+ { url = "https://files.pythonhosted.org/packages/36/11/62250ea25780d4b59c2c6044ec161235c47cc05a18d0ec0a05657de75b7d/matplotlib-3.8.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:72f9322712e4562e792b2961971891b9fbbb0e525011e09ea0d1f416c4645661", size = 7606117, upload-time = "2024-04-04T01:48:42.545Z" },
+ { url = "https://files.pythonhosted.org/packages/14/60/12d4f27b859a74359306662da69c2d08826a2b05cfe7f96e66b490f41573/matplotlib-3.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:232ce322bfd020a434caaffbd9a95333f7c2491e59cfc014041d95e38ab90d1c", size = 7500108, upload-time = "2024-04-04T01:48:50.21Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/ba/9e4f7f34dccf2d2768504410410db8d551c940457a2bec658dc4fa3b5aa2/matplotlib-3.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6addbd5b488aedb7f9bc19f91cd87ea476206f45d7116fcfe3d31416702a82fa", size = 11382998, upload-time = "2024-04-04T01:49:01.346Z" },
+ { url = "https://files.pythonhosted.org/packages/80/3b/e363612ac1a514abfb5505aa209dd5b724b3232a6de98710d7759559706a/matplotlib-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4ccdc64e3039fc303defd119658148f2349239871db72cd74e2eeaa9b80b71", size = 11613309, upload-time = "2024-04-04T01:49:13.428Z" },
+ { url = "https://files.pythonhosted.org/packages/32/4c/63164901acadb3ada55c5e0fd6b7f29c9033d7e131302884cd735611b77a/matplotlib-3.8.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b7a2a253d3b36d90c8993b4620183b55665a429da8357a4f621e78cd48b2b30b", size = 9546019, upload-time = "2024-04-04T01:49:23.752Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/d5/6227732ecab9165586966ccb54301e3164f61b470c954c4cf6940654fbe1/matplotlib-3.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:8080d5081a86e690d7688ffa542532e87f224c38a6ed71f8fbed34dd1d9fedae", size = 7658174, upload-time = "2024-04-04T01:49:32.066Z" },
+ { url = "https://files.pythonhosted.org/packages/91/eb/65f3bd78ce757dadd455c220273349428384b162485cd8aa380b61a867ed/matplotlib-3.8.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6485ac1f2e84676cff22e693eaa4fbed50ef5dc37173ce1f023daef4687df616", size = 7604083, upload-time = "2024-04-04T01:49:40.442Z" },
+ { url = "https://files.pythonhosted.org/packages/da/2b/2bb6073ca8d336da07ace7d98bf7bb9da8233f55876bb3db6a5ee924f3e9/matplotlib-3.8.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c89ee9314ef48c72fe92ce55c4e95f2f39d70208f9f1d9db4e64079420d8d732", size = 7496013, upload-time = "2024-04-04T01:49:48.174Z" },
+ { url = "https://files.pythonhosted.org/packages/61/cd/976d3a9c10328da1d2fe183f7c92c45f1e125536226a6eb3a820c4753cd1/matplotlib-3.8.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50bac6e4d77e4262c4340d7a985c30912054745ec99756ce213bfbc3cb3808eb", size = 11376749, upload-time = "2024-04-04T01:49:58.572Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/ba/412149958e951876096198609b958b90a8a2c9bc07a96eeeaa9e2c480f30/matplotlib-3.8.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f51c4c869d4b60d769f7b4406eec39596648d9d70246428745a681c327a8ad30", size = 11600837, upload-time = "2024-04-04T01:50:09.279Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/4f/e5b56ca109d8ab6bae37f519f15b891fc18809ddb8bc1aa26e0bfca83e25/matplotlib-3.8.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b12ba985837e4899b762b81f5b2845bd1a28f4fdd1a126d9ace64e9c4eb2fb25", size = 9538883, upload-time = "2024-04-04T01:50:19.268Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/ca/e7bd1876a341ed8c456095962a582696cac1691cb6e55bd5ead15a755c5d/matplotlib-3.8.4-cp312-cp312-win_amd64.whl", hash = "sha256:7a6769f58ce51791b4cb8b4d7642489df347697cd3e23d88266aaaee93b41d9a", size = 7659712, upload-time = "2024-04-04T01:50:26.938Z" },
+]
+
+[[package]]
+name = "matplotlib-inline"
+version = "0.2.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "traitlets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bd/c0/9f7c9a46090390368a4d7bcb76bb87a4a36c421e4c0792cdb53486ffac7a/matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79", size = 8150, upload-time = "2026-05-08T17:33:33.49Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6", size = 9534, upload-time = "2026-05-08T17:33:32.055Z" },
+]
+
+[[package]]
+name = "mdit-py-plugins"
+version = "0.6.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a5/69/6da5581c6a7fede7dc261bf4e67d6adca4196f176b43288b55b3db395b6e/mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d", size = 66663, upload-time = "2026-05-13T09:03:37.76Z" },
+]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
+]
+
+[[package]]
+name = "ml-dtypes"
+version = "0.2.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fa/47/09ca9556bf99cfe7ddf129a3423642bd482a27a717bf115090493fa42429/ml_dtypes-0.2.0.tar.gz", hash = "sha256:6488eb642acaaf08d8020f6de0a38acee7ac324c1e6e92ee0c0fea42422cb797", size = 698948, upload-time = "2023-06-06T15:14:43.679Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/66/9f/3c133f83f3e5a7959345585e9ac715ef8bf6e8987551f240032e1b0d3ce6/ml_dtypes-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df6a76e1c8adf484feb138ed323f9f40a7b6c21788f120f7c78bec20ac37ee81", size = 1154492, upload-time = "2023-06-06T15:14:11.966Z" },
+ { url = "https://files.pythonhosted.org/packages/19/05/7a6480a69f8555a047a56ae6af9490bcdc5e432658208f3404d8e8442d02/ml_dtypes-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc29a0524ef5e23a7fbb8d881bdecabeb3fc1d19d9db61785d077a86cb94fab2", size = 1012633, upload-time = "2023-06-06T15:14:14.055Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/1d/d5cf76e5e40f69dbd273036e3172ae4a614577cb141673427b80cac948df/ml_dtypes-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08c391c2794f2aad358e6f4c70785a9a7b1df980ef4c232b3ccd4f6fe39f719", size = 1017764, upload-time = "2023-06-06T15:14:15.632Z" },
+ { url = "https://files.pythonhosted.org/packages/55/51/c430b4f5f4a6df00aa41c1ee195e179489565e61cfad559506ca7442ce67/ml_dtypes-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:75015818a7fccf99a5e8ed18720cb430f3e71a8838388840f4cdf225c036c983", size = 938593, upload-time = "2023-06-06T15:14:17.473Z" },
+ { url = "https://files.pythonhosted.org/packages/15/da/43bee505963da0c730ee50e951c604bfdb90d4cccc9c0044c946b10e68a7/ml_dtypes-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e70047ec2c83eaee01afdfdabee2c5b0c133804d90d0f7db4dd903360fcc537c", size = 1154491, upload-time = "2023-06-06T15:14:19.199Z" },
+ { url = "https://files.pythonhosted.org/packages/49/a0/01570d615d16f504be091b914a6ae9a29e80d09b572ebebc32ecb1dfb22d/ml_dtypes-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36d28b8861a8931695e5a31176cad5ae85f6504906650dea5598fbec06c94606", size = 1012631, upload-time = "2023-06-06T15:14:21.51Z" },
+ { url = "https://files.pythonhosted.org/packages/87/91/d57c2d22e4801edeb7f3e7939214c0ea8a28c6e16f85208c2df2145e0213/ml_dtypes-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e85ba8e24cf48d456e564688e981cf379d4c8e644db0a2f719b78de281bac2ca", size = 1017764, upload-time = "2023-06-06T15:14:24.116Z" },
+ { url = "https://files.pythonhosted.org/packages/08/89/c727fde1a3d12586e0b8c01abf53754707d76beaa9987640e70807d4545f/ml_dtypes-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:832a019a1b6db5c4422032ca9940a990fa104eee420f643713241b3a518977fa", size = 938744, upload-time = "2023-06-06T15:14:25.77Z" },
+]
+
+[[package]]
+name = "ml-dtypes"
+version = "0.4.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "(python_full_version == '3.12.*' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.13' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-tf') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version == '3.12.*' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fd/15/76f86faa0902836cc133939732f7611ace68cf54148487a99c539c272dc8/ml_dtypes-0.4.1.tar.gz", hash = "sha256:fad5f2de464fd09127e49b7fd1252b9006fb43d2edc1ff112d390c324af5ca7a", size = 692594, upload-time = "2024-09-13T19:07:11.624Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/56/9e/76b84f77c7afee3b116dc8407903a2d5004ba3059a8f3dcdcfa6ebf33fff/ml_dtypes-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1fe8b5b5e70cd67211db94b05cfd58dace592f24489b038dc6f9fe347d2e07d5", size = 397975, upload-time = "2024-09-13T19:06:44.265Z" },
+ { url = "https://files.pythonhosted.org/packages/03/7b/32650e1b2a2713a5923a0af2a8503d0d4a8fc99d1e1e0a1c40e996634460/ml_dtypes-0.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c09a6d11d8475c2a9fd2bc0695628aec105f97cab3b3a3fb7c9660348ff7d24", size = 2182570, upload-time = "2024-09-13T19:06:46.189Z" },
+ { url = "https://files.pythonhosted.org/packages/16/86/a9f7569e7e4f5395f927de38a13b92efa73f809285d04f2923b291783dd2/ml_dtypes-0.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5e8f75fa371020dd30f9196e7d73babae2abd51cf59bdd56cb4f8de7e13354", size = 2160365, upload-time = "2024-09-13T19:06:48.198Z" },
+ { url = "https://files.pythonhosted.org/packages/04/1b/9a3afb437702503514f3934ec8d7904270edf013d28074f3e700e5dfbb0f/ml_dtypes-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:15fdd922fea57e493844e5abb930b9c0bd0af217d9edd3724479fc3d7ce70e3f", size = 126633, upload-time = "2024-09-13T19:06:50.656Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/76/9835c8609c29f2214359e88f29255fc4aad4ea0f613fb48aa8815ceda1b6/ml_dtypes-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2d55b588116a7085d6e074cf0cdb1d6fa3875c059dddc4d2c94a4cc81c23e975", size = 397973, upload-time = "2024-09-13T19:06:51.748Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/99/e68c56fac5de973007a10254b6e17a0362393724f40f66d5e4033f4962c2/ml_dtypes-0.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e138a9b7a48079c900ea969341a5754019a1ad17ae27ee330f7ebf43f23877f9", size = 2185134, upload-time = "2024-09-13T19:06:53.197Z" },
+ { url = "https://files.pythonhosted.org/packages/28/bc/6a2344338ea7b61cd7b46fb24ec459360a5a0903b57c55b156c1e46c644a/ml_dtypes-0.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74c6cfb5cf78535b103fde9ea3ded8e9f16f75bc07789054edc7776abfb3d752", size = 2163661, upload-time = "2024-09-13T19:06:54.519Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/d3/ddfd9878b223b3aa9a930c6100a99afca5cfab7ea703662e00323acb7568/ml_dtypes-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:274cc7193dd73b35fb26bef6c5d40ae3eb258359ee71cd82f6e96a8c948bdaa6", size = 126727, upload-time = "2024-09-13T19:06:55.897Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/1a/99e924f12e4b62139fbac87419698c65f956d58de0dbfa7c028fa5b096aa/ml_dtypes-0.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:827d3ca2097085cf0355f8fdf092b888890bb1b1455f52801a2d7756f056f54b", size = 405077, upload-time = "2024-09-13T19:06:57.538Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/8c/7b610bd500617854c8cc6ed7c8cfb9d48d6a5c21a1437a36a4b9bc8a3598/ml_dtypes-0.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:772426b08a6172a891274d581ce58ea2789cc8abc1c002a27223f314aaf894e7", size = 2181554, upload-time = "2024-09-13T19:06:59.196Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/c6/f89620cecc0581dc1839e218c4315171312e46c62a62da6ace204bda91c0/ml_dtypes-0.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:126e7d679b8676d1a958f2651949fbfa182832c3cd08020d8facd94e4114f3e9", size = 2160488, upload-time = "2024-09-13T19:07:03.131Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/11/a742d3c31b2cc8557a48efdde53427fd5f9caa2fa3c9c27d826e78a66f51/ml_dtypes-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:df0fb650d5c582a9e72bb5bd96cfebb2cdb889d89daff621c8fbc60295eba66c", size = 127462, upload-time = "2024-09-13T19:07:04.916Z" },
+]
+
+[[package]]
+name = "ml-dtypes"
+version = "0.5.4"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "(python_full_version < '3.13' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.13' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314, upload-time = "2025-11-17T22:32:31.031Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fe/3a/c5b855752a70267ff729c349e650263adb3c206c29d28cc8ea7ace30a1d5/ml_dtypes-0.5.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b95e97e470fe60ed493fd9ae3911d8da4ebac16bd21f87ffa2b7c588bf22ea2c", size = 679735, upload-time = "2025-11-17T22:31:31.367Z" },
+ { url = "https://files.pythonhosted.org/packages/41/79/7433f30ee04bd4faa303844048f55e1eb939131c8e5195a00a96a0939b64/ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4b801ebe0b477be666696bda493a9be8356f1f0057a57f1e35cd26928823e5a", size = 5051883, upload-time = "2025-11-17T22:31:33.658Z" },
+ { url = "https://files.pythonhosted.org/packages/10/b1/8938e8830b0ee2e167fc75a094dea766a1152bde46752cd9bfc57ee78a82/ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:388d399a2152dd79a3f0456a952284a99ee5c93d3e2f8dfe25977511e0515270", size = 5030369, upload-time = "2025-11-17T22:31:35.595Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/a3/51886727bd16e2f47587997b802dd56398692ce8c6c03c2e5bb32ecafe26/ml_dtypes-0.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:4ff7f3e7ca2972e7de850e7b8fcbb355304271e2933dd90814c1cb847414d6e2", size = 210738, upload-time = "2025-11-17T22:31:37.43Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/5e/712092cfe7e5eb667b8ad9ca7c54442f21ed7ca8979745f1000e24cf8737/ml_dtypes-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6c7ecb74c4bd71db68a6bea1edf8da8c34f3d9fe218f038814fd1d310ac76c90", size = 679734, upload-time = "2025-11-17T22:31:39.223Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/cf/912146dfd4b5c0eea956836c01dcd2fce6c9c844b2691f5152aca196ce4f/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc11d7e8c44a65115d05e2ab9989d1e045125d7be8e05a071a48bc76eb6d6040", size = 5056165, upload-time = "2025-11-17T22:31:41.071Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/80/19189ea605017473660e43762dc853d2797984b3c7bf30ce656099add30c/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b9a53598f21e453ea2fbda8aa783c20faff8e1eeb0d7ab899309a0053f1483", size = 5034975, upload-time = "2025-11-17T22:31:42.758Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/24/70bd59276883fdd91600ca20040b41efd4902a923283c4d6edcb1de128d2/ml_dtypes-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:7c23c54a00ae43edf48d44066a7ec31e05fdc2eee0be2b8b50dd1903a1db94bb", size = 210742, upload-time = "2025-11-17T22:31:44.068Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/c9/64230ef14e40aa3f1cb254ef623bf812735e6bec7772848d19131111ac0d/ml_dtypes-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:557a31a390b7e9439056644cb80ed0735a6e3e3bb09d67fd5687e4b04238d1de", size = 160709, upload-time = "2025-11-17T22:31:46.557Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/b8/3c70881695e056f8a32f8b941126cf78775d9a4d7feba8abcb52cb7b04f2/ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a174837a64f5b16cab6f368171a1a03a27936b31699d167684073ff1c4237dac", size = 676927, upload-time = "2025-11-17T22:31:48.182Z" },
+ { url = "https://files.pythonhosted.org/packages/54/0f/428ef6881782e5ebb7eca459689448c0394fa0a80bea3aa9262cba5445ea/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7f7c643e8b1320fd958bf098aa7ecf70623a42ec5154e3be3be673f4c34d900", size = 5028464, upload-time = "2025-11-17T22:31:50.135Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/cb/28ce52eb94390dda42599c98ea0204d74799e4d8047a0eb559b6fd648056/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ad459e99793fa6e13bd5b7e6792c8f9190b4e5a1b45c63aba14a4d0a7f1d5ff", size = 5009002, upload-time = "2025-11-17T22:31:52.001Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/f0/0cfadd537c5470378b1b32bd859cf2824972174b51b873c9d95cfd7475a5/ml_dtypes-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:c1a953995cccb9e25a4ae19e34316671e4e2edaebe4cf538229b1fc7109087b7", size = 212222, upload-time = "2025-11-17T22:31:53.742Z" },
+ { url = "https://files.pythonhosted.org/packages/16/2e/9acc86985bfad8f2c2d30291b27cd2bb4c74cea08695bd540906ed744249/ml_dtypes-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:9bad06436568442575beb2d03389aa7456c690a5b05892c471215bfd8cf39460", size = 160793, upload-time = "2025-11-17T22:31:55.358Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/a1/4008f14bbc616cfb1ac5b39ea485f9c63031c4634ab3f4cf72e7541f816a/ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48", size = 676888, upload-time = "2025-11-17T22:31:56.907Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/b7/dff378afc2b0d5a7d6cd9d3209b60474d9819d1189d347521e1688a60a53/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b", size = 5036993, upload-time = "2025-11-17T22:31:58.497Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/33/40cd74219417e78b97c47802037cf2d87b91973e18bb968a7da48a96ea44/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d", size = 5010956, upload-time = "2025-11-17T22:31:59.931Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/8b/200088c6859d8221454825959df35b5244fa9bdf263fd0249ac5fb75e281/ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328", size = 212224, upload-time = "2025-11-17T22:32:01.349Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/75/dfc3775cb36367816e678f69a7843f6f03bd4e2bcd79941e01ea960a068e/ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175", size = 160798, upload-time = "2025-11-17T22:32:02.864Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/74/e9ddb35fd1dd43b1106c20ced3f53c2e8e7fc7598c15638e9f80677f81d4/ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6", size = 702083, upload-time = "2025-11-17T22:32:04.08Z" },
+ { url = "https://files.pythonhosted.org/packages/74/f5/667060b0aed1aa63166b22897fdf16dca9eb704e6b4bbf86848d5a181aa7/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d", size = 5354111, upload-time = "2025-11-17T22:32:05.546Z" },
+ { url = "https://files.pythonhosted.org/packages/40/49/0f8c498a28c0efa5f5c95a9e374c83ec1385ca41d0e85e7cf40e5d519a21/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298", size = 5366453, upload-time = "2025-11-17T22:32:07.115Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/27/12607423d0a9c6bbbcc780ad19f1f6baa2b68b18ce4bddcdc122c4c68dc9/ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6", size = 225612, upload-time = "2025-11-17T22:32:08.615Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/80/5a5929e92c72936d5b19872c5fb8fc09327c1da67b3b68c6a13139e77e20/ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1", size = 164145, upload-time = "2025-11-17T22:32:09.782Z" },
+ { url = "https://files.pythonhosted.org/packages/72/4e/1339dc6e2557a344f5ba5590872e80346f76f6cb2ac3dd16e4666e88818c/ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22", size = 673781, upload-time = "2025-11-17T22:32:11.364Z" },
+ { url = "https://files.pythonhosted.org/packages/04/f9/067b84365c7e83bda15bba2b06c6ca250ce27b20630b1128c435fb7a09aa/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465", size = 5036145, upload-time = "2025-11-17T22:32:12.783Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/bb/82c7dcf38070b46172a517e2334e665c5bf374a262f99a283ea454bece7c/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f", size = 5010230, upload-time = "2025-11-17T22:32:14.38Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/93/2bfed22d2498c468f6bcd0d9f56b033eaa19f33320389314c19ef6766413/ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56", size = 221032, upload-time = "2025-11-17T22:32:15.763Z" },
+ { url = "https://files.pythonhosted.org/packages/76/a3/9c912fe6ea747bb10fe2f8f54d027eb265db05dfb0c6335e3e063e74e6e8/ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049", size = 163353, upload-time = "2025-11-17T22:32:16.932Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/02/48aa7d84cc30ab4ee37624a2fd98c56c02326785750cd212bc0826c2f15b/ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9", size = 702085, upload-time = "2025-11-17T22:32:18.175Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/e7/85cb99fe80a7a5513253ec7faa88a65306be071163485e9a626fce1b6e84/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7", size = 5355358, upload-time = "2025-11-17T22:32:19.7Z" },
+ { url = "https://files.pythonhosted.org/packages/79/2b/a826ba18d2179a56e144aef69e57fb2ab7c464ef0b2111940ee8a3a223a2/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf", size = 5366332, upload-time = "2025-11-17T22:32:21.193Z" },
+ { url = "https://files.pythonhosted.org/packages/84/44/f4d18446eacb20ea11e82f133ea8f86e2bf2891785b67d9da8d0ab0ef525/ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1", size = 236612, upload-time = "2025-11-17T22:32:22.579Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/3f/3d42e9a78fe5edf792a83c074b13b9b770092a4fbf3462872f4303135f09/ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d", size = 168825, upload-time = "2025-11-17T22:32:23.766Z" },
+]
+
+[[package]]
+name = "mpmath"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
+]
+
+[[package]]
+name = "msgpack"
+version = "1.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4d/f2/bfb55a6236ed8725a96b0aa3acbd0ec17588e6a2c3b62a93eb513ed8783f/msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e", size = 173581, upload-time = "2025-10-08T09:15:56.596Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f5/a2/3b68a9e769db68668b25c6108444a35f9bd163bb848c0650d516761a59c0/msgpack-1.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0051fffef5a37ca2cd16978ae4f0aef92f164df86823871b5162812bebecd8e2", size = 81318, upload-time = "2025-10-08T09:14:38.722Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/e1/2b720cc341325c00be44e1ed59e7cfeae2678329fbf5aa68f5bda57fe728/msgpack-1.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a605409040f2da88676e9c9e5853b3449ba8011973616189ea5ee55ddbc5bc87", size = 83786, upload-time = "2025-10-08T09:14:40.082Z" },
+ { url = "https://files.pythonhosted.org/packages/71/e5/c2241de64bfceac456b140737812a2ab310b10538a7b34a1d393b748e095/msgpack-1.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b696e83c9f1532b4af884045ba7f3aa741a63b2bc22617293a2c6a7c645f251", size = 398240, upload-time = "2025-10-08T09:14:41.151Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/09/2a06956383c0fdebaef5aa9246e2356776f12ea6f2a44bd1368abf0e46c4/msgpack-1.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:365c0bbe981a27d8932da71af63ef86acc59ed5c01ad929e09a0b88c6294e28a", size = 406070, upload-time = "2025-10-08T09:14:42.821Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/74/2957703f0e1ef20637d6aead4fbb314330c26f39aa046b348c7edcf6ca6b/msgpack-1.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:41d1a5d875680166d3ac5c38573896453bbbea7092936d2e107214daf43b1d4f", size = 393403, upload-time = "2025-10-08T09:14:44.38Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/09/3bfc12aa90f77b37322fc33e7a8a7c29ba7c8edeadfa27664451801b9860/msgpack-1.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354e81bcdebaab427c3df4281187edc765d5d76bfb3a7c125af9da7a27e8458f", size = 398947, upload-time = "2025-10-08T09:14:45.56Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/4f/05fcebd3b4977cb3d840f7ef6b77c51f8582086de5e642f3fefee35c86fc/msgpack-1.1.2-cp310-cp310-win32.whl", hash = "sha256:e64c8d2f5e5d5fda7b842f55dec6133260ea8f53c4257d64494c534f306bf7a9", size = 64769, upload-time = "2025-10-08T09:14:47.334Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/3e/b4547e3a34210956382eed1c85935fff7e0f9b98be3106b3745d7dec9c5e/msgpack-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:db6192777d943bdaaafb6ba66d44bf65aa0e9c5616fa1d2da9bb08828c6b39aa", size = 71293, upload-time = "2025-10-08T09:14:48.665Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/97/560d11202bcd537abca693fd85d81cebe2107ba17301de42b01ac1677b69/msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c", size = 82271, upload-time = "2025-10-08T09:14:49.967Z" },
+ { url = "https://files.pythonhosted.org/packages/83/04/28a41024ccbd67467380b6fb440ae916c1e4f25e2cd4c63abe6835ac566e/msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0", size = 84914, upload-time = "2025-10-08T09:14:50.958Z" },
+ { url = "https://files.pythonhosted.org/packages/71/46/b817349db6886d79e57a966346cf0902a426375aadc1e8e7a86a75e22f19/msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296", size = 416962, upload-time = "2025-10-08T09:14:51.997Z" },
+ { url = "https://files.pythonhosted.org/packages/da/e0/6cc2e852837cd6086fe7d8406af4294e66827a60a4cf60b86575a4a65ca8/msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef", size = 426183, upload-time = "2025-10-08T09:14:53.477Z" },
+ { url = "https://files.pythonhosted.org/packages/25/98/6a19f030b3d2ea906696cedd1eb251708e50a5891d0978b012cb6107234c/msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c", size = 411454, upload-time = "2025-10-08T09:14:54.648Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/cd/9098fcb6adb32187a70b7ecaabf6339da50553351558f37600e53a4a2a23/msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e", size = 422341, upload-time = "2025-10-08T09:14:56.328Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/ae/270cecbcf36c1dc85ec086b33a51a4d7d08fc4f404bdbc15b582255d05ff/msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e", size = 64747, upload-time = "2025-10-08T09:14:57.882Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/79/309d0e637f6f37e83c711f547308b91af02b72d2326ddd860b966080ef29/msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68", size = 71633, upload-time = "2025-10-08T09:14:59.177Z" },
+ { url = "https://files.pythonhosted.org/packages/73/4d/7c4e2b3d9b1106cd0aa6cb56cc57c6267f59fa8bfab7d91df5adc802c847/msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406", size = 64755, upload-time = "2025-10-08T09:15:00.48Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/bd/8b0d01c756203fbab65d265859749860682ccd2a59594609aeec3a144efa/msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa", size = 81939, upload-time = "2025-10-08T09:15:01.472Z" },
+ { url = "https://files.pythonhosted.org/packages/34/68/ba4f155f793a74c1483d4bdef136e1023f7bcba557f0db4ef3db3c665cf1/msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb", size = 85064, upload-time = "2025-10-08T09:15:03.764Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/60/a064b0345fc36c4c3d2c743c82d9100c40388d77f0b48b2f04d6041dbec1/msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f", size = 417131, upload-time = "2025-10-08T09:15:05.136Z" },
+ { url = "https://files.pythonhosted.org/packages/65/92/a5100f7185a800a5d29f8d14041f61475b9de465ffcc0f3b9fba606e4505/msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42", size = 427556, upload-time = "2025-10-08T09:15:06.837Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/87/ffe21d1bf7d9991354ad93949286f643b2bb6ddbeab66373922b44c3b8cc/msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9", size = 404920, upload-time = "2025-10-08T09:15:08.179Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/41/8543ed2b8604f7c0d89ce066f42007faac1eaa7d79a81555f206a5cdb889/msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620", size = 415013, upload-time = "2025-10-08T09:15:09.83Z" },
+ { url = "https://files.pythonhosted.org/packages/41/0d/2ddfaa8b7e1cee6c490d46cb0a39742b19e2481600a7a0e96537e9c22f43/msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029", size = 65096, upload-time = "2025-10-08T09:15:11.11Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/ec/d431eb7941fb55a31dd6ca3404d41fbb52d99172df2e7707754488390910/msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b", size = 72708, upload-time = "2025-10-08T09:15:12.554Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/31/5b1a1f70eb0e87d1678e9624908f86317787b536060641d6798e3cf70ace/msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69", size = 64119, upload-time = "2025-10-08T09:15:13.589Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/31/b46518ecc604d7edf3a4f94cb3bf021fc62aa301f0cb849936968164ef23/msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf", size = 81212, upload-time = "2025-10-08T09:15:14.552Z" },
+ { url = "https://files.pythonhosted.org/packages/92/dc/c385f38f2c2433333345a82926c6bfa5ecfff3ef787201614317b58dd8be/msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7", size = 84315, upload-time = "2025-10-08T09:15:15.543Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/68/93180dce57f684a61a88a45ed13047558ded2be46f03acb8dec6d7c513af/msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999", size = 412721, upload-time = "2025-10-08T09:15:16.567Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/ba/459f18c16f2b3fc1a1ca871f72f07d70c07bf768ad0a507a698b8052ac58/msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e", size = 424657, upload-time = "2025-10-08T09:15:17.825Z" },
+ { url = "https://files.pythonhosted.org/packages/38/f8/4398c46863b093252fe67368b44edc6c13b17f4e6b0e4929dbf0bdb13f23/msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162", size = 402668, upload-time = "2025-10-08T09:15:19.003Z" },
+ { url = "https://files.pythonhosted.org/packages/28/ce/698c1eff75626e4124b4d78e21cca0b4cc90043afb80a507626ea354ab52/msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794", size = 419040, upload-time = "2025-10-08T09:15:20.183Z" },
+ { url = "https://files.pythonhosted.org/packages/67/32/f3cd1667028424fa7001d82e10ee35386eea1408b93d399b09fb0aa7875f/msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c", size = 65037, upload-time = "2025-10-08T09:15:21.416Z" },
+ { url = "https://files.pythonhosted.org/packages/74/07/1ed8277f8653c40ebc65985180b007879f6a836c525b3885dcc6448ae6cb/msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9", size = 72631, upload-time = "2025-10-08T09:15:22.431Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/db/0314e4e2db56ebcf450f277904ffd84a7988b9e5da8d0d61ab2d057df2b6/msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84", size = 64118, upload-time = "2025-10-08T09:15:23.402Z" },
+ { url = "https://files.pythonhosted.org/packages/22/71/201105712d0a2ff07b7873ed3c220292fb2ea5120603c00c4b634bcdafb3/msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00", size = 81127, upload-time = "2025-10-08T09:15:24.408Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/9f/38ff9e57a2eade7bf9dfee5eae17f39fc0e998658050279cbb14d97d36d9/msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939", size = 84981, upload-time = "2025-10-08T09:15:25.812Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/a9/3536e385167b88c2cc8f4424c49e28d49a6fc35206d4a8060f136e71f94c/msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e", size = 411885, upload-time = "2025-10-08T09:15:27.22Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/40/dc34d1a8d5f1e51fc64640b62b191684da52ca469da9cd74e84936ffa4a6/msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931", size = 419658, upload-time = "2025-10-08T09:15:28.4Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/ef/2b92e286366500a09a67e03496ee8b8ba00562797a52f3c117aa2b29514b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014", size = 403290, upload-time = "2025-10-08T09:15:29.764Z" },
+ { url = "https://files.pythonhosted.org/packages/78/90/e0ea7990abea5764e4655b8177aa7c63cdfa89945b6e7641055800f6c16b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2", size = 415234, upload-time = "2025-10-08T09:15:31.022Z" },
+ { url = "https://files.pythonhosted.org/packages/72/4e/9390aed5db983a2310818cd7d3ec0aecad45e1f7007e0cda79c79507bb0d/msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717", size = 66391, upload-time = "2025-10-08T09:15:32.265Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/f1/abd09c2ae91228c5f3998dbd7f41353def9eac64253de3c8105efa2082f7/msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b", size = 73787, upload-time = "2025-10-08T09:15:33.219Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/b0/9d9f667ab48b16ad4115c1935d94023b82b3198064cb84a123e97f7466c1/msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af", size = 66453, upload-time = "2025-10-08T09:15:34.225Z" },
+ { url = "https://files.pythonhosted.org/packages/16/67/93f80545eb1792b61a217fa7f06d5e5cb9e0055bed867f43e2b8e012e137/msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a", size = 85264, upload-time = "2025-10-08T09:15:35.61Z" },
+ { url = "https://files.pythonhosted.org/packages/87/1c/33c8a24959cf193966ef11a6f6a2995a65eb066bd681fd085afd519a57ce/msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b", size = 89076, upload-time = "2025-10-08T09:15:36.619Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/6b/62e85ff7193663fbea5c0254ef32f0c77134b4059f8da89b958beb7696f3/msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245", size = 435242, upload-time = "2025-10-08T09:15:37.647Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/47/5c74ecb4cc277cf09f64e913947871682ffa82b3b93c8dad68083112f412/msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90", size = 432509, upload-time = "2025-10-08T09:15:38.794Z" },
+ { url = "https://files.pythonhosted.org/packages/24/a4/e98ccdb56dc4e98c929a3f150de1799831c0a800583cde9fa022fa90602d/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20", size = 415957, upload-time = "2025-10-08T09:15:40.238Z" },
+ { url = "https://files.pythonhosted.org/packages/da/28/6951f7fb67bc0a4e184a6b38ab71a92d9ba58080b27a77d3e2fb0be5998f/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27", size = 422910, upload-time = "2025-10-08T09:15:41.505Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/03/42106dcded51f0a0b5284d3ce30a671e7bd3f7318d122b2ead66ad289fed/msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b", size = 75197, upload-time = "2025-10-08T09:15:42.954Z" },
+ { url = "https://files.pythonhosted.org/packages/15/86/d0071e94987f8db59d4eeb386ddc64d0bb9b10820a8d82bcd3e53eeb2da6/msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff", size = 85772, upload-time = "2025-10-08T09:15:43.954Z" },
+ { url = "https://files.pythonhosted.org/packages/81/f2/08ace4142eb281c12701fc3b93a10795e4d4dc7f753911d836675050f886/msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46", size = 70868, upload-time = "2025-10-08T09:15:44.959Z" },
+]
+
+[[package]]
+name = "msgpack-numpy"
+version = "0.4.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "msgpack", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/08/94/61e8aee142733ebfdc400a05bdac6e1763c4514bba3b42743d223f388450/msgpack-numpy-0.4.8.tar.gz", hash = "sha256:c667d3180513422f9c7545be5eec5d296dcbb357e06f72ed39cc683797556e69", size = 10923, upload-time = "2022-06-09T03:43:08.739Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/5d/f25ac7d4fb77cbd53ddc6d05d833c6bf52b12770a44fa9a447eed470ca9a/msgpack_numpy-0.4.8-py2.py3-none-any.whl", hash = "sha256:773c19d4dfbae1b3c7b791083e2caf66983bb19b40901646f61d8731554ae3da", size = 6919, upload-time = "2022-06-09T03:43:06.82Z" },
+]
+
+[[package]]
+name = "myst-nb"
+version = "1.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "importlib-metadata" },
+ { name = "ipykernel" },
+ { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "ipython", version = "9.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "jupyter-cache" },
+ { name = "myst-parser" },
+ { name = "nbclient" },
+ { name = "nbformat" },
+ { name = "pyyaml" },
+ { name = "sphinx" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bd/b4/ff1abeea67e8cfe0a8c033389f6d1d8b0bfecfd611befb5cbdeab884fce6/myst_nb-1.4.0.tar.gz", hash = "sha256:c145598de62446a6fd009773dd071a40d3b76106ace780de1abdfc6961f614c2", size = 82285, upload-time = "2026-03-02T21:14:56.95Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/94/93/0a378b48488879a1d925b42a804edfc6e0cd0ef854220f2dce738a46e7e9/myst_nb-1.4.0-py3-none-any.whl", hash = "sha256:0e2c86e7d3b82c3aa51383f82d6268f7714f3b772c23a796ab09538a8e68b4e4", size = 82555, upload-time = "2026-03-02T21:14:55.652Z" },
+]
+
+[[package]]
+name = "myst-parser"
+version = "3.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "docutils" },
+ { name = "jinja2" },
+ { name = "markdown-it-py" },
+ { name = "mdit-py-plugins" },
+ { name = "pyyaml" },
+ { name = "sphinx" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/49/64/e2f13dac02f599980798c01156393b781aec983b52a6e4057ee58f07c43a/myst_parser-3.0.1.tar.gz", hash = "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87", size = 92392, upload-time = "2024-04-28T20:22:42.116Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e2/de/21aa8394f16add8f7427f0a1326ccd2b3a2a8a3245c9252bc5ac034c6155/myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1", size = 83163, upload-time = "2024-04-28T20:22:39.985Z" },
+]
+
+[[package]]
+name = "namex"
+version = "0.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0c/c0/ee95b28f029c73f8d49d8f52edaed02a1d4a9acb8b69355737fdb1faa191/namex-0.1.0.tar.gz", hash = "sha256:117f03ccd302cc48e3f5c58a296838f6b89c83455ab8683a1e85f2a430aa4306", size = 6649, upload-time = "2025-05-26T23:17:38.918Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b2/bc/465daf1de06409cdd4532082806770ee0d8d7df434da79c76564d0f69741/namex-0.1.0-py3-none-any.whl", hash = "sha256:e2012a474502f1e2251267062aae3114611f07df4224b6e06334c57b0f2ce87c", size = 5905, upload-time = "2025-05-26T23:17:37.695Z" },
+]
+
+[[package]]
+name = "napari"
+version = "0.6.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "app-model" },
+ { name = "appdirs" },
+ { name = "cachey" },
+ { name = "certifi" },
+ { name = "dask", extra = ["array"] },
+ { name = "imageio" },
+ { name = "jsonschema" },
+ { name = "lazy-loader" },
+ { name = "magicgui" },
+ { name = "napari-console" },
+ { name = "napari-plugin-engine" },
+ { name = "napari-svg" },
+ { name = "npe2" },
+ { name = "numpy" },
+ { name = "numpydoc" },
+ { name = "pandas" },
+ { name = "pillow" },
+ { name = "pint", version = "0.24.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pint", version = "0.25.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "psutil" },
+ { name = "psygnal" },
+ { name = "pydantic" },
+ { name = "pygments" },
+ { name = "pyopengl" },
+ { name = "pywin32", marker = "sys_platform == 'win32' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pyyaml" },
+ { name = "qtpy" },
+ { name = "scikit-image", version = "0.25.2", source = { registry = "https://pypi.org/simple" }, extra = ["data"], marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scikit-image", version = "0.26.0", source = { registry = "https://pypi.org/simple" }, extra = ["data"], marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "superqt" },
+ { name = "tifffile", version = "2025.5.10", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "toolz" },
+ { name = "tqdm" },
+ { name = "typing-extensions" },
+ { name = "vispy" },
+ { name = "wrapt", version = "1.14.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wrapt", version = "2.1.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-fmpose3d') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.12' and extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.12' and extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bc/a4/f1573c137aeccb86f8a57cae2470b28e22f55b8fa9fa5a2c2621ff1457de/napari-0.6.6.tar.gz", hash = "sha256:8e1adfc737c55c2a619689fad8d9e4d115582e56f09096cf771816b0ec75c3a7", size = 3251386, upload-time = "2025-10-16T09:25:58.223Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl", hash = "sha256:c65cc13de1901ec2bf8d11c665e8df9f39e678d98b76cf690d1094e9decaca69", size = 3537483, upload-time = "2025-10-16T09:25:55.679Z" },
+]
+
+[[package]]
+name = "napari-console"
+version = "0.1.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "ipykernel" },
+ { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "ipython", version = "9.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "qtconsole" },
+ { name = "qtpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5a/03/6e1fcd9aa9ac4746ce2b44050ea8f7192d883f4d3da4e7ff08589ac3ad3b/napari_console-0.1.4.tar.gz", hash = "sha256:e185e4d36d8171ae23ca383dc69c38df76592a984d6c99ad08372d188a1fbb9b", size = 20152, upload-time = "2025-10-15T14:24:18.456Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl", hash = "sha256:565df1fa15db579552af9e9d9d3883067c00191be282ad47d80f9b0d50b4e5ad", size = 9786, upload-time = "2025-10-15T14:24:17.677Z" },
+]
+
+[[package]]
+name = "napari-deeplabcut"
+version = "0.3.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "dask-image" },
+ { name = "matplotlib" },
+ { name = "napari" },
+ { name = "natsort" },
+ { name = "numpy" },
+ { name = "opencv-python-headless" },
+ { name = "pandas" },
+ { name = "pyside6" },
+ { name = "pyyaml" },
+ { name = "qtpy" },
+ { name = "scikit-image", version = "0.25.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scikit-image", version = "0.26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "shiboken6" },
+ { name = "tables", version = "3.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tables", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/16/0f/3e3956a7278c17ba4b263d24a270404cd782b1bcec3de6f860cbdeca057f/napari_deeplabcut-0.3.1.0.tar.gz", hash = "sha256:c4bc3a3643dd984f2045e59d5c06f299c7388fce1832ee7478ee0be7fddc5872", size = 1941735, upload-time = "2026-05-19T10:46:46.116Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/df/13/b43e8f1bc5d279178509c6b6929bb8c84ad16d73be5da0afc2452dc8d60a/napari_deeplabcut-0.3.1.0-py3-none-any.whl", hash = "sha256:84ee5c3cf8a2eecb0d8a59ec522d77e690987b77d33862a35b8dd4d9522253ac", size = 1194984, upload-time = "2026-05-19T10:46:44.313Z" },
+]
+
+[[package]]
+name = "napari-plugin-engine"
+version = "0.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/72/c653308edaf7f7c84d82e388a1c46bc8f26c385027af58b5bf728f600b47/napari_plugin_engine-0.2.1.tar.gz", hash = "sha256:46829cf02f368c8f2f1aa8b998ec73bcf14a2c1f5c15abd94b82154d7aef510d", size = 55809, upload-time = "2026-02-10T08:31:20.385Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/06/bc/2509813cddd0e02736121e21bef54deeec7f0f89af2c6096d753ee1feb09/napari_plugin_engine-0.2.1-py3-none-any.whl", hash = "sha256:de30babe6fd9477816f037207938a2da7faeddc0e9e8663cb29f3d74235b6dc5", size = 33849, upload-time = "2026-02-10T08:31:19.037Z" },
+]
+
+[[package]]
+name = "napari-svg"
+version = "0.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "imageio" },
+ { name = "numpy" },
+ { name = "vispy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a7/90/802f8288d16c1513b908d644779e733461a53b6c1a2c7561f1464c9f1516/napari_svg-0.2.1.tar.gz", hash = "sha256:031f13b34b0948afbdcb11eb00728fe32ef7e4e3aa3905f923001d6871a08ad9", size = 17533, upload-time = "2025-01-14T07:26:30.657Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl", hash = "sha256:9eaa54fbbf9bfd5078b67b7d1edc9eccfd872dab89fd586374909fef4ed89a49", size = 16458, upload-time = "2025-01-14T07:26:29.328Z" },
+]
+
+[[package]]
+name = "natsort"
+version = "8.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575, upload-time = "2023-06-20T04:17:19.925Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268, upload-time = "2023-06-20T04:17:17.522Z" },
+]
+
+[[package]]
+name = "nbclient"
+version = "0.10.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jupyter-client" },
+ { name = "jupyter-core" },
+ { name = "nbformat" },
+ { name = "traitlets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554, upload-time = "2025-12-23T07:45:46.369Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465, upload-time = "2025-12-23T07:45:44.51Z" },
+]
+
+[[package]]
+name = "nbformat"
+version = "5.10.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "fastjsonschema" },
+ { name = "jsonschema" },
+ { name = "jupyter-core" },
+ { name = "traitlets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" },
+]
+
+[[package]]
+name = "ndindex"
+version = "1.10.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f5/92/4b9d2f4e0f3eabcfc7b02b48261f6e5ad36a3e2c1bbdcc4e3b7b6c768fa6/ndindex-1.10.1.tar.gz", hash = "sha256:0f6113c1f031248f8818cbee1aa92aa3c9472b7701debcce9fddebcd2f610f11", size = 271395, upload-time = "2025-11-19T20:40:08.899Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4b/71/aff23bd84111d038efdcdaea4d218b463a0b2129ff49f30613cbc6f535ff/ndindex-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8644c76e74c0fbbdaa54752de30b7c6b98b1e8f6c05f0c6228632a29c862d83f", size = 172022, upload-time = "2025-11-19T20:38:12.429Z" },
+ { url = "https://files.pythonhosted.org/packages/99/a6/adcc17b685b24362983b00f965ee5c8607f74e7c68049a20facbd7ceb0b6/ndindex-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a9a211ec2198994cb3600cd46adb335a740f27e4d406b40d48ed7b98d2d2a89b", size = 171057, upload-time = "2025-11-19T20:38:13.846Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/28/b0b1bde7818d2ccd5c288802c1f24b69705e03f3975bc948c005eccab25a/ndindex-1.10.1-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cdb86a4176f2ae23bd4bcd0401ca35d5dad2d1ed0d0dca1ff64480ebe41b75d9", size = 498925, upload-time = "2025-11-19T20:38:17.214Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/46/55c3800048ef5310de542f188e1aad00e0b1d37713230c0eae980e88c895/ndindex-1.10.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ce3bd0882572269ca09285112cf38ce84baa2aaa5891551af968ca7c18f84bb", size = 495662, upload-time = "2025-11-19T20:38:20.026Z" },
+ { url = "https://files.pythonhosted.org/packages/48/a4/0103c3ee3778d7079c3ff7dd879c79362afe3a7e9d3b8dcdaa25b49ca413/ndindex-1.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2d6442ecce9b395aade5e9f2431e169e01393953a069f6d2d53a63b6c94d1d06", size = 1471263, upload-time = "2025-11-19T20:38:21.545Z" },
+ { url = "https://files.pythonhosted.org/packages/95/5a/eaa38b18757c3d8e7b2438faa5001a02f193b51a68a5558d6066f3c407e6/ndindex-1.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bada24abee6bc6ca438b2e6b68a752fc9b58b67bdcb54008e2bc6330ecb0a777", size = 1522878, upload-time = "2025-11-19T20:38:23.064Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/93/a40920c849fa128c9439bc3eb0add814696216dde235497eaa415f14d5e7/ndindex-1.10.1-cp310-cp310-win32.whl", hash = "sha256:bc236d1612714cbd80610cf25a6ef92584ff1402e9d5a5c50e926195716f7d22", size = 149268, upload-time = "2025-11-19T20:38:25.12Z" },
+ { url = "https://files.pythonhosted.org/packages/85/d9/baf1655d0b2d36eb46134fddf7dd0ef0093203c9c91d17f8ce01b9060366/ndindex-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:4cea15cff221e76abd12e3e940c26124184735cf421c229307f5db6742e14dd7", size = 157151, upload-time = "2025-11-19T20:38:27.229Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/d9/c94ab6151c9fdd199c2b560f23e3759a9fb86a7a1275855e0b97291bf05a/ndindex-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e2ad917bcdf8dc5ba1e21f01054c991d26862d4d01c3c203a50e907096d558ac", size = 172128, upload-time = "2025-11-19T20:38:28.977Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/34/880c4073750766e44492d51280d025f28e36475394ca3d741b0a4adad4b0/ndindex-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e851990a68937db5f485cd9f3e760c1fd47fa0f2a99f63a5e2cc880908faf3bb", size = 171423, upload-time = "2025-11-19T20:38:30.357Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/1e/0342da55dabe4075efc2b2ab91a6a22ed3047c5bd511ef771a7a3f822c90/ndindex-1.10.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27385939f317b55773ea53f6bf9334810cf1d66206034c0a6a6f2a88f2001c3c", size = 519590, upload-time = "2025-11-19T20:38:32.464Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/cb/7a02b6f29b15a16cd0002f4591d14493eff8e9236f7ca4c02ee4d4bcefbd/ndindex-1.10.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fdf3ca16efcdfbb8800aa88fbab1bc6528e6a0504bcb9cf7af4cb9d50e9f5d9", size = 516676, upload-time = "2025-11-19T20:38:34.276Z" },
+ { url = "https://files.pythonhosted.org/packages/67/d5/38da808f968a54b0fead2d7e15ca011d3df93c96a07f4914e8ef3974506e/ndindex-1.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3307817bdc92846b18f309fae3582856f567dd6e0742fb0b41ac68682bfc4e2a", size = 1491141, upload-time = "2025-11-19T20:38:35.785Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/1f/8c66ef982a01ae4cbdabba679a2bc711f262cedf23bfb9682293146f8a98/ndindex-1.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae73cd2d66b09ef2f2a7d7f93bad396d6abf168d1ee825e403c6c5fb8ae1341c", size = 1543876, upload-time = "2025-11-19T20:38:37.456Z" },
+ { url = "https://files.pythonhosted.org/packages/05/a1/7c7e3a3c6e81b4284fd0d53cbaec51d9e5b90df26dd78e9bde06cb307217/ndindex-1.10.1-cp311-cp311-win32.whl", hash = "sha256:890bb92f0a779e6f16bdbcc8bd2e06c32bcc0239e5893ba246114eb924aecaaa", size = 149149, upload-time = "2025-11-19T20:38:38.911Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/38/99e1fb0effdef74b883be615ea0053ebcea28a53fd8b896263f4e99b0113/ndindex-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:1827a40301405b44ad709e388c5b48cf35cd90a67f77e63f0f17d87f6000fa81", size = 157246, upload-time = "2025-11-19T20:38:40.197Z" },
+ { url = "https://files.pythonhosted.org/packages/65/90/774ddd08b2a1b41faa56da111f0fbfeb4f17ee537214c938ef41d61af949/ndindex-1.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87f83e8c35a7f49a68cd3a3054c406e6c22f8c1315f3905f7a778c657669187e", size = 177348, upload-time = "2025-11-19T20:38:41.768Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/ee/a423e857f5b45da3adc8ddbcfbfd4a0e9a047edce3915d3e3d6e189b6bd9/ndindex-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf9e05986b2eb8c5993bce0f911d6cedd15bda30b5e35dd354b1ad1f4cc3599d", size = 176561, upload-time = "2025-11-19T20:38:43.06Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/40/139b6b050ba2b2a0bb40e0381a352b1eb6551302dcb8f86fb4c97dd34e92/ndindex-1.10.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:046c1e88d46b2bd2fd3483e06d27b4e85132b55bc693f2fca2db0bb56eea1e78", size = 542901, upload-time = "2025-11-19T20:38:44.43Z" },
+ { url = "https://files.pythonhosted.org/packages/27/ae/defd665dbbeb2fffa077491365ed160acaec49274ce8d4b979f55db71f18/ndindex-1.10.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03cf1e6cdac876bd8fc92d3b65bb223496b1581d10eab3ba113f7c195121a959", size = 546875, upload-time = "2025-11-19T20:38:45.938Z" },
+ { url = "https://files.pythonhosted.org/packages/59/43/6d54d48e8eaee25cdab70d3e4c4f579ddb0255e4f1660040d5ad55e029c6/ndindex-1.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:752e78a5e87911ded117c57a7246596f26c9c6da066de3c2b533b3db694949bb", size = 1510036, upload-time = "2025-11-19T20:38:47.444Z" },
+ { url = "https://files.pythonhosted.org/packages/09/61/e28ba3b98eacd18193176526526b34d7d70d2a6f9fd2b4d8309ab5692678/ndindex-1.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9dd58d91220b1c1fe516324bfcf4114566c98e84b1cbbe416abe345c75bd557", size = 1571849, upload-time = "2025-11-19T20:38:48.951Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/63/83fff78a3712cb9f478dd84a19ec389acf6f8c7b01dc347a65ae74e6123d/ndindex-1.10.1-cp312-cp312-win32.whl", hash = "sha256:3b0d9ce2c8488444499ab6d40e92e09867bf4413f5cf04c01635de923f44aa67", size = 149792, upload-time = "2025-11-19T20:38:50.959Z" },
+ { url = "https://files.pythonhosted.org/packages/52/fd/a5e3c8c043d0dddea6cd4567bfaea568f022ac197301882b3d85d9c1e9b3/ndindex-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:5c026dbbf2455d97ce6456d8a50b349aee8fefa11027d020638c89e9be2c9c4c", size = 158164, upload-time = "2025-11-19T20:38:52.242Z" },
+ { url = "https://files.pythonhosted.org/packages/60/ea/03676266cb38cc671679a9d258cc59bfc58c69726db87b0d6eeafb308895/ndindex-1.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:157b5c34a1b779f5d27b790d9bd7e7b156d284e76be83c591a3ba003984f4956", size = 176323, upload-time = "2025-11-19T20:38:53.528Z" },
+ { url = "https://files.pythonhosted.org/packages/89/f4/2d350439031b108b0bb8897cad315390c5ad88c14d87419a54c2ffa95c80/ndindex-1.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f99b3e89220da3244d03c9c5473669c7107d361c129fd9b064622744dee1ce15", size = 175584, upload-time = "2025-11-19T20:38:57.968Z" },
+ { url = "https://files.pythonhosted.org/packages/77/34/a51b7c6f7159718a6a0a694fc1058b94d793c416d9a4fd649f1924cce5f8/ndindex-1.10.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6928e47fb008903f2e41309b7ff1e59b16abbcd59e2e945454571c28b2433c9e", size = 524127, upload-time = "2025-11-19T20:38:59.412Z" },
+ { url = "https://files.pythonhosted.org/packages/21/91/d8f19f0b8fc9c5585b50fda44c05415da0bdc5fa9c9c69011015dac27880/ndindex-1.10.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e69a2cb1ac7be955c3c77f1def83f410775a81525c9ce2d4c0a3f2a61589ed47", size = 528213, upload-time = "2025-11-19T20:39:00.882Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/a9/77d9d037e871a3faa8579b354ca2dd09cc5bbf3e085d9e3c67f786d55ee3/ndindex-1.10.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cb76e0f3f235d8b1c768b17e771de48775d281713795c3aa045e8114ad61bdda", size = 1492172, upload-time = "2025-11-19T20:39:02.387Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/29/ad13676fc9312e0aa1a80a7c04bcb0b502b877ed4956136117ad663eced0/ndindex-1.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7da34a78410c14341d5fff73be5ce924bd36500bf7f640fc59b8607d3a0df95e", size = 1552614, upload-time = "2025-11-19T20:39:04.232Z" },
+ { url = "https://files.pythonhosted.org/packages/63/34/e6e6fd81423810c07ae623c4d36e099f42a812994977e8e3bfa182c02472/ndindex-1.10.1-cp313-cp313-win32.whl", hash = "sha256:9599fcb7411ffe601c367f0a5d4bc0ed588e3e7d9dc7604bdb32c8f669456b9e", size = 149330, upload-time = "2025-11-19T20:39:05.727Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/d3/830a20626e2ec0e31a926be90e67068a029930f99e6cfebf2f9768e7b7b1/ndindex-1.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:ef3ef22390a892d16286505083ee5b326317b21c255a0c7f744b1290a0b964a6", size = 157309, upload-time = "2025-11-19T20:39:07.394Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/73/3bdeecd1f6ec0ad81478a53d96da4ba9be74ed297c95f2b4fbe2b80843e1/ndindex-1.10.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:72af787dcee3661f36fff9d144d989aacefe32e2c8b51ceef9babd46afb93a18", size = 181022, upload-time = "2025-11-19T20:39:10.487Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/b1/0d97ba134b5aa71b5ed638fac193a7ec4d987e091e2f4e4162ebdaacbda1/ndindex-1.10.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fa60637dfae1ee3fc057e420a52cc4ace38cf2c0d1a0451af2a3cba84d281842", size = 181289, upload-time = "2025-11-19T20:39:11.793Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/d7/1df02df24880ce3f3c8137b6f3ca5a901a58d9079dcfd8c818419277ff87/ndindex-1.10.1-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0ebdba2fade3f6916fe21fd49e2a0935af4f58c56100a60f3f2eb26e20baee7", size = 632517, upload-time = "2025-11-19T20:39:13.259Z" },
+ { url = "https://files.pythonhosted.org/packages/34/96/b509c2b14e9b10710fe6ab6ba8bda1ee6ce36ab16397ff2f5bbb33bbbba3/ndindex-1.10.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:346a4bf09f5771548665c8206e81daadb6b9925d409746e709894bdd98adc701", size = 616179, upload-time = "2025-11-19T20:39:14.757Z" },
+ { url = "https://files.pythonhosted.org/packages/38/e3/f89d60cf351c33a484bf1a4546a5dee6f4e7a6a973613ffa12bd316b14ad/ndindex-1.10.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:23d35696f802548143b5cc199bf2f171efb0061aa7934959251dd3bae56d038c", size = 1588373, upload-time = "2025-11-19T20:39:16.62Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/19/002fc1e6a4abeef8d92e9aa2e43aea4d462f6b170090f7752ea8887f4897/ndindex-1.10.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a91e1a0398120233d5c3b23ccb2d4b78e970d66136f1a7221fa9a53873c3d5c5", size = 1636436, upload-time = "2025-11-19T20:39:18.266Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/8f/28b1ad78c787ac8fafd6e26419a80366617784b1779e3857fa687492f6bc/ndindex-1.10.1-cp313-cp313t-win32.whl", hash = "sha256:78bfe25941d2dac406391ddd9baf0b0fce163807b98ecc2c47a3030ee8466319", size = 158780, upload-time = "2025-11-19T20:39:20.454Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/56/b81060607a19865bb8be8d705b1b3e8aefb8747c0fbd383e38b4cae4bd71/ndindex-1.10.1-cp313-cp313t-win_amd64.whl", hash = "sha256:08bfdc1f7a0b408d15b3ce61d141ebbebdb47a25341967e425e104c5bd512a5c", size = 167485, upload-time = "2025-11-19T20:39:21.733Z" },
+ { url = "https://files.pythonhosted.org/packages/da/9b/aac1131e9f3a5635ba7b0312c3bfa610511ab4108f85c0d914a32887aa00/ndindex-1.10.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9b5297f207ebc068c7cdf9e3cd7b95aa5c9ec04295d0a7e56b529f66787d4685", size = 176478, upload-time = "2025-11-19T20:39:23.747Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/05/a0d8ca0432c84550bc17af6d6479a803936895b8b8403a1216c5a55475fb/ndindex-1.10.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c5e9762452b163e33cfb6e821f86e45ba0b53bdfcd23ab5d57b48a8f566898cb", size = 175480, upload-time = "2025-11-19T20:39:25.365Z" },
+ { url = "https://files.pythonhosted.org/packages/09/4a/028ab78a9f29fd2a7e86a90337cde4658eaa77b425c63045d83a1d2e4f26/ndindex-1.10.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf80241b40adffdc3276b2c9fb63a96c6c98b4a9d941892738de8add65083962", size = 528125, upload-time = "2025-11-19T20:39:26.798Z" },
+ { url = "https://files.pythonhosted.org/packages/00/a9/bd823b345fb06c83ade6ef1c1933521d4357cd04490e684d4fa30126926c/ndindex-1.10.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf5855881884b8467dfcf45764ccf2e4279075be14b155b89c96994bb08d2e6f", size = 527328, upload-time = "2025-11-19T20:39:28.292Z" },
+ { url = "https://files.pythonhosted.org/packages/91/4f/40b9c15588cbf9dde43c4fb88a31dd1f636a913fa29649f18f8e3ebca36a/ndindex-1.10.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e81a9bd36fe054b6c9fcc53d26bc9a28cf15d1ab52a0f5b854f894116f3a54e1", size = 1497508, upload-time = "2025-11-19T20:39:30.735Z" },
+ { url = "https://files.pythonhosted.org/packages/24/8f/b8048f7837d2e9dff0af507b398307fa84a2aa9ea3db71b4aa800b21da4a/ndindex-1.10.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:588e8875d836a93b3cd9af482c8074bb02288ae1aff92cf277e1f02d9ae0f992", size = 1552625, upload-time = "2025-11-19T20:39:32.404Z" },
+ { url = "https://files.pythonhosted.org/packages/20/aa/0ecb53c7e690a44769f2f92a843723ccb1d0ce080d93ba1ea811304cca12/ndindex-1.10.1-cp314-cp314-win32.whl", hash = "sha256:28741daca5926adff402247cd406f453ed5bb6042e82d6855938f805190e5ce9", size = 151237, upload-time = "2025-11-19T20:39:34.847Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/4e/197982fa8b4e6e6b9d15c38505c41076d1c552921f09f4d35acbbbbc0b70/ndindex-1.10.1-cp314-cp314-win_amd64.whl", hash = "sha256:59a3222befc0f7cdc85fb9b90a567ae890f70a864bdeb660517e9ebcb36bf1bc", size = 158925, upload-time = "2025-11-19T20:39:37.149Z" },
+ { url = "https://files.pythonhosted.org/packages/24/ad/116b6154046a69fc04e2d4490905801d3839a3f21290c0b4d49b1044e251/ndindex-1.10.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:967b87b88dadb62555ec1039695c347254eccb8ca3d124c0e5dbe084c525fa93", size = 181724, upload-time = "2025-11-19T20:39:38.635Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/00/3ce4351366c890bcc87a5e9f1f90102547962eef356ac7c799bfdd0dddce/ndindex-1.10.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c67dde588c0fb89d872931a4ed5f9b4d21c1c70a3d92fdf0812a1de154239816", size = 181653, upload-time = "2025-11-19T20:39:40.048Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/05/a6fda696a2f02a3f8dd2ee9d816cb2edff6423bf0110a4876cc3b1259732/ndindex-1.10.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c65ca639a7abf72d79f22424f4abd18dece1f289a2b7b028a0ca455edd2168d4", size = 630898, upload-time = "2025-11-19T20:39:41.495Z" },
+ { url = "https://files.pythonhosted.org/packages/73/78/eb2e5d067d4c054451e33eaece74cbdcb58236dc60516e73d783dae34c7e/ndindex-1.10.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5c3634a8df43e7928122225a3d64d850c8957bd1edf2e403907deacb478af27b", size = 614419, upload-time = "2025-11-19T20:39:43.254Z" },
+ { url = "https://files.pythonhosted.org/packages/78/51/261bfb49eb7920c2a7314cacba5821930a529911dce48c7c6cd786096a5a/ndindex-1.10.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9d581f931e61f182478f18bdf5edd3955899df5da4892ed0d5de547a4cfd5b6f", size = 1587517, upload-time = "2025-11-19T20:39:44.809Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/37/084a332ecdf8b0049151bd78001a7baf2daf7f500d043beb8a1f95d0f4e3/ndindex-1.10.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:78ce45106ebf67aeba99714818c721d8fd5fb9534daebd2565665a2d64b50fc9", size = 1635372, upload-time = "2025-11-19T20:39:47.231Z" },
+ { url = "https://files.pythonhosted.org/packages/28/f4/716580fbb03018ab1daa86ed12c1925c67e79689db5fee82393e840758a2/ndindex-1.10.1-cp314-cp314t-win32.whl", hash = "sha256:fe5341e24dc992b09c258456ac90a09a6d25efdc2cb86dcc91d32c8891e1df9a", size = 162186, upload-time = "2025-11-19T20:39:48.81Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/20/28f669c09a470e7f523b0cc10b94336664d9648594015e3f2a1ec29047b1/ndindex-1.10.1-cp314-cp314t-win_amd64.whl", hash = "sha256:37f87f0e7690ae0324334740e0661d6297f2e62c9bf925127d249fb7eddd0ad8", size = 171077, upload-time = "2025-11-19T20:39:50.108Z" },
+]
+
+[[package]]
+name = "nest-asyncio"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" },
+]
+
+[[package]]
+name = "networkx"
+version = "3.4.2"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" },
+]
+
+[[package]]
+name = "networkx"
+version = "3.6.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" },
+]
+
+[[package]]
+name = "nodeenv"
+version = "1.10.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
+]
+
+[[package]]
+name = "npe2"
+version = "0.8.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "build" },
+ { name = "platformdirs" },
+ { name = "psygnal" },
+ { name = "pydantic" },
+ { name = "pydantic-extra-types" },
+ { name = "pyyaml" },
+ { name = "rich" },
+ { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tomli-w" },
+ { name = "typer" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/29/0a/1a6ed44b038484a064a0ac8e5f96aae6ddb8768789145cf7e65e699b324b/npe2-0.8.2.tar.gz", hash = "sha256:6e41cc1f2c873257d864980dd281b5bb649a84cef02feeb0cdda1a9d23fd8f8b", size = 122768, upload-time = "2026-04-04T20:55:11.9Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/52/a6/f318fbc7bbb62361d36cb694f399f765f57fb282fdadc3aa2fef4dafd62d/npe2-0.8.2-py3-none-any.whl", hash = "sha256:80eff5fef50352cf0f3407c4c1122a7ae186aede40a21fd595cf91988cd55a9d", size = 93921, upload-time = "2026-04-04T20:55:10.552Z" },
+]
+
+[[package]]
+name = "numba"
+version = "0.65.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "llvmlite" },
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f6/c5/db2ac3685833d626c0dcae6bd2330cd68433e1fd248d15f70998160d3ad7/numba-0.65.1.tar.gz", hash = "sha256:19357146c32fe9ed25059ab915e8465fb13951cf6b0aace3826b76886373ab23", size = 2765600, upload-time = "2026-04-24T02:02:56.551Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/de/1b/3c5a7daf683a95465bf23504bcd1a2d5db8cd5e5e276ca87505d020dffe9/numba-0.65.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:9d993ed0a257aa4116e6f553f114004bcfdee540c7276ab8ea48f650d514c452", size = 2680870, upload-time = "2026-04-24T02:02:10.623Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/a4/1831836814018a898e7d252aebe09c0f3ce1f26d145b68264b4ae0be6822/numba-0.65.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f098109f361681e57295f7e84d8ab2426902539a141811de0703ace52826981", size = 3739780, upload-time = "2026-04-24T02:02:13.097Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/1b/a813ddc81def09e257d2b1f67521982ce4b06204a87268796ffc8187271c/numba-0.65.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973fd8173f2312815e6b7aaae887c4ce8a817eeff46a4f8840b828305b75bc95", size = 3446722, upload-time = "2026-04-24T02:02:15.083Z" },
+ { url = "https://files.pythonhosted.org/packages/09/52/ee1d8b3becda384fe0552221641e05aa668a35e8a77470db4db7f6475000/numba-0.65.1-cp310-cp310-win_amd64.whl", hash = "sha256:c63aa0c4193694026452da55d0ef9d85156c1a7a333454c103bb30dec81b7bf8", size = 2747539, upload-time = "2026-04-24T02:02:16.79Z" },
+ { url = "https://files.pythonhosted.org/packages/96/b3/650500c2eab4534d98e9166f4298e0f3c69c742afdf24e6eabccd1f16ad8/numba-0.65.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:7020d74b19cdb8cff16506542fdd510756e28c5e7f3bd0b7f574f0f42272fcd9", size = 2680563, upload-time = "2026-04-24T02:02:18.414Z" },
+ { url = "https://files.pythonhosted.org/packages/44/0b/0615dbedb98f5b32a35a53290fbdc6e22306968109278d7e58df82d7a9f6/numba-0.65.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f80ed83774b5173abd6581cd8d2165d1d38e13d2e5c8155c0c0b421784745420", size = 3745018, upload-time = "2026-04-24T02:02:20.252Z" },
+ { url = "https://files.pythonhosted.org/packages/49/aa/4361698f35bf63bff67dfe6c90493731177f48ede954f77b0588731537bc/numba-0.65.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ed425a43b0a5f9772f2f4e2dd0bbd12eabecae1af0b24efcfd4e053f012aac6", size = 3450962, upload-time = "2026-04-24T02:02:22.449Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/9a/af61ec03b3116c161fd7a06b9e8a265729a8718458333e8ffbb06d9a3978/numba-0.65.1-cp311-cp311-win_amd64.whl", hash = "sha256:df40a5028a975b9ea66f6a2a3f7abbdbd541a863070e34ed367aff21141248e4", size = 2747417, upload-time = "2026-04-24T02:02:24.43Z" },
+ { url = "https://files.pythonhosted.org/packages/57/bc/76f8f8c5cf9adee47fdb7bbb03be8900f76f902d451d7477cf12b845e1de/numba-0.65.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ac3f1e77c352dd0ea9712732c2d8f9ca507717435eec5b5013bf138ac33c4a08", size = 2681371, upload-time = "2026-04-24T02:02:26.105Z" },
+ { url = "https://files.pythonhosted.org/packages/69/47/a415af0283e4db0398104c6d1c11c9861a98dc67a7aa442a7769ed5d6196/numba-0.65.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:52bc6f3ceb8fcaff9b2ae26b4c6b1e9fee39db8d355534c0fe4f39a901246b84", size = 3802467, upload-time = "2026-04-24T02:02:27.712Z" },
+ { url = "https://files.pythonhosted.org/packages/46/36/246f73ec99cfeab2f2cb2ce7d4218766cc36a2da418901223f4f4da9c813/numba-0.65.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90ca10b3463bae0bd70589726fe3c77d01d6b5fc86bee54bcdf9fb6b47c28977", size = 3502628, upload-time = "2026-04-24T02:02:29.763Z" },
+ { url = "https://files.pythonhosted.org/packages/db/9e/3c679b2ee078425b9e99a91e44f8d132a6830d8ccce5227bc5e9181aeed8/numba-0.65.1-cp312-cp312-win_amd64.whl", hash = "sha256:5971c632be2a2351500431f46213821dba8d02b18a9f7d02fd36bd2743e41a6a", size = 2750611, upload-time = "2026-04-24T02:02:31.477Z" },
+ { url = "https://files.pythonhosted.org/packages/79/37/14a4579049c1eb673afd0de0cb4842982acd55b9ce2643e763db858bcea0/numba-0.65.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:1735c15c1134a5108b4d6a5c77fc0947924ea066a738dc09a52008c13df9cad3", size = 2681344, upload-time = "2026-04-24T02:02:33.65Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/22/b8d873f6466b20aa563fc9b33acd48dec89a07803ddaa2f1c8ca1cd33126/numba-0.65.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c09f49117ef255e1f1c6dad0c7a1ed39868243862a73be5706793241a3755f1b", size = 3810619, upload-time = "2026-04-24T02:02:36.041Z" },
+ { url = "https://files.pythonhosted.org/packages/62/08/e16a8b5d9a018962ebb5c66be662317cde32b9f5dab08441f90bed5522fb/numba-0.65.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:594a8680b3fadac99e97e489b1fd89007177e5336713745c3b769528c635a464", size = 3509783, upload-time = "2026-04-24T02:02:38.245Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/a5/03c970d57f4c1741354837353ce39fb5206952ae1dba8922d29c86f64805/numba-0.65.1-cp313-cp313-win_amd64.whl", hash = "sha256:85be74c0d036842699a30058f82fb88fc5ffdc59f7615cab5792ea92914c9b62", size = 2750534, upload-time = "2026-04-24T02:02:39.903Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/2e/8aed9b726d9ba5f11ad287645fd479e88278db3060a25cb1225d730eb2b7/numba-0.65.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:33f5eb68eb1c843511615d14663ce60258525d6a4c65ab040e2c2b0c4cf17450", size = 2681554, upload-time = "2026-04-24T02:02:41.812Z" },
+ { url = "https://files.pythonhosted.org/packages/87/96/f3eb235fafa82a34e2ab5dd7dc9ffff998ebf5f0bbc23fa56a96aeb44da6/numba-0.65.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71e73029bf53a62cc6afcf96be4bd942290d8b4c55f0a454fb536158115790f7", size = 3779602, upload-time = "2026-04-24T02:02:43.726Z" },
+ { url = "https://files.pythonhosted.org/packages/09/90/b0f09b48752d23640b8284f22aa597737e8adaddc7fbfacc4708b7f73a4c/numba-0.65.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a07635e0be926b9bdbffb09137c230fb13f6ec0e564914ba937cee12ce3eb35", size = 3479532, upload-time = "2026-04-24T02:02:45.427Z" },
+ { url = "https://files.pythonhosted.org/packages/56/46/3f7fc04fb853559e74b210e0b62c19974ec844cefec611f9e535f4da3761/numba-0.65.1-cp314-cp314-win_amd64.whl", hash = "sha256:2a20fcdabdefbdacf88d85caf70c3b18c4bcb7ebb8f82e6a19486383dd26ab63", size = 2752637, upload-time = "2026-04-24T02:02:47.664Z" },
+ { url = "https://files.pythonhosted.org/packages/81/7b/c1a341a9067367778f4152a5f01061cf281fb09582c92c510ec4918cabf6/numba-0.65.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:548dd4b3a4508d5062768d1514b2cd7b015f9a25ec7af651c50dee243965e652", size = 2684600, upload-time = "2026-04-24T02:02:49.653Z" },
+ { url = "https://files.pythonhosted.org/packages/03/36/98ddbcf3e4f04a6dd07e1c67249955920579ba4af6bb6868e3088f4ed282/numba-0.65.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:78abc28feff2c2ff8307fff3975b6438352759c9acb797ecd6b1fb6e7e39e31d", size = 3817198, upload-time = "2026-04-24T02:02:51.266Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/83/0dad21057ece5a835599f5d24099b091703995e23dbbf894f259e91c010b/numba-0.65.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee7676cb389555805f9b9a1840cbcd1ea6c8bd5376ab6918e3a29c5ea1dbda20", size = 3533862, upload-time = "2026-04-24T02:02:52.987Z" },
+ { url = "https://files.pythonhosted.org/packages/32/36/8be7118ffd4c8440881046eac3d0982cc5ab42909508cf5d67024d62a2e4/numba-0.65.1-cp314-cp314t-win_amd64.whl", hash = "sha256:20609346e3bd75204950dcbbfe383a8d7dbf4902f442aedbf00f97fef4aa8f38", size = 2758237, upload-time = "2026-04-24T02:02:54.612Z" },
+]
+
+[[package]]
+name = "numexpr"
+version = "2.14.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cb/2f/fdba158c9dbe5caca9c3eca3eaffffb251f2fb8674bf8e2d0aed5f38d319/numexpr-2.14.1.tar.gz", hash = "sha256:4be00b1086c7b7a5c32e31558122b7b80243fe098579b170967da83f3152b48b", size = 119400, upload-time = "2025-10-13T16:17:27.351Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/db/91/ccd504cbe5b88d06987c77f42ba37a13ef05065fdab4afe6dcfeb2961faf/numexpr-2.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d0fab3fd06a04f6b86102552b26aa5d85e20ac7d8296c15764c726eeabae6cc8", size = 163200, upload-time = "2025-10-13T16:16:25.47Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/89/6b07977baf2af75fb6692f9e7a1fb612a15f600fc921f3f565366de01f4a/numexpr-2.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:64ae5dfd62d74a3ef82fe0b37f80527247f3626171ad82025900f46ffca4b39a", size = 152085, upload-time = "2025-10-13T16:16:29.508Z" },
+ { url = "https://files.pythonhosted.org/packages/28/c2/c5775541256c4bf16b4d88fa1cffa74a0126703e513093c8774d911b0bb7/numexpr-2.14.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:955c92b064f9074d2970cf3138f5e3b965be673b82024962ed526f39bc25a920", size = 449435, upload-time = "2025-10-13T16:13:16.257Z" },
+ { url = "https://files.pythonhosted.org/packages/34/d4/d1a410901c620f7a6a3c5c2b1fc9dab22170be05a89d2c02ae699e27bd3f/numexpr-2.14.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:75440c54fc01e130396650fdf307aa9d41a67dc06ddbfb288971b591c13a395b", size = 440197, upload-time = "2025-10-13T16:14:44.109Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/c8/fa85f0cc5c39db587ba4927b862a92477c017ee8476e415e8120a100457b/numexpr-2.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dde9fa47ed319e1e1728940a539df3cb78326b7754bc7c6ab3152afc91808f9b", size = 1414125, upload-time = "2025-10-13T16:13:19.882Z" },
+ { url = "https://files.pythonhosted.org/packages/08/72/a58ddc05e0eabb3fa8d3fcd319f3d97870e6b41520832acfd04a6734c2c0/numexpr-2.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76db0bc6267e591ab9c4df405ffb533598e4c88239db7338d11ae9e4b368a85a", size = 1463041, upload-time = "2025-10-13T16:14:47.502Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/c5/bdd1862302bb71a78dba941eaf7060e1274f1cf6af2d1b0f1880bfcb289b/numexpr-2.14.1-cp310-cp310-win32.whl", hash = "sha256:0d1dcbdc4d0374c0d523cee2f94f06b001623cbc1fd163612841017a3495427c", size = 166833, upload-time = "2025-10-13T16:17:03.543Z" },
+ { url = "https://files.pythonhosted.org/packages/18/af/26773a246716922794388786529e5640676399efabb0ee217ce034df9d27/numexpr-2.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:823cd82c8e7937981339f634e7a9c6a92cb2d0b9d0a5cf627a5e394fffc05377", size = 160068, upload-time = "2025-10-13T16:17:05.191Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/a3/67999bdd1ed1f938d38f3fedd4969632f2f197b090e50505f7cc1fa82510/numexpr-2.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2d03fcb4644a12f70a14d74006f72662824da5b6128bf1bcd10cc3ed80e64c34", size = 163195, upload-time = "2025-10-13T16:16:31.212Z" },
+ { url = "https://files.pythonhosted.org/packages/25/95/d64f680ea1fc56d165457287e0851d6708800f9fcea346fc1b9957942ee6/numexpr-2.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2773ee1133f77009a1fc2f34fe236f3d9823779f5f75450e183137d49f00499f", size = 152088, upload-time = "2025-10-13T16:16:33.186Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/7f/3bae417cb13ae08afd86d08bb0301c32440fe0cae4e6262b530e0819aeda/numexpr-2.14.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ebe4980f9494b9f94d10d2e526edc29e72516698d3bf95670ba79415492212a4", size = 451126, upload-time = "2025-10-13T16:13:22.248Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/1a/edbe839109518364ac0bd9e918cf874c755bb2c128040e920f198c494263/numexpr-2.14.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a381e5e919a745c9503bcefffc1c7f98c972c04ec58fc8e999ed1a929e01ba6", size = 442012, upload-time = "2025-10-13T16:14:51.416Z" },
+ { url = "https://files.pythonhosted.org/packages/66/b1/be4ce99bff769a5003baddac103f34681997b31d4640d5a75c0e8ed59c78/numexpr-2.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d08856cfc1b440eb1caaa60515235369654321995dd68eb9377577392020f6cb", size = 1415975, upload-time = "2025-10-13T16:13:26.088Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/33/b33b8fdc032a05d9ebb44a51bfcd4b92c178a2572cd3e6c1b03d8a4b45b2/numexpr-2.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03130afa04edf83a7b590d207444f05a00363c9b9ea5d81c0f53b1ea13fad55a", size = 1464683, upload-time = "2025-10-13T16:14:58.87Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/b2/ddcf0ac6cf0a1d605e5aecd4281507fd79a9628a67896795ab2e975de5df/numexpr-2.14.1-cp311-cp311-win32.whl", hash = "sha256:db78fa0c9fcbaded3ae7453faf060bd7a18b0dc10299d7fcd02d9362be1213ed", size = 166838, upload-time = "2025-10-13T16:17:06.765Z" },
+ { url = "https://files.pythonhosted.org/packages/64/72/4ca9bd97b2eb6dce9f5e70a3b6acec1a93e1fb9b079cb4cba2cdfbbf295d/numexpr-2.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:e9b2f957798c67a2428be96b04bce85439bed05efe78eb78e4c2ca43737578e7", size = 160069, upload-time = "2025-10-13T16:17:08.752Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/20/c473fc04a371f5e2f8c5749e04505c13e7a8ede27c09e9f099b2ad6f43d6/numexpr-2.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ebae0ab18c799b0e6b8c5a8d11e1fa3848eb4011271d99848b297468a39430", size = 162790, upload-time = "2025-10-13T16:16:34.903Z" },
+ { url = "https://files.pythonhosted.org/packages/45/93/b6760dd1904c2a498e5f43d1bb436f59383c3ddea3815f1461dfaa259373/numexpr-2.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47041f2f7b9e69498fb311af672ba914a60e6e6d804011caacb17d66f639e659", size = 152196, upload-time = "2025-10-13T16:16:36.593Z" },
+ { url = "https://files.pythonhosted.org/packages/72/94/cc921e35593b820521e464cbbeaf8212bbdb07f16dc79fe283168df38195/numexpr-2.14.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d686dfb2c1382d9e6e0ee0b7647f943c1886dba3adbf606c625479f35f1956c1", size = 452468, upload-time = "2025-10-13T16:13:29.531Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/43/560e9ba23c02c904b5934496486d061bcb14cd3ebba2e3cf0e2dccb6c22b/numexpr-2.14.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee6d4fbbbc368e6cdd0772734d6249128d957b3b8ad47a100789009f4de7083", size = 443631, upload-time = "2025-10-13T16:15:02.473Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/6c/78f83b6219f61c2c22d71ab6e6c2d4e5d7381334c6c29b77204e59edb039/numexpr-2.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3a2839efa25f3c8d4133252ea7342d8f81226c7c4dda81f97a57e090b9d87a48", size = 1417670, upload-time = "2025-10-13T16:13:33.464Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/bb/1ccc9dcaf46281568ce769888bf16294c40e98a5158e4b16c241de31d0d3/numexpr-2.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9f9137f1351b310436662b5dc6f4082a245efa8950c3b0d9008028df92fefb9b", size = 1466212, upload-time = "2025-10-13T16:15:12.828Z" },
+ { url = "https://files.pythonhosted.org/packages/31/9f/203d82b9e39dadd91d64bca55b3c8ca432e981b822468dcef41a4418626b/numexpr-2.14.1-cp312-cp312-win32.whl", hash = "sha256:36f8d5c1bd1355df93b43d766790f9046cccfc1e32b7c6163f75bcde682cda07", size = 166996, upload-time = "2025-10-13T16:17:10.369Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/67/ffe750b5452eb66de788c34e7d21ec6d886abb4d7c43ad1dc88ceb3d998f/numexpr-2.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:fdd886f4b7dbaf167633ee396478f0d0aa58ea2f9e7ccc3c6431019623e8d68f", size = 160187, upload-time = "2025-10-13T16:17:11.974Z" },
+ { url = "https://files.pythonhosted.org/packages/73/b4/9f6d637fd79df42be1be29ee7ba1f050fab63b7182cb922a0e08adc12320/numexpr-2.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:09078ba73cffe94745abfbcc2d81ab8b4b4e9d7bfbbde6cac2ee5dbf38eee222", size = 162794, upload-time = "2025-10-13T16:16:38.291Z" },
+ { url = "https://files.pythonhosted.org/packages/35/ae/d58558d8043de0c49f385ea2fa789e3cfe4d436c96be80200c5292f45f15/numexpr-2.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dce0b5a0447baa7b44bc218ec2d7dcd175b8eee6083605293349c0c1d9b82fb6", size = 152203, upload-time = "2025-10-13T16:16:39.907Z" },
+ { url = "https://files.pythonhosted.org/packages/13/65/72b065f9c75baf8f474fd5d2b768350935989d4917db1c6c75b866d4067c/numexpr-2.14.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:06855053de7a3a8425429bd996e8ae3c50b57637ad3e757e0fa0602a7874be30", size = 455860, upload-time = "2025-10-13T16:13:35.811Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/f9/c9457652dfe28e2eb898372da2fe786c6db81af9540c0f853ee04a0699cc/numexpr-2.14.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f9366d23a2e991fd5a8b5e61a17558f028ba86158a4552f8f239b005cdf83c", size = 446574, upload-time = "2025-10-13T16:15:17.367Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/99/8d3879c4d67d3db5560cf2de65ce1778b80b75f6fa415eb5c3e7bd37ba27/numexpr-2.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c5f1b1605695778896534dfc6e130d54a65cd52be7ed2cd0cfee3981fd676bf5", size = 1417306, upload-time = "2025-10-13T16:13:42.813Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/05/6bddac9f18598ba94281e27a6943093f7d0976544b0cb5d92272c64719bd/numexpr-2.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a4ba71db47ea99c659d88ee6233fa77b6dc83392f1d324e0c90ddf617ae3f421", size = 1466145, upload-time = "2025-10-13T16:15:27.464Z" },
+ { url = "https://files.pythonhosted.org/packages/24/5d/cbeb67aca0c5a76ead13df7e8bd8dd5e0d49145f90da697ba1d9f07005b0/numexpr-2.14.1-cp313-cp313-win32.whl", hash = "sha256:638dce8320f4a1483d5ca4fda69f60a70ed7e66be6e68bc23fb9f1a6b78a9e3b", size = 166996, upload-time = "2025-10-13T16:17:13.803Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/23/9281bceaeb282cead95f0aa5f7f222ffc895670ea689cc1398355f6e3001/numexpr-2.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:9fdcd4735121658a313f878fd31136d1bfc6a5b913219e7274e9fca9f8dac3bb", size = 160189, upload-time = "2025-10-13T16:17:15.417Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/76/7aac965fd93a56803cbe502aee2adcad667253ae34b0badf6c5af7908b6c/numexpr-2.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:557887ad7f5d3c2a40fd7310e50597045a68e66b20a77b3f44d7bc7608523b4b", size = 163524, upload-time = "2025-10-13T16:16:42.213Z" },
+ { url = "https://files.pythonhosted.org/packages/58/65/79d592d5e63fbfab3b59a60c386853d9186a44a3fa3c87ba26bdc25b6195/numexpr-2.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:af111c8fe6fc55d15e4c7cab11920fc50740d913636d486545b080192cd0ad73", size = 152919, upload-time = "2025-10-13T16:16:44.229Z" },
+ { url = "https://files.pythonhosted.org/packages/84/78/3c8335f713d4aeb99fa758d7c62f0be1482d4947ce5b508e2052bb7aeee9/numexpr-2.14.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33265294376e7e2ae4d264d75b798a915d2acf37b9dd2b9405e8b04f84d05cfc", size = 465972, upload-time = "2025-10-13T16:13:45.061Z" },
+ { url = "https://files.pythonhosted.org/packages/35/81/9ee5f69b811e8f18746c12d6f71848617684edd3161927f95eee7a305631/numexpr-2.14.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83647d846d3eeeb9a9255311236135286728b398d0d41d35dedb532dca807fe9", size = 456953, upload-time = "2025-10-13T16:15:31.186Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/39/9b8bc6e294d85cbb54a634e47b833e9f3276a8bdf7ce92aa808718a0212d/numexpr-2.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6e575fd3ad41ddf3355d0c7ef6bd0168619dc1779a98fe46693cad5e95d25e6e", size = 1426199, upload-time = "2025-10-13T16:13:48.231Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/ce/0d4fcd31ab49319740d934fba1734d7dad13aa485532ca754e555ca16c8b/numexpr-2.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:67ea4771029ce818573b1998f5ca416bd255156feea017841b86176a938f7d19", size = 1474214, upload-time = "2025-10-13T16:15:38.893Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/47/b2a93cbdb3ba4e009728ad1b9ef1550e2655ea2c86958ebaf03b9615f275/numexpr-2.14.1-cp313-cp313t-win32.whl", hash = "sha256:15015d47d3d1487072d58c0e7682ef2eb608321e14099c39d52e2dd689483611", size = 167676, upload-time = "2025-10-13T16:17:17.351Z" },
+ { url = "https://files.pythonhosted.org/packages/86/99/ee3accc589ed032eea68e12172515ed96a5568534c213ad109e1f4411df1/numexpr-2.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:94c711f6d8f17dfb4606842b403699603aa591ab9f6bf23038b488ea9cfb0f09", size = 161096, upload-time = "2025-10-13T16:17:19.174Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/36/9db78dfbfdfa1f8bf0872993f1a334cdd8fca5a5b6567e47dcb128bcb7c2/numexpr-2.14.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ede79f7ff06629f599081de644546ce7324f1581c09b0ac174da88a470d39c21", size = 162848, upload-time = "2025-10-13T16:16:46.216Z" },
+ { url = "https://files.pythonhosted.org/packages/13/c1/a5c78ae637402c5550e2e0ba175275d2515d432ec28af0cdc23c9b476e65/numexpr-2.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2eac7a5a2f70b3768c67056445d1ceb4ecd9b853c8eda9563823b551aeaa5082", size = 152270, upload-time = "2025-10-13T16:16:47.92Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/ed/aabd8678077848dd9a751c5558c2057839f5a09e2a176d8dfcd0850ee00e/numexpr-2.14.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5aedf38d4c0c19d3cecfe0334c3f4099fb496f54c146223d30fa930084bc8574", size = 455918, upload-time = "2025-10-13T16:13:50.338Z" },
+ { url = "https://files.pythonhosted.org/packages/88/e1/3db65117f02cdefb0e5e4c440daf1c30beb45051b7f47aded25b7f4f2f34/numexpr-2.14.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439ec4d57b853792ebe5456e3160312281c3a7071ecac5532ded3278ede614de", size = 446512, upload-time = "2025-10-13T16:15:42.313Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/fb/7ceb9ee55b5f67e4a3e4d73d5af4c7e37e3c9f37f54bee90361b64b17e3f/numexpr-2.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e23b87f744e04e302d82ac5e2189ae20a533566aec76a46885376e20b0645bf8", size = 1417845, upload-time = "2025-10-13T16:13:53.836Z" },
+ { url = "https://files.pythonhosted.org/packages/45/2d/9b5764d0eafbbb2889288f80de773791358acf6fad1a55767538d8b79599/numexpr-2.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:44f84e0e5af219dbb62a081606156420815890e041b87252fbcea5df55214c4c", size = 1466211, upload-time = "2025-10-13T16:15:48.985Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/21/204db708eccd71aa8bc55bcad55bc0fc6c5a4e01ad78e14ee5714a749386/numexpr-2.14.1-cp314-cp314-win32.whl", hash = "sha256:1f1a5e817c534539351aa75d26088e9e1e0ef1b3a6ab484047618a652ccc4fc3", size = 168835, upload-time = "2025-10-13T16:17:20.82Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/3e/d83e9401a1c3449a124f7d4b3fb44084798e0d30f7c11e60712d9b94cf11/numexpr-2.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:587c41509bc373dfb1fe6086ba55a73147297247bedb6d588cda69169fc412f2", size = 162608, upload-time = "2025-10-13T16:17:22.228Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/d6/ec947806bb57836d6379a8c8a253c2aeaa602b12fef2336bfd2462bb4ed5/numexpr-2.14.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ec368819502b64f190c3f71be14a304780b5935c42aae5bf22c27cc2cbba70b5", size = 163525, upload-time = "2025-10-13T16:16:50.133Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/77/048f30dcf661a3d52963a88c29b52b6d5ce996d38e9313a56a922451c1e0/numexpr-2.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7e87f6d203ac57239de32261c941e9748f9309cbc0da6295eabd0c438b920d3a", size = 152917, upload-time = "2025-10-13T16:16:52.055Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/d3/956a13e628d722d649fbf2fded615134a308c082e122a48bad0e90a99ce9/numexpr-2.14.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd72d8c2a165fe45ea7650b16eb8cc1792a94a722022006bb97c86fe51fd2091", size = 466242, upload-time = "2025-10-13T16:13:55.795Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/dd/abe848678d82486940892f2cacf39e82eec790e8930d4d713d3f9191063b/numexpr-2.14.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70d80fcb418a54ca208e9a38e58ddc425c07f66485176b261d9a67c7f2864f73", size = 457149, upload-time = "2025-10-13T16:15:52.036Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/bb/797b583b5fb9da5700a5708ca6eb4f889c94d81abb28de4d642c0f4b3258/numexpr-2.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:edea2f20c2040df8b54ee8ca8ebda63de9545b2112872466118e9df4d0ae99f3", size = 1426493, upload-time = "2025-10-13T16:13:59.244Z" },
+ { url = "https://files.pythonhosted.org/packages/77/c4/0519ab028fdc35e3e7ee700def7f2b4631b175cd9e1202bd7966c1695c33/numexpr-2.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:790447be6879a6c51b9545f79612d24c9ea0a41d537a84e15e6a8ddef0b6268e", size = 1474413, upload-time = "2025-10-13T16:15:59.211Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/4a/33044878c8f4a75213cfe9c11d4c02058bb710a7a063fe14f362e8de1077/numexpr-2.14.1-cp314-cp314t-win32.whl", hash = "sha256:538961096c2300ea44240209181e31fae82759d26b51713b589332b9f2a4117e", size = 169502, upload-time = "2025-10-13T16:17:23.829Z" },
+ { url = "https://files.pythonhosted.org/packages/41/a2/5a1a2c72528b429337f49911b18c302ecd36eeab00f409147e1aa4ae4519/numexpr-2.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:a40b350cd45b4446076fa11843fa32bbe07024747aeddf6d467290bf9011b392", size = 163589, upload-time = "2025-10-13T16:17:25.696Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "1.26.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload-time = "2024-02-05T23:48:01.194Z" },
+ { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload-time = "2024-02-05T23:48:29.038Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload-time = "2024-02-05T23:48:54.098Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload-time = "2024-02-05T23:49:25.361Z" },
+ { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload-time = "2024-02-05T23:49:51.983Z" },
+ { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload-time = "2024-02-05T23:50:22.515Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload-time = "2024-02-05T23:50:35.834Z" },
+ { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload-time = "2024-02-05T23:51:03.701Z" },
+ { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554, upload-time = "2024-02-05T23:51:50.149Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127, upload-time = "2024-02-05T23:52:15.314Z" },
+ { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994, upload-time = "2024-02-05T23:52:47.569Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005, upload-time = "2024-02-05T23:53:15.637Z" },
+ { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297, upload-time = "2024-02-05T23:53:42.16Z" },
+ { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567, upload-time = "2024-02-05T23:54:11.696Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812, upload-time = "2024-02-05T23:54:26.453Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913, upload-time = "2024-02-05T23:54:53.933Z" },
+ { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901, upload-time = "2024-02-05T23:55:32.801Z" },
+ { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868, upload-time = "2024-02-05T23:55:56.28Z" },
+ { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109, upload-time = "2024-02-05T23:56:20.368Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613, upload-time = "2024-02-05T23:56:56.054Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172, upload-time = "2024-02-05T23:57:21.56Z" },
+ { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643, upload-time = "2024-02-05T23:57:56.585Z" },
+ { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803, upload-time = "2024-02-05T23:58:08.963Z" },
+ { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754, upload-time = "2024-02-05T23:58:36.364Z" },
+]
+
+[[package]]
+name = "numpydoc"
+version = "1.10.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "sphinx" },
+ { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e9/3c/dfccc9e7dee357fb2aa13c3890d952a370dd0ed071e0f7ed62ed0df567c1/numpydoc-1.10.0.tar.gz", hash = "sha256:3f7970f6eee30912260a6b31ac72bba2432830cd6722569ec17ee8d3ef5ffa01", size = 94027, upload-time = "2025-12-02T16:39:12.937Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/62/5e/3a6a3e90f35cea3853c45e5d5fb9b7192ce4384616f932cf7591298ab6e1/numpydoc-1.10.0-py3-none-any.whl", hash = "sha256:3149da9874af890bcc2a82ef7aae5484e5aa81cb2778f08e3c307ba6d963721b", size = 69255, upload-time = "2025-12-02T16:39:11.561Z" },
+]
+
+[[package]]
+name = "nvidia-cublas"
+version = "13.1.1.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nvidia-cuda-nvrtc", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758, upload-time = "2026-04-08T18:46:58.655Z" },
+ { url = "https://files.pythonhosted.org/packages/45/9e/2f562daf80eb8f7a685fb7bea4fda71f6048e4f359d6fdd1b6e70206cb2f/nvidia_cublas-13.1.1.3-py3-none-win_amd64.whl", hash = "sha256:b6cdce694e47ff6aadf0a69df1cab6628d696f5ff56e8d16af50309d855fa20f", size = 404358158, upload-time = "2026-04-08T18:47:26.987Z" },
+]
+
+[[package]]
+name = "nvidia-cublas-cu11"
+version = "11.10.3.66"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "setuptools", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wheel", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ce/41/fdeb62b5437996e841d83d7d2714ca75b886547ee8017ee2fe6ea409d983/nvidia_cublas_cu11-11.10.3.66-py3-none-manylinux1_x86_64.whl", hash = "sha256:d32e4d75f94ddfb93ea0a5dda08389bcc65d8916a25cb9f37ac89edaeed3bded", size = 317097917, upload-time = "2022-08-11T17:32:30.789Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/08/57e6b6481af73590259a9600c32a68eb853966e354fca147cde17ed9ea27/nvidia_cublas_cu11-11.10.3.66-py3-none-win_amd64.whl", hash = "sha256:8ac17ba6ade3ed56ab898a036f9ae0756f1e81052a317bf98f8c6d18dc3ae49e", size = 311065222, upload-time = "2022-08-03T21:16:08.044Z" },
+]
+
+[[package]]
+name = "nvidia-cublas-cu12"
+version = "12.8.4.1"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0", size = 590537124, upload-time = "2025-03-07T01:43:53.556Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" },
+ { url = "https://files.pythonhosted.org/packages/70/61/7d7b3c70186fb651d0fbd35b01dbfc8e755f69fd58f817f3d0f642df20c3/nvidia_cublas_cu12-12.8.4.1-py3-none-win_amd64.whl", hash = "sha256:47e9b82132fa8d2b4944e708049229601448aaad7e6f296f630f2d1a32de35af", size = 567544208, upload-time = "2025-03-07T01:53:30.535Z" },
+]
+
+[[package]]
+name = "nvidia-cuda-cupti"
+version = "13.0.85"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" },
+ { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/df/b74b10025c1205695c5676373f2edd3e87a7202cc62ead0dfbc373b0f6ea/nvidia_cuda_cupti-13.0.85-py3-none-win_amd64.whl", hash = "sha256:683f58d301548deeefcb8f6fac1b8d907691b9d8b18eccab417f51e362102f00", size = 7736776, upload-time = "2025-09-04T08:38:08.38Z" },
+]
+
+[[package]]
+name = "nvidia-cuda-cupti-cu11"
+version = "11.7.101"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "setuptools", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wheel", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e6/9d/dd0cdcd800e642e3c82ee3b5987c751afd4f3fb9cc2752517f42c3bc6e49/nvidia_cuda_cupti_cu11-11.7.101-py3-none-manylinux1_x86_64.whl", hash = "sha256:e0cfd9854e1f2edaa36ca20d21cd0bdd5dcfca4e3b9e130a082e05b33b6c5895", size = 11845698, upload-time = "2022-08-03T20:58:36.733Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/4e/f314475cc4740b6138daf9c6496b165bab1f07c161bd4ac5e69285ab07d6/nvidia_cuda_cupti_cu11-11.7.101-py3-none-win_amd64.whl", hash = "sha256:7cc5b8f91ae5e1389c3c0ad8866b3b016a175e827ea8f162a672990a402ab2b0", size = 8461264, upload-time = "2022-08-03T21:14:45.554Z" },
+]
+
+[[package]]
+name = "nvidia-cuda-cupti-cu12"
+version = "12.8.90"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed", size = 9533318, upload-time = "2025-03-07T01:40:10.421Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" },
+ { url = "https://files.pythonhosted.org/packages/41/bc/83f5426095d93694ae39fe1311431b5d5a9bb82e48bf0dd8e19be2765942/nvidia_cuda_cupti_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:bb479dcdf7e6d4f8b0b01b115260399bf34154a1a2e9fe11c85c517d87efd98e", size = 7015759, upload-time = "2025-03-07T01:51:11.355Z" },
+]
+
+[[package]]
+name = "nvidia-cuda-nvrtc"
+version = "13.0.88"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/af/345fedb9f4c76c84ab4fa445b36bd4048a4d9db60e6bc76b4f913ff4b852/nvidia_cuda_nvrtc-13.0.88-py3-none-win_amd64.whl", hash = "sha256:6bcd4e7f8e205cbe644f5a98f2f799bef9556fefc89dd786e79a16312ce49872", size = 76807835, upload-time = "2025-09-04T08:39:15.274Z" },
+]
+
+[[package]]
+name = "nvidia-cuda-nvrtc-cu11"
+version = "11.7.99"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/25/922c5996aada6611b79b53985af7999fc629aee1d5d001b6a22431e18fec/nvidia_cuda_nvrtc_cu11-11.7.99-2-py3-none-manylinux1_x86_64.whl", hash = "sha256:9f1562822ea264b7e34ed5930567e89242d266448e936b85bc97a3370feabb03", size = 21011023, upload-time = "2022-09-21T23:12:53.384Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/8d/0709ba16c2831c17ec1c2ea1eeb89ada11ffa8d966d773cce0a7463b22bb/nvidia_cuda_nvrtc_cu11-11.7.99-py3-none-manylinux1_x86_64.whl", hash = "sha256:f7d9610d9b7c331fa0da2d1b2858a4a8315e6d49765091d28711c8946e7425e7", size = 21010447, upload-time = "2022-08-03T20:59:13.991Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/10/c9fc448f33d439981d6a74b693526871c4ef13e8d81a7b4de12e3a12a1b9/nvidia_cuda_nvrtc_cu11-11.7.99-py3-none-win_amd64.whl", hash = "sha256:f2effeb1309bdd1b3854fc9b17eaf997808f8b25968ce0c7070945c4265d64a3", size = 17040212, upload-time = "2022-08-03T21:15:19.801Z" },
+]
+
+[[package]]
+name = "nvidia-cuda-nvrtc-cu12"
+version = "12.8.93"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8", size = 43106076, upload-time = "2025-03-07T01:41:59.817Z" },
+ { url = "https://files.pythonhosted.org/packages/45/51/52a3d84baa2136cc8df15500ad731d74d3a1114d4c123e043cb608d4a32b/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:7a4b6b2904850fe78e0bd179c4b655c404d4bb799ef03ddc60804247099ae909", size = 73586838, upload-time = "2025-03-07T01:52:13.483Z" },
+]
+
+[[package]]
+name = "nvidia-cuda-runtime"
+version = "13.0.96"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/94/6b867483bec07da24ffa32736c79fabb94ef3a7af4d787a9d4a974868576/nvidia_cuda_runtime-13.0.96-py3-none-win_amd64.whl", hash = "sha256:f79298c8a098cec150a597c8eba58ecdab96e3bdc4b9bc4f9983635031740492", size = 2927037, upload-time = "2025-10-09T09:04:23.782Z" },
+]
+
+[[package]]
+name = "nvidia-cuda-runtime-cu11"
+version = "11.7.99"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "setuptools", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wheel", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/36/92/89cf558b514125d2ebd8344dd2f0533404b416486ff681d5434a5832a019/nvidia_cuda_runtime_cu11-11.7.99-py3-none-manylinux1_x86_64.whl", hash = "sha256:cc768314ae58d2641f07eac350f40f99dcb35719c4faff4bc458a7cd2b119e31", size = 849253, upload-time = "2022-08-03T20:58:27.979Z" },
+ { url = "https://files.pythonhosted.org/packages/32/2c/d89ea2b4051fbabff8d2edda8c735dabae6d5d1b8d5215f9749d38dcdb72/nvidia_cuda_runtime_cu11-11.7.99-py3-none-win_amd64.whl", hash = "sha256:bc77fa59a7679310df9d5c70ab13c4e34c64ae2124dd1efd7e5474b71be125c7", size = 991354, upload-time = "2022-08-03T21:14:37.958Z" },
+]
+
+[[package]]
+name = "nvidia-cuda-runtime-cu12"
+version = "12.8.90"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d", size = 965265, upload-time = "2025-03-07T01:39:43.533Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" },
+ { url = "https://files.pythonhosted.org/packages/30/a5/a515b7600ad361ea14bfa13fb4d6687abf500adc270f19e89849c0590492/nvidia_cuda_runtime_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:c0c6027f01505bfed6c3b21ec546f69c687689aad5f1a377554bc6ca4aa993a8", size = 944318, upload-time = "2025-03-07T01:51:01.794Z" },
+]
+
+[[package]]
+name = "nvidia-cudnn-cu11"
+version = "8.5.0.96"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nvidia-cublas-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dc/30/66d4347d6e864334da5bb1c7571305e501dcb11b9155971421bb7bb5315f/nvidia_cudnn_cu11-8.5.0.96-2-py3-none-manylinux1_x86_64.whl", hash = "sha256:402f40adfc6f418f9dae9ab402e773cfed9beae52333f6d86ae3107a1b9527e7", size = 557141533, upload-time = "2022-09-21T21:55:09.845Z" },
+ { url = "https://files.pythonhosted.org/packages/db/69/4d28d4706946f89fffe3f87373a079ae95dc17f9c0fcd840fe570c67e36b/nvidia_cudnn_cu11-8.5.0.96-py3-none-manylinux1_x86_64.whl", hash = "sha256:71f8111eb830879ff2836db3cccf03bbd735df9b0d17cd93761732ac50a8a108", size = 557140881, upload-time = "2022-08-10T00:14:42.613Z" },
+]
+
+[[package]]
+name = "nvidia-cudnn-cu12"
+version = "9.10.2.21"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nvidia-cublas-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fa/41/e79269ce215c857c935fd86bcfe91a451a584dfc27f1e068f568b9ad1ab7/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8", size = 705026878, upload-time = "2025-06-06T21:52:51.348Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/90/0bd6e586701b3a890fd38aa71c387dab4883d619d6e5ad912ccbd05bfd67/nvidia_cudnn_cu12-9.10.2.21-py3-none-win_amd64.whl", hash = "sha256:c6288de7d63e6cf62988f0923f96dc339cea362decb1bf5b3141883392a7d65e", size = 692992268, upload-time = "2025-06-06T21:55:18.114Z" },
+]
+
+[[package]]
+name = "nvidia-cudnn-cu13"
+version = "9.20.0.48"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nvidia-cublas", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/5e/edb9c0ae051602c3ccaffe424256463636d639e27d7f302dde9975ef9e7a/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0c45dd8eeb50b603f07995b1b300c62ffe6a1980482b82b3bcf94a4ca9d49304", size = 366173588, upload-time = "2026-03-09T19:29:34.474Z" },
+ { url = "https://files.pythonhosted.org/packages/78/39/21507455b1bca8b5702a9e9fc6ce73735f216f558dac2c9ede58e4d456b8/nvidia_cudnn_cu13-9.20.0.48-py3-none-win_amd64.whl", hash = "sha256:af8139732b99c0118be65ea5aac97f0d46018f8c552889e49d2fb0c6261a4a24", size = 350712614, upload-time = "2026-03-09T19:31:11.398Z" },
+]
+
+[[package]]
+name = "nvidia-cufft"
+version = "12.0.0.61"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nvidia-nvjitlink", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" },
+ { url = "https://files.pythonhosted.org/packages/85/b2/f8af21a2ed1beed337a6a02c5a28aeb85441f4d578ec3d529543c775ea4b/nvidia_cufft-12.0.0.61-py3-none-win_amd64.whl", hash = "sha256:2abce5b39d2f5ae12730fb7e5db6696533e36c26e2d3e8fd1750bdd2853364eb", size = 213342123, upload-time = "2025-09-04T08:40:51.145Z" },
+]
+
+[[package]]
+name = "nvidia-cufft-cu11"
+version = "10.9.0.58"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/74/79/b912a77e38e41f15a0581a59f5c3548d1ddfdda3225936fb67c342719e7a/nvidia_cufft_cu11-10.9.0.58-py3-none-manylinux1_x86_64.whl", hash = "sha256:222f9da70c80384632fd6035e4c3f16762d64ea7a843829cb278f98b3cb7dd81", size = 168405414, upload-time = "2022-10-03T23:29:47.505Z" },
+ { url = "https://files.pythonhosted.org/packages/71/7a/a2ad9951d57c3cc23f4fa6d84b146afd9f375ffbc744b38935930ac4393f/nvidia_cufft_cu11-10.9.0.58-py3-none-manylinux2014_aarch64.whl", hash = "sha256:34b7315104e615b230dc3c2d1861f13bff9ec465c5d3b4bb65b4986d03a1d8d4", size = 111231060, upload-time = "2024-08-17T00:00:57.04Z" },
+ { url = "https://files.pythonhosted.org/packages/64/c8/133717b43182ba063803e983e7680a94826a9f4ff5734af0ca315803f1b3/nvidia_cufft_cu11-10.9.0.58-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e21037259995243cc370dd63c430d77ae9280bedb68d5b5a18226bfc92e5d748", size = 168405419, upload-time = "2024-08-17T00:02:03.562Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/b4/e432a74f8db0e84f734dc14d36c0e529225132bf7e239da21f55893351a6/nvidia_cufft_cu11-10.9.0.58-py3-none-win_amd64.whl", hash = "sha256:c4d316f17c745ec9c728e30409612eaf77a8404c3733cdf6c9c1569634d1ca03", size = 172237004, upload-time = "2022-10-03T23:39:58.288Z" },
+]
+
+[[package]]
+name = "nvidia-cufft-cu12"
+version = "11.3.3.83"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211, upload-time = "2025-03-07T01:44:56.873Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/ec/ce1629f1e478bb5ccd208986b5f9e0316a78538dd6ab1d0484f012f8e2a1/nvidia_cufft_cu12-11.3.3.83-py3-none-win_amd64.whl", hash = "sha256:7a64a98ef2a7c47f905aaf8931b69a3a43f27c55530c698bb2ed7c75c0b42cb7", size = 192216559, upload-time = "2025-03-07T01:53:57.106Z" },
+]
+
+[[package]]
+name = "nvidia-cufile"
+version = "1.15.1.6"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" },
+]
+
+[[package]]
+name = "nvidia-cufile-cu12"
+version = "1.13.1.3"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a", size = 1120705, upload-time = "2025-03-07T01:45:41.434Z" },
+]
+
+[[package]]
+name = "nvidia-curand"
+version = "10.4.0.35"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" },
+ { url = "https://files.pythonhosted.org/packages/99/27/72103153b1ffc00e09fdc40ac970235343dcd1ea8bd762e84d2d73219ffa/nvidia_curand-10.4.0.35-py3-none-win_amd64.whl", hash = "sha256:65b1710aa6961d326b411e314b374290904c5ddf41dc3f766ebc3f1d7d4ca69f", size = 55242481, upload-time = "2025-08-04T10:30:41.831Z" },
+]
+
+[[package]]
+name = "nvidia-curand-cu11"
+version = "10.2.10.91"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "setuptools", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wheel", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8f/11/af78d54b2420e64a4dd19e704f5bb69dcb5a6a3138b4465d6a48cdf59a21/nvidia_curand_cu11-10.2.10.91-py3-none-manylinux1_x86_64.whl", hash = "sha256:eecb269c970fa599a2660c9232fa46aaccbf90d9170b96c462e13bcb4d129e2c", size = 54628716, upload-time = "2022-08-03T21:13:08.944Z" },
+ { url = "https://files.pythonhosted.org/packages/45/76/b98f30e058c9bbd9a56eb9b1102b9aab775704bad9286bf8e3998147e2e9/nvidia_curand_cu11-10.2.10.91-py3-none-win_amd64.whl", hash = "sha256:f742052af0e1e75523bde18895a9ed016ecf1e5aa0ecddfcc3658fd11a1ff417", size = 54342083, upload-time = "2022-08-03T21:17:21.537Z" },
+]
+
+[[package]]
+name = "nvidia-curand-cu12"
+version = "10.3.9.90"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/45/5e/92aa15eca622a388b80fbf8375d4760738df6285b1e92c43d37390a33a9a/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd", size = 63625754, upload-time = "2025-03-07T01:46:10.735Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/75/70c05b2f3ed5be3bb30b7102b6eb78e100da4bbf6944fd6725c012831cab/nvidia_curand_cu12-10.3.9.90-py3-none-win_amd64.whl", hash = "sha256:f149a8ca457277da854f89cf282d6ef43176861926c7ac85b2a0fbd237c587ec", size = 62765309, upload-time = "2025-03-07T01:54:20.478Z" },
+]
+
+[[package]]
+name = "nvidia-cusolver"
+version = "12.0.4.66"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nvidia-cublas", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "nvidia-cusparse", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "nvidia-nvjitlink", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" },
+ { url = "https://files.pythonhosted.org/packages/99/ef/332a0101260ca78a1daef046bf0b06199e8ed4dac1d2aa698289c358169c/nvidia_cusolver-12.0.4.66-py3-none-win_amd64.whl", hash = "sha256:16515bd33a8e76bb54d024cfa068fa68d30e80fc34b9e1090813ea9362e0cb65", size = 193551444, upload-time = "2025-09-04T08:41:46.813Z" },
+]
+
+[[package]]
+name = "nvidia-cusolver-cu11"
+version = "11.4.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nvidia-cublas-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3e/77/66149e3153b19312fb782ea367f3f950123b93916a45538b573fe373570a/nvidia_cusolver_cu11-11.4.0.1-2-py3-none-manylinux1_x86_64.whl", hash = "sha256:72fa7261d755ed55c0074960df5904b65e2326f7adce364cbe4945063c1be412", size = 102594907, upload-time = "2022-09-21T23:13:22.001Z" },
+ { url = "https://files.pythonhosted.org/packages/25/4b/272f9aa7838e545b47878e4aec4f09b0fecf17dbd312cf5c5dc398b0637f/nvidia_cusolver_cu11-11.4.0.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:700b781bfefd57d161443aff9ace1878584b93e0b2cfef3d6e9296d96febbf99", size = 102592389, upload-time = "2022-08-03T21:13:24.499Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/42/f682b0b001562d16664bd7b015165cf2c2d392a8d0506472f28b2833953e/nvidia_cusolver_cu11-11.4.0.1-py3-none-win_amd64.whl", hash = "sha256:00f70b256add65f8c1eb3b6a65308795a93e7740f6df9e273eccbba770d370c4", size = 100095353, upload-time = "2022-08-03T21:17:36.992Z" },
+]
+
+[[package]]
+name = "nvidia-cusolver-cu12"
+version = "11.7.3.90"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nvidia-cublas-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cusparse-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841, upload-time = "2025-03-07T01:46:54.356Z" },
+ { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" },
+ { url = "https://files.pythonhosted.org/packages/13/c0/76ca8551b8a84146ffa189fec81c26d04adba4bc0dbe09cd6e6fd9b7de04/nvidia_cusolver_cu12-11.7.3.90-py3-none-win_amd64.whl", hash = "sha256:4a550db115fcabc4d495eb7d39ac8b58d4ab5d8e63274d3754df1c0ad6a22d34", size = 256720438, upload-time = "2025-03-07T01:54:39.898Z" },
+]
+
+[[package]]
+name = "nvidia-cusparse"
+version = "12.6.3.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nvidia-nvjitlink", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" },
+ { url = "https://files.pythonhosted.org/packages/02/b0/b043d6f3480f102f885cf87fc3ffd3edcb5e23b855025a50e2ef4d059185/nvidia_cusparse-12.6.3.3-py3-none-win_amd64.whl", hash = "sha256:cbcf42feb737bd7ec15b4c0a63e62351886bd3f975027b8815d7f720a2b5ea79", size = 143783033, upload-time = "2025-09-04T08:42:12.391Z" },
+]
+
+[[package]]
+name = "nvidia-cusparse-cu11"
+version = "11.7.4.91"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "setuptools", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wheel", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ea/6f/6d032cc1bb7db88a989ddce3f4968419a7edeafda362847f42f614b1f845/nvidia_cusparse_cu11-11.7.4.91-py3-none-manylinux1_x86_64.whl", hash = "sha256:a3389de714db63321aa11fbec3919271f415ef19fda58aed7f2ede488c32733d", size = 173182291, upload-time = "2022-08-03T21:13:55.407Z" },
+ { url = "https://files.pythonhosted.org/packages/15/3e/d32da819d918b0b9ef3fa89ed8238d2c3b8e315ac32441229783a4b0c4ce/nvidia_cusparse_cu11-11.7.4.91-py3-none-win_amd64.whl", hash = "sha256:304a01599534f5186a8ed1c3756879282c72c118bc77dd890dc1ff868cad25b9", size = 172505954, upload-time = "2022-08-03T21:18:07.425Z" },
+]
+
+[[package]]
+name = "nvidia-cusparse-cu12"
+version = "12.5.8.93"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129, upload-time = "2025-03-07T01:47:40.407Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" },
+ { url = "https://files.pythonhosted.org/packages/62/07/f3b2ad63f8e3d257a599f422ae34eb565e70c41031aecefa3d18b62cabd1/nvidia_cusparse_cu12-12.5.8.93-py3-none-win_amd64.whl", hash = "sha256:9a33604331cb2cac199f2e7f5104dfbb8a5a898c367a53dfda9ff2acb6b6b4dd", size = 284937404, upload-time = "2025-03-07T01:55:07.742Z" },
+]
+
+[[package]]
+name = "nvidia-cusparselt-cu12"
+version = "0.7.1"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/73/b9/598f6ff36faaece4b3c50d26f50e38661499ff34346f00e057760b35cc9d/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5", size = 283835557, upload-time = "2025-02-26T00:16:54.265Z" },
+ { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/d8/a6b0d0d0c2435e9310f3e2bb0d9c9dd4c33daef86aa5f30b3681defd37ea/nvidia_cusparselt_cu12-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f67fbb5831940ec829c9117b7f33807db9f9678dc2a617fbe781cac17b4e1075", size = 271020911, upload-time = "2025-02-26T00:14:47.204Z" },
+]
+
+[[package]]
+name = "nvidia-cusparselt-cu13"
+version = "0.8.1"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/46/e1/cdc1797eadf82d3a9a575a19b33fdc871a97edbec42c00b5b5e914f4aff4/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4dca476c50bf4780d46cd0bfbd82e2bc10a08e4fef7950917ce8d7578d22a23f", size = 221051344, upload-time = "2025-09-05T18:49:51.289Z" },
+ { url = "https://files.pythonhosted.org/packages/34/7d/2661f2fb3ac4302f3a246f5fc030213ac60c1fe0bce84f9783dbd831dbb7/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:786ce87568c303fadb5afcc7102d454cd3040d75f6f8626f5db460d1871f4dd0", size = 170148586, upload-time = "2025-09-05T18:50:50.248Z" },
+ { url = "https://files.pythonhosted.org/packages/31/83/f3647ce26916c94a6ca4ff1810623e2c405cff2dea6e78d29516b2514df9/nvidia_cusparselt_cu13-0.8.1-py3-none-win_amd64.whl", hash = "sha256:dccbd362f91a7b9024d1f55ee9f548ac065027ff15d8c8b0db889ab3a8f31215", size = 156885108, upload-time = "2025-09-05T18:51:35.958Z" },
+]
+
+[[package]]
+name = "nvidia-nccl-cu11"
+version = "2.14.3"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/55/92/914cdb650b6a5d1478f83148597a25e90ea37d739bd563c5096b0e8a5f43/nvidia_nccl_cu11-2.14.3-py3-none-manylinux1_x86_64.whl", hash = "sha256:5e5534257d1284b8e825bc3a182c6f06acd6eb405e9f89d49340e98cd8f136eb", size = 177099966, upload-time = "2022-08-31T23:13:15.915Z" },
+]
+
+[[package]]
+name = "nvidia-nccl-cu12"
+version = "2.27.5"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bb/1c/857979db0ef194ca5e21478a0612bcdbbe59458d7694361882279947b349/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a", size = 322400625, upload-time = "2025-06-26T04:11:04.496Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" },
+]
+
+[[package]]
+name = "nvidia-nccl-cu13"
+version = "2.29.7"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/72/0d/daf50d44177ee0cbc7ff0a0c91eb5ff676c82be42f9a970bc7597f440c3a/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:674a12383e3c38a1bcccae7d4f3633b37852230b6047883cb2f4c2d1b36d9bf5", size = 206014712, upload-time = "2026-03-03T05:34:20.843Z" },
+ { url = "https://files.pythonhosted.org/packages/67/f4/58e4e91b6919367c7aafb8e36fce9aad1a3047e536bf7e2fd560927d3a4c/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:edd81538446786ec3b73972543e53bb43bcaf0bfc8ef76cb679fcc390ffe136d", size = 205976000, upload-time = "2026-03-03T05:36:24.472Z" },
+]
+
+[[package]]
+name = "nvidia-nvjitlink"
+version = "13.0.88"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/01/07530b0e37546231052e30234540289c42eaffa486f1a34a87fed340157b/nvidia_nvjitlink-13.0.88-py3-none-win_amd64.whl", hash = "sha256:634e96e3da9ef845ae744097a1f289238ecf946ce0b82e93cdce14b9782e682f", size = 36035115, upload-time = "2025-09-04T08:43:03.001Z" },
+]
+
+[[package]]
+name = "nvidia-nvjitlink-cu12"
+version = "12.8.93"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7", size = 38386204, upload-time = "2025-03-07T01:49:43.612Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/d7/34f02dad2e30c31b10a51f6b04e025e5dd60e5f936af9045a9b858a05383/nvidia_nvjitlink_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:bd93fbeeee850917903583587f4fc3a4eafa022e34572251368238ab5e6bd67f", size = 268553710, upload-time = "2025-03-07T01:56:24.13Z" },
+]
+
+[[package]]
+name = "nvidia-nvshmem-cu12"
+version = "3.4.5"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1d/6a/03aa43cc9bd3ad91553a88b5f6fb25ed6a3752ae86ce2180221962bc2aa5/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b48363fc6964dede448029434c6abed6c5e37f823cb43c3bcde7ecfc0457e15", size = 138936938, upload-time = "2025-09-06T00:32:05.589Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" },
+]
+
+[[package]]
+name = "nvidia-nvshmem-cu13"
+version = "3.4.5"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" },
+]
+
+[[package]]
+name = "nvidia-nvtx"
+version = "13.0.85"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/50/0e2220f8620a177de994211186ffc5bfa9f2ce1e1282797f8f90096f9f88/nvidia_nvtx-13.0.85-py3-none-win_amd64.whl", hash = "sha256:d66ea44254dd3c6eacc300047af6e1288d2269dd072b417e0adffbf479e18519", size = 137066, upload-time = "2025-09-04T08:39:25.649Z" },
+]
+
+[[package]]
+name = "nvidia-nvtx-cu11"
+version = "11.7.91"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "setuptools", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wheel", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/23/d5/09493ff0e64fd77523afbbb075108f27a13790479efe86b9ffb4587671b5/nvidia_nvtx_cu11-11.7.91-py3-none-manylinux1_x86_64.whl", hash = "sha256:b22c64eee426a62fc00952b507d6d29cf62b4c9df7a480fcc417e540e05fd5ac", size = 98579, upload-time = "2022-08-03T20:59:22.605Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/e1/37b5a5da8ec3594890356e9d60617feb36cfea1223ac511a78c615870916/nvidia_nvtx_cu11-11.7.91-py3-none-win_amd64.whl", hash = "sha256:dfd7fcb2a91742513027d63a26b757f38dd8b07fecac282c4d132a9d373ff064", size = 65886, upload-time = "2022-08-03T21:15:27.865Z" },
+]
+
+[[package]]
+name = "nvidia-nvtx-cu12"
+version = "12.8.90"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/10/c0/1b303feea90d296f6176f32a2a70b5ef230f9bdeb3a72bddb0dc922dc137/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615", size = 91161, upload-time = "2025-03-07T01:42:23.922Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/99/4c9c0c329bf9fc125008c3b54c7c94c0023518d06fc025ae36431375e1fe/nvidia_nvtx_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:619c8304aedc69f02ea82dd244541a83c3d9d40993381b3b590f1adaed3db41e", size = 56492, upload-time = "2025-03-07T01:52:24.69Z" },
+]
+
+[[package]]
+name = "oauthlib"
+version = "3.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" },
+]
+
+[[package]]
+name = "opencv-python"
+version = "4.11.0.86"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/17/06/68c27a523103dad5837dc5b87e71285280c4f098c60e4fe8a8db6486ab09/opencv-python-4.11.0.86.tar.gz", hash = "sha256:03d60ccae62304860d232272e4a4fda93c39d595780cb40b161b310244b736a4", size = 95171956, upload-time = "2025-01-16T13:52:24.737Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/05/4d/53b30a2a3ac1f75f65a59eb29cf2ee7207ce64867db47036ad61743d5a23/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:432f67c223f1dc2824f5e73cdfcd9db0efc8710647d4e813012195dc9122a52a", size = 37326322, upload-time = "2025-01-16T13:52:25.887Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/84/0a67490741867eacdfa37bc18df96e08a9d579583b419010d7f3da8ff503/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:9d05ef13d23fe97f575153558653e2d6e87103995d54e6a35db3f282fe1f9c66", size = 56723197, upload-time = "2025-01-16T13:55:21.222Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/bd/29c126788da65c1fb2b5fb621b7fed0ed5f9122aa22a0868c5e2c15c6d23/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b92ae2c8852208817e6776ba1ea0d6b1e0a1b5431e971a2a0ddd2a8cc398202", size = 42230439, upload-time = "2025-01-16T13:51:35.822Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/8b/90eb44a40476fa0e71e05a0283947cfd74a5d36121a11d926ad6f3193cc4/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b02611523803495003bd87362db3e1d2a0454a6a63025dc6658a9830570aa0d", size = 62986597, upload-time = "2025-01-16T13:52:08.836Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/d7/1d5941a9dde095468b288d989ff6539dd69cd429dbf1b9e839013d21b6f0/opencv_python-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:810549cb2a4aedaa84ad9a1c92fbfdfc14090e2749cedf2c1589ad8359aa169b", size = 29384337, upload-time = "2025-01-16T13:52:13.549Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/7d/f1c30a92854540bf789e9cd5dde7ef49bbe63f855b85a2e6b3db8135c591/opencv_python-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:085ad9b77c18853ea66283e98affefe2de8cc4c1f43eda4c100cf9b2721142ec", size = 39488044, upload-time = "2025-01-16T13:52:21.928Z" },
+]
+
+[[package]]
+name = "opencv-python-headless"
+version = "4.11.0.86"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload-time = "2025-01-16T13:53:40.22Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460, upload-time = "2025-01-16T13:52:57.015Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330, upload-time = "2025-01-16T13:55:45.731Z" },
+ { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060, upload-time = "2025-01-16T13:51:59.625Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856, upload-time = "2025-01-16T13:53:29.654Z" },
+ { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425, upload-time = "2025-01-16T13:52:49.048Z" },
+ { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386, upload-time = "2025-01-16T13:52:56.418Z" },
+]
+
+[[package]]
+name = "openvino-dev"
+version = "2022.1.0"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f2/99/9e55ddb1abce5ff7def768470810044a6de48a5da99b9195498ad75dfe8a/openvino_dev-2022.1.0-7019-py3-none-any.whl", hash = "sha256:3d911b31a89e92dcb92a29026cfb396920211aca9525ea50032419f10169098d", size = 5774377, upload-time = "2022-03-22T13:07:17.333Z" },
+]
+
+[[package]]
+name = "opt-einsum"
+version = "3.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8c/b9/2ac072041e899a52f20cf9510850ff58295003aa75525e58343591b0cbfb/opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac", size = 63004, upload-time = "2024-09-26T14:33:24.483Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd", size = 71932, upload-time = "2024-09-26T14:33:23.039Z" },
+]
+
+[[package]]
+name = "optree"
+version = "0.19.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/44/63/92328a17ab7836562fe0129e605f685a88db35ce98427c34ff48ee4ec157/optree-0.19.1.tar.gz", hash = "sha256:4497d1c9197b8c6842e511368163d318ce536521ebdcff8bebb7551dcdfac532", size = 177531, upload-time = "2026-05-06T02:32:39.704Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1f/48/53367634a0ab6c2f0e502d83f8d6e27b70b6848ff1e1ff9cf042d1e1f1a0/optree-0.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1b28b0d89def1b4554051f3de2a1ed81e20216b6454a59a0d16c9f55c08cff77", size = 398400, upload-time = "2026-05-06T02:30:31.384Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/4f/350c82cd77a510f0f495e38a6f333b4b45a413dbc224142bc59975bc09d6/optree-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:14f959bc6bea6e0532f9239c67ea6952f3b8d0755ea9b4dd498284b649275aba", size = 370049, upload-time = "2026-05-06T02:30:33.065Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/e1/81b660daea2a75f574549e62c198d0b4e8e148b5de6f5f72e90a5cc1c334/optree-0.19.1-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1687c962bb1691525178a6e90dde5840197cd7a7ad914b407eb7b635f15d47cb", size = 390143, upload-time = "2026-05-06T02:30:34.585Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/ec/ee009b5a31227b089d72fec2af3bb6bc0efd95bbe87ffe46f11061b9d371/optree-0.19.1-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:d7edead66cace8b3b905488e391b38487614f75ae4fa7f3b612c7fe0e54b8a90", size = 445740, upload-time = "2026-05-06T02:30:35.891Z" },
+ { url = "https://files.pythonhosted.org/packages/15/5c/2fe8ac73b7e979f3ed477ad99b7e034a11207d728b84ed2f52da259e7cda/optree-0.19.1-cp310-cp310-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9fb767746231ff279d273e8ff71af2a8f89c0c3870ca367c45fd4526d331ae4b", size = 446631, upload-time = "2026-05-06T02:30:36.958Z" },
+ { url = "https://files.pythonhosted.org/packages/21/42/489fb272de36e0233149d46887879deb9497edc4a0214674bd2a80b8d4ec/optree-0.19.1-cp310-cp310-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:75fe038a1bed44f487a084af7a978874c51bba55f850bc12bf8068f3242463d6", size = 442053, upload-time = "2026-05-06T02:30:38.24Z" },
+ { url = "https://files.pythonhosted.org/packages/90/09/1f0bc2b584a51702407592bbccfe2b404187f6f5ee5b4b0c112a73e1a7ec/optree-0.19.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fff5fd89a9b333d91a05a7ca2e66c8e6632d0bdbc94c1725a341b77001f09511", size = 425490, upload-time = "2026-05-06T02:30:39.432Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/f4/d8685b55323c1f42695c1ed647d6541ee9c289eb821abc6e0cb84b0e4f72/optree-0.19.1-cp310-cp310-manylinux_2_39_riscv64.whl", hash = "sha256:d4bac18638fa56efd2377cf8c43e17cd083aa566e69a31ce10f7fdaefd9676a3", size = 390552, upload-time = "2026-05-06T02:30:40.506Z" },
+ { url = "https://files.pythonhosted.org/packages/07/84/ee12e234ddcf4fd4b7893ce03ec37f3c3edabdac911fd5384aa3f5c04c05/optree-0.19.1-cp310-cp310-win32.whl", hash = "sha256:ef2409d4efda1c5a6eb69f83ffff89fb04d5607fd056704552ec359fb865cd6c", size = 303117, upload-time = "2026-05-06T02:30:41.61Z" },
+ { url = "https://files.pythonhosted.org/packages/91/b5/4e23965aacae04eb4cf42cd8108405a6628e645ee3ab759277e03063af0e/optree-0.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:01c88294235b118b7478b5e80d360e5f110977cdf79f84d61dae21c2eb1d4cdd", size = 327866, upload-time = "2026-05-06T02:30:42.664Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/f2/4671a78193f96e86c1343fe04324091e163973d0058b292c10bc3387bb70/optree-0.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a496b864fe1fe0b5ea23d1ee3d1ef958d910704661808db2b2d2e16a0cfac96c", size = 414314, upload-time = "2026-05-06T02:30:43.812Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/93/7decea24656f416d61fa57b7113b1fbdbc042b7ab421399a84e1755676a1/optree-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1667e502e0eda9477925fb17c2ad879b199a2283ac98f18e6453692819b7811", size = 385006, upload-time = "2026-05-06T02:30:44.895Z" },
+ { url = "https://files.pythonhosted.org/packages/af/2e/9d1bd2527481681c4399beeeabba11dca36b16ec814579f2e8cc6bc2af96/optree-0.19.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:42e367a9d81e57c31a23247094727987a2f64b708901233a42a24d44d24e93f6", size = 406124, upload-time = "2026-05-06T02:30:46.13Z" },
+ { url = "https://files.pythonhosted.org/packages/df/29/cdb40de6307809fa8e9452e4f9a65881a3140d01d9d589a07e9d054d8e1c/optree-0.19.1-cp311-cp311-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:96fad6c7b3a6fde3a0c8655fd003359cd247f8400749217502591a5ffc328699", size = 466772, upload-time = "2026-05-06T02:30:47.766Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/15/4645e1816e815a1306bbb7e3e2e6ba124f6dc325f8088a2db69301219a0c/optree-0.19.1-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f3a900df0ffb9b8259961b337289754531a7e0a5de2f681e9c80866b6a7cb74e", size = 466203, upload-time = "2026-05-06T02:30:49.04Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/d2/5758c76bdd7034b721d84c7f0fd911f3b39dcb489eeb27f674aaae8a5f5c/optree-0.19.1-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:27c8dc0f89ade9233aa7ed25ce15991da188e6950eb17cc0c313fc1f327c5b0b", size = 465030, upload-time = "2026-05-06T02:30:50.254Z" },
+ { url = "https://files.pythonhosted.org/packages/09/b9/f668bc51129c0fec7728ae8b43180417fe1c1fe99f71d302739f6cc50944/optree-0.19.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:38f2e503fad50aff58cade85db448002d4adc72f4b3b50dcc7f3ef4bcd3b0173", size = 447141, upload-time = "2026-05-06T02:30:51.42Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/08/a7b8862e4465bf250c3ccc78db4d10b9a2cf90ce4db3681cbdf7eb076fb7/optree-0.19.1-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:5dc35cb31540ab6ed9850b0f8865ccd400994ebd51fcf0c156cc772073f43c04", size = 410016, upload-time = "2026-05-06T02:30:52.695Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/04/04b71a34cf5e663a1df029acceb5efc8a96c8dc4b0b6af6e98486638e913/optree-0.19.1-cp311-cp311-win32.whl", hash = "sha256:d32b1261be71211f77837e839e43a3e3e8fc57707091d2454d0a88590fb6abe8", size = 311810, upload-time = "2026-05-06T02:30:53.879Z" },
+ { url = "https://files.pythonhosted.org/packages/22/64/3cc7b08cb1c0f1949895f9490217ca8db6ced7f3bf75c65a5bf31c07bf1e/optree-0.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:cd28a527bb363a1d7d28e8b2fb62816ace6743418bb86e9c5f27ea6877dcdf6c", size = 337620, upload-time = "2026-05-06T02:30:55.262Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/14/85f4b05765287658529f09ede10461224161dcf0e29e6fce1ae488451cfe/optree-0.19.1-cp311-cp311-win_arm64.whl", hash = "sha256:7853b58aa084e882ea078f390936bd92e46972eb8f9b5e654360b6480ca7283b", size = 349337, upload-time = "2026-05-06T02:30:56.647Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/a7/cb5567029a608a296b0ca224025d03bba0365b41df19085b9b580191f6f2/optree-0.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:96e5c7c3b9144f08ae40c3d9848cfbcfa36b6bead0f8215ad071d5922ee6c4a5", size = 424023, upload-time = "2026-05-06T02:30:57.732Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/a1/3651fb32fa8617108204aa4056d283af742020e0987d106f41402005d800/optree-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d9d198343e1e6ced18bef0cbff84091c1877964fc4a121df33f18840e073a01", size = 394782, upload-time = "2026-05-06T02:30:59.239Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/1e/676470909aa64d7aba7c5edf83b171dc83b7af901d9ebb8e6d7512fe913a/optree-0.19.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a1202371d9fe3aa75f3e886b1f871aac4991a655aadb65e54f58a3ae9388ab2", size = 413157, upload-time = "2026-05-06T02:31:00.339Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/41/1a4c58f2af5742b9d9e21ea9e45c6c3c49463b5e2a0537e84ead1e9597ca/optree-0.19.1-cp312-cp312-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:d41ccc4c20bfeae01d1d221c057a6d026e84e32229664952eddcdbe4b9b71417", size = 476923, upload-time = "2026-05-06T02:31:01.492Z" },
+ { url = "https://files.pythonhosted.org/packages/10/c1/f62167bd9d6f6c948b191a0943923404678d47100f777f4a8fb37816e6f8/optree-0.19.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d934f240b109c6891dd06b2e30400b123b8a4b6ed31dcd0db2ae2378d30a6e8", size = 475385, upload-time = "2026-05-06T02:31:02.836Z" },
+ { url = "https://files.pythonhosted.org/packages/30/5e/5323c5fa3024fdd900bdd8f14621139ed844c2247bf1a26e7cf5c1116188/optree-0.19.1-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ddeefb7ca799c09647e332ebc1a5f6c09888a5a0e51f2dff4ca55e65b42a8c14", size = 474406, upload-time = "2026-05-06T02:31:04.023Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/6a/54e4c47e61a51504a5224c933722e0c8a69925aacec4c08175e9675aeb81/optree-0.19.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0ce49f64f804f7f35f2f9c2a21e3ba94c090199fccdcfd40e3ded4426c5c175", size = 457596, upload-time = "2026-05-06T02:31:05.695Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/12/bba07c0b769586c6bd54e81f1f734cad103dbe30abbadee940fe7d3e330e/optree-0.19.1-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:e0f02600832ab8d0f6c934dcb5c339e17a36938d477641a45798e02625ebe107", size = 417900, upload-time = "2026-05-06T02:31:07.251Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/8f/6ae994bb47f9394b33912a14593f9247737dd6c3303811550e5a3e918107/optree-0.19.1-cp312-cp312-win32.whl", hash = "sha256:f10d58c1a17e1b32f9d9b5e1b9d1ad964d99c1113d9df0b9f62f2fe7dde19909", size = 317302, upload-time = "2026-05-06T02:31:08.627Z" },
+ { url = "https://files.pythonhosted.org/packages/31/97/d7e3ec79dcdde81f785a0446acf75fea77723f5ca4b98556350d7877986f/optree-0.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:06f5c8a4cf356a1a276ce5cec1be44719ed260690f79c036d04b4d427e801258", size = 341362, upload-time = "2026-05-06T02:31:09.689Z" },
+ { url = "https://files.pythonhosted.org/packages/33/97/813afb84a81fd8ae65444730907c05f0775fd6c79d3359c9e84bd3370445/optree-0.19.1-cp312-cp312-win_arm64.whl", hash = "sha256:a33bd23fc5c67ecb9ff491b75fde10cd9b53f47f8a876de842090e8c7a2437e1", size = 351838, upload-time = "2026-05-06T02:31:11.086Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/7b/0f2f3c9d55dda5127624daf68ff802ab624b739dd4b32aef505dac0c8e02/optree-0.19.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:f144cfd65fb17c6aa2c51818614eb009e6052d3d6ace91f7e570b1318cdcac4c", size = 929090, upload-time = "2026-05-06T02:31:12.267Z" },
+ { url = "https://files.pythonhosted.org/packages/15/e2/670d260dfd0532d64272dd6f7edd540a09d7040c0342b6cc6cf773568ea4/optree-0.19.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:39a006735d2a0a68751a3bc33d670184fddcd86db63b0293e1e819739e8105e4", size = 391528, upload-time = "2026-05-06T02:31:14.212Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/96/46c15e80b0c97e2ba6aba11339008a37cabc5ccf55c31c6c60aecdb79638/optree-0.19.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d2cb43c36638f469f5d8f4cf638e914de90c62242d8bed29f1b4487e0346ab94", size = 398231, upload-time = "2026-05-06T02:31:15.519Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/39/9d7d22cdaeb9a40ace2485f91c5b7c5f3a7f688575e2621e436561211cc1/optree-0.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e70faa00ab69331f49f8337d45021bed09ae2265d1db72eea9d7817af2b73c64", size = 429852, upload-time = "2026-05-06T02:31:16.992Z" },
+ { url = "https://files.pythonhosted.org/packages/79/4c/1da9e8375e7b7fd9671dc5987682b042f6412c4d6fd9da03296403818d9f/optree-0.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1c5d21176b670407f4555aae40711668832599c4fb0627000c5ce3ed0d6e2dae", size = 398688, upload-time = "2026-05-06T02:31:18.113Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/50/cd2d178099618093f5a9fd1c9de80af2b428879922eae1e9f27f1002c8be/optree-0.19.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f658fa46305b2bdccdc5bb2cb07818aeaef88a1085499deda5be48a0a58d2971", size = 417560, upload-time = "2026-05-06T02:31:19.391Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/b0/f22ff5632083b5032caa80208dd202f8e963ed4aac11afa0a0f6a307fd68/optree-0.19.1-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:e757079d44a00319447f43df5c51e55bf9b62d9f05eea0e2db5ff7c7ca5ec71d", size = 482937, upload-time = "2026-05-06T02:31:20.799Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/d4/7499d28be8b11eb40668262d27802119fe7e6ec4cd8816b76a1acd7b08f5/optree-0.19.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9690c132822d9dee479cf7dff8cc52a67c8af42a4f7529d21f0f4f1d99e4c84e", size = 477864, upload-time = "2026-05-06T02:31:22.077Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/6e/6c6fa6f1159ac68f4ee7666610127fb4c14d47a2fa7a0a48de3aecc24d4b/optree-0.19.1-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:544b70958dbd7e732bc6874e0180c609c9052115937d0ec28123bb49c1a574aa", size = 478319, upload-time = "2026-05-06T02:31:23.266Z" },
+ { url = "https://files.pythonhosted.org/packages/68/b5/8a2427bbe4ee59e2ce26a14125728e3b48c7030c80984ba07d0e5d804d37/optree-0.19.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9dde5b756946c1f1458aeab248a7a9b0c01bb06b5787de9f06d52ad38b745557", size = 462379, upload-time = "2026-05-06T02:31:24.543Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/0c/a073eeaea4d4f68e02d5883ed8268746a296e6749e3c46e0124ca45f306c/optree-0.19.1-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:f1d7838e8b1b62258abd73a5911afad1153ed76822070558c3ba7e0bb5b44192", size = 423061, upload-time = "2026-05-06T02:31:25.652Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/34/637b151d071ca94aea0087322f470ce84c5828ef6b9c0de7dc7b4420a1cf/optree-0.19.1-cp313-cp313-win32.whl", hash = "sha256:9870d33ec50cca0c46c2b431cea24c6247457da15fd4ad66ccb8ab78145c1490", size = 317439, upload-time = "2026-05-06T02:31:27.304Z" },
+ { url = "https://files.pythonhosted.org/packages/50/52/49b8a8d9e94c57c6fa5008953f84a1c36a4119a3b90dcb7df745f1f05a00/optree-0.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:aa0845b725bcd0029e179cf9b4bc2cc016c7358e56fc7c0d2c43bf4d514c96cf", size = 343906, upload-time = "2026-05-06T02:31:28.774Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/a9/1ae0a9685f5301f454f01d2490065b98df6956f90b1b2fd1cea9daa6d820/optree-0.19.1-cp313-cp313-win_arm64.whl", hash = "sha256:6f0b1efc177bed6495f78d39d5aa495ccb31cc20bcf64bb1b806ca4c919f4049", size = 353146, upload-time = "2026-05-06T02:31:29.976Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/77/4c8108cbce2c8ae2aa4b6adc7874082882e32cf131cb64b3a4411f50dec4/optree-0.19.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b964bcdb5cfe367cdf56447e80ba5a49123098d8c4e8e68b41c20890eec6e58e", size = 469723, upload-time = "2026-05-06T02:31:31.425Z" },
+ { url = "https://files.pythonhosted.org/packages/64/33/ce9b54646ed4ab5773a9dc59767dadfe3de8bb2e97a3ed19205b995a7a31/optree-0.19.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:08ccec0ee5a565eb5aa4fe30383016a358627ea23d968ec8ab28b1f2ce4ce3d8", size = 437071, upload-time = "2026-05-06T02:31:33.027Z" },
+ { url = "https://files.pythonhosted.org/packages/79/55/04260128a726e3550b49467a65bff859452897144b68bae54b2f2e5c27f1/optree-0.19.1-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:672588408906051d3e9a99aca6c0af93c6e0b638137a701418088eaa0bb6c719", size = 433503, upload-time = "2026-05-06T02:31:34.423Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/99/6a4cc29389667efa089a0c476b7c36b7d0a66e10dd2d8c2d19c776977566/optree-0.19.1-cp313-cp313t-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:d16cef4d0555d49ce221d80249f1285a2d3faf932e451c3ce6cb8ccb6a846767", size = 496305, upload-time = "2026-05-06T02:31:35.835Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/46/506aa1a64abce69e2f4cec9cdac3da0cae207cf04c5e70e7f143bf8b29d8/optree-0.19.1-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dc2db0b449baff53aa7e583306101de0ade5e5ae9e6fce78400eb2319bbd23dc", size = 492759, upload-time = "2026-05-06T02:31:37.265Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/28/2210de9a68722007fe007da3cae1a5971b92fc8113b5eecef66a04637959/optree-0.19.1-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:76b3e9e5d37e6b05ec82fff91758c8c0e27e159b35faea4b33d5eb975d720257", size = 495447, upload-time = "2026-05-06T02:31:38.505Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/61/40c3463e52914d552c66c760ae15e673137c4cc1d1d9f8da0d745656193a/optree-0.19.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03faa8e23fdaf3a18f9a1568c2c0eb0641a6aa05baf3a20639bd11fb34664700", size = 475564, upload-time = "2026-05-06T02:31:39.732Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/66/1603680fa924e68e5697c1229510c0645db0a9c633a12d1a9bfdbfc9cb74/optree-0.19.1-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:a9b9c7e9148ec470124dc4c1d1cd1485dbeb35973357b5911b181a79090426d2", size = 442414, upload-time = "2026-05-06T02:31:40.908Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/58/34820bab11f28ba6b03fe9e151880ad591b43f26648f058c94451fbdfc3a/optree-0.19.1-cp313-cp313t-win32.whl", hash = "sha256:ab8ad9803376d553a2958471b6bb6842b7e15888e19cc6aeb76da96c6afd948d", size = 348644, upload-time = "2026-05-06T02:31:42.038Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/2b/0be3f8b9765f366e3e12d0590e9c6514de110d0c5b3b9002f49e56bf15b1/optree-0.19.1-cp313-cp313t-win_amd64.whl", hash = "sha256:afd4abeb2783b2367093287bc6268ac9af244b20c8d9b01696ccfe817483b66c", size = 382445, upload-time = "2026-05-06T02:31:43.166Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/fa/8c0882cdd42e28a23c1998297c8ad1202194510cbba8b050251429c641c0/optree-0.19.1-cp313-cp313t-win_arm64.whl", hash = "sha256:b9120510d3f951e268e417a3f64f335bc1c539e1e80bff2129ddc6fb60ac7b56", size = 388040, upload-time = "2026-05-06T02:31:44.661Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/da/4e16e26375c56c9e40760697af4e2b72f196c2099e96cc783b63dcc862a8/optree-0.19.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:e1951ddc870f67430310fd17393971c30510ee9fd290525b44c12afe25f3c307", size = 927808, upload-time = "2026-05-06T02:31:45.954Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/87/ff1c6bb6b79a5d0b70b83f7ae8b78811a406a749b3ae4478a2122a7afb66/optree-0.19.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ae9d42718ebf985cdad3182364b5cf829193b8fd2c6d993fbb4111d38e2bdf96", size = 390981, upload-time = "2026-05-06T02:31:47.38Z" },
+ { url = "https://files.pythonhosted.org/packages/82/25/fc648710102960f87d18cd8fc8a24afe14a5ec7827c64dfb1340230c0794/optree-0.19.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:930268ebfdebca43a8808f6293910d6ade2fe7c84fa784692017d7120d285226", size = 397756, upload-time = "2026-05-06T02:31:48.76Z" },
+ { url = "https://files.pythonhosted.org/packages/24/f6/a7bf5d75a6481038bbb61846d87d43124d63741385796ef7b37d326f46bd/optree-0.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:b2757c5d922aab76cfc9b870c373fb35209c2094e3c912733b326c043e85a0c6", size = 427424, upload-time = "2026-05-06T02:31:49.838Z" },
+ { url = "https://files.pythonhosted.org/packages/49/cc/14dd93887295859457e507fc46a847b68ae8f20c42b2fde4d8a749c94bbc/optree-0.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b17a7b70ff8bd406c2142914c5ab0a57f8bcfb9f52181f7012e32406bbdbfdda", size = 398242, upload-time = "2026-05-06T02:31:51.262Z" },
+ { url = "https://files.pythonhosted.org/packages/17/b5/ac51aa118dd918761519fbc031865b1d6f850453e9a7ac0c3da21109c4f0/optree-0.19.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:987bba55366917d9829f45b5ee86499ecc87a30e9103072db9ab8d67f9958179", size = 419568, upload-time = "2026-05-06T02:31:52.349Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/41/25144e61f76278b9e0a5d4189c7083fe853164c5f7328a1f5aac43d964c2/optree-0.19.1-cp314-cp314-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:d3bba2af7a5fce0c25e99024688e68dfe9be41e3d6e92720febbefdc879fba38", size = 482797, upload-time = "2026-05-06T02:31:53.471Z" },
+ { url = "https://files.pythonhosted.org/packages/22/47/2c76c7ce937323988770c41126e0e380bcb73a816f68a767f23b5c33aced/optree-0.19.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dae6c247cc8751bd2f167951468769f5c98f8cfdae31c0db0f2eb4145a6ec560", size = 479794, upload-time = "2026-05-06T02:31:54.843Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/ca/bd9553f94bec0bc7860f10ae177c14ca265ab19ddb463122be22fa335ee8/optree-0.19.1-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:17a986fd91ccdc18bb7b587ca1f508c1761580a93517e6db33a13b22e46acb9b", size = 481084, upload-time = "2026-05-06T02:31:56.261Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/1a/4834b1f2fb1847412353d7342eb7a1d001a4f3bd9d24155e057135a4aa44/optree-0.19.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3d0e1493429ae1d1a5e34855774ee604c974a8f76656bd0e562cdbf9466c9b1f", size = 462955, upload-time = "2026-05-06T02:31:57.829Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/88/598fb91c06fee3d8b08568779b011225dc2b66140927bd0b2b2d9b40a566/optree-0.19.1-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:f61a01ed9991193ed6f3db8e956ede05218190a32ca2ddfb71cfc40c8daba1d5", size = 423754, upload-time = "2026-05-06T02:31:59.291Z" },
+ { url = "https://files.pythonhosted.org/packages/20/8a/83c64ecadc686e08310fc9c20bc0bbe6453e89b69257e08887818dac7886/optree-0.19.1-cp314-cp314-win32.whl", hash = "sha256:b0c920579bddc3b18a0e051850f017618e24efcc19ba83dcd415cf74db5fd904", size = 325214, upload-time = "2026-05-06T02:32:00.802Z" },
+ { url = "https://files.pythonhosted.org/packages/96/c3/4f2f318b98465376bbb7a06a33da553c688b3ed39dafbb8307f824eef74a/optree-0.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:50d77b91a8cd01adf422472b7edf39fc445b0268816176a868a385d28f8367c2", size = 351654, upload-time = "2026-05-06T02:32:01.944Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/ab/55d7508e87055c730fe7207cfd0c45183a07ddf1f91d9e73d017a7f8c1f4/optree-0.19.1-cp314-cp314-win_arm64.whl", hash = "sha256:c682ab6711b7a623503711fa661a2bba7886e1c21dc06c3b7febba101b458051", size = 361610, upload-time = "2026-05-06T02:32:03.003Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/2d/4f7facd482d56079b7adb8ce3fede19f41629bc0463e8ee25907f1dba36c/optree-0.19.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:068edb89fadd94f6f57fdb51f4ad2c764b5a0bfd00903c55ffe433c2863a8037", size = 469130, upload-time = "2026-05-06T02:32:04.395Z" },
+ { url = "https://files.pythonhosted.org/packages/92/60/f7539012aa8a7488c1e34f66b76eadc384c3152dd9800973f1b5fe045dfd/optree-0.19.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a609c90e4f64e4f3e2b5b3cc022210314834737e0e61a745485e33b33eae773b", size = 437286, upload-time = "2026-05-06T02:32:05.527Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/3f/a5f8fb3ec3840f885de52d7a793ba57ace17990e3a9b3797218425ffe842/optree-0.19.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfae64c4c371640a4b3e2a9e3e6aa3a3e8cdf2da5247a88fef5b632614b948a6", size = 431954, upload-time = "2026-05-06T02:32:06.83Z" },
+ { url = "https://files.pythonhosted.org/packages/68/dc/6d0ef14bc82bd54046c1a066d25fa6854123a6b29fd691f1f95dec3ab45f/optree-0.19.1-cp314-cp314t-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:470742544ff2d4b63843023f38dcfb83e82c3a9877c783dee0e69cbb974de6d1", size = 494631, upload-time = "2026-05-06T02:32:08.038Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/9a/9e183c610c414cba581f9afda7610589d89cae229d627b14f8480425d975/optree-0.19.1-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1a74e0656ccef45b1fec07b9d964ce97f3def8bab73711f56175076c4259884f", size = 491786, upload-time = "2026-05-06T02:32:09.363Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/73/266b9de8eb5b16bfe7010c90c55840517d5d61ee6e0ca64901440296d97a/optree-0.19.1-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f55841132ba8a34dbbd85e0c2cf990602384eea0e4638df986cd3266482f4a17", size = 490876, upload-time = "2026-05-06T02:32:11.388Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/8d/42a8ca6277ef93d47ab0986e30a25134206afe0c6e6c3425c8736b2677ba/optree-0.19.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5f8383952f18d5a4ec6b248d8ae6fe27012434ad9750aa33a821ad4846da5af", size = 475079, upload-time = "2026-05-06T02:32:12.768Z" },
+ { url = "https://files.pythonhosted.org/packages/63/91/e363f4adda292f891ca0cf5748010fea955737bdf494cc11d4c3bcda6935/optree-0.19.1-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:de8acbed5965beae6f6b0456fcb8d1afaea1fe300810739e88645e22138849bc", size = 440119, upload-time = "2026-05-06T02:32:14.096Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/eb/489d22ef3cadb2f5f3bbd6e6099d17b5a521ff533e086f78f005c3358017/optree-0.19.1-cp314-cp314t-win32.whl", hash = "sha256:312048e69dc88de26915674f961bf38980a765a6b48ead2f1672858a39402c41", size = 357465, upload-time = "2026-05-06T02:32:15.424Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/34/7f48b7034ff75d2eb3e94e2196709ddbf762798fb621f9508899fa66b44e/optree-0.19.1-cp314-cp314t-win_amd64.whl", hash = "sha256:60e9345405d7b06cafdf1b1dd2e2261ceddddce10f35729240f90e2bab845a0b", size = 397783, upload-time = "2026-05-06T02:32:16.853Z" },
+ { url = "https://files.pythonhosted.org/packages/07/42/6d6f93416c66820cb8753e65b5ff43c47480af9c4911bd2b8406ff0f7f27/optree-0.19.1-cp314-cp314t-win_arm64.whl", hash = "sha256:4e103e212d1e8fe0399ed076eff80a905fb14929729bbd994d3660110a27a252", size = 396064, upload-time = "2026-05-06T02:32:18.077Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/d4/ffeedc86f8b91e5c17994f38bd1f7aa2e20f9b70a6d3ae906af16414626c/optree-0.19.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3f4f1c276fa06227cdaf58349d22a3231b3dd3d47de1f90a86222ebf831fc397", size = 417543, upload-time = "2026-05-06T02:32:32.592Z" },
+ { url = "https://files.pythonhosted.org/packages/52/0b/80fb1b289940e34858cb89f05bc7ce23d6d1272886c2f78bc7e3ab1a306b/optree-0.19.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:77d93eafbd0046c7350bc592ab8e3814abbd39a6d716b5b1e5d652cc486f445c", size = 390184, upload-time = "2026-05-06T02:32:34.273Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/67/f31784a7a2dcc0c1f84b691afc552ea5b26db5f56657692a12954a828db4/optree-0.19.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3507ae5db5827eef3da42d04c5a41df649cedc2e42d5d39dc0f869d36915a00b", size = 409025, upload-time = "2026-05-06T02:32:35.817Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/a5/647b93eb16244cc7f6dfccc025ac495245e306ff4cb8f9ad15718219141a/optree-0.19.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:732c4581fb666869b8b391ec4ca13d2729795f9abe72b5aec2e582bcbea1975d", size = 449514, upload-time = "2026-05-06T02:32:37.014Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/8e/d251c9338771ef0f9db8e538bd77810100c495734b57494464c7e223f0d0/optree-0.19.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e12ee3776a16f6feaa8263b92469ad546b870af71d50602745855d8449219221", size = 341586, upload-time = "2026-05-06T02:32:38.308Z" },
+]
+
+[[package]]
+name = "packaging"
+version = "26.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
+]
+
+[[package]]
+name = "pandas"
+version = "2.3.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+ { name = "python-dateutil" },
+ { name = "pytz" },
+ { name = "tzdata" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" },
+ { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" },
+ { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" },
+ { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" },
+ { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" },
+ { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" },
+ { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" },
+ { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" },
+ { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" },
+ { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" },
+ { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" },
+ { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" },
+ { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" },
+ { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" },
+ { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" },
+ { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" },
+ { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" },
+ { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" },
+ { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" },
+ { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" },
+ { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" },
+ { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" },
+ { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" },
+]
+
+[package.optional-dependencies]
+hdf5 = [
+ { name = "tables", version = "3.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tables", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+performance = [
+ { name = "bottleneck" },
+ { name = "numba" },
+ { name = "numexpr" },
+]
+
+[[package]]
+name = "parso"
+version = "0.8.7"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/30/4b/90c937815137d43ce71ba043cd3566221e9df6b9c805f24b5d138c9d40a7/parso-0.8.7.tar.gz", hash = "sha256:eaaac4c9fdd5e9e8852dc778d2d7405897ec510f2a298071453e5e3a07914bb1", size = 401824, upload-time = "2026-05-01T23:13:02.138Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl", hash = "sha256:a8926eb2a1b915486941fdbd31e86a4baf88fe8c210f25f2f35ecec5b574ca1c", size = 107025, upload-time = "2026-05-01T23:12:58.867Z" },
+]
+
+[[package]]
+name = "partd"
+version = "1.4.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "locket" },
+ { name = "toolz" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b2/3a/3f06f34820a31257ddcabdfafc2672c5816be79c7e353b02c1f318daa7d4/partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c", size = 21029, upload-time = "2024-05-06T19:51:41.945Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f", size = 18905, upload-time = "2024-05-06T19:51:39.271Z" },
+]
+
+[[package]]
+name = "patsy"
+version = "1.0.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/be/44/ed13eccdd0519eff265f44b670d46fbb0ec813e2274932dc1c0e48520f7d/patsy-1.0.2.tar.gz", hash = "sha256:cdc995455f6233e90e22de72c37fcadb344e7586fb83f06696f54d92f8ce74c0", size = 399942, upload-time = "2025-10-20T16:17:37.535Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl", hash = "sha256:37bfddbc58fcf0362febb5f54f10743f8b21dd2aa73dec7e7ef59d1b02ae668a", size = 233301, upload-time = "2025-10-20T16:17:36.563Z" },
+]
+
+[[package]]
+name = "pexpect"
+version = "4.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "ptyprocess" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" },
+]
+
+[[package]]
+name = "pillow"
+version = "12.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", size = 5354355, upload-time = "2026-04-01T14:42:15.402Z" },
+ { url = "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", size = 4695871, upload-time = "2026-04-01T14:42:18.234Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", size = 6269734, upload-time = "2026-04-01T14:42:20.608Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", size = 8076080, upload-time = "2026-04-01T14:42:23.095Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", size = 6382236, upload-time = "2026-04-01T14:42:25.82Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", size = 7070220, upload-time = "2026-04-01T14:42:28.68Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", size = 6493124, upload-time = "2026-04-01T14:42:31.579Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", size = 7194324, upload-time = "2026-04-01T14:42:34.615Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", size = 6376363, upload-time = "2026-04-01T14:42:37.19Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", size = 7083523, upload-time = "2026-04-01T14:42:39.62Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", size = 2463318, upload-time = "2026-04-01T14:42:42.063Z" },
+ { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" },
+ { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" },
+ { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" },
+ { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" },
+ { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" },
+ { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" },
+ { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" },
+ { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" },
+ { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" },
+ { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" },
+ { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" },
+ { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" },
+ { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" },
+ { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" },
+ { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" },
+ { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" },
+ { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" },
+ { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" },
+ { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" },
+ { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" },
+ { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" },
+ { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" },
+ { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" },
+ { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" },
+ { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" },
+ { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" },
+ { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" },
+]
+
+[[package]]
+name = "pims"
+version = "0.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "imageio" },
+ { name = "numpy" },
+ { name = "packaging" },
+ { name = "slicerator" },
+ { name = "tifffile", version = "2025.5.10", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz", hash = "sha256:55907a4c301256086d2aa4e34a5361b9109f24e375c2071e1117b9491e82946b", size = 87779, upload-time = "2024-06-10T19:20:42.842Z" }
+
+[[package]]
+name = "pint"
+version = "0.24.4"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "flexcache", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "flexparser", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "platformdirs", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/20/bb/52b15ddf7b7706ed591134a895dbf6e41c8348171fb635e655e0a4bbb0ea/pint-0.24.4.tar.gz", hash = "sha256:35275439b574837a6cd3020a5a4a73645eb125ce4152a73a2f126bf164b91b80", size = 342225, upload-time = "2024-11-07T16:29:46.061Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/16/bd2f5904557265882108dc2e04f18abc05ab0c2b7082ae9430091daf1d5c/Pint-0.24.4-py3-none-any.whl", hash = "sha256:aa54926c8772159fcf65f82cc0d34de6768c151b32ad1deb0331291c38fe7659", size = 302029, upload-time = "2024-11-07T16:29:43.976Z" },
+]
+
+[[package]]
+name = "pint"
+version = "0.25.3"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "flexcache", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "flexparser", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "platformdirs", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/52/9d/b1379cdbd33a49d17d627bc24e2b63cca06a1c5343b38072d2889499e82e/pint-0.25.3.tar.gz", hash = "sha256:f8f5df6cf65314d74da1ade1bf96f8e3e4d0c41b51577ac53c49e7d44ca5acee", size = 255106, upload-time = "2026-03-19T21:57:08.72Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1b/dd/a9fe6a0a09512da23951c68bf36466aeecd89def3183dc095edbc807ddc5/pint-0.25.3-py3-none-any.whl", hash = "sha256:27eb25143bd5de9fcc4d5a4b484f16faf6b4615aa93ece6b3373a8c1a3c1b97d", size = 307488, upload-time = "2026-03-19T21:57:07.022Z" },
+]
+
+[[package]]
+name = "platformdirs"
+version = "4.9.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
+]
+
+[[package]]
+name = "pooch"
+version = "1.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+ { name = "platformdirs" },
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/83/43/85ef45e8b36c6a48546af7b266592dc32d7f67837a6514d111bced6d7d75/pooch-1.9.0.tar.gz", hash = "sha256:de46729579b9857ffd3e741987a2f6d5e0e03219892c167c6578c0091fb511ed", size = 61788, upload-time = "2026-01-30T19:15:09.649Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl", hash = "sha256:f265597baa9f760d25ceb29d0beb8186c243d6607b0f60b83ecf14078dbc703b", size = 67175, upload-time = "2026-01-30T19:15:08.36Z" },
+]
+
+[[package]]
+name = "pre-commit"
+version = "4.6.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cfgv" },
+ { name = "identify" },
+ { name = "nodeenv" },
+ { name = "pyyaml" },
+ { name = "virtualenv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" },
+]
+
+[[package]]
+name = "prompt-toolkit"
+version = "3.0.52"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "wcwidth" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" },
+]
+
+[[package]]
+name = "protobuf"
+version = "4.25.9"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d9/8e/d08c41a8c004e1d437ef467e7c4f9c3295cd784eba48ed5d1d01f94b1dad/protobuf-4.25.9.tar.gz", hash = "sha256:b0dc7e7c68de8b1ce831dacb12fb407e838edbb8b6cc0dc3a2a6b4cbf6de9cff", size = 381040, upload-time = "2026-03-25T23:09:36.423Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a8/e9/59435bd04bdd46cb38c42a336b22f9843e8e586ff83c35a5423f8b14704e/protobuf-4.25.9-cp310-abi3-win32.whl", hash = "sha256:bde396f568b0b46fc8fbfe9f02facf25b6755b2578a3b8ac61e74b9d69499e03", size = 392879, upload-time = "2026-03-25T23:09:21.32Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/16/42a5c7f1001783d2b5bfcecde10127f09010f78982c86ae409122ce3ece6/protobuf-4.25.9-cp310-abi3-win_amd64.whl", hash = "sha256:3683c05154252206f7cb2d371626514b3708199d9bcf683b503dabf3a2e38e06", size = 413900, upload-time = "2026-03-25T23:09:23.589Z" },
+ { url = "https://files.pythonhosted.org/packages/56/5b/0074a0a9eb01f3d1c4648ca5e81b22090c811b210b61df9018ac6d6c5cda/protobuf-4.25.9-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:9560813560e6ee72c11ca8873878bdb7ee003c96a57ebb013245fe84e2540904", size = 394826, upload-time = "2026-03-25T23:09:25.194Z" },
+ { url = "https://files.pythonhosted.org/packages/54/aa/b2dba856f64c36b2a06c67be1472de98cca07a2322d0f0cbf03279a40e5b/protobuf-4.25.9-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:999146ef02e7fa6a692477badd1528bcd7268df211852a3df2d834ba2b480791", size = 294191, upload-time = "2026-03-25T23:09:26.613Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/5c/53f18822017b8bda6bd8bb4e02048e911fdc79a3dafdc83ab994fe922a84/protobuf-4.25.9-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:438c636de8fb706a0de94a12a268ef1ae8f5ba5ae655a7671fcda5968ba3c9be", size = 295178, upload-time = "2026-03-25T23:09:27.839Z" },
+ { url = "https://files.pythonhosted.org/packages/16/28/d5065b212685875d3924bcdb3201cbf467cb4d58a18aa19a8dfd99ea80a9/protobuf-4.25.9-py3-none-any.whl", hash = "sha256:d49b615e7c935194ac161f0965699ac84df6112c378e05ec53da65d2e4cbb6d4", size = 156822, upload-time = "2026-03-25T23:09:34.957Z" },
+]
+
+[[package]]
+name = "protobuf"
+version = "5.29.6"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7e/57/394a763c103e0edf87f0938dafcd918d53b4c011dfc5c8ae80f3b0452dbb/protobuf-5.29.6.tar.gz", hash = "sha256:da9ee6a5424b6b30fd5e45c5ea663aef540ca95f9ad99d1e887e819cdf9b8723", size = 425623, upload-time = "2026-02-04T22:54:40.584Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d4/88/9ee58ff7863c479d6f8346686d4636dd4c415b0cbeed7a6a7d0617639c2a/protobuf-5.29.6-cp310-abi3-win32.whl", hash = "sha256:62e8a3114992c7c647bce37dcc93647575fc52d50e48de30c6fcb28a6a291eb1", size = 423357, upload-time = "2026-02-04T22:54:25.805Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/66/2dc736a4d576847134fb6d80bd995c569b13cdc7b815d669050bf0ce2d2c/protobuf-5.29.6-cp310-abi3-win_amd64.whl", hash = "sha256:7e6ad413275be172f67fdee0f43484b6de5a904cc1c3ea9804cb6fe2ff366eda", size = 435175, upload-time = "2026-02-04T22:54:28.592Z" },
+ { url = "https://files.pythonhosted.org/packages/06/db/49b05966fd208ae3f44dcd33837b6243b4915c57561d730a43f881f24dea/protobuf-5.29.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:b5a169e664b4057183a34bdc424540e86eea47560f3c123a0d64de4e137f9269", size = 418619, upload-time = "2026-02-04T22:54:30.266Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/d7/48cbf6b0c3c39761e47a99cb483405f0fde2be22cf00d71ef316ce52b458/protobuf-5.29.6-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:a8866b2cff111f0f863c1b3b9e7572dc7eaea23a7fae27f6fc613304046483e6", size = 320284, upload-time = "2026-02-04T22:54:31.782Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/dd/cadd6ec43069247d91f6345fa7a0d2858bef6af366dbd7ba8f05d2c77d3b/protobuf-5.29.6-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:e3387f44798ac1106af0233c04fb8abf543772ff241169946f698b3a9a3d3ab9", size = 320478, upload-time = "2026-02-04T22:54:32.909Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/cb/e3065b447186cb70aa65acc70c86baf482d82bf75625bf5a2c4f6919c6a3/protobuf-5.29.6-py3-none-any.whl", hash = "sha256:6b9edb641441b2da9fa8f428760fc136a49cf97a52076010cf22a2ff73438a86", size = 173126, upload-time = "2026-02-04T22:54:39.462Z" },
+]
+
+[[package]]
+name = "protobuf"
+version = "6.33.6"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" },
+ { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" },
+ { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" },
+]
+
+[[package]]
+name = "psutil"
+version = "7.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" },
+ { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" },
+ { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" },
+ { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" },
+ { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" },
+ { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" },
+ { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" },
+ { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" },
+]
+
+[[package]]
+name = "psygnal"
+version = "0.15.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4e/79/20c3e23e75272e9ddf018097cf872ab088bccba978888472656629efa4a3/psygnal-0.15.1.tar.gz", hash = "sha256:f64f62dee2306fc1c22050a59b6c6cdad126e04b0cf50e393ff858a1da719096", size = 123147, upload-time = "2026-01-04T16:38:41.959Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e6/44/ab13cb6147d010258826a43e574ad94599af0de29df13795fff9efee656c/psygnal-0.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ee55e3997f796fd84d4fdbd829bb1b19d323e087c43d072744604a3016c8851", size = 587322, upload-time = "2026-01-04T16:38:04.827Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/a2/68c042a607ca613e9450dfee99cc5c2a4d10d95392fb1de2ba932dd0a605/psygnal-0.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:912bcf110bfe7b4aa121d24987b6a58afb967ff090a049dad136eaf3cbcc7bea", size = 576207, upload-time = "2026-01-04T16:38:06.183Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/86/123c7b169ad32994a0cd801cd1f11c1a2be84555807e9c8a8a4682c67a9f/psygnal-0.15.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b2e860c11fe075fd80c93a24081c577ef7ec5c9da41f0e75990aa4cccf3f79cf", size = 864261, upload-time = "2026-01-04T16:38:07.895Z" },
+ { url = "https://files.pythonhosted.org/packages/20/f1/886cec7bec2f27fe453cfa32bfcaac08a83aab2a04895af68f93e1c493b8/psygnal-0.15.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d5b8bebcf99699ef50b6ef572868a490f6d191dc4466e5bd9818ca27e17cd581", size = 872582, upload-time = "2026-01-04T16:38:09.745Z" },
+ { url = "https://files.pythonhosted.org/packages/21/a3/da972a05568ee8a9dc6c6567bee2c0cc5af8c681baebcb9fdbbf3cceb4f7/psygnal-0.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:06e0a90490e1205620d97ac52fbbe3282a22b126a26d02b3e1196bb46de16c7a", size = 411043, upload-time = "2026-01-04T16:38:11.588Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/a7/69495410025cc4298765545ce3b8c635cd4c8d3a362b7fbbc15b80e9fc8f/psygnal-0.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1adc41515f648696990964433f1e25d8dfd306813a3645366c85e01986ba57a0", size = 581002, upload-time = "2026-01-04T16:38:12.753Z" },
+ { url = "https://files.pythonhosted.org/packages/75/1f/19a8126ccf3cd3974ba5d08a435a049b666961d90f5848ba83599d7a29de/psygnal-0.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:38ff18455b2ac73d4e8eea82ef298ce904b52e4dfdc603a24380c9c440e37519", size = 567775, upload-time = "2026-01-04T16:38:14.04Z" },
+ { url = "https://files.pythonhosted.org/packages/54/c5/b1348880d603edb82128a721397a1ddcf3dfcf5384fe5689db6e471118ae/psygnal-0.15.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c923c322eeefb1140886927cfe7bda7c32341087e290e812b9c69a624ab72d54", size = 855961, upload-time = "2026-01-04T16:38:15.612Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/42/3da2d6f3583bd1a849f7faa2fd3492b14bfda05012519ceaea5992658af0/psygnal-0.15.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2714ddaa41ea3134c0ee91cebd5fb11a88f254ea1d5948806ab0ad5f8be603d5", size = 862721, upload-time = "2026-01-04T16:38:17.059Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/14/6fc7e97fdecf7e8c5c105684bab784920312a3259800d8b53e3cf8783f42/psygnal-0.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:877516056a5a383427a647fff2fad5179eaa3e12de2c083c273e748435414aef", size = 415696, upload-time = "2026-01-04T16:38:18.355Z" },
+ { url = "https://files.pythonhosted.org/packages/76/65/b7bbca96bc477aa9ac2264e5907b2f4ccfcd1319f776dd1f35eec06cc2f4/psygnal-0.15.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d56f0f35eaf4a21f660de76885222faf9e8c7112454528d3394d464f3d4d1a3", size = 598340, upload-time = "2026-01-04T16:38:19.752Z" },
+ { url = "https://files.pythonhosted.org/packages/40/f2/56577465a1b42a5e6780bb5fab53fb68f8bfd72f0131ed397576529af724/psygnal-0.15.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0febcf757a1323d9b8bd75735ee3569213d8110012a7bf0f478e85c5ab459fc6", size = 575311, upload-time = "2026-01-04T16:38:21.137Z" },
+ { url = "https://files.pythonhosted.org/packages/79/81/f642ac08104049383076f83480ed412c9626e068769a1c34873c595bec0e/psygnal-0.15.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b5e4837dfbfa4974dabe0795e32be9aadcd87603adf734738ce1114f72238a05", size = 889770, upload-time = "2026-01-04T16:38:22.629Z" },
+ { url = "https://files.pythonhosted.org/packages/de/43/e571fa40b72780abed080ef829e5ad98017b6fe48d28c15a2404e006b676/psygnal-0.15.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:07b4c4e03bbf4e8cad7e25f4fbc1ba9575fb9c3d14991bc7edfeb8b09c8d6d54", size = 881105, upload-time = "2026-01-04T16:38:23.896Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/26/ef3ab825eb08eaecbbceeeb56383694fe64ce399dbfd1d0767bb85688785/psygnal-0.15.1-cp312-cp312-win_amd64.whl", hash = "sha256:4f0ce91b9c18e92281bf2c3fc4bb4e808d90f0b023d0a37b302d354188520338", size = 418969, upload-time = "2026-01-04T16:38:25.731Z" },
+ { url = "https://files.pythonhosted.org/packages/46/21/5a142165d27063abf5921807d3c3d973f5d44ab414a13b210839a43ead4d/psygnal-0.15.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2087aadc9404f007f79c2899e329932869e362c50de58b90631c5f49b4768cc5", size = 596768, upload-time = "2026-01-04T16:38:27.053Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/25/c1712931d61c118691e73daf29ef708c679ea9ba187c797dd5deee360411/psygnal-0.15.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f3bf68ca42569dfdce20c6cf915d34b78b9e3ddddacb9f78728224fda6946b4", size = 574808, upload-time = "2026-01-04T16:38:28.779Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/4f/3593e5adb88a188c798604aed95fbc1479f30230e7f51e8f2c770e6a3832/psygnal-0.15.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e9fca977f5335deea39aed22e31d9795983e4f243e59a7d3c4105793adb7693d", size = 885616, upload-time = "2026-01-04T16:38:30.081Z" },
+ { url = "https://files.pythonhosted.org/packages/58/4c/14779ed4c3a1d71fa1a9a87ecfb184ad3335dd64681067f77c1c47b14ae9/psygnal-0.15.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c85b7d05b92ccbec47c75ab8a5545eda462e81a492c82424aba5ab81a3ad89d", size = 876516, upload-time = "2026-01-04T16:38:31.422Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/bc/4f771e3cdcde4db4023dbf36d6f0aab44e02b9de719353c22954b655e2ff/psygnal-0.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:ac0e693b29e0a429e97315a52313321855bef6140e9975b7ae78b4d93c8fbb42", size = 419172, upload-time = "2026-01-04T16:38:32.82Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/2e/975bd61727578d88df62797f78390965ca7905780cf01eb59cb095a13638/psygnal-0.15.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:803fc33c4280c822c6f4b22e6c3ea7c4483e190f3cc69e69350098b3799476f3", size = 595706, upload-time = "2026-01-04T16:38:34.139Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/55/e487f1d91497eb75e86c3fdfef69a21b1cab24d023383dd7648b08797d6a/psygnal-0.15.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4f53b4b83355b0a785b745987fd04e59bbf169a9028ed81a68ca7e05fb76d458", size = 575133, upload-time = "2026-01-04T16:38:35.448Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/2f/f286355accd0e68d3eef52e63c8b9ab6ba33ec3107177719a036b3319657/psygnal-0.15.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bcbca12190f5aa65c1f8fb04a81fa6f4463c5f5dde25cd74c3a56ceff6f37b02", size = 889565, upload-time = "2026-01-04T16:38:37.003Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/dc/40c6026c88d7f9220ecc913afe0501045a512c9b82f9b7e036bb089dc287/psygnal-0.15.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1ac399566852fe4354ce26a1acbe12319232e8c2b615fe5ad1e114c547095cf6", size = 880863, upload-time = "2026-01-04T16:38:38.381Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/85/b4f45ec3057c473b5622fc002b3a636a698c34d3a0917a064ff5247f1984/psygnal-0.15.1-cp314-cp314-win_amd64.whl", hash = "sha256:d3a03055f331ce91d44581c71edb79938ccc133a94af2ce7ad3a18fa57ac7be5", size = 423654, upload-time = "2026-01-04T16:38:39.7Z" },
+ { url = "https://files.pythonhosted.org/packages/46/49/7742544684bee728ec123515d2694cee859aa2a705951a461230b00f18cc/psygnal-0.15.1-py3-none-any.whl", hash = "sha256:4221140e633e45b076953c64bcb9b41a744833527f9a037c1ca98bc270798cbf", size = 90638, upload-time = "2026-01-04T16:38:40.841Z" },
+]
+
+[[package]]
+name = "ptyprocess"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" },
+]
+
+[[package]]
+name = "pure-eval"
+version = "0.2.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" },
+]
+
+[[package]]
+name = "py-cpuinfo"
+version = "9.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/37/a8/d832f7293ebb21690860d2e01d8115e5ff6f2ae8bbdc953f0eb0fa4bd2c7/py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690", size = 104716, upload-time = "2022-10-25T20:38:06.303Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335, upload-time = "2022-10-25T20:38:27.636Z" },
+]
+
+[[package]]
+name = "pyarrow"
+version = "24.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a5/bf/a34fee1d624152124fa8355c42f34195ad5fe5233ce5bb87946432047d52/pyarrow-24.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb", size = 35076681, upload-time = "2026-04-21T08:51:46.845Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/41/64180033d7027afce12dc96d0fe1f504c6fa112190582b458acea2399530/pyarrow-24.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147", size = 36684260, upload-time = "2026-04-21T08:51:53.642Z" },
+ { url = "https://files.pythonhosted.org/packages/57/02/9b9320e673dd8a99411fac78690f3df92f6dd6f59754c750110bca66d64e/pyarrow-24.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c", size = 45698566, upload-time = "2026-04-21T10:46:02.133Z" },
+ { url = "https://files.pythonhosted.org/packages/67/33/f75e91b9a64c3f33c787e263c93b871ad91b8a4a68c1d5cebddd9840e835/pyarrow-24.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041", size = 48835562, upload-time = "2026-04-21T10:46:10.278Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/63/097510448e47e4091faa41c43ba92f97cecaab8f4535b56a3d149578f634/pyarrow-24.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491", size = 49394997, upload-time = "2026-04-21T10:46:18.08Z" },
+ { url = "https://files.pythonhosted.org/packages/60/6b/c047d6222ab279024a062742d1807e2fbaf27bba88a98637299ff47b9236/pyarrow-24.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1", size = 51911424, upload-time = "2026-04-21T10:46:25.347Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/ba/464cc70761c2a525d97ebd84e21c31ebd47f3ef4bdcee117009f51c46f24/pyarrow-24.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591", size = 27251730, upload-time = "2026-04-21T10:46:30.913Z" },
+ { url = "https://files.pythonhosted.org/packages/62/c9/a47ab7ece0d86cbe6678418a0fbd1ac4bb493b9184a3891dfa0e7f287ae0/pyarrow-24.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74", size = 35068898, upload-time = "2026-04-21T10:46:36.599Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/bc/8db86617a9a58008acf8913d6fed68ea2a46acb6de928db28d724c891a68/pyarrow-24.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3", size = 36679915, upload-time = "2026-04-21T10:46:42.602Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/8e/fb178720400ef69db251eb4a9c3ccf4af269bc1feb5055529b8fc87170d1/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868", size = 45697931, upload-time = "2026-04-21T10:46:48.403Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/27/99c42abe8e21b44f4917f62631f3aa31404882a2c41d8a4cd5c110e13d52/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e", size = 48837449, upload-time = "2026-04-21T10:46:55.329Z" },
+ { url = "https://files.pythonhosted.org/packages/36/b6/333749e2666e9032891125bf9c691146e92901bece62030ac1430e2e7c88/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57", size = 49395949, upload-time = "2026-04-21T10:47:01.869Z" },
+ { url = "https://files.pythonhosted.org/packages/17/25/c5201706a2dd374e8ba6ee3fd7a8c89fb7ffc16eed5217a91fd2bd7f7626/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c", size = 51912986, upload-time = "2026-04-21T10:47:09.872Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/d2/4d1bbba65320b21a49678d6fbdc6ff7c649251359fdcfc03568c4136231d/pyarrow-24.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981", size = 27255371, upload-time = "2026-04-21T10:47:15.943Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" },
+ { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" },
+ { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" },
+ { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759, upload-time = "2026-04-21T10:48:07.258Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471, upload-time = "2026-04-21T10:48:13.347Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981, upload-time = "2026-04-21T10:48:20.201Z" },
+ { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172, upload-time = "2026-04-21T10:48:27.541Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733, upload-time = "2026-04-21T10:48:34.7Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335, upload-time = "2026-04-21T10:48:42.099Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748, upload-time = "2026-04-21T10:49:42.532Z" },
+ { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554, upload-time = "2026-04-21T10:48:48.526Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301, upload-time = "2026-04-21T10:48:55.181Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929, upload-time = "2026-04-21T10:49:03.676Z" },
+ { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365, upload-time = "2026-04-21T10:49:11.714Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819, upload-time = "2026-04-21T10:49:21.474Z" },
+ { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252, upload-time = "2026-04-21T10:49:31.164Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/80/d022a34ff05d2cbedd8ccf841fc1f532ecfa9eb5ed1711b56d0e0ea71fc9/pyarrow-24.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838", size = 35007997, upload-time = "2026-04-21T10:49:48.796Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/ff/f01485fda6f4e5d441afb8dd5e7681e4db18826c1e271852f5d3957d6a80/pyarrow-24.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b", size = 36678720, upload-time = "2026-04-21T10:49:55.858Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/c2/2d2d5fea814237923f71b36495211f20b43a1576f9a4d6da7e751a64ec6f/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795", size = 45741852, upload-time = "2026-04-21T10:50:04.624Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/3a/28ba9c1c1ebdbb5f1b94dfebb46f207e52e6a554b7fe4132540fde29a3a0/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26", size = 48889852, upload-time = "2026-04-21T10:50:12.293Z" },
+ { url = "https://files.pythonhosted.org/packages/df/51/4a389acfd31dca009f8fb82d7f510bb4130f2b3a8e18cf00194d0687d8ac/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde", size = 49445207, upload-time = "2026-04-21T10:50:20.677Z" },
+ { url = "https://files.pythonhosted.org/packages/19/4b/0bab2b23d2ae901b1b9a03c0efd4b2d070256f8ce3fc43f6e58c167b2081/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76", size = 51954117, upload-time = "2026-04-21T10:50:29.14Z" },
+ { url = "https://files.pythonhosted.org/packages/29/88/f4e9145da0417b3d2c12035a8492b35ff4a3dbc653e614fcfb51d9dedb38/pyarrow-24.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e", size = 28001155, upload-time = "2026-04-21T10:51:22.337Z" },
+ { url = "https://files.pythonhosted.org/packages/79/4f/46a49a63f43526da895b1a45bbb51d5baf8e4d77159f8528fc3e5490007f/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05", size = 35250387, upload-time = "2026-04-21T10:50:35.552Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/da/d5e0cd5ef00796922404806d5f00325cdadc3441ce2c13fe7115f2df9a64/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a", size = 36797102, upload-time = "2026-04-21T10:50:42.417Z" },
+ { url = "https://files.pythonhosted.org/packages/34/c7/5904145b0a593a05236c882933d439b5720f0a145381179063722fbfc123/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072", size = 45745118, upload-time = "2026-04-21T10:50:49.324Z" },
+ { url = "https://files.pythonhosted.org/packages/13/d3/cca42fe166d1c6e4d5b80e530b7949104d10e17508a90ae202dac205ce2a/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931", size = 48844765, upload-time = "2026-04-21T10:50:55.579Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/49/942c3b79878ba928324d1e17c274ed84581db8c0a749b24bcf4cbdf15bd3/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699", size = 49471890, upload-time = "2026-04-21T10:51:02.439Z" },
+ { url = "https://files.pythonhosted.org/packages/76/97/ff71431000a75d84135a1ace5ca4ba11726a231a8007bbb320a4c54075d5/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136", size = 51932250, upload-time = "2026-04-21T10:51:10.576Z" },
+ { url = "https://files.pythonhosted.org/packages/51/be/6f79d55816d5c22557cf27533543d5d70dfe692adfbee4b99f2760674f38/pyarrow-24.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19", size = 28131282, upload-time = "2026-04-21T10:51:16.815Z" },
+]
+
+[[package]]
+name = "pyasn1"
+version = "0.6.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" },
+]
+
+[[package]]
+name = "pyasn1-modules"
+version = "0.4.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyasn1", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" },
+]
+
+[[package]]
+name = "pybtex"
+version = "0.26.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "latexcodec" },
+ { name = "pyyaml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4d/f5/f30da9c93f0fa6d619332b2f69597219b625f35780473a05164a9981fd9a/pybtex-0.26.1.tar.gz", hash = "sha256:2e5543bea424e60e9e42eef70bff597be48649d8f68ba061a7a092b2477d5464", size = 692991, upload-time = "2026-04-03T13:05:39.014Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/44/f6/775eb92e865b28cdb4ad1f2bed7a5446197516f76b58a950faa3be3fd08d/pybtex-0.26.1-py3-none-any.whl", hash = "sha256:e26c0412cc54f5f21b2a6d9d175762a2d2af9ccf3a8f651cdb89ec035db77aa1", size = 126134, upload-time = "2026-04-03T13:05:40.623Z" },
+]
+
+[[package]]
+name = "pybtex-docutils"
+version = "1.0.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "docutils" },
+ { name = "pybtex" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7e/84/796ea94d26188a853660f81bded39f8de4cfe595130aef0dea1088705a11/pybtex-docutils-1.0.3.tar.gz", hash = "sha256:3a7ebdf92b593e00e8c1c538aa9a20bca5d92d84231124715acc964d51d93c6b", size = 18348, upload-time = "2023-08-22T18:47:54.833Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/11/b1/ce1f4596211efb5410e178a803f08e59b20bedb66837dcf41e21c54f9ec1/pybtex_docutils-1.0.3-py3-none-any.whl", hash = "sha256:8fd290d2ae48e32fcb54d86b0efb8d573198653c7e2447d5bec5847095f430b9", size = 6385, upload-time = "2023-08-22T06:43:20.513Z" },
+]
+
+[[package]]
+name = "pycocotools"
+version = "2.0.11"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a2/df/32354b5dda963ffdfc8f75c9acf8828ef7890723a4ed57bb3ff2dc1d6f7e/pycocotools-2.0.11.tar.gz", hash = "sha256:34254d76da85576fcaf5c1f3aa9aae16b8cb15418334ba4283b800796bd1993d", size = 25381, upload-time = "2025-12-15T22:31:46.148Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dd/4b/0c040fcda2c4fa4827b1a64e3185d99d5f954e45cc9463ba7385a1173a77/pycocotools-2.0.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:484d33515353186aadba9e2a290d81b107275cdb9565084e31a5568a52a0b120", size = 160351, upload-time = "2025-12-15T22:30:53.998Z" },
+ { url = "https://files.pythonhosted.org/packages/49/fe/861db6515824815eaabce27734653a6b100ddb22364b3345dd862b2c5b65/pycocotools-2.0.11-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ca9f120f719ec405ad0c74ccfdb8402b0c37bd5f88ab5b6482a0de2efd5a36f4", size = 463947, upload-time = "2025-12-15T22:30:55.419Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/a1/b4b49b85763043372e66baa10dffa42337cf4687d6db22546c27f3a4d732/pycocotools-2.0.11-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e40a3a898c6e5340b8d70cf7984868b9bff8c3d80187de9a3b661d504d665978", size = 472455, upload-time = "2025-12-15T22:30:56.895Z" },
+ { url = "https://files.pythonhosted.org/packages/48/70/fac670296e6a2b45eb7434d0480b9af6cb85a8de4f4848b49b01154bc859/pycocotools-2.0.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7cd4cdfd2c676f30838aa0b1047441892fb4f97d70bf3df480bcc7a18a64d7d4", size = 457911, upload-time = "2025-12-15T22:30:58.377Z" },
+ { url = "https://files.pythonhosted.org/packages/33/f5/6158de63354dfcb677c8da34a4d205cc532e3277338ab7e6dea1310ba8de/pycocotools-2.0.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08c79789fd79e801ae4ecfcfeec32b31e36254e7a2b4019af28c104975d5e730", size = 476472, upload-time = "2025-12-15T22:30:59.736Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/01/46d2a782cda19ba1beb7c431f417e1e478f0bf1273fa5fe5d10de7c18d76/pycocotools-2.0.11-cp310-cp310-win_amd64.whl", hash = "sha256:f78cbb1a32d061fcad4bdba083de70a39a21c1c3d9235a3f77d8f007541ec5ef", size = 80165, upload-time = "2025-12-15T22:31:00.886Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/5c/6bd945781bb04c2148929183d1d67b05ce07996313b0f87bb88c6a805493/pycocotools-2.0.11-cp310-cp310-win_arm64.whl", hash = "sha256:e21311ea71f85591680d8992858e2d44a2a156dc3b2bf1c5c901c4a19348177b", size = 69358, upload-time = "2025-12-15T22:31:01.815Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/3f/41ce3fce61b7721158f21b61727eb054805babc0088cfa48506935b80a36/pycocotools-2.0.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:81bdceebb4c64e9265213e2d733808a12f9c18dfb14457323cc6b9af07fa0e61", size = 158947, upload-time = "2025-12-15T22:31:03.291Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/9b/a739705b246445bd1376394bf9d1ec2dd292b16740e92f203461b2bb12ed/pycocotools-2.0.11-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c05f91ccc658dfe01325267209c4b435da1722c93eeb5749fabc1d087b6882", size = 485174, upload-time = "2025-12-15T22:31:04.395Z" },
+ { url = "https://files.pythonhosted.org/packages/34/70/7a12752784e57d8034a76c245c618a2f88a9d2463862b990f314aea7e5d6/pycocotools-2.0.11-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18ba75ff58cedb33a85ce2c18f1452f1fe20c9dd59925eec5300b2bf6205dbe1", size = 493172, upload-time = "2025-12-15T22:31:05.504Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/fc/d703599ac728209dba08aea8d4bee884d5adabfcd9041abed1658d863747/pycocotools-2.0.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:693417797f0377fd094eb815c0a1e7d1c3c0251b71e3b3779fce3b3cf24793c5", size = 480506, upload-time = "2025-12-15T22:31:06.77Z" },
+ { url = "https://files.pythonhosted.org/packages/81/d9/e1cfc320bbb2cd58c3b4398c3821cbe75d93c16ed3135ac9e774a18a02d3/pycocotools-2.0.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b6a07071c441d0f5e480a8f287106191582e40289d4e242dfe684e0c8a751088", size = 497595, upload-time = "2025-12-15T22:31:08.277Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/23/d17f6111c2a6ae8631d4fa90202bea05844da715d61431fbc34d276462d5/pycocotools-2.0.11-cp311-cp311-win_amd64.whl", hash = "sha256:8e159232adae3aef6b4e2d37b008bff107b26e9ed3b48e70ea6482302834bd34", size = 80519, upload-time = "2025-12-15T22:31:09.613Z" },
+ { url = "https://files.pythonhosted.org/packages/00/4c/76b00b31a724c3f5ccdab0f85e578afb2ca38d33be0a0e98f1770cafd958/pycocotools-2.0.11-cp311-cp311-win_arm64.whl", hash = "sha256:4fc9889e819452b9c142036e1eabac8a13a8bd552d8beba299a57e0da6bfa1ec", size = 69304, upload-time = "2025-12-15T22:31:10.592Z" },
+ { url = "https://files.pythonhosted.org/packages/87/12/2f2292332456e4e4aba1dec0e3de8f1fc40fb2f4fdb0ca1cb17db9861682/pycocotools-2.0.11-cp312-abi3-macosx_10_13_universal2.whl", hash = "sha256:a2e9634bc7cadfb01c88e0b98589aaf0bd12983c7927bde93f19c0103e5441f4", size = 147795, upload-time = "2025-12-15T22:31:11.519Z" },
+ { url = "https://files.pythonhosted.org/packages/63/3c/68d7ea376aada9046e7ea2d7d0dad0d27e1ae8b4b3c26a28346689390ab2/pycocotools-2.0.11-cp312-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fd4121766cc057133534679c0ec3f9023dbd96e9b31cf95c86a069ebdac2b65", size = 398434, upload-time = "2025-12-15T22:31:12.558Z" },
+ { url = "https://files.pythonhosted.org/packages/23/59/dc81895beff4e1207a829d40d442ea87cefaac9f6499151965f05c479619/pycocotools-2.0.11-cp312-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a82d1c9ed83f75da0b3f244f2a3cf559351a283307bd9b79a4ee2b93ab3231dd", size = 411685, upload-time = "2025-12-15T22:31:13.995Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/0b/5a8a7de300862a2eb5e2ecd3cb015126231379206cd3ebba8f025388d770/pycocotools-2.0.11-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:89e853425018e2c2920ee0f2112cf7c140a1dcf5f4f49abd9c2da112c3e0f4b3", size = 390500, upload-time = "2025-12-15T22:31:15.138Z" },
+ { url = "https://files.pythonhosted.org/packages/63/b5/519bb68647f06feea03d5f355c33c05800aeae4e57b9482b2859eb00752e/pycocotools-2.0.11-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:87af87b8d06d5b852a885a319d9362dca3bed9f8bbcc3feb6513acb1f88ea242", size = 409790, upload-time = "2025-12-15T22:31:16.326Z" },
+ { url = "https://files.pythonhosted.org/packages/83/b4/f6708404ff494706b80e714b919f76dc4ec9845a4007affd6d6b0843f928/pycocotools-2.0.11-cp312-abi3-win_amd64.whl", hash = "sha256:ffe806ce535f5996445188f9a35643791dc54beabc61bd81e2b03367356d604f", size = 77570, upload-time = "2025-12-15T22:31:17.703Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/63/778cd0ddc9d4a78915ac0a72b56d7fb204f7c3fabdad067d67ea0089762e/pycocotools-2.0.11-cp312-abi3-win_arm64.whl", hash = "sha256:c230f5e7b14bd19085217b4f40bba81bf14a182b150b8e9fab1c15d504ade343", size = 64564, upload-time = "2025-12-15T22:31:18.652Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/78/31c81e99d596a20c137d8a2e7a25f39a88f88fada5e0b253fce7323ecf0d/pycocotools-2.0.11-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:fd72b9734e6084b217c1fc3945bfd4ec05bdc75a44e4f0c461a91442bb804973", size = 168931, upload-time = "2025-12-15T22:31:19.845Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/63/fdd488e4cd0fdc6f93134f2cd68b1fce441d41566e86236bf6156961ef9b/pycocotools-2.0.11-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7eb43b79448476b094240450420b7425d06e297880144b8ea6f01e9b4340e43", size = 484856, upload-time = "2025-12-15T22:31:21.231Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/fc/c83648a8fb7ea3b8e2ce2e761b469807e6cadb81577bf1af31c4f2ef0d87/pycocotools-2.0.11-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c3546b93b39943347c4f5b0694b5824105cbe2174098a416bcad4acd9c21e957", size = 480994, upload-time = "2025-12-15T22:31:22.426Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/2d/35e1122c0d007288aa9545be9549cbc7a4987b2c22f21d75045260a8b5b8/pycocotools-2.0.11-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:efd1694b2075f2f10c5828f10f6e6c4e44368841fd07dae385c3aa015c8e25f9", size = 467956, upload-time = "2025-12-15T22:31:23.754Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/ff/30cfe8142470da3e45abe43a9842449ca0180d993320559890e2be19e4a5/pycocotools-2.0.11-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:368244f30eb8d6cae7003aa2c0831fbdf0153664a32859ec7fbceea52bfb6878", size = 474658, upload-time = "2025-12-15T22:31:24.883Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/62/254ca92604106c7a5af3258e589e465e681fe0166f9b10f97d8ca70934d6/pycocotools-2.0.11-cp313-cp313t-win_amd64.whl", hash = "sha256:ac8aa17263e6489aa521f9fa91e959dfe0ea3a5519fde2cbf547312cdce7559e", size = 89681, upload-time = "2025-12-15T22:31:26.025Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/f0/c019314dc122ad5e6281de420adc105abe9b59d00008f72ef3ad32b1e328/pycocotools-2.0.11-cp313-cp313t-win_arm64.whl", hash = "sha256:04480330df5013f6edd94891a0ee8294274185f1b5093d1b0f23d51778f0c0e9", size = 70520, upload-time = "2025-12-15T22:31:26.999Z" },
+ { url = "https://files.pythonhosted.org/packages/66/2b/58b35c88f2086c043ff1c87bd8e7bf36f94e84f7b01a5e00b6f5fabb92a7/pycocotools-2.0.11-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a6b13baf6bfcf881b6d6ac6e23c776f87a68304cd86e53d1d6b9afa31e363c4e", size = 169883, upload-time = "2025-12-15T22:31:28.233Z" },
+ { url = "https://files.pythonhosted.org/packages/24/c0/b970eefb78746c8b4f8b3fa1b49d9f3ec4c5429ef3c5d4bbcc55abebe478/pycocotools-2.0.11-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78bae4a9de9d34c4759754a848dfb3306f9ef1c2fcb12164ffbd3d013d008321", size = 486894, upload-time = "2025-12-15T22:31:29.283Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/f7/db7436820a1948d96fa9764b6026103e808840979be01246049f2c1e7f94/pycocotools-2.0.11-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83d896f4310379849dfcfa7893afb0ff21f4f3cdb04ab3f61b05dd98953dd0ad", size = 483249, upload-time = "2025-12-15T22:31:31.687Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/a6/a14a12c9f50c41998fdc0d31fd3755bcbce124bac9abb1d6b99d1853cafd/pycocotools-2.0.11-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:eebd723503a2eb2c8b285f56ea3be1d9f3875cd7c40d945358a428db94f14015", size = 469070, upload-time = "2025-12-15T22:31:32.821Z" },
+ { url = "https://files.pythonhosted.org/packages/46/de/aa4f65ece3da8e89310a1be00cad0700170fd13f41a3aaae2712291269d5/pycocotools-2.0.11-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bd7a1e19ef56a828a94bace673372071d334a9232cd32ae3cd48845a04d45c4f", size = 475589, upload-time = "2025-12-15T22:31:34.188Z" },
+ { url = "https://files.pythonhosted.org/packages/44/6f/04a30df03ae6236b369b361df0c50531d173d03678978806aa2182e02d1e/pycocotools-2.0.11-cp314-cp314t-win_amd64.whl", hash = "sha256:63026e11a56211058d0e84e8263f74cbccd5e786fac18d83fd221ecb9819fcc7", size = 93863, upload-time = "2025-12-15T22:31:35.38Z" },
+ { url = "https://files.pythonhosted.org/packages/da/05/8942b640d6307a21c3ede188e8c56f07bedf246fac0e501437dbda72a350/pycocotools-2.0.11-cp314-cp314t-win_arm64.whl", hash = "sha256:8cedb8ccb97ffe9ed2c8c259234fa69f4f1e8665afe3a02caf93f6ef2952c07f", size = 72038, upload-time = "2025-12-15T22:31:36.768Z" },
+]
+
+[[package]]
+name = "pyconify"
+version = "0.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fd/7f/94d424dc756a6287271cf40cf1b2a44c10e3f137bf3246a2b4a7416ca3d3/pyconify-0.2.1.tar.gz", hash = "sha256:8dd53757d9fbed41711434460932b2b5dbc25da720cd9f9a44af0187b2dfc07d", size = 22478, upload-time = "2025-02-06T13:20:53.592Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/72/40/50dd2e8bfec81676e4619903bd452c10dc0d8efac1533e79e67cc76759b5/pyconify-0.2.1-py3-none-any.whl", hash = "sha256:d3b53eee1f8a2d60c1d135610f42e789774dbe71c6d8af68af0a21d3b3ec9eb7", size = 19459, upload-time = "2025-02-06T13:20:51.613Z" },
+]
+
+[[package]]
+name = "pycparser"
+version = "3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
+]
+
+[[package]]
+name = "pydantic"
+version = "2.13.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-types" },
+ { name = "pydantic-core" },
+ { name = "typing-extensions" },
+ { name = "typing-inspection" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" },
+]
+
+[[package]]
+name = "pydantic-compat"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9c/7e/43400b6e0800065a982efcfa3e87c8f8d247d60ea75ca1a9d01702e050f8/pydantic_compat-0.1.2.tar.gz", hash = "sha256:c5c5bca39ca2d22cad00c02898e400e1920e5127649a8e860637f15566739373", size = 12838, upload-time = "2023-10-24T23:25:09.544Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7f/65/2edf586ff7b3dfc520977c6529c9b718c86ef8459ece088f1ef1f74bf1d4/pydantic_compat-0.1.2-py3-none-any.whl", hash = "sha256:37a4df48565a35aedc947f0fde5edbdff270a30836d995923287292bb59d5677", size = 13092, upload-time = "2023-10-24T23:25:08.155Z" },
+]
+
+[[package]]
+name = "pydantic-core"
+version = "2.46.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" },
+ { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" },
+ { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" },
+ { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" },
+ { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" },
+ { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" },
+ { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" },
+ { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" },
+ { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" },
+ { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" },
+ { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" },
+ { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" },
+ { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" },
+ { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" },
+ { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" },
+ { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" },
+ { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" },
+ { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" },
+ { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" },
+ { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" },
+ { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" },
+ { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" },
+ { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" },
+ { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" },
+ { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" },
+ { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" },
+ { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" },
+ { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" },
+ { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" },
+ { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" },
+ { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" },
+ { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" },
+ { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" },
+ { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" },
+ { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" },
+ { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" },
+ { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" },
+ { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" },
+ { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" },
+ { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" },
+ { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" },
+ { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" },
+ { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" },
+ { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" },
+]
+
+[[package]]
+name = "pydantic-extra-types"
+version = "2.11.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/71/dba38ee2651f84f7842206adbd2233d8bbdb59fb85e9fa14232486a8c471/pydantic_extra_types-2.11.1.tar.gz", hash = "sha256:46792d2307383859e923d8fcefa82108b1a141f8a9c0198982b3832ab5ef1049", size = 172002, upload-time = "2026-03-16T08:08:03.92Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/17/c1/3226e6d7f5a4f736f38ac11a6fbb262d701889802595cdb0f53a885ac2e0/pydantic_extra_types-2.11.1-py3-none-any.whl", hash = "sha256:1722ea2bddae5628ace25f2aa685b69978ef533123e5638cfbddb999e0100ec1", size = 79526, upload-time = "2026-03-16T08:08:02.533Z" },
+]
+
+[[package]]
+name = "pydata-sphinx-theme"
+version = "0.15.4"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "accessible-pygments", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "babel", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "beautifulsoup4", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "docutils", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pygments", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "sphinx", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/67/ea/3ab478cccacc2e8ef69892c42c44ae547bae089f356c4b47caf61730958d/pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d", size = 2400673, upload-time = "2024-06-25T19:28:45.041Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e7/d3/c622950d87a2ffd1654208733b5bd1c5645930014abed8f4c0d74863988b/pydata_sphinx_theme-0.15.4-py3-none-any.whl", hash = "sha256:2136ad0e9500d0949f96167e63f3e298620040aea8f9c74621959eda5d4cf8e6", size = 4640157, upload-time = "2024-06-25T19:28:42.383Z" },
+]
+
+[[package]]
+name = "pydata-sphinx-theme"
+version = "0.16.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "accessible-pygments", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "babel", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "beautifulsoup4", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "docutils", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pygments", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "sphinx", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693, upload-time = "2024-12-17T10:53:39.537Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e2/0d/8ba33fa83a7dcde13eb3c1c2a0c1cc29950a048bfed6d9b0d8b6bd710b4c/pydata_sphinx_theme-0.16.1-py3-none-any.whl", hash = "sha256:225331e8ac4b32682c18fcac5a57a6f717c4e632cea5dd0e247b55155faeccde", size = 6723264, upload-time = "2024-12-17T10:53:35.645Z" },
+]
+
+[[package]]
+name = "pygments"
+version = "2.20.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
+]
+
+[[package]]
+name = "pyopengl"
+version = "3.1.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6f/16/912b7225d56284859cd9a672827f18be43f8012f8b7b932bc4bd959a298e/pyopengl-3.1.10.tar.gz", hash = "sha256:c4a02d6866b54eb119c8e9b3fb04fa835a95ab802dd96607ab4cdb0012df8335", size = 1915580, upload-time = "2025-08-18T02:33:01.76Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/de/e4/1ba6f44e491c4eece978685230dde56b14d51a0365bc1b774ddaa94d14cd/pyopengl-3.1.10-py3-none-any.whl", hash = "sha256:794a943daced39300879e4e47bd94525280685f42dbb5a998d336cfff151d74f", size = 3194996, upload-time = "2025-08-18T02:32:59.902Z" },
+]
+
+[[package]]
+name = "pyparsing"
+version = "3.3.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" },
+]
+
+[[package]]
+name = "pyproject-hooks"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" },
+]
+
+[[package]]
+name = "pyside6"
+version = "6.9.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyside6-addons" },
+ { name = "pyside6-essentials" },
+ { name = "shiboken6" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fe/15/1fa71e44d3b345b9eca82d38cf7e6d7505168b978fe98b3610c0e25d8c0e/pyside6-6.9.3-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:6fd9fbbc14e2a5707df611bfacbf3f71283c637b8a29b28708801eeb28bfcb69", size = 554720, upload-time = "2025-09-30T12:02:07.235Z" },
+ { url = "https://files.pythonhosted.org/packages/34/0e/1e9841cc46196c55ac3eac0b8e08044a88cc70c8cc29e9dc1e33b2ced2b7/pyside6-6.9.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6485aebec8eba4e55d1ec1cebe68ca1413589880cc8ccd8a49acae852ec6cfb3", size = 554849, upload-time = "2025-09-30T12:02:08.909Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/6b/6aeb7124a5c08ac537776d7d0be1011cf22ccfc6f95cf901fe1eb1a16e91/pyside6-6.9.3-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:8f4ff61b24a64153373b68a96339bd765fc010d02c4d98d0f6dba2a6c9686e11", size = 554846, upload-time = "2025-09-30T12:02:10.762Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/29/4eb0ab29ee7d60da4c5fefdb514c22ae3c91c3f855f46608cfbe23816518/pyside6-6.9.3-cp39-abi3-win_amd64.whl", hash = "sha256:71b41b9ebd1c044c3777f2b32278d3919f07bda4f15c504bc165b643bc3cec01", size = 561082, upload-time = "2025-09-30T12:02:12.411Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/c2/b936d50974a14846ccb883cbce0034ec431e1013af7f2aa29d3746a1565f/pyside6-6.9.3-cp39-abi3-win_arm64.whl", hash = "sha256:33fb70addd02e1adaa45573485c431bca43cf12bb7b2596535b824ff169138ce", size = 545910, upload-time = "2025-09-30T12:02:13.746Z" },
+]
+
+[[package]]
+name = "pyside6-addons"
+version = "6.9.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyside6-essentials" },
+ { name = "shiboken6" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/23/56/714c55e4514ec6603be8126355baf416e507557a73e7fc76870e6f5c20b9/pyside6_addons-6.9.3-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:189c9a9a2fdaffa95e91731f5c0afdc47ba231f5f683d3f8977b22c233749ba4", size = 316906068, upload-time = "2025-09-30T12:03:28.268Z" },
+ { url = "https://files.pythonhosted.org/packages/17/fe/d5c67665f866b8859d02aa1a859f101a1b2fd348cb61746a3e16fd98fb20/pyside6_addons-6.9.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:68932327e1c33d729d79b2b94242f97b77601efe0427e757cd3fd588939ea479", size = 167175405, upload-time = "2025-09-30T12:04:11.793Z" },
+ { url = "https://files.pythonhosted.org/packages/66/f2/66da0d8ba8e4eb934d2bb042f2199664d2366c121844e36376f724b53fd9/pyside6_addons-6.9.3-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:61f4f4859bd1711eea2202fe364a701597140ed4f3900ddf1f90d91ae631fdf9", size = 163102125, upload-time = "2025-09-30T12:04:55.193Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/fb/849599666942ce35e9f250bce1abc7b1248eccdcbe8b3fe697a0f98a1326/pyside6_addons-6.9.3-cp39-abi3-win_amd64.whl", hash = "sha256:0aece2a81ccf16ef9b750b09601b6876aa116bbc700e848ce82df42906f04c5c", size = 160681555, upload-time = "2025-09-30T12:05:36.44Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/2d/e71face0149519d6ded4e77b5c5e266c094ce5ef8c5c96c2af2f0afadf3c/pyside6_addons-6.9.3-cp39-abi3-win_arm64.whl", hash = "sha256:3b8749c9c2b297f53c980192e89ce38ac59019f290a2669fe1abb3128f9a09d9", size = 33759045, upload-time = "2025-09-30T12:05:48.373Z" },
+]
+
+[[package]]
+name = "pyside6-essentials"
+version = "6.9.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "shiboken6" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/80/a9/51549844900837c70e5c89cbd840956c2533732ef4f05c2b656e35ca8182/pyside6_essentials-6.9.3-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:ad3664ff0ced9f92ed7872e512c86328894d29f262e6c3400400232a36dda357", size = 134586559, upload-time = "2025-09-30T12:06:22.681Z" },
+ { url = "https://files.pythonhosted.org/packages/85/e8/9396cf11a60f80175bb3c5c1d498d84e87b7af653ab4ea001acf821a3981/pyside6_essentials-6.9.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c70d5544e892b201a677b615156fab6a0fef865e7fc287f55a0eae00a682e83f", size = 97495307, upload-time = "2025-09-30T12:06:49.213Z" },
+ { url = "https://files.pythonhosted.org/packages/57/a5/0187b2845a6fa534217988f9351c0b85ba1c04e8fd31f1e3c13ba1c4386e/pyside6_essentials-6.9.3-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:e22e517c592657e291c0cbe7d04078ab415cb225188d7e895f8f7adcdba755d2", size = 95199568, upload-time = "2025-09-30T12:07:13.726Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/52/b558c79bc32310361a37d1e894c2251a3557daec380be5f6d2a9223e8ef3/pyside6_essentials-6.9.3-cp39-abi3-win_amd64.whl", hash = "sha256:21f98077c135864473089e59a6a0bd828e64c6644b3dd7267b102da4a2ee8f21", size = 73593457, upload-time = "2025-09-30T12:07:33.98Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/22/4ec828f6360e6e9bcd6fde2dec20fa4865fe1a77cb6bac6812f7d0aa55b3/pyside6_essentials-6.9.3-cp39-abi3-win_arm64.whl", hash = "sha256:2e34081933e005686d79265cc04370a28fea3844ab63d432e493adcd4465070c", size = 54301788, upload-time = "2025-09-30T12:07:49.504Z" },
+]
+
+[[package]]
+name = "pytest"
+version = "9.0.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+ { name = "pygments" },
+ { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
+]
+
+[[package]]
+name = "pytest-cov"
+version = "7.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "coverage", extra = ["toml"] },
+ { name = "pluggy" },
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" },
+]
+
+[[package]]
+name = "python-dateutil"
+version = "2.9.0.post0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
+]
+
+[[package]]
+name = "pytz"
+version = "2026.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861, upload-time = "2026-05-04T01:35:29.667Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" },
+]
+
+[[package]]
+name = "pywin32"
+version = "311"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" },
+ { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" },
+ { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" },
+ { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" },
+ { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" },
+ { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" },
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" },
+ { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" },
+ { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
+ { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
+ { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
+ { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
+ { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
+ { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
+ { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
+ { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
+ { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
+ { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
+ { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
+ { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
+ { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
+ { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
+ { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
+ { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
+ { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
+ { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
+ { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
+ { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
+ { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
+ { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
+]
+
+[[package]]
+name = "pyzmq"
+version = "27.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi", marker = "implementation_name == 'pypy' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850, upload-time = "2025-09-08T23:07:26.274Z" },
+ { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380, upload-time = "2025-09-08T23:07:29.78Z" },
+ { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421, upload-time = "2025-09-08T23:07:31.263Z" },
+ { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149, upload-time = "2025-09-08T23:07:33.17Z" },
+ { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070, upload-time = "2025-09-08T23:07:35.205Z" },
+ { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441, upload-time = "2025-09-08T23:07:37.432Z" },
+ { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529, upload-time = "2025-09-08T23:07:39.047Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276, upload-time = "2025-09-08T23:07:40.695Z" },
+ { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208, upload-time = "2025-09-08T23:07:42.298Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766, upload-time = "2025-09-08T23:07:43.869Z" },
+ { url = "https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86", size = 1333328, upload-time = "2025-09-08T23:07:45.946Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/a0/fc7e78a23748ad5443ac3275943457e8452da67fda347e05260261108cbc/pyzmq-27.1.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581", size = 908803, upload-time = "2025-09-08T23:07:47.551Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/22/37d15eb05f3bdfa4abea6f6d96eb3bb58585fbd3e4e0ded4e743bc650c97/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f", size = 668836, upload-time = "2025-09-08T23:07:49.436Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/c4/2a6fe5111a01005fc7af3878259ce17684fabb8852815eda6225620f3c59/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e", size = 857038, upload-time = "2025-09-08T23:07:51.234Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/eb/bfdcb41d0db9cd233d6fb22dc131583774135505ada800ebf14dfb0a7c40/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e", size = 1657531, upload-time = "2025-09-08T23:07:52.795Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/21/e3180ca269ed4a0de5c34417dfe71a8ae80421198be83ee619a8a485b0c7/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2", size = 2034786, upload-time = "2025-09-08T23:07:55.047Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/b1/5e21d0b517434b7f33588ff76c177c5a167858cc38ef740608898cd329f2/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394", size = 1894220, upload-time = "2025-09-08T23:07:57.172Z" },
+ { url = "https://files.pythonhosted.org/packages/03/f2/44913a6ff6941905efc24a1acf3d3cb6146b636c546c7406c38c49c403d4/pyzmq-27.1.0-cp311-cp311-win32.whl", hash = "sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f", size = 567155, upload-time = "2025-09-08T23:07:59.05Z" },
+ { url = "https://files.pythonhosted.org/packages/23/6d/d8d92a0eb270a925c9b4dd039c0b4dc10abc2fcbc48331788824ef113935/pyzmq-27.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97", size = 633428, upload-time = "2025-09-08T23:08:00.663Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/14/01afebc96c5abbbd713ecfc7469cfb1bc801c819a74ed5c9fad9a48801cb/pyzmq-27.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07", size = 559497, upload-time = "2025-09-08T23:08:02.15Z" },
+ { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" },
+ { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" },
+ { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" },
+ { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436, upload-time = "2025-09-08T23:08:20.801Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301, upload-time = "2025-09-08T23:08:22.47Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197, upload-time = "2025-09-08T23:08:24.286Z" },
+ { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275, upload-time = "2025-09-08T23:08:26.063Z" },
+ { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469, upload-time = "2025-09-08T23:08:27.623Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961, upload-time = "2025-09-08T23:08:29.672Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282, upload-time = "2025-09-08T23:08:31.349Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468, upload-time = "2025-09-08T23:08:33.543Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394, upload-time = "2025-09-08T23:08:35.51Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964, upload-time = "2025-09-08T23:08:37.178Z" },
+ { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029, upload-time = "2025-09-08T23:08:40.595Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541, upload-time = "2025-09-08T23:08:42.668Z" },
+ { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197, upload-time = "2025-09-08T23:08:44.973Z" },
+ { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175, upload-time = "2025-09-08T23:08:46.601Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427, upload-time = "2025-09-08T23:08:48.187Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929, upload-time = "2025-09-08T23:08:49.76Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193, upload-time = "2025-09-08T23:08:51.7Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388, upload-time = "2025-09-08T23:08:53.393Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316, upload-time = "2025-09-08T23:08:55.702Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472, upload-time = "2025-09-08T23:08:58.18Z" },
+ { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401, upload-time = "2025-09-08T23:08:59.802Z" },
+ { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170, upload-time = "2025-09-08T23:09:01.418Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266, upload-time = "2025-09-08T23:09:40.048Z" },
+ { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206, upload-time = "2025-09-08T23:09:41.902Z" },
+ { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747, upload-time = "2025-09-08T23:09:43.741Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371, upload-time = "2025-09-08T23:09:45.575Z" },
+ { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862, upload-time = "2025-09-08T23:09:47.448Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/c6/c4dcdecdbaa70969ee1fdced6d7b8f60cfabe64d25361f27ac4665a70620/pyzmq-27.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066", size = 836265, upload-time = "2025-09-08T23:09:49.376Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/79/f38c92eeaeb03a2ccc2ba9866f0439593bb08c5e3b714ac1d553e5c96e25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604", size = 800208, upload-time = "2025-09-08T23:09:51.073Z" },
+ { url = "https://files.pythonhosted.org/packages/49/0e/3f0d0d335c6b3abb9b7b723776d0b21fa7f3a6c819a0db6097059aada160/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c", size = 567747, upload-time = "2025-09-08T23:09:52.698Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/cf/f2b3784d536250ffd4be70e049f3b60981235d70c6e8ce7e3ef21e1adb25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271", size = 747371, upload-time = "2025-09-08T23:09:54.563Z" },
+ { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862, upload-time = "2025-09-08T23:09:56.509Z" },
+]
+
+[[package]]
+name = "qdarkstyle"
+version = "3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "qtpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1a/1c/00ca31b13727ade22d1b42b61dc86056493a72f01912082a61cb34e5abf6/QDarkStyle-3.1.tar.gz", hash = "sha256:600584d625343e0ddd128de08393d3c35637786a49827f174d29aa7caa8279c1", size = 698602, upload-time = "2022-05-30T15:56:52.06Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/48/59/01f454d0eacb6670c77add68611b7a572455ae69ba902d270ed761869f87/QDarkStyle-3.1-py2.py3-none-any.whl", hash = "sha256:679a38fcd040de9fac8b8cae483310302fdb12c8d912845249c41dc54974a9b2", size = 870167, upload-time = "2022-05-30T15:56:49.502Z" },
+]
+
+[[package]]
+name = "qtconsole"
+version = "5.7.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "ipykernel" },
+ { name = "ipython-pygments-lexers" },
+ { name = "jupyter-client" },
+ { name = "jupyter-core" },
+ { name = "packaging" },
+ { name = "pygments" },
+ { name = "qtpy" },
+ { name = "traitlets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cd/28/4070eb0bacb99bc00bf60173fda25fb6a559d6600040035ba96c196d3647/qtconsole-5.7.2.tar.gz", hash = "sha256:27b485b9161925924c1d8e78e66bb342e6e3bc49bf675d0a67b49bad9c291521", size = 436661, upload-time = "2026-03-25T02:24:38.895Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7a/60/aba9f3c3f1f48c7fbdf03bed45b576a916cd09c08242939b5294b85e37b4/qtconsole-5.7.2-py3-none-any.whl", hash = "sha256:e1d1f6a792123363626e643a7a4ee561217773571043992693fba7eccfa89f95", size = 125883, upload-time = "2026-03-25T02:24:36.95Z" },
+]
+
+[[package]]
+name = "qtpy"
+version = "2.4.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/70/01/392eba83c8e47b946b929d7c46e0f04b35e9671f8bb6fc36b6f7945b4de8/qtpy-2.4.3.tar.gz", hash = "sha256:db744f7832e6d3da90568ba6ccbca3ee2b3b4a890c3d6fbbc63142f6e4cdf5bb", size = 66982, upload-time = "2025-02-11T15:09:25.759Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/69/76/37c0ccd5ab968a6a438f9c623aeecc84c202ab2fabc6a8fd927580c15b5a/QtPy-2.4.3-py3-none-any.whl", hash = "sha256:72095afe13673e017946cc258b8d5da43314197b741ed2890e563cf384b51aa1", size = 95045, upload-time = "2025-02-11T15:09:24.162Z" },
+]
+
+[[package]]
+name = "referencing"
+version = "0.37.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "rpds-py" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" },
+]
+
+[[package]]
+name = "requests"
+version = "2.34.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" },
+]
+
+[[package]]
+name = "requests-oauthlib"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "oauthlib", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "requests", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" },
+]
+
+[[package]]
+name = "rich"
+version = "15.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" },
+]
+
+[[package]]
+name = "rpds-py"
+version = "0.30.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" },
+ { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" },
+ { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" },
+ { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" },
+ { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" },
+ { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" },
+ { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" },
+ { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" },
+ { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" },
+ { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" },
+ { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" },
+ { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" },
+ { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" },
+ { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" },
+ { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" },
+ { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" },
+ { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" },
+ { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" },
+ { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" },
+ { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" },
+ { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" },
+ { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" },
+ { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" },
+ { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" },
+ { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" },
+ { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" },
+ { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" },
+ { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" },
+ { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" },
+ { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" },
+ { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" },
+ { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" },
+ { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" },
+ { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" },
+ { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" },
+ { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" },
+ { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" },
+ { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" },
+ { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" },
+ { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" },
+ { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" },
+ { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" },
+ { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" },
+ { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" },
+ { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" },
+ { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" },
+ { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" },
+ { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" },
+ { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" },
+ { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" },
+ { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" },
+ { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" },
+]
+
+[[package]]
+name = "ruamel-yaml"
+version = "0.19.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c7/3b/ebda527b56beb90cb7652cb1c7e4f91f48649fbcd8d2eb2fb6e77cd3329b/ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993", size = 142709, upload-time = "2026-01-02T16:50:31.84Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", size = 118102, upload-time = "2026-01-02T16:50:29.201Z" },
+]
+
+[[package]]
+name = "ruff"
+version = "0.15.13"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/24/21/a7d5c126d5b557715ef81098f3db2fe20f622a039ff2e626af28d674ab80/ruff-0.15.13.tar.gz", hash = "sha256:f9d89f17f7ba7fb2ed42921f0df75da797a9a5d71bc39049e2c687cf2baf44b7", size = 4678180, upload-time = "2026-05-14T13:44:37.869Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c6/61/11d458dc6ac22504fd8e237b29dfd40504c7fbbcc8930402cfe51a8e63ed/ruff-0.15.13-py3-none-linux_armv6l.whl", hash = "sha256:444b580fc72fd6887e650acd3e575e18cdc79dbcf42fb4030b491057921f61f8", size = 10738279, upload-time = "2026-05-14T13:44:18.7Z" },
+ { url = "https://files.pythonhosted.org/packages/86/ca/caa871ee7be718c45256fada4e16a218ee3e33f0c4a46b729a60a24912e6/ruff-0.15.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6590d009e7cb7ebf36f83dbdd44a3fa48a0994ff6f1cdc1b08006abe58f98dc7", size = 11124798, upload-time = "2026-05-14T13:44:06.427Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/19/43f5f2e568dddde567fc41f8471f9432c09563e19d3e617a48cfa52f8f0a/ruff-0.15.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1c26d2f66163deeb6e08d8b39fbbe983ce3c71cea06a6d7591cfd1421793c629", size = 10460761, upload-time = "2026-05-14T13:44:04.375Z" },
+ { url = "https://files.pythonhosted.org/packages/99/df/cf938cd6de3003178f03ad7c1ea2a6c099468c03a35037985070b37e76be/ruff-0.15.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbd6f94b434f896308e4d57fb7bfde0d02b99f7a64b3bdab0fdfa6a864203a5", size = 10804451, upload-time = "2026-05-14T13:44:25.221Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/7d/5d0973129b154ded2225729169d7068f26b467760b146493fde138415f23/ruff-0.15.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3259f3be4d181bda591da5db2571aed6853c6a048157756448020bc6c5cd22", size = 10534285, upload-time = "2026-05-14T13:44:08.888Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/e3/6b999bbc66cd51e5f073842bc2a3995e99c5e0e72e16b15e7261f7abf57a/ruff-0.15.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae9c17e5eb4430c154e76abc25d79a318190f5a997f38fb6b114416c5319ffc9", size = 11312063, upload-time = "2026-05-14T13:44:11.274Z" },
+ { url = "https://files.pythonhosted.org/packages/af/5a/642639e9f5db04f1e97fbd6e091c6fd20725bdf072fb114d00eefb9e6eb8/ruff-0.15.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e2e39bff6c341f4b577a21b801326fab0b11847f48fcaa83f00a113c9b3cb55", size = 12183079, upload-time = "2026-05-14T13:44:01.634Z" },
+ { url = "https://files.pythonhosted.org/packages/19/4c/7585735f6b53b0f12de13618b2f7d250a844f018822efc899df2e7b8295f/ruff-0.15.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8d9a8e08013542e94d3220bc5b62cc3e5ef87c5f74bff367d3fac14fab013e6", size = 11440833, upload-time = "2026-05-14T13:43:59.043Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/31/bf1a0803d077e679cfeee5f2f67290a0fa79c7385b5d9a8c17b9db2c48f0/ruff-0.15.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc411dfebe5eebe55ce041c6ae080eb7668955e866daa2fbb16692a784f1c4ca", size = 11434486, upload-time = "2026-05-14T13:44:27.761Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/4e/62c9b999875d4f14db80f277c030578f5e249c9852d65b7ac7ad0b43c041/ruff-0.15.13-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:768494eb08b9cee54e2fd27969966f74db5a57f6eaa7a90fcb3306af34dfc4bd", size = 11385189, upload-time = "2026-05-14T13:44:13.704Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/89/7e959047a104df3eb12863447c110140191fc5b6c4f379ea2e803fcdb0e4/ruff-0.15.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fb75f9a3a7e42ffe117d734494e6c5e5cb3565d66e12612cb63d0e572a41a5b6", size = 10781380, upload-time = "2026-05-14T13:43:56.734Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/52/5fd18f3b88cab63e88aa11516b3b4e1e5f720e5c330f8dbe5c26210f41f8/ruff-0.15.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8cb74dd33bb2f6613faf7fc03b660053b5ac4f80e706d5788c6335e2a8048d51", size = 10540605, upload-time = "2026-05-14T13:44:20.748Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/e0/9e35f338990d3e41a82875ff7053ffe97541dae81c9d02143177f381d572/ruff-0.15.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7ef823f817fcd191dc934e984be9cf4094f808effa16f2542ad8e821ba02bbf2", size = 11036554, upload-time = "2026-05-14T13:44:16.256Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/13/070fb048c24080fba188f66371e2a92785be257ad02242066dc7255ac6e9/ruff-0.15.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f345a13937bd7f09f6f5d19fa0721b0c103e00e7f62bc67089a8e5e037719e0b", size = 11528133, upload-time = "2026-05-14T13:44:22.808Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/8c/b1e1666aef7fc6555094d73ae6cd981701781ae85b97ceefc0eebd0b4668/ruff-0.15.13-py3-none-win32.whl", hash = "sha256:4044f94208b3b05ba0fc4a4abd0558cf4d6459bd18325eead7fd8cc66f909b41", size = 10721455, upload-time = "2026-05-14T13:44:35.697Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/a6/870a3e8a50590bb92be184ad928c2922f088b00d9dc5c5ec7b924ee08c22/ruff-0.15.13-py3-none-win_amd64.whl", hash = "sha256:7064884d442b7d477b4e7473d12da7f08851d2b1982763c5d3f388a19468a1a4", size = 11900409, upload-time = "2026-05-14T13:44:30.389Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/36/9c015cd052fca743dae8cb2aeb16b551444787467db42ceab0fc968865af/ruff-0.15.13-py3-none-win_arm64.whl", hash = "sha256:2471da9bd1068c8c064b5fd9c0c4b6dddffd6369cb1cd68b29993b1709ff1b21", size = 11179336, upload-time = "2026-05-14T13:44:33.026Z" },
+]
+
+[[package]]
+name = "safetensors"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" },
+ { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" },
+ { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" },
+ { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" },
+ { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430, upload-time = "2025-11-19T15:18:11.884Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977, upload-time = "2025-11-19T15:18:17.523Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890, upload-time = "2025-11-19T15:18:22.666Z" },
+ { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885, upload-time = "2025-11-19T15:18:27.146Z" },
+]
+
+[[package]]
+name = "scikit-image"
+version = "0.25.2"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "imageio", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "lazy-loader", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pillow", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tifffile", version = "2025.5.10", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c7/a8/3c0f256012b93dd2cb6fda9245e9f4bff7dc0486880b248005f15ea2255e/scikit_image-0.25.2.tar.gz", hash = "sha256:e5a37e6cd4d0c018a7a55b9d601357e3382826d3888c10d0213fc63bff977dde", size = 22693594, upload-time = "2025-02-18T18:05:24.538Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/11/cb/016c63f16065c2d333c8ed0337e18a5cdf9bc32d402e4f26b0db362eb0e2/scikit_image-0.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d3278f586793176599df6a4cf48cb6beadae35c31e58dc01a98023af3dc31c78", size = 13988922, upload-time = "2025-02-18T18:04:11.069Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ca/ff4731289cbed63c94a0c9a5b672976603118de78ed21910d9060c82e859/scikit_image-0.25.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5c311069899ce757d7dbf1d03e32acb38bb06153236ae77fcd820fd62044c063", size = 13192698, upload-time = "2025-02-18T18:04:15.362Z" },
+ { url = "https://files.pythonhosted.org/packages/39/6d/a2aadb1be6d8e149199bb9b540ccde9e9622826e1ab42fe01de4c35ab918/scikit_image-0.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be455aa7039a6afa54e84f9e38293733a2622b8c2fb3362b822d459cc5605e99", size = 14153634, upload-time = "2025-02-18T18:04:18.496Z" },
+ { url = "https://files.pythonhosted.org/packages/96/08/916e7d9ee4721031b2f625db54b11d8379bd51707afaa3e5a29aecf10bc4/scikit_image-0.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c464b90e978d137330be433df4e76d92ad3c5f46a22f159520ce0fdbea8a09", size = 14767545, upload-time = "2025-02-18T18:04:22.556Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/ee/c53a009e3997dda9d285402f19226fbd17b5b3cb215da391c4ed084a1424/scikit_image-0.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:60516257c5a2d2f74387c502aa2f15a0ef3498fbeaa749f730ab18f0a40fd054", size = 12812908, upload-time = "2025-02-18T18:04:26.364Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/97/3051c68b782ee3f1fb7f8f5bb7d535cf8cb92e8aae18fa9c1cdf7e15150d/scikit_image-0.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f4bac9196fb80d37567316581c6060763b0f4893d3aca34a9ede3825bc035b17", size = 14003057, upload-time = "2025-02-18T18:04:30.395Z" },
+ { url = "https://files.pythonhosted.org/packages/19/23/257fc696c562639826065514d551b7b9b969520bd902c3a8e2fcff5b9e17/scikit_image-0.25.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d989d64ff92e0c6c0f2018c7495a5b20e2451839299a018e0e5108b2680f71e0", size = 13180335, upload-time = "2025-02-18T18:04:33.449Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/14/0c4a02cb27ca8b1e836886b9ec7c9149de03053650e9e2ed0625f248dd92/scikit_image-0.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2cfc96b27afe9a05bc92f8c6235321d3a66499995675b27415e0d0c76625173", size = 14144783, upload-time = "2025-02-18T18:04:36.594Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/9b/9fb556463a34d9842491d72a421942c8baff4281025859c84fcdb5e7e602/scikit_image-0.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24cc986e1f4187a12aa319f777b36008764e856e5013666a4a83f8df083c2641", size = 14785376, upload-time = "2025-02-18T18:04:39.856Z" },
+ { url = "https://files.pythonhosted.org/packages/de/ec/b57c500ee85885df5f2188f8bb70398481393a69de44a00d6f1d055f103c/scikit_image-0.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:b4f6b61fc2db6340696afe3db6b26e0356911529f5f6aee8c322aa5157490c9b", size = 12791698, upload-time = "2025-02-18T18:04:42.868Z" },
+ { url = "https://files.pythonhosted.org/packages/35/8c/5df82881284459f6eec796a5ac2a0a304bb3384eec2e73f35cfdfcfbf20c/scikit_image-0.25.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8db8dd03663112783221bf01ccfc9512d1cc50ac9b5b0fe8f4023967564719fb", size = 13986000, upload-time = "2025-02-18T18:04:47.156Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/e6/93bebe1abcdce9513ffec01d8af02528b4c41fb3c1e46336d70b9ed4ef0d/scikit_image-0.25.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:483bd8cc10c3d8a7a37fae36dfa5b21e239bd4ee121d91cad1f81bba10cfb0ed", size = 13235893, upload-time = "2025-02-18T18:04:51.049Z" },
+ { url = "https://files.pythonhosted.org/packages/53/4b/eda616e33f67129e5979a9eb33c710013caa3aa8a921991e6cc0b22cea33/scikit_image-0.25.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d1e80107bcf2bf1291acfc0bf0425dceb8890abe9f38d8e94e23497cbf7ee0d", size = 14178389, upload-time = "2025-02-18T18:04:54.245Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/b5/b75527c0f9532dd8a93e8e7cd8e62e547b9f207d4c11e24f0006e8646b36/scikit_image-0.25.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a17e17eb8562660cc0d31bb55643a4da996a81944b82c54805c91b3fe66f4824", size = 15003435, upload-time = "2025-02-18T18:04:57.586Z" },
+ { url = "https://files.pythonhosted.org/packages/34/e3/49beb08ebccda3c21e871b607c1cb2f258c3fa0d2f609fed0a5ba741b92d/scikit_image-0.25.2-cp312-cp312-win_amd64.whl", hash = "sha256:bdd2b8c1de0849964dbc54037f36b4e9420157e67e45a8709a80d727f52c7da2", size = 12899474, upload-time = "2025-02-18T18:05:01.166Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/7c/9814dd1c637f7a0e44342985a76f95a55dd04be60154247679fd96c7169f/scikit_image-0.25.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7efa888130f6c548ec0439b1a7ed7295bc10105458a421e9bf739b457730b6da", size = 13921841, upload-time = "2025-02-18T18:05:03.963Z" },
+ { url = "https://files.pythonhosted.org/packages/84/06/66a2e7661d6f526740c309e9717d3bd07b473661d5cdddef4dd978edab25/scikit_image-0.25.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dd8011efe69c3641920614d550f5505f83658fe33581e49bed86feab43a180fc", size = 13196862, upload-time = "2025-02-18T18:05:06.986Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/63/3368902ed79305f74c2ca8c297dfeb4307269cbe6402412668e322837143/scikit_image-0.25.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28182a9d3e2ce3c2e251383bdda68f8d88d9fff1a3ebe1eb61206595c9773341", size = 14117785, upload-time = "2025-02-18T18:05:10.69Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/9b/c3da56a145f52cd61a68b8465d6a29d9503bc45bc993bb45e84371c97d94/scikit_image-0.25.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8abd3c805ce6944b941cfed0406d88faeb19bab3ed3d4b50187af55cf24d147", size = 14977119, upload-time = "2025-02-18T18:05:13.871Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/97/5fcf332e1753831abb99a2525180d3fb0d70918d461ebda9873f66dcc12f/scikit_image-0.25.2-cp313-cp313-win_amd64.whl", hash = "sha256:64785a8acefee460ec49a354706db0b09d1f325674107d7fa3eadb663fb56d6f", size = 12885116, upload-time = "2025-02-18T18:05:17.844Z" },
+ { url = "https://files.pythonhosted.org/packages/10/cc/75e9f17e3670b5ed93c32456fda823333c6279b144cd93e2c03aa06aa472/scikit_image-0.25.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:330d061bd107d12f8d68f1d611ae27b3b813b8cdb0300a71d07b1379178dd4cd", size = 13862801, upload-time = "2025-02-18T18:05:20.783Z" },
+]
+
+[package.optional-dependencies]
+data = [
+ { name = "pooch", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+
+[[package]]
+name = "scikit-image"
+version = "0.26.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "imageio", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "lazy-loader", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pillow", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a1/b4/2528bb43c67d48053a7a649a9666432dc307d66ba02e3a6d5c40f46655df/scikit_image-0.26.0.tar.gz", hash = "sha256:f5f970ab04efad85c24714321fcc91613fcb64ef2a892a13167df2f3e59199fa", size = 22729739, upload-time = "2025-12-20T17:12:21.824Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/16/8a407688b607f86f81f8c649bf0d68a2a6d67375f18c2d660aba20f5b648/scikit_image-0.26.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b1ede33a0fb3731457eaf53af6361e73dd510f449dac437ab54573b26788baf0", size = 12355510, upload-time = "2025-12-20T17:10:31.628Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/f9/7efc088ececb6f6868fd4475e16cfafc11f242ce9ab5fc3557d78b5da0d4/scikit_image-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7af7aa331c6846bd03fa28b164c18d0c3fd419dbb888fb05e958ac4257a78fdd", size = 12056334, upload-time = "2025-12-20T17:10:34.559Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/1e/bc7fb91fb5ff65ef42346c8b7ee8b09b04eabf89235ab7dbfdfd96cbd1ea/scikit_image-0.26.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ea6207d9e9d21c3f464efe733121c0504e494dbdc7728649ff3e23c3c5a4953", size = 13297768, upload-time = "2025-12-20T17:10:37.733Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/2a/e71c1a7d90e70da67b88ccc609bd6ae54798d5847369b15d3a8052232f9d/scikit_image-0.26.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74aa5518ccea28121f57a95374581d3b979839adc25bb03f289b1bc9b99c58af", size = 13711217, upload-time = "2025-12-20T17:10:40.935Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/59/9637ee12c23726266b91296791465218973ce1ad3e4c56fc81e4d8e7d6e1/scikit_image-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d5c244656de905e195a904e36dbc18585e06ecf67d90f0482cbde63d7f9ad59d", size = 14337782, upload-time = "2025-12-20T17:10:43.452Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/5c/a3e1e0860f9294663f540c117e4bf83d55e5b47c281d475cc06227e88411/scikit_image-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21a818ee6ca2f2131b9e04d8eb7637b5c18773ebe7b399ad23dcc5afaa226d2d", size = 14805997, upload-time = "2025-12-20T17:10:45.93Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/c6/2eeacf173da041a9e388975f54e5c49df750757fcfc3ee293cdbbae1ea0a/scikit_image-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:9490360c8d3f9a7e85c8de87daf7c0c66507960cf4947bb9610d1751928721c7", size = 11878486, upload-time = "2025-12-20T17:10:48.246Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/a4/a852c4949b9058d585e762a66bf7e9a2cd3be4795cd940413dfbfbb0ce79/scikit_image-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:0baa0108d2d027f34d748e84e592b78acc23e965a5de0e4bb03cf371de5c0581", size = 11346518, upload-time = "2025-12-20T17:10:50.575Z" },
+ { url = "https://files.pythonhosted.org/packages/99/e8/e13757982264b33a1621628f86b587e9a73a13f5256dad49b19ba7dc9083/scikit_image-0.26.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d454b93a6fa770ac5ae2d33570f8e7a321bb80d29511ce4b6b78058ebe176e8c", size = 12376452, upload-time = "2025-12-20T17:10:52.796Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/be/f8dd17d0510f9911f9f17ba301f7455328bf13dae416560126d428de9568/scikit_image-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3409e89d66eff5734cd2b672d1c48d2759360057e714e1d92a11df82c87cba37", size = 12061567, upload-time = "2025-12-20T17:10:55.207Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/2b/c70120a6880579fb42b91567ad79feb4772f7be72e8d52fec403a3dde0c6/scikit_image-0.26.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c717490cec9e276afb0438dd165b7c3072d6c416709cc0f9f5a4c1070d23a44", size = 13084214, upload-time = "2025-12-20T17:10:57.468Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/a2/70401a107d6d7466d64b466927e6b96fcefa99d57494b972608e2f8be50f/scikit_image-0.26.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7df650e79031634ac90b11e64a9eedaf5a5e06fcd09bcd03a34be01745744466", size = 13561683, upload-time = "2025-12-20T17:10:59.49Z" },
+ { url = "https://files.pythonhosted.org/packages/13/a5/48bdfd92794c5002d664e0910a349d0a1504671ef5ad358150f21643c79a/scikit_image-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cefd85033e66d4ea35b525bb0937d7f42d4cdcfed2d1888e1570d5ce450d3932", size = 14112147, upload-time = "2025-12-20T17:11:02.083Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/b5/ac71694da92f5def5953ca99f18a10fe98eac2dd0a34079389b70b4d0394/scikit_image-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3f5bf622d7c0435884e1e141ebbe4b2804e16b2dd23ae4c6183e2ea99233be70", size = 14661625, upload-time = "2025-12-20T17:11:04.528Z" },
+ { url = "https://files.pythonhosted.org/packages/23/4d/a3cc1e96f080e253dad2251bfae7587cf2b7912bcd76fd43fd366ff35a87/scikit_image-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:abed017474593cd3056ae0fe948d07d0747b27a085e92df5474f4955dd65aec0", size = 11911059, upload-time = "2025-12-20T17:11:06.61Z" },
+ { url = "https://files.pythonhosted.org/packages/35/8a/d1b8055f584acc937478abf4550d122936f420352422a1a625eef2c605d8/scikit_image-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d57e39ef67a95d26860c8caf9b14b8fb130f83b34c6656a77f191fa6d1d04d8", size = 11348740, upload-time = "2025-12-20T17:11:09.118Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/48/02357ffb2cca35640f33f2cfe054a4d6d5d7a229b88880a64f1e45c11f4e/scikit_image-0.26.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a2e852eccf41d2d322b8e60144e124802873a92b8d43a6f96331aa42888491c7", size = 12346329, upload-time = "2025-12-20T17:11:11.599Z" },
+ { url = "https://files.pythonhosted.org/packages/67/b9/b792c577cea2c1e94cda83b135a656924fc57c428e8a6d302cd69aac1b60/scikit_image-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:98329aab3bc87db352b9887f64ce8cdb8e75f7c2daa19927f2e121b797b678d5", size = 12031726, upload-time = "2025-12-20T17:11:13.871Z" },
+ { url = "https://files.pythonhosted.org/packages/07/a9/9564250dfd65cb20404a611016db52afc6268b2b371cd19c7538ea47580f/scikit_image-0.26.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:915bb3ba66455cf8adac00dc8fdf18a4cd29656aec7ddd38cb4dda90289a6f21", size = 13094910, upload-time = "2025-12-20T17:11:16.2Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/b8/0d8eeb5a9fd7d34ba84f8a55753a0a3e2b5b51b2a5a0ade648a8db4a62f7/scikit_image-0.26.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b36ab5e778bf50af5ff386c3ac508027dc3aaeccf2161bdf96bde6848f44d21b", size = 13660939, upload-time = "2025-12-20T17:11:18.464Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/d6/91d8973584d4793d4c1a847d388e34ef1218d835eeddecfc9108d735b467/scikit_image-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:09bad6a5d5949c7896c8347424c4cca899f1d11668030e5548813ab9c2865dcb", size = 14138938, upload-time = "2025-12-20T17:11:20.919Z" },
+ { url = "https://files.pythonhosted.org/packages/39/9a/7e15d8dc10d6bbf212195fb39bdeb7f226c46dd53f9c63c312e111e2e175/scikit_image-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:aeb14db1ed09ad4bee4ceb9e635547a8d5f3549be67fc6c768c7f923e027e6cd", size = 14752243, upload-time = "2025-12-20T17:11:23.347Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/58/2b11b933097bc427e42b4a8b15f7de8f24f2bac1fd2779d2aea1431b2c31/scikit_image-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:ac529eb9dbd5954f9aaa2e3fe9a3fd9661bfe24e134c688587d811a0233127f1", size = 11906770, upload-time = "2025-12-20T17:11:25.297Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/ec/96941474a18a04b69b6f6562a5bd79bd68049fa3728d3b350976eccb8b93/scikit_image-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:a2d211bc355f59725efdcae699b93b30348a19416cc9e017f7b2fb599faf7219", size = 11342506, upload-time = "2025-12-20T17:11:27.399Z" },
+ { url = "https://files.pythonhosted.org/packages/03/e5/c1a9962b0cf1952f42d32b4a2e48eed520320dbc4d2ff0b981c6fa508b6b/scikit_image-0.26.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9eefb4adad066da408a7601c4c24b07af3b472d90e08c3e7483d4e9e829d8c49", size = 12663278, upload-time = "2025-12-20T17:11:29.358Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/97/c1a276a59ce8e4e24482d65c1a3940d69c6b3873279193b7ebd04e5ee56b/scikit_image-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6caec76e16c970c528d15d1c757363334d5cb3069f9cea93d2bead31820511f3", size = 12405142, upload-time = "2025-12-20T17:11:31.282Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/4a/f1cbd1357caef6c7993f7efd514d6e53d8fd6f7fe01c4714d51614c53289/scikit_image-0.26.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a07200fe09b9d99fcdab959859fe0f7db8df6333d6204344425d476850ce3604", size = 12942086, upload-time = "2025-12-20T17:11:33.683Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/6f/74d9fb87c5655bd64cf00b0c44dc3d6206d9002e5f6ba1c9aeb13236f6bf/scikit_image-0.26.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92242351bccf391fc5df2d1529d15470019496d2498d615beb68da85fe7fdf37", size = 13265667, upload-time = "2025-12-20T17:11:36.11Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/73/faddc2413ae98d863f6fa2e3e14da4467dd38e788e1c23346cf1a2b06b97/scikit_image-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:52c496f75a7e45844d951557f13c08c81487c6a1da2e3c9c8a39fcde958e02cc", size = 14001966, upload-time = "2025-12-20T17:11:38.55Z" },
+ { url = "https://files.pythonhosted.org/packages/02/94/9f46966fa042b5d57c8cd641045372b4e0df0047dd400e77ea9952674110/scikit_image-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:20ef4a155e2e78b8ab973998e04d8a361d49d719e65412405f4dadd9155a61d9", size = 14359526, upload-time = "2025-12-20T17:11:41.087Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/b4/2840fe38f10057f40b1c9f8fb98a187a370936bf144a4ac23452c5ef1baf/scikit_image-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c9087cf7d0e7f33ab5c46d2068d86d785e70b05400a891f73a13400f1e1faf6a", size = 12287629, upload-time = "2025-12-20T17:11:43.11Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ba/73b6ca70796e71f83ab222690e35a79612f0117e5aaf167151b7d46f5f2c/scikit_image-0.26.0-cp313-cp313t-win_arm64.whl", hash = "sha256:27d58bc8b2acd351f972c6508c1b557cfed80299826080a4d803dd29c51b707e", size = 11647755, upload-time = "2025-12-20T17:11:45.279Z" },
+ { url = "https://files.pythonhosted.org/packages/51/44/6b744f92b37ae2833fd423cce8f806d2368859ec325a699dc30389e090b9/scikit_image-0.26.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:63af3d3a26125f796f01052052f86806da5b5e54c6abef152edb752683075a9c", size = 12365810, upload-time = "2025-12-20T17:11:47.357Z" },
+ { url = "https://files.pythonhosted.org/packages/40/f5/83590d9355191f86ac663420fec741b82cc547a4afe7c4c1d986bf46e4db/scikit_image-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ce00600cd70d4562ed59f80523e18cdcc1fae0e10676498a01f73c255774aefd", size = 12075717, upload-time = "2025-12-20T17:11:49.483Z" },
+ { url = "https://files.pythonhosted.org/packages/72/48/253e7cf5aee6190459fe136c614e2cbccc562deceb4af96e0863f1b8ee29/scikit_image-0.26.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6381edf972b32e4f54085449afde64365a57316637496c1325a736987083e2ab", size = 13161520, upload-time = "2025-12-20T17:11:51.58Z" },
+ { url = "https://files.pythonhosted.org/packages/73/c3/cec6a3cbaadfdcc02bd6ff02f3abfe09eaa7f4d4e0a525a1e3a3f4bce49c/scikit_image-0.26.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6624a76c6085218248154cc7e1500e6b488edcd9499004dd0d35040607d7505", size = 13684340, upload-time = "2025-12-20T17:11:53.708Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/0d/39a776f675d24164b3a267aa0db9f677a4cb20127660d8bf4fd7fef66817/scikit_image-0.26.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f775f0e420faac9c2aa6757135f4eb468fb7b70e0b67fa77a5e79be3c30ee331", size = 14203839, upload-time = "2025-12-20T17:11:55.89Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/25/2514df226bbcedfe9b2caafa1ba7bc87231a0c339066981b182b08340e06/scikit_image-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede4d6d255cc5da9faeb2f9ba7fedbc990abbc652db429f40a16b22e770bb578", size = 14770021, upload-time = "2025-12-20T17:11:58.014Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/5b/0671dc91c0c79340c3fe202f0549c7d3681eb7640fe34ab68a5f090a7c7f/scikit_image-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:0660b83968c15293fd9135e8d860053ee19500d52bf55ca4fb09de595a1af650", size = 12023490, upload-time = "2025-12-20T17:12:00.013Z" },
+ { url = "https://files.pythonhosted.org/packages/65/08/7c4cb59f91721f3de07719085212a0b3962e3e3f2d1818cbac4eeb1ea53e/scikit_image-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:b8d14d3181c21c11170477a42542c1addc7072a90b986675a71266ad17abc37f", size = 11473782, upload-time = "2025-12-20T17:12:01.983Z" },
+ { url = "https://files.pythonhosted.org/packages/49/41/65c4258137acef3d73cb561ac55512eacd7b30bb4f4a11474cad526bc5db/scikit_image-0.26.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:cde0bbd57e6795eba83cb10f71a677f7239271121dc950bc060482834a668ad1", size = 12686060, upload-time = "2025-12-20T17:12:03.886Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/32/76971f8727b87f1420a962406388a50e26667c31756126444baf6668f559/scikit_image-0.26.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:163e9afb5b879562b9aeda0dd45208a35316f26cc7a3aed54fd601604e5cf46f", size = 12422628, upload-time = "2025-12-20T17:12:05.921Z" },
+ { url = "https://files.pythonhosted.org/packages/37/0d/996febd39f757c40ee7b01cdb861867327e5c8e5f595a634e8201462d958/scikit_image-0.26.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724f79fd9b6cb6f4a37864fe09f81f9f5d5b9646b6868109e1b100d1a7019e59", size = 12962369, upload-time = "2025-12-20T17:12:07.912Z" },
+ { url = "https://files.pythonhosted.org/packages/48/b4/612d354f946c9600e7dea012723c11d47e8d455384e530f6daaaeb9bf62c/scikit_image-0.26.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3268f13310e6857508bd87202620df996199a016a1d281b309441d227c822394", size = 13272431, upload-time = "2025-12-20T17:12:10.255Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/6e/26c00b466e06055a086de2c6e2145fe189ccdc9a1d11ccc7de020f2591ad/scikit_image-0.26.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fac96a1f9b06cd771cbbb3cd96c5332f36d4efd839b1d8b053f79e5887acde62", size = 14016362, upload-time = "2025-12-20T17:12:12.793Z" },
+ { url = "https://files.pythonhosted.org/packages/47/88/00a90402e1775634043c2a0af8a3c76ad450866d9fa444efcc43b553ba2d/scikit_image-0.26.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2c1e7bd342f43e7a97e571b3f03ba4c1293ea1a35c3f13f41efdc8a81c1dc8f2", size = 14364151, upload-time = "2025-12-20T17:12:14.909Z" },
+ { url = "https://files.pythonhosted.org/packages/da/ca/918d8d306bd43beacff3b835c6d96fac0ae64c0857092f068b88db531a7c/scikit_image-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b702c3bb115e1dcf4abf5297429b5c90f2189655888cbed14921f3d26f81d3a4", size = 12413484, upload-time = "2025-12-20T17:12:17.046Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/cd/4da01329b5a8d47ff7ec3c99a2b02465a8017b186027590dc7425cee0b56/scikit_image-0.26.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0608aa4a9ec39e0843de10d60edb2785a30c1c47819b67866dd223ebd149acaf", size = 11769501, upload-time = "2025-12-20T17:12:19.339Z" },
+]
+
+[package.optional-dependencies]
+data = [
+ { name = "pooch", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+
+[[package]]
+name = "scikit-learn"
+version = "1.7.2"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "joblib", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "threadpoolctl", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" },
+ { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" },
+ { url = "https://files.pythonhosted.org/packages/43/83/564e141eef908a5863a54da8ca342a137f45a0bfb71d1d79704c9894c9d1/scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e", size = 9331967, upload-time = "2025-09-09T08:20:32.421Z" },
+ { url = "https://files.pythonhosted.org/packages/18/d6/ba863a4171ac9d7314c4d3fc251f015704a2caeee41ced89f321c049ed83/scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1", size = 8648645, upload-time = "2025-09-09T08:20:34.436Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/0e/97dbca66347b8cf0ea8b529e6bb9367e337ba2e8be0ef5c1a545232abfde/scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d", size = 9715424, upload-time = "2025-09-09T08:20:36.776Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/32/1f3b22e3207e1d2c883a7e09abb956362e7d1bd2f14458c7de258a26ac15/scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1", size = 9509234, upload-time = "2025-09-09T08:20:38.957Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/71/34ddbd21f1da67c7a768146968b4d0220ee6831e4bcbad3e03dd3eae88b6/scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1", size = 8894244, upload-time = "2025-09-09T08:20:41.166Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/aa/3996e2196075689afb9fce0410ebdb4a09099d7964d061d7213700204409/scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96", size = 9259818, upload-time = "2025-09-09T08:20:43.19Z" },
+ { url = "https://files.pythonhosted.org/packages/43/5d/779320063e88af9c4a7c2cf463ff11c21ac9c8bd730c4a294b0000b666c9/scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476", size = 8636997, upload-time = "2025-09-09T08:20:45.468Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/d0/0c577d9325b05594fdd33aa970bf53fb673f051a45496842caee13cfd7fe/scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b", size = 9478381, upload-time = "2025-09-09T08:20:47.982Z" },
+ { url = "https://files.pythonhosted.org/packages/82/70/8bf44b933837ba8494ca0fc9a9ab60f1c13b062ad0197f60a56e2fc4c43e/scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44", size = 9300296, upload-time = "2025-09-09T08:20:50.366Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/99/ed35197a158f1fdc2fe7c3680e9c70d0128f662e1fee4ed495f4b5e13db0/scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290", size = 8731256, upload-time = "2025-09-09T08:20:52.627Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/93/a3038cb0293037fd335f77f31fe053b89c72f17b1c8908c576c29d953e84/scikit_learn-1.7.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b7dacaa05e5d76759fb071558a8b5130f4845166d88654a0f9bdf3eb57851b7", size = 9212382, upload-time = "2025-09-09T08:20:54.731Z" },
+ { url = "https://files.pythonhosted.org/packages/40/dd/9a88879b0c1104259136146e4742026b52df8540c39fec21a6383f8292c7/scikit_learn-1.7.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:abebbd61ad9e1deed54cca45caea8ad5f79e1b93173dece40bb8e0c658dbe6fe", size = 8592042, upload-time = "2025-09-09T08:20:57.313Z" },
+ { url = "https://files.pythonhosted.org/packages/46/af/c5e286471b7d10871b811b72ae794ac5fe2989c0a2df07f0ec723030f5f5/scikit_learn-1.7.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:502c18e39849c0ea1a5d681af1dbcf15f6cce601aebb657aabbfe84133c1907f", size = 9434180, upload-time = "2025-09-09T08:20:59.671Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/fd/df59faa53312d585023b2da27e866524ffb8faf87a68516c23896c718320/scikit_learn-1.7.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a4c328a71785382fe3fe676a9ecf2c86189249beff90bf85e22bdb7efaf9ae0", size = 9283660, upload-time = "2025-09-09T08:21:01.71Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/c7/03000262759d7b6f38c836ff9d512f438a70d8a8ddae68ee80de72dcfb63/scikit_learn-1.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:63a9afd6f7b229aad94618c01c252ce9e6fa97918c5ca19c9a17a087d819440c", size = 8702057, upload-time = "2025-09-09T08:21:04.234Z" },
+ { url = "https://files.pythonhosted.org/packages/55/87/ef5eb1f267084532c8e4aef98a28b6ffe7425acbfd64b5e2f2e066bc29b3/scikit_learn-1.7.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9acb6c5e867447b4e1390930e3944a005e2cb115922e693c08a323421a6966e8", size = 9558731, upload-time = "2025-09-09T08:21:06.381Z" },
+ { url = "https://files.pythonhosted.org/packages/93/f8/6c1e3fc14b10118068d7938878a9f3f4e6d7b74a8ddb1e5bed65159ccda8/scikit_learn-1.7.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:2a41e2a0ef45063e654152ec9d8bcfc39f7afce35b08902bfe290c2498a67a6a", size = 9038852, upload-time = "2025-09-09T08:21:08.628Z" },
+ { url = "https://files.pythonhosted.org/packages/83/87/066cafc896ee540c34becf95d30375fe5cbe93c3b75a0ee9aa852cd60021/scikit_learn-1.7.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98335fb98509b73385b3ab2bd0639b1f610541d3988ee675c670371d6a87aa7c", size = 9527094, upload-time = "2025-09-09T08:21:11.486Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/2b/4903e1ccafa1f6453b1ab78413938c8800633988c838aa0be386cbb33072/scikit_learn-1.7.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191e5550980d45449126e23ed1d5e9e24b2c68329ee1f691a3987476e115e09c", size = 9367436, upload-time = "2025-09-09T08:21:13.602Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/aa/8444be3cfb10451617ff9d177b3c190288f4563e6c50ff02728be67ad094/scikit_learn-1.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:57dc4deb1d3762c75d685507fbd0bc17160144b2f2ba4ccea5dc285ab0d0e973", size = 9275749, upload-time = "2025-09-09T08:21:15.96Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/82/dee5acf66837852e8e68df6d8d3a6cb22d3df997b733b032f513d95205b7/scikit_learn-1.7.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fa8f63940e29c82d1e67a45d5297bdebbcb585f5a5a50c4914cc2e852ab77f33", size = 9208906, upload-time = "2025-09-09T08:21:18.557Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/30/9029e54e17b87cb7d50d51a5926429c683d5b4c1732f0507a6c3bed9bf65/scikit_learn-1.7.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:f95dc55b7902b91331fa4e5845dd5bde0580c9cd9612b1b2791b7e80c3d32615", size = 8627836, upload-time = "2025-09-09T08:21:20.695Z" },
+ { url = "https://files.pythonhosted.org/packages/60/18/4a52c635c71b536879f4b971c2cedf32c35ee78f48367885ed8025d1f7ee/scikit_learn-1.7.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9656e4a53e54578ad10a434dc1f993330568cfee176dff07112b8785fb413106", size = 9426236, upload-time = "2025-09-09T08:21:22.645Z" },
+ { url = "https://files.pythonhosted.org/packages/99/7e/290362f6ab582128c53445458a5befd471ed1ea37953d5bcf80604619250/scikit_learn-1.7.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96dc05a854add0e50d3f47a1ef21a10a595016da5b007c7d9cd9d0bffd1fcc61", size = 9312593, upload-time = "2025-09-09T08:21:24.65Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/87/24f541b6d62b1794939ae6422f8023703bbf6900378b2b34e0b4384dfefd/scikit_learn-1.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:bb24510ed3f9f61476181e4db51ce801e2ba37541def12dc9333b946fc7a9cf8", size = 8820007, upload-time = "2025-09-09T08:21:26.713Z" },
+]
+
+[[package]]
+name = "scikit-learn"
+version = "1.8.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "joblib", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "threadpoolctl", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c9/92/53ea2181da8ac6bf27170191028aee7251f8f841f8d3edbfdcaf2008fde9/scikit_learn-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da", size = 8595835, upload-time = "2025-12-10T07:07:39.385Z" },
+ { url = "https://files.pythonhosted.org/packages/01/18/d154dc1638803adf987910cdd07097d9c526663a55666a97c124d09fb96a/scikit_learn-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1", size = 8080381, upload-time = "2025-12-10T07:07:41.93Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/44/226142fcb7b7101e64fdee5f49dbe6288d4c7af8abf593237b70fca080a4/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b", size = 8799632, upload-time = "2025-12-10T07:07:43.899Z" },
+ { url = "https://files.pythonhosted.org/packages/36/4d/4a67f30778a45d542bbea5db2dbfa1e9e100bf9ba64aefe34215ba9f11f6/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1", size = 9103788, upload-time = "2025-12-10T07:07:45.982Z" },
+ { url = "https://files.pythonhosted.org/packages/89/3c/45c352094cfa60050bcbb967b1faf246b22e93cb459f2f907b600f2ceda5/scikit_learn-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b", size = 8081706, upload-time = "2025-12-10T07:07:48.111Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/46/5416595bb395757f754feb20c3d776553a386b661658fb21b7c814e89efe/scikit_learn-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961", size = 7688451, upload-time = "2025-12-10T07:07:49.873Z" },
+ { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242, upload-time = "2025-12-10T07:07:51.568Z" },
+ { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075, upload-time = "2025-12-10T07:07:53.697Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" },
+ { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359, upload-time = "2025-12-10T07:07:59.838Z" },
+ { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898, upload-time = "2025-12-10T07:08:01.36Z" },
+ { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770, upload-time = "2025-12-10T07:08:03.251Z" },
+ { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458, upload-time = "2025-12-10T07:08:05.336Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341, upload-time = "2025-12-10T07:08:07.732Z" },
+ { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022, upload-time = "2025-12-10T07:08:09.862Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409, upload-time = "2025-12-10T07:08:12.028Z" },
+ { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760, upload-time = "2025-12-10T07:08:13.688Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045, upload-time = "2025-12-10T07:08:15.215Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324, upload-time = "2025-12-10T07:08:17.561Z" },
+ { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651, upload-time = "2025-12-10T07:08:19.952Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045, upload-time = "2025-12-10T07:08:22.11Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994, upload-time = "2025-12-10T07:08:23.943Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518, upload-time = "2025-12-10T07:08:25.71Z" },
+ { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667, upload-time = "2025-12-10T07:08:27.541Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524, upload-time = "2025-12-10T07:08:29.822Z" },
+ { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133, upload-time = "2025-12-10T07:08:31.865Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223, upload-time = "2025-12-10T07:08:34.166Z" },
+ { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518, upload-time = "2025-12-10T07:08:36.339Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546, upload-time = "2025-12-10T07:08:38.128Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305, upload-time = "2025-12-10T07:08:41.013Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257, upload-time = "2025-12-10T07:08:42.873Z" },
+ { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673, upload-time = "2025-12-10T07:08:45.362Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467, upload-time = "2025-12-10T07:08:47.408Z" },
+ { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395, upload-time = "2025-12-10T07:08:49.337Z" },
+ { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647, upload-time = "2025-12-10T07:08:51.601Z" },
+]
+
+[[package]]
+name = "scipy"
+version = "1.15.3"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" },
+ { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" },
+ { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" },
+ { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" },
+ { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" },
+ { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" },
+ { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" },
+ { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" },
+ { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" },
+ { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" },
+ { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" },
+ { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" },
+ { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" },
+ { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" },
+ { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" },
+ { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" },
+ { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" },
+ { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" },
+ { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" },
+ { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" },
+ { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" },
+ { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" },
+ { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" },
+]
+
+[[package]]
+name = "scipy"
+version = "1.17.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" },
+ { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" },
+ { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" },
+ { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" },
+ { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" },
+ { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" },
+ { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" },
+ { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" },
+ { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" },
+ { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" },
+ { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" },
+ { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" },
+ { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" },
+ { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" },
+ { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" },
+ { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" },
+ { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" },
+ { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" },
+ { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" },
+ { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" },
+ { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" },
+ { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" },
+]
+
+[[package]]
+name = "sentry-sdk"
+version = "2.60.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/54/a2/2e6c090db384cc515069f4f85542bd5baf6786852073020ea73d4a76d3ea/sentry_sdk-2.60.0.tar.gz", hash = "sha256:0bd25e54e78ca02d0be512529fa644bbbf9e8470d7b26371294012d4ca93c978", size = 452946, upload-time = "2026-05-13T13:34:52.516Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/29/41/f2b800b7f12a05dd48c2a6280d4dd812d1425fc66ed3fe3fd99420c41d1a/sentry_sdk-2.60.0-py3-none-any.whl", hash = "sha256:28a536c03291c8bcb363cf35c611b32738ec118ff64d8d6383b096448ac4c803", size = 475616, upload-time = "2026-05-13T13:34:50.259Z" },
+]
+
+[[package]]
+name = "setuptools"
+version = "81.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" },
+]
+
+[[package]]
+name = "shapely"
+version = "2.1.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9", size = 315489, upload-time = "2025-09-24T13:51:41.432Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/05/89/c3548aa9b9812a5d143986764dededfa48d817714e947398bdda87c77a72/shapely-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7ae48c236c0324b4e139bea88a306a04ca630f49be66741b340729d380d8f52f", size = 1825959, upload-time = "2025-09-24T13:50:00.682Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/8a/7ebc947080442edd614ceebe0ce2cdbd00c25e832c240e1d1de61d0e6b38/shapely-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eba6710407f1daa8e7602c347dfc94adc02205ec27ed956346190d66579eb9ea", size = 1629196, upload-time = "2025-09-24T13:50:03.447Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/86/c9c27881c20d00fc409e7e059de569d5ed0abfcec9c49548b124ebddea51/shapely-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef4a456cc8b7b3d50ccec29642aa4aeda959e9da2fe9540a92754770d5f0cf1f", size = 2951065, upload-time = "2025-09-24T13:50:05.266Z" },
+ { url = "https://files.pythonhosted.org/packages/50/8a/0ab1f7433a2a85d9e9aea5b1fbb333f3b09b309e7817309250b4b7b2cc7a/shapely-2.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e38a190442aacc67ff9f75ce60aec04893041f16f97d242209106d502486a142", size = 3058666, upload-time = "2025-09-24T13:50:06.872Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/c6/5a30ffac9c4f3ffd5b7113a7f5299ccec4713acd5ee44039778a7698224e/shapely-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:40d784101f5d06a1fd30b55fc11ea58a61be23f930d934d86f19a180909908a4", size = 3966905, upload-time = "2025-09-24T13:50:09.417Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/72/e92f3035ba43e53959007f928315a68fbcf2eeb4e5ededb6f0dc7ff1ecc3/shapely-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f6f6cd5819c50d9bcf921882784586aab34a4bd53e7553e175dece6db513a6f0", size = 4129260, upload-time = "2025-09-24T13:50:11.183Z" },
+ { url = "https://files.pythonhosted.org/packages/42/24/605901b73a3d9f65fa958e63c9211f4be23d584da8a1a7487382fac7fdc5/shapely-2.1.2-cp310-cp310-win32.whl", hash = "sha256:fe9627c39c59e553c90f5bc3128252cb85dc3b3be8189710666d2f8bc3a5503e", size = 1544301, upload-time = "2025-09-24T13:50:12.521Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/89/6db795b8dd3919851856bd2ddd13ce434a748072f6fdee42ff30cbd3afa3/shapely-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:1d0bfb4b8f661b3b4ec3565fa36c340bfb1cda82087199711f86a88647d26b2f", size = 1722074, upload-time = "2025-09-24T13:50:13.909Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/8d/1ff672dea9ec6a7b5d422eb6d095ed886e2e523733329f75fdcb14ee1149/shapely-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:91121757b0a36c9aac3427a651a7e6567110a4a67c97edf04f8d55d4765f6618", size = 1820038, upload-time = "2025-09-24T13:50:15.628Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/ce/28fab8c772ce5db23a0d86bf0adaee0c4c79d5ad1db766055fa3dab442e2/shapely-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:16a9c722ba774cf50b5d4541242b4cce05aafd44a015290c82ba8a16931ff63d", size = 1626039, upload-time = "2025-09-24T13:50:16.881Z" },
+ { url = "https://files.pythonhosted.org/packages/70/8b/868b7e3f4982f5006e9395c1e12343c66a8155c0374fdc07c0e6a1ab547d/shapely-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cc4f7397459b12c0b196c9efe1f9d7e92463cbba142632b4cc6d8bbbbd3e2b09", size = 3001519, upload-time = "2025-09-24T13:50:18.606Z" },
+ { url = "https://files.pythonhosted.org/packages/13/02/58b0b8d9c17c93ab6340edd8b7308c0c5a5b81f94ce65705819b7416dba5/shapely-2.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:136ab87b17e733e22f0961504d05e77e7be8c9b5a8184f685b4a91a84efe3c26", size = 3110842, upload-time = "2025-09-24T13:50:21.77Z" },
+ { url = "https://files.pythonhosted.org/packages/af/61/8e389c97994d5f331dcffb25e2fa761aeedfb52b3ad9bcdd7b8671f4810a/shapely-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:16c5d0fc45d3aa0a69074979f4f1928ca2734fb2e0dde8af9611e134e46774e7", size = 4021316, upload-time = "2025-09-24T13:50:23.626Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/d4/9b2a9fe6039f9e42ccf2cb3e84f219fd8364b0c3b8e7bbc857b5fbe9c14c/shapely-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ddc759f72b5b2b0f54a7e7cde44acef680a55019eb52ac63a7af2cf17cb9cd2", size = 4178586, upload-time = "2025-09-24T13:50:25.443Z" },
+ { url = "https://files.pythonhosted.org/packages/16/f6/9840f6963ed4decf76b08fd6d7fed14f8779fb7a62cb45c5617fa8ac6eab/shapely-2.1.2-cp311-cp311-win32.whl", hash = "sha256:2fa78b49485391224755a856ed3b3bd91c8455f6121fee0db0e71cefb07d0ef6", size = 1543961, upload-time = "2025-09-24T13:50:26.968Z" },
+ { url = "https://files.pythonhosted.org/packages/38/1e/3f8ea46353c2a33c1669eb7327f9665103aa3a8dfe7f2e4ef714c210b2c2/shapely-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:c64d5c97b2f47e3cd9b712eaced3b061f2b71234b3fc263e0fcf7d889c6559dc", size = 1722856, upload-time = "2025-09-24T13:50:28.497Z" },
+ { url = "https://files.pythonhosted.org/packages/24/c0/f3b6453cf2dfa99adc0ba6675f9aaff9e526d2224cbd7ff9c1a879238693/shapely-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe2533caae6a91a543dec62e8360fe86ffcdc42a7c55f9dfd0128a977a896b94", size = 1833550, upload-time = "2025-09-24T13:50:30.019Z" },
+ { url = "https://files.pythonhosted.org/packages/86/07/59dee0bc4b913b7ab59ab1086225baca5b8f19865e6101db9ebb7243e132/shapely-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ba4d1333cc0bc94381d6d4308d2e4e008e0bd128bdcff5573199742ee3634359", size = 1643556, upload-time = "2025-09-24T13:50:32.291Z" },
+ { url = "https://files.pythonhosted.org/packages/26/29/a5397e75b435b9895cd53e165083faed5d12fd9626eadec15a83a2411f0f/shapely-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bd308103340030feef6c111d3eb98d50dc13feea33affc8a6f9fa549e9458a3", size = 2988308, upload-time = "2025-09-24T13:50:33.862Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/37/e781683abac55dde9771e086b790e554811a71ed0b2b8a1e789b7430dd44/shapely-2.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e7d4d7ad262a48bb44277ca12c7c78cb1b0f56b32c10734ec9a1d30c0b0c54b", size = 3099844, upload-time = "2025-09-24T13:50:35.459Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/f3/9876b64d4a5a321b9dc482c92bb6f061f2fa42131cba643c699f39317cb9/shapely-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9eddfe513096a71896441a7c37db72da0687b34752c4e193577a145c71736fc", size = 3988842, upload-time = "2025-09-24T13:50:37.478Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/a0/704c7292f7014c7e74ec84eddb7b109e1fbae74a16deae9c1504b1d15565/shapely-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:980c777c612514c0cf99bc8a9de6d286f5e186dcaf9091252fcd444e5638193d", size = 4152714, upload-time = "2025-09-24T13:50:39.9Z" },
+ { url = "https://files.pythonhosted.org/packages/53/46/319c9dc788884ad0785242543cdffac0e6530e4d0deb6c4862bc4143dcf3/shapely-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9111274b88e4d7b54a95218e243282709b330ef52b7b86bc6aaf4f805306f454", size = 1542745, upload-time = "2025-09-24T13:50:41.414Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/bf/cb6c1c505cb31e818e900b9312d514f381fbfa5c4363edfce0fcc4f8c1a4/shapely-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:743044b4cfb34f9a67205cee9279feaf60ba7d02e69febc2afc609047cb49179", size = 1722861, upload-time = "2025-09-24T13:50:43.35Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/90/98ef257c23c46425dc4d1d31005ad7c8d649fe423a38b917db02c30f1f5a/shapely-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b510dda1a3672d6879beb319bc7c5fd302c6c354584690973c838f46ec3e0fa8", size = 1832644, upload-time = "2025-09-24T13:50:44.886Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/ab/0bee5a830d209adcd3a01f2d4b70e587cdd9fd7380d5198c064091005af8/shapely-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8cff473e81017594d20ec55d86b54bc635544897e13a7cfc12e36909c5309a2a", size = 1642887, upload-time = "2025-09-24T13:50:46.735Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/5e/7d7f54ba960c13302584c73704d8c4d15404a51024631adb60b126a4ae88/shapely-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe7b77dc63d707c09726b7908f575fc04ff1d1ad0f3fb92aec212396bc6cfe5e", size = 2970931, upload-time = "2025-09-24T13:50:48.374Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/a2/83fc37e2a58090e3d2ff79175a95493c664bcd0b653dd75cb9134645a4e5/shapely-2.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ed1a5bbfb386ee8332713bf7508bc24e32d24b74fc9a7b9f8529a55db9f4ee6", size = 3082855, upload-time = "2025-09-24T13:50:50.037Z" },
+ { url = "https://files.pythonhosted.org/packages/44/2b/578faf235a5b09f16b5f02833c53822294d7f21b242f8e2d0cf03fb64321/shapely-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a84e0582858d841d54355246ddfcbd1fce3179f185da7470f41ce39d001ee1af", size = 3979960, upload-time = "2025-09-24T13:50:51.74Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/04/167f096386120f692cc4ca02f75a17b961858997a95e67a3cb6a7bbd6b53/shapely-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc3487447a43d42adcdf52d7ac73804f2312cbfa5d433a7d2c506dcab0033dfd", size = 4142851, upload-time = "2025-09-24T13:50:53.49Z" },
+ { url = "https://files.pythonhosted.org/packages/48/74/fb402c5a6235d1c65a97348b48cdedb75fb19eca2b1d66d04969fc1c6091/shapely-2.1.2-cp313-cp313-win32.whl", hash = "sha256:9c3a3c648aedc9f99c09263b39f2d8252f199cb3ac154fadc173283d7d111350", size = 1541890, upload-time = "2025-09-24T13:50:55.337Z" },
+ { url = "https://files.pythonhosted.org/packages/41/47/3647fe7ad990af60ad98b889657a976042c9988c2807cf322a9d6685f462/shapely-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:ca2591bff6645c216695bdf1614fca9c82ea1144d4a7591a466fef64f28f0715", size = 1722151, upload-time = "2025-09-24T13:50:57.153Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/49/63953754faa51ffe7d8189bfbe9ca34def29f8c0e34c67cbe2a2795f269d/shapely-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2d93d23bdd2ed9dc157b46bc2f19b7da143ca8714464249bef6771c679d5ff40", size = 1834130, upload-time = "2025-09-24T13:50:58.49Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/ee/dce001c1984052970ff60eb4727164892fb2d08052c575042a47f5a9e88f/shapely-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01d0d304b25634d60bd7cf291828119ab55a3bab87dc4af1e44b07fb225f188b", size = 1642802, upload-time = "2025-09-24T13:50:59.871Z" },
+ { url = "https://files.pythonhosted.org/packages/da/e7/fc4e9a19929522877fa602f705706b96e78376afb7fad09cad5b9af1553c/shapely-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8d8382dd120d64b03698b7298b89611a6ea6f55ada9d39942838b79c9bc89801", size = 3018460, upload-time = "2025-09-24T13:51:02.08Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/18/7519a25db21847b525696883ddc8e6a0ecaa36159ea88e0fef11466384d0/shapely-2.1.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:19efa3611eef966e776183e338b2d7ea43569ae99ab34f8d17c2c054d3205cc0", size = 3095223, upload-time = "2025-09-24T13:51:04.472Z" },
+ { url = "https://files.pythonhosted.org/packages/48/de/b59a620b1f3a129c3fecc2737104a0a7e04e79335bd3b0a1f1609744cf17/shapely-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:346ec0c1a0fcd32f57f00e4134d1200e14bf3f5ae12af87ba83ca275c502498c", size = 4030760, upload-time = "2025-09-24T13:51:06.455Z" },
+ { url = "https://files.pythonhosted.org/packages/96/b3/c6655ee7232b417562bae192ae0d3ceaadb1cc0ffc2088a2ddf415456cc2/shapely-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6305993a35989391bd3476ee538a5c9a845861462327efe00dd11a5c8c709a99", size = 4170078, upload-time = "2025-09-24T13:51:08.584Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/8e/605c76808d73503c9333af8f6cbe7e1354d2d238bda5f88eea36bfe0f42a/shapely-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:c8876673449f3401f278c86eb33224c5764582f72b653a415d0e6672fde887bf", size = 1559178, upload-time = "2025-09-24T13:51:10.73Z" },
+ { url = "https://files.pythonhosted.org/packages/36/f7/d317eb232352a1f1444d11002d477e54514a4a6045536d49d0c59783c0da/shapely-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:4a44bc62a10d84c11a7a3d7c1c4fe857f7477c3506e24c9062da0db0ae0c449c", size = 1739756, upload-time = "2025-09-24T13:51:12.105Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9a522f460d28e2bf4e12396240a5fc1518788b2fcd73535166d748399ef0c223", size = 1831290, upload-time = "2025-09-24T13:51:13.56Z" },
+ { url = "https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ff629e00818033b8d71139565527ced7d776c269a49bd78c9df84e8f852190c", size = 1641463, upload-time = "2025-09-24T13:51:14.972Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/57/91d59ae525ca641e7ac5551c04c9503aee6f29b92b392f31790fcb1a4358/shapely-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f67b34271dedc3c653eba4e3d7111aa421d5be9b4c4c7d38d30907f796cb30df", size = 2970145, upload-time = "2025-09-24T13:51:16.961Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21952dc00df38a2c28375659b07a3979d22641aeb104751e769c3ee825aadecf", size = 3073806, upload-time = "2025-09-24T13:51:18.712Z" },
+ { url = "https://files.pythonhosted.org/packages/03/83/f768a54af775eb41ef2e7bec8a0a0dbe7d2431c3e78c0a8bdba7ab17e446/shapely-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1f2f33f486777456586948e333a56ae21f35ae273be99255a191f5c1fa302eb4", size = 3980803, upload-time = "2025-09-24T13:51:20.37Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/cb/559c7c195807c91c79d38a1f6901384a2878a76fbdf3f1048893a9b7534d/shapely-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cf831a13e0d5a7eb519e96f58ec26e049b1fad411fc6fc23b162a7ce04d9cffc", size = 4133301, upload-time = "2025-09-24T13:51:21.887Z" },
+ { url = "https://files.pythonhosted.org/packages/80/cd/60d5ae203241c53ef3abd2ef27c6800e21afd6c94e39db5315ea0cbafb4a/shapely-2.1.2-cp314-cp314-win32.whl", hash = "sha256:61edcd8d0d17dd99075d320a1dd39c0cb9616f7572f10ef91b4b5b00c4aeb566", size = 1583247, upload-time = "2025-09-24T13:51:23.401Z" },
+ { url = "https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:a444e7afccdb0999e203b976adb37ea633725333e5b119ad40b1ca291ecf311c", size = 1773019, upload-time = "2025-09-24T13:51:24.873Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/05/a44f3f9f695fa3ada22786dc9da33c933da1cbc4bfe876fe3a100bafe263/shapely-2.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5ebe3f84c6112ad3d4632b1fd2290665aa75d4cef5f6c5d77c4c95b324527c6a", size = 1834137, upload-time = "2025-09-24T13:51:26.665Z" },
+ { url = "https://files.pythonhosted.org/packages/52/7e/4d57db45bf314573427b0a70dfca15d912d108e6023f623947fa69f39b72/shapely-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5860eb9f00a1d49ebb14e881f5caf6c2cf472c7fd38bd7f253bbd34f934eb076", size = 1642884, upload-time = "2025-09-24T13:51:28.029Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/27/4e29c0a55d6d14ad7422bf86995d7ff3f54af0eba59617eb95caf84b9680/shapely-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b705c99c76695702656327b819c9660768ec33f5ce01fa32b2af62b56ba400a1", size = 3018320, upload-time = "2025-09-24T13:51:29.903Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/bb/992e6a3c463f4d29d4cd6ab8963b75b1b1040199edbd72beada4af46bde5/shapely-2.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a1fd0ea855b2cf7c9cddaf25543e914dd75af9de08785f20ca3085f2c9ca60b0", size = 3094931, upload-time = "2025-09-24T13:51:32.699Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/16/82e65e21070e473f0ed6451224ed9fa0be85033d17e0c6e7213a12f59d12/shapely-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:df90e2db118c3671a0754f38e36802db75fe0920d211a27481daf50a711fdf26", size = 4030406, upload-time = "2025-09-24T13:51:34.189Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/75/c24ed871c576d7e2b64b04b1fe3d075157f6eb54e59670d3f5ffb36e25c7/shapely-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:361b6d45030b4ac64ddd0a26046906c8202eb60d0f9f53085f5179f1d23021a0", size = 4169511, upload-time = "2025-09-24T13:51:36.297Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/f7/b3d1d6d18ebf55236eec1c681ce5e665742aab3c0b7b232720a7d43df7b6/shapely-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:b54df60f1fbdecc8ebc2c5b11870461a6417b3d617f555e5033f1505d36e5735", size = 1602607, upload-time = "2025-09-24T13:51:37.757Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/f6/f09272a71976dfc138129b8faf435d064a811ae2f708cb147dccdf7aacdb/shapely-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:0036ac886e0923417932c2e6369b6c52e38e0ff5d9120b90eef5cd9a5fc5cae9", size = 1796682, upload-time = "2025-09-24T13:51:39.233Z" },
+]
+
+[[package]]
+name = "shellingham"
+version = "1.5.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
+]
+
+[[package]]
+name = "shiboken6"
+version = "6.9.3"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6b/ed/eac552326349629f8c3a89b57a754a860d9665aea78c172777395905d36b/shiboken6-6.9.3-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:e9b240828790b8e21a50e66449a5aa8b99f9b8a538c80c1a325fa04f8364985e", size = 400999, upload-time = "2025-09-30T12:00:08.123Z" },
+ { url = "https://files.pythonhosted.org/packages/be/82/c1c6932f9849bc5e75c93c38a29419505a6e3e0037261e28f3e7ecbf2751/shiboken6-6.9.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f3f5337a3a8fc660ba1462265bd9a2bdda9588f8d90fbc3d5ac4ce3134c11e59", size = 204927, upload-time = "2025-09-30T12:00:10.334Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/fc/026f4c8660494e513fd8c6d95d4d694d490795c6880f1fd23ec996a83e13/shiboken6-6.9.3-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:f66e82510e3a3170a42e3ef865329559b69811ce83102ad1ee57920319427c10", size = 200684, upload-time = "2025-09-30T12:00:11.967Z" },
+ { url = "https://files.pythonhosted.org/packages/50/be/f6bb3fc89623dd01ac67cd6be357e585ea5081536b275008b6c8d6fa7c28/shiboken6-6.9.3-cp39-abi3-win_amd64.whl", hash = "sha256:f1498176d2d5bcade7d662e1fc5143980fb008b6c78b966289d29a4ae86cd0c6", size = 1166242, upload-time = "2025-09-30T12:00:13.447Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/66/097a5f80ed1058071466c8c4fd72f94886851f6e03028f980ef0c42a7b54/shiboken6-6.9.3-cp39-abi3-win_arm64.whl", hash = "sha256:0c87bcfd483a1980794e03cf5ec6b2061691a1b075f92b78edb0d13847139ecf", size = 1727949, upload-time = "2025-09-30T12:00:15.23Z" },
+]
+
+[[package]]
+name = "six"
+version = "1.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
+]
+
+[[package]]
+name = "slicerator"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0c/52/f38586b82b2935f8b59a09b0a79c545a22ed062e728c9418bafeb51f61e0/slicerator-1.1.0.tar.gz", hash = "sha256:44010a7f5cd87680c07213b5cabe81d1fb71252962943e5373ee7d14605d6046", size = 38283, upload-time = "2022-04-07T18:54:08.17Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e8/ae/fa6cd331b364ad2bbc31652d025f5747d89cbb75576733dfdf8efe3e4d62/slicerator-1.1.0-py3-none-any.whl", hash = "sha256:167668d48c6d3a5ba0bd3d54b2688e81ee267dc20aef299e547d711e6f3c441a", size = 10274, upload-time = "2022-04-07T18:54:07.029Z" },
+]
+
+[[package]]
+name = "smmap"
+version = "5.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" },
+]
+
+[[package]]
+name = "snowballstemmer"
+version = "3.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" },
+]
+
+[[package]]
+name = "soupsieve"
+version = "2.8.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" },
+]
+
+[[package]]
+name = "sphinx"
+version = "7.4.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "alabaster" },
+ { name = "babel" },
+ { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "docutils" },
+ { name = "imagesize" },
+ { name = "jinja2" },
+ { name = "packaging" },
+ { name = "pygments" },
+ { name = "requests" },
+ { name = "snowballstemmer" },
+ { name = "sphinxcontrib-applehelp" },
+ { name = "sphinxcontrib-devhelp" },
+ { name = "sphinxcontrib-htmlhelp" },
+ { name = "sphinxcontrib-jsmath" },
+ { name = "sphinxcontrib-qthelp" },
+ { name = "sphinxcontrib-serializinghtml" },
+ { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911, upload-time = "2024-07-20T14:46:56.059Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0d/ef/153f6803c5d5f8917dbb7f7fcf6d34a871ede3296fa89c2c703f5f8a6c8e/sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239", size = 3401624, upload-time = "2024-07-20T14:46:52.142Z" },
+]
+
+[[package]]
+name = "sphinx-book-theme"
+version = "1.1.4"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "pydata-sphinx-theme", version = "0.15.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "sphinx", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/45/19/d002ed96bdc7738c15847c730e1e88282d738263deac705d5713b4d8fa94/sphinx_book_theme-1.1.4.tar.gz", hash = "sha256:73efe28af871d0a89bd05856d300e61edce0d5b2fbb7984e84454be0fedfe9ed", size = 439188, upload-time = "2025-02-20T16:32:32.581Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/51/9e/c41d68be04eef5b6202b468e0f90faf0c469f3a03353f2a218fd78279710/sphinx_book_theme-1.1.4-py3-none-any.whl", hash = "sha256:843b3f5c8684640f4a2d01abd298beb66452d1b2394cd9ef5be5ebd5640ea0e1", size = 433952, upload-time = "2025-02-20T16:32:31.009Z" },
+]
+
+[[package]]
+name = "sphinx-book-theme"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "pydata-sphinx-theme", version = "0.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "sphinx", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/eb/f7/154786f3cfb7692cd7acc24b6dfe4dcd1146b66f376b17df9e47125555e9/sphinx_book_theme-1.2.0.tar.gz", hash = "sha256:4a7ebfc7da4395309ac942ddfc38fbec5c5254c3be22195e99ad12586fbda9e3", size = 443962, upload-time = "2026-03-09T23:20:30.442Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/02/bf/6f506a37c7f8ecc4576caf9486e303c7af249f6d70447bb51dde9d78cb99/sphinx_book_theme-1.2.0-py3-none-any.whl", hash = "sha256:709605d308e1991c5ef0cf19c481dbe9084b62852e317fafab74382a0ee7ccfa", size = 455936, upload-time = "2026-03-09T23:20:28.788Z" },
+]
+
+[[package]]
+name = "sphinx-comments"
+version = "0.0.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "sphinx" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c0/75/5bbf29e83eaf79843180cf424d0d550bda14a1792ca51dcf79daa065ba93/sphinx-comments-0.0.3.tar.gz", hash = "sha256:00170afff27019fad08e421da1ae49c681831fb2759786f07c826e89ac94cf21", size = 7960, upload-time = "2020-08-12T00:07:31.183Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/26/97/a5c39f619375d4f81d5422377fb027075898efa6b6202c1ccf1e5bb38a32/sphinx_comments-0.0.3-py3-none-any.whl", hash = "sha256:1e879b4e9bfa641467f83e3441ac4629225fc57c29995177d043252530c21d00", size = 4591, upload-time = "2020-08-12T00:07:30.297Z" },
+]
+
+[[package]]
+name = "sphinx-copybutton"
+version = "0.5.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "sphinx" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039, upload-time = "2023-04-14T08:10:22.998Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" },
+]
+
+[[package]]
+name = "sphinx-design"
+version = "0.6.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "sphinx", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2b/69/b34e0cb5336f09c6866d53b4a19d76c227cdec1bbc7ac4de63ca7d58c9c7/sphinx_design-0.6.1.tar.gz", hash = "sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632", size = 2193689, upload-time = "2024-08-02T13:48:44.277Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c6/43/65c0acbd8cc6f50195a3a1fc195c404988b15c67090e73c7a41a9f57d6bd/sphinx_design-0.6.1-py3-none-any.whl", hash = "sha256:b11f37db1a802a183d61b159d9a202314d4d2fe29c163437001324fe2f19549c", size = 2215338, upload-time = "2024-08-02T13:48:42.106Z" },
+]
+
+[[package]]
+name = "sphinx-design"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "sphinx", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/13/7b/804f311da4663a4aecc6cf7abd83443f3d4ded970826d0c958edc77d4527/sphinx_design-0.7.0.tar.gz", hash = "sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a", size = 2203582, upload-time = "2026-01-19T13:12:53.297Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/30/cf/45dd359f6ca0c3762ce0490f681da242f0530c49c81050c035c016bfdd3a/sphinx_design-0.7.0-py3-none-any.whl", hash = "sha256:f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282", size = 2220350, upload-time = "2026-01-19T13:12:51.077Z" },
+]
+
+[[package]]
+name = "sphinx-external-toc"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "pyyaml" },
+ { name = "sphinx" },
+ { name = "sphinx-multitoc-numbering" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c5/f8/85bcd2f1c142e580a1394c18920506d9399b8e8e97e4899bbee9c74a896e/sphinx_external_toc-1.1.0.tar.gz", hash = "sha256:f81833865006f6b4a9b2550a2474a6e3d7e7f2cb23ba23309260577ea65552f6", size = 37194, upload-time = "2026-01-16T13:15:59.03Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9f/80/1704c9179012e289dee2178354e385277ea51f4fa827c4bf7e36c77b0f4b/sphinx_external_toc-1.1.0-py3-none-any.whl", hash = "sha256:26c390b8d85aa641366fed2d3674910ec6820f48b91027affef485a2655ad7d0", size = 30609, upload-time = "2026-01-16T13:15:57.926Z" },
+]
+
+[[package]]
+name = "sphinx-jupyterbook-latex"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+ { name = "sphinx" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/80/29/18a1fc30e9315e72f068637079169525069a7c0b2fbe51cf689af0576214/sphinx_jupyterbook_latex-1.0.0.tar.gz", hash = "sha256:f54c6674c13f1616f9a93443e98b9b5353f9fdda8e39b6ec552ccf0b3e5ffb62", size = 11945, upload-time = "2023-12-11T15:37:25.034Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/70/1f/1d4ecaf58b17fe61497644655f40b04d84a88348e41a6f0c6392394d95e4/sphinx_jupyterbook_latex-1.0.0-py3-none-any.whl", hash = "sha256:e0cd3e9e1c5af69136434e21a533343fdf013475c410a414d5b7b4922b4f3891", size = 13319, upload-time = "2023-12-11T15:37:23.25Z" },
+]
+
+[[package]]
+name = "sphinx-multitoc-numbering"
+version = "0.1.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "sphinx" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/37/1e/577bae038372885ebc34bd8c0f290295785a0250cac6528eb6d50e4b92d5/sphinx-multitoc-numbering-0.1.3.tar.gz", hash = "sha256:c9607671ac511236fa5d61a7491c1031e700e8d498c9d2418e6c61d1251209ae", size = 4542, upload-time = "2021-03-15T12:01:43.758Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/9f/902f2030674cd9473fdbe5a2c2dec2618c27ec853484c35f82cf8df40ece/sphinx_multitoc_numbering-0.1.3-py3-none-any.whl", hash = "sha256:33d2e707a9b2b8ad636b3d4302e658a008025106fe0474046c651144c26d8514", size = 4616, upload-time = "2021-03-15T12:01:42.419Z" },
+]
+
+[[package]]
+name = "sphinx-thebe"
+version = "0.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "sphinx" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/da/fd/926ba4af1eb2708b1ac0fa4376e4bfb11d9a32b2a00e3614137a569c1ddf/sphinx_thebe-0.3.1.tar.gz", hash = "sha256:576047f45560e82f64aa5f15200b1eb094dcfe1c5b8f531a8a65bd208e25a493", size = 20789, upload-time = "2024-02-07T13:31:57.002Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ca/7c/a53bdb465fd364bc3d255d96d5d70e6ba5183cfb4e45b8aa91c59b099124/sphinx_thebe-0.3.1-py3-none-any.whl", hash = "sha256:e7e7edee9f0d601c76bc70156c471e114939484b111dd8e74fe47ac88baffc52", size = 9030, upload-time = "2024-02-07T13:31:55.286Z" },
+]
+
+[[package]]
+name = "sphinx-togglebutton"
+version = "0.4.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "docutils" },
+ { name = "setuptools" },
+ { name = "sphinx" },
+ { name = "wheel" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cc/be/169a0b0a8ad9588e8697c85e1d489aaaca7416073c2fc0267c360af5aae9/sphinx_togglebutton-0.4.5.tar.gz", hash = "sha256:c870dfbd3bc6e119b50ff9a37a64f8991902269e856728931c7d89877e8d4b3d", size = 18101, upload-time = "2026-03-27T13:50:41.984Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d8/2e/3dd55564928c5d61f92827d4b91307dde7911a40fbe0000645d73202eea9/sphinx_togglebutton-0.4.5-py3-none-any.whl", hash = "sha256:74eac6d2426110c3e1e6f989a98e07d7823141a335df1ad8a9d637bdf6a7af62", size = 44907, upload-time = "2026-03-27T13:50:40.94Z" },
+]
+
+[[package]]
+name = "sphinxcontrib-applehelp"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" },
+]
+
+[[package]]
+name = "sphinxcontrib-bibtex"
+version = "2.7.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "docutils" },
+ { name = "pybtex" },
+ { name = "pybtex-docutils" },
+ { name = "sphinx" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/15/6a/8e0b2c2420286389e7fed78ff361ec30e2f1d58c8560af8d64df5e7b61e0/sphinxcontrib_bibtex-2.7.0.tar.gz", hash = "sha256:fee700f7aae29bb8f654c62913f00d34ac44fc0b8ca0fa67ac922ff4453addee", size = 120669, upload-time = "2026-05-06T09:29:24.935Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/52/c0/d28e62407f4733bbe0169287bc012f0ac3b4a2021066b285570654119c8b/sphinxcontrib_bibtex-2.7.0-py3-none-any.whl", hash = "sha256:28cf0ec7a957d1c7548d5749317ed472ce877e1b629f430f88e3789aa51f87b1", size = 40287, upload-time = "2026-05-06T09:29:23.253Z" },
+]
+
+[[package]]
+name = "sphinxcontrib-devhelp"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" },
+]
+
+[[package]]
+name = "sphinxcontrib-htmlhelp"
+version = "2.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" },
+]
+
+[[package]]
+name = "sphinxcontrib-jsmath"
+version = "1.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" },
+]
+
+[[package]]
+name = "sphinxcontrib-mermaid"
+version = "2.0.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jinja2" },
+ { name = "pyyaml" },
+ { name = "sphinx" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/19/75/3a1cc926da8c563c58ddc124a7b3fe5ccadcae96c96e3a6f8ac3653a210a/sphinxcontrib_mermaid-2.0.2.tar.gz", hash = "sha256:f09576c78ca93fa0e3034fd9c45aaffa7c44ab449de9c43b8b8d262afe52bc66", size = 19265, upload-time = "2026-05-05T13:59:02.959Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/16/8d/93be7e0f7fa915a576859b3bfac7a7baa3303181c44d7db7eefbd3e8a69f/sphinxcontrib_mermaid-2.0.2-py3-none-any.whl", hash = "sha256:d862e514991279fb4816302c5cfe167d2557bf3ce7125ae0cb47dac80a0f46ce", size = 14094, upload-time = "2026-05-05T13:59:01.585Z" },
+]
+
+[[package]]
+name = "sphinxcontrib-qthelp"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" },
+]
+
+[[package]]
+name = "sphinxcontrib-serializinghtml"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" },
+]
+
+[[package]]
+name = "sqlalchemy"
+version = "2.0.49"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/09/45/461788f35e0364a8da7bda51a1fe1b09762d0c32f12f63727998d85a873b/sqlalchemy-2.0.49.tar.gz", hash = "sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f", size = 9898221, upload-time = "2026-04-03T16:38:11.704Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/96/76/f908955139842c362aa877848f42f9249642d5b69e06cee9eae5111da1bd/sqlalchemy-2.0.49-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f", size = 2159321, upload-time = "2026-04-03T16:50:11.8Z" },
+ { url = "https://files.pythonhosted.org/packages/24/e2/17ba0b7bfbd8de67196889b6d951de269e8a46057d92baca162889beb16d/sqlalchemy-2.0.49-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b", size = 3238937, upload-time = "2026-04-03T16:54:45.731Z" },
+ { url = "https://files.pythonhosted.org/packages/90/1e/410dd499c039deacff395eec01a9da057125fcd0c97e3badc252c6a2d6a7/sqlalchemy-2.0.49-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1", size = 3237188, upload-time = "2026-04-03T16:56:53.217Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/06/e797a8b98a3993ac4bc785309b9b6d005457fc70238ee6cefa7c8867a92e/sqlalchemy-2.0.49-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339", size = 3190061, upload-time = "2026-04-03T16:54:47.489Z" },
+ { url = "https://files.pythonhosted.org/packages/44/d3/5a9f7ef580af1031184b38235da6ac58c3b571df01c9ec061c44b2b0c5a6/sqlalchemy-2.0.49-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d", size = 3211477, upload-time = "2026-04-03T16:56:55.056Z" },
+ { url = "https://files.pythonhosted.org/packages/69/ec/7be8c8cb35f038e963a203e4fe5a028989167cc7299927b7cf297c271e37/sqlalchemy-2.0.49-cp310-cp310-win32.whl", hash = "sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3", size = 2119965, upload-time = "2026-04-03T17:00:50.009Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/31/0defb93e3a10b0cf7d1271aedd87251a08c3a597ee4f353281769b547b5a/sqlalchemy-2.0.49-cp310-cp310-win_amd64.whl", hash = "sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75", size = 2142935, upload-time = "2026-04-03T17:00:51.675Z" },
+ { url = "https://files.pythonhosted.org/packages/60/b5/e3617cc67420f8f403efebd7b043128f94775e57e5b84e7255203390ceae/sqlalchemy-2.0.49-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe", size = 2159126, upload-time = "2026-04-03T16:50:13.242Z" },
+ { url = "https://files.pythonhosted.org/packages/20/9b/91ca80403b17cd389622a642699e5f6564096b698e7cdcbcbb6409898bc4/sqlalchemy-2.0.49-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014", size = 3315509, upload-time = "2026-04-03T16:54:49.332Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/61/0722511d98c54de95acb327824cb759e8653789af2b1944ab1cc69d32565/sqlalchemy-2.0.49-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536", size = 3315014, upload-time = "2026-04-03T16:56:56.376Z" },
+ { url = "https://files.pythonhosted.org/packages/46/55/d514a653ffeb4cebf4b54c47bec32ee28ad89d39fafba16eeed1d81dccd5/sqlalchemy-2.0.49-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88", size = 3267388, upload-time = "2026-04-03T16:54:51.272Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/16/0dcc56cb6d3335c1671a2258f5d2cb8267c9a2260e27fde53cbfb1b3540a/sqlalchemy-2.0.49-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700", size = 3289602, upload-time = "2026-04-03T16:56:57.63Z" },
+ { url = "https://files.pythonhosted.org/packages/51/6c/f8ab6fb04470a133cd80608db40aa292e6bae5f162c3a3d4ab19544a67af/sqlalchemy-2.0.49-cp311-cp311-win32.whl", hash = "sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a", size = 2119044, upload-time = "2026-04-03T17:00:53.455Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/59/55a6d627d04b6ebb290693681d7683c7da001eddf90b60cfcc41ee907978/sqlalchemy-2.0.49-cp311-cp311-win_amd64.whl", hash = "sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af", size = 2143642, upload-time = "2026-04-03T17:00:54.769Z" },
+ { url = "https://files.pythonhosted.org/packages/49/b3/2de412451330756aaaa72d27131db6dde23995efe62c941184e15242a5fa/sqlalchemy-2.0.49-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b", size = 2157681, upload-time = "2026-04-03T16:53:07.132Z" },
+ { url = "https://files.pythonhosted.org/packages/50/84/b2a56e2105bd11ebf9f0b93abddd748e1a78d592819099359aa98134a8bf/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982", size = 3338976, upload-time = "2026-04-03T17:07:40Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/fa/65fcae2ed62f84ab72cf89536c7c3217a156e71a2c111b1305ab6f0690e2/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672", size = 3351937, upload-time = "2026-04-03T17:12:23.374Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/2f/6fd118563572a7fe475925742eb6b3443b2250e346a0cc27d8d408e73773/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e", size = 3281646, upload-time = "2026-04-03T17:07:41.949Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/d7/410f4a007c65275b9cf82354adb4bb8ba587b176d0a6ee99caa16fe638f8/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750", size = 3316695, upload-time = "2026-04-03T17:12:25.642Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/95/81f594aa60ded13273a844539041ccf1e66c5a7bed0a8e27810a3b52d522/sqlalchemy-2.0.49-cp312-cp312-win32.whl", hash = "sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0", size = 2117483, upload-time = "2026-04-03T17:05:40.896Z" },
+ { url = "https://files.pythonhosted.org/packages/47/9e/fd90114059175cac64e4fafa9bf3ac20584384d66de40793ae2e2f26f3bb/sqlalchemy-2.0.49-cp312-cp312-win_amd64.whl", hash = "sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4", size = 2144494, upload-time = "2026-04-03T17:05:42.282Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/81/81755f50eb2478eaf2049728491d4ea4f416c1eb013338682173259efa09/sqlalchemy-2.0.49-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120", size = 2154547, upload-time = "2026-04-03T16:53:08.64Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/bc/3494270da80811d08bcfa247404292428c4fe16294932bce5593f215cad9/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2", size = 3280782, upload-time = "2026-04-03T17:07:43.508Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/f5/038741f5e747a5f6ea3e72487211579d8cbea5eb9827a9cbd61d0108c4bd/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3", size = 3297156, upload-time = "2026-04-03T17:12:27.697Z" },
+ { url = "https://files.pythonhosted.org/packages/88/50/a6af0ff9dc954b43a65ca9b5367334e45d99684c90a3d3413fc19a02d43c/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7", size = 3228832, upload-time = "2026-04-03T17:07:45.38Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/d1/5f6bdad8de0bf546fc74370939621396515e0cdb9067402d6ba1b8afbe9a/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33", size = 3267000, upload-time = "2026-04-03T17:12:29.657Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/30/ad62227b4a9819a5e1c6abff77c0f614fa7c9326e5a3bdbee90f7139382b/sqlalchemy-2.0.49-cp313-cp313-win32.whl", hash = "sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b", size = 2115641, upload-time = "2026-04-03T17:05:43.989Z" },
+ { url = "https://files.pythonhosted.org/packages/17/3a/7215b1b7d6d49dc9a87211be44562077f5f04f9bb5a59552c1c8e2d98173/sqlalchemy-2.0.49-cp313-cp313-win_amd64.whl", hash = "sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148", size = 2141498, upload-time = "2026-04-03T17:05:45.7Z" },
+ { url = "https://files.pythonhosted.org/packages/28/4b/52a0cb2687a9cd1648252bb257be5a1ba2c2ded20ba695c65756a55a15a4/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518", size = 3560807, upload-time = "2026-04-03T16:58:31.666Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/d8/fda95459204877eed0458550d6c7c64c98cc50c2d8d618026737de9ed41a/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d", size = 3527481, upload-time = "2026-04-03T17:06:00.155Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0a/2aac8b78ac6487240cf7afef8f203ca783e8796002dc0cf65c4ee99ff8bb/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0", size = 3468565, upload-time = "2026-04-03T16:58:33.414Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/3d/ce71cfa82c50a373fd2148b3c870be05027155ce791dc9a5dcf439790b8b/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08", size = 3477769, upload-time = "2026-04-03T17:06:02.787Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/e8/0a9f5c1f7c6f9ca480319bf57c2d7423f08d31445974167a27d14483c948/sqlalchemy-2.0.49-cp313-cp313t-win32.whl", hash = "sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d", size = 2143319, upload-time = "2026-04-03T17:02:04.328Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/51/fb5240729fbec73006e137c4f7a7918ffd583ab08921e6ff81a999d6517a/sqlalchemy-2.0.49-cp313-cp313t-win_amd64.whl", hash = "sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba", size = 2175104, upload-time = "2026-04-03T17:02:05.989Z" },
+ { url = "https://files.pythonhosted.org/packages/55/33/bf28f618c0a9597d14e0b9ee7d1e0622faff738d44fe986ee287cdf1b8d0/sqlalchemy-2.0.49-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e", size = 2156356, upload-time = "2026-04-03T16:53:09.914Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/a7/5f476227576cb8644650eff68cc35fa837d3802b997465c96b8340ced1e2/sqlalchemy-2.0.49-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a", size = 3276486, upload-time = "2026-04-03T17:07:46.9Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/84/efc7c0bf3a1c5eef81d397f6fddac855becdbb11cb38ff957888603014a7/sqlalchemy-2.0.49-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066", size = 3281479, upload-time = "2026-04-03T17:12:32.226Z" },
+ { url = "https://files.pythonhosted.org/packages/91/68/bb406fa4257099c67bd75f3f2261b129c63204b9155de0d450b37f004698/sqlalchemy-2.0.49-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187", size = 3226269, upload-time = "2026-04-03T17:07:48.678Z" },
+ { url = "https://files.pythonhosted.org/packages/67/84/acb56c00cca9f251f437cb49e718e14f7687505749ea9255d7bd8158a6df/sqlalchemy-2.0.49-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401", size = 3248260, upload-time = "2026-04-03T17:12:34.381Z" },
+ { url = "https://files.pythonhosted.org/packages/56/19/6a20ea25606d1efd7bd1862149bb2a22d1451c3f851d23d887969201633f/sqlalchemy-2.0.49-cp314-cp314-win32.whl", hash = "sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5", size = 2118463, upload-time = "2026-04-03T17:05:47.093Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/4f/8297e4ed88e80baa1f5aa3c484a0ee29ef3c69c7582f206c916973b75057/sqlalchemy-2.0.49-cp314-cp314-win_amd64.whl", hash = "sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5", size = 2144204, upload-time = "2026-04-03T17:05:48.694Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/33/95e7216df810c706e0cd3655a778604bbd319ed4f43333127d465a46862d/sqlalchemy-2.0.49-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977", size = 3565474, upload-time = "2026-04-03T16:58:35.128Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/a4/ed7b18d8ccf7f954a83af6bb73866f5bc6f5636f44c7731fbb741f72cc4f/sqlalchemy-2.0.49-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01", size = 3530567, upload-time = "2026-04-03T17:06:04.587Z" },
+ { url = "https://files.pythonhosted.org/packages/73/a3/20faa869c7e21a827c4a2a42b41353a54b0f9f5e96df5087629c306df71e/sqlalchemy-2.0.49-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61", size = 3474282, upload-time = "2026-04-03T16:58:37.131Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/50/276b9a007aa0764304ad467eceb70b04822dc32092492ee5f322d559a4dc/sqlalchemy-2.0.49-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a", size = 3480406, upload-time = "2026-04-03T17:06:07.176Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/c3/c80fcdb41905a2df650c2a3e0337198b6848876e63d66fe9188ef9003d24/sqlalchemy-2.0.49-cp314-cp314t-win32.whl", hash = "sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158", size = 2149151, upload-time = "2026-04-03T17:02:07.281Z" },
+ { url = "https://files.pythonhosted.org/packages/05/52/9f1a62feab6ed368aff068524ff414f26a6daebc7361861035ae00b05530/sqlalchemy-2.0.49-cp314-cp314t-win_amd64.whl", hash = "sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7", size = 2184178, upload-time = "2026-04-03T17:02:08.623Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/30/8519fdde58a7bdf155b714359791ad1dc018b47d60269d5d160d311fdc36/sqlalchemy-2.0.49-py3-none-any.whl", hash = "sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0", size = 1942158, upload-time = "2026-04-03T16:53:44.135Z" },
+]
+
+[[package]]
+name = "stack-data"
+version = "0.6.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "asttokens" },
+ { name = "executing" },
+ { name = "pure-eval" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" },
+]
+
+[[package]]
+name = "statsmodels"
+version = "0.14.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+ { name = "packaging" },
+ { name = "pandas" },
+ { name = "patsy" },
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0d/81/e8d74b34f85285f7335d30c5e3c2d7c0346997af9f3debf9a0a9a63de184/statsmodels-0.14.6.tar.gz", hash = "sha256:4d17873d3e607d398b85126cd4ed7aad89e4e9d89fc744cdab1af3189a996c2a", size = 20689085, upload-time = "2025-12-05T23:08:39.522Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b5/6d/9ec309a175956f88eb8420ac564297f37cf9b1f73f89db74da861052dc29/statsmodels-0.14.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4ff0649a2df674c7ffb6fa1a06bffdb82a6adf09a48e90e000a15a6aaa734b0", size = 10142419, upload-time = "2025-12-05T19:27:35.625Z" },
+ { url = "https://files.pythonhosted.org/packages/86/8f/338c5568315ec5bf3ac7cd4b71e34b98cb3b0f834919c0c04a0762f878a1/statsmodels-0.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:109012088b3e370080846ab053c76d125268631410142daad2f8c10770e8e8d9", size = 10022819, upload-time = "2025-12-05T19:27:49.385Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/77/5fc4cbc2d608f9b483b0675f82704a8bcd672962c379fe4d82100d388dbf/statsmodels-0.14.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e93bd5d220f3cb6fc5fc1bffd5b094966cab8ee99f6c57c02e95710513d6ac3f", size = 10118927, upload-time = "2025-12-05T23:07:51.256Z" },
+ { url = "https://files.pythonhosted.org/packages/94/55/b86c861c32186403fe121d9ab27bc16d05839b170d92a978beb33abb995e/statsmodels-0.14.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:06eec42d682fdb09fe5d70a05930857efb141754ec5a5056a03304c1b5e32fd9", size = 10413015, upload-time = "2025-12-05T23:08:53.95Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/be/daf0dba729ccdc4176605f4a0fd5cfe71cdda671749dca10e74a732b8b1c/statsmodels-0.14.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0444e88557df735eda7db330806fe09d51c9f888bb1f5906cb3a61fb1a3ed4a8", size = 10441248, upload-time = "2025-12-05T23:09:09.353Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/1c/2e10b7c7cc44fa418272996bf0427b8016718fd62f995d9c1f7ab37adf35/statsmodels-0.14.6-cp310-cp310-win_amd64.whl", hash = "sha256:e83a9abe653835da3b37fb6ae04b45480c1de11b3134bd40b09717192a1456ea", size = 9583410, upload-time = "2025-12-05T19:28:02.086Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/4d/df4dd089b406accfc3bb5ee53ba29bb3bdf5ae61643f86f8f604baa57656/statsmodels-0.14.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ad5c2810fc6c684254a7792bf1cbaf1606cdee2a253f8bd259c43135d87cfb4", size = 10121514, upload-time = "2025-12-05T19:28:16.521Z" },
+ { url = "https://files.pythonhosted.org/packages/82/af/ec48daa7f861f993b91a0dcc791d66e1cf56510a235c5cbd2ab991a31d5c/statsmodels-0.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:341fa68a7403e10a95c7b6e41134b0da3a7b835ecff1eb266294408535a06eb6", size = 10003346, upload-time = "2025-12-05T19:28:29.568Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/2c/c8f7aa24cd729970728f3f98822fb45149adc216f445a9301e441f7ac760/statsmodels-0.14.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdf1dfe2a3ca56f5529118baf33a13efed2783c528f4a36409b46bbd2d9d48eb", size = 10129872, upload-time = "2025-12-05T23:09:25.724Z" },
+ { url = "https://files.pythonhosted.org/packages/40/c6/9ae8e9b0721e9b6eb5f340c3a0ce8cd7cce4f66e03dd81f80d60f111987f/statsmodels-0.14.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3764ba8195c9baf0925a96da0743ff218067a269f01d155ca3558deed2658ca", size = 10381964, upload-time = "2025-12-05T23:09:41.326Z" },
+ { url = "https://files.pythonhosted.org/packages/28/8c/cf3d30c8c2da78e2ad1f50ade8b7fabec3ff4cdfc56fbc02e097c4577f90/statsmodels-0.14.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e8d2e519852adb1b420e018f5ac6e6684b2b877478adf7fda2cfdb58f5acb5d", size = 10409611, upload-time = "2025-12-05T23:09:57.131Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/cc/018f14ecb58c6cb89de9d52695740b7d1f5a982aa9ea312483ea3c3d5f77/statsmodels-0.14.6-cp311-cp311-win_amd64.whl", hash = "sha256:2738a00fca51196f5a7d44b06970ace6b8b30289839e4808d656f8a98e35faa7", size = 9580385, upload-time = "2025-12-05T19:28:42.778Z" },
+ { url = "https://files.pythonhosted.org/packages/25/ce/308e5e5da57515dd7cab3ec37ea2d5b8ff50bef1fcc8e6d31456f9fae08e/statsmodels-0.14.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe76140ae7adc5ff0e60a3f0d56f4fffef484efa803c3efebf2fcd734d72ecb5", size = 10091932, upload-time = "2025-12-05T19:28:55.446Z" },
+ { url = "https://files.pythonhosted.org/packages/05/30/affbabf3c27fb501ec7b5808230c619d4d1a4525c07301074eb4bda92fa9/statsmodels-0.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26d4f0ed3b31f3c86f83a92f5c1f5cbe63fc992cd8915daf28ca49be14463a1c", size = 9997345, upload-time = "2025-12-05T19:29:10.278Z" },
+ { url = "https://files.pythonhosted.org/packages/48/f5/3a73b51e6450c31652c53a8e12e24eac64e3824be816c0c2316e7dbdcb7d/statsmodels-0.14.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8c00a42863e4f4733ac9d078bbfad816249c01451740e6f5053ecc7db6d6368", size = 10058649, upload-time = "2025-12-05T23:10:12.775Z" },
+ { url = "https://files.pythonhosted.org/packages/81/68/dddd76117df2ef14c943c6bbb6618be5c9401280046f4ddfc9fb4596a1b8/statsmodels-0.14.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b58cf7474aa9e7e3b0771a66537148b2df9b5884fbf156096c0e6c1ff0469d", size = 10339446, upload-time = "2025-12-05T23:10:28.503Z" },
+ { url = "https://files.pythonhosted.org/packages/56/4a/dce451c74c4050535fac1ec0c14b80706d8fc134c9da22db3c8a0ec62c33/statsmodels-0.14.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:81e7dcc5e9587f2567e52deaff5220b175bf2f648951549eae5fc9383b62bc37", size = 10368705, upload-time = "2025-12-05T23:10:44.339Z" },
+ { url = "https://files.pythonhosted.org/packages/60/15/3daba2df40be8b8a9a027d7f54c8dedf24f0d81b96e54b52293f5f7e3418/statsmodels-0.14.6-cp312-cp312-win_amd64.whl", hash = "sha256:b5eb07acd115aa6208b4058211138393a7e6c2cf12b6f213ede10f658f6a714f", size = 9543991, upload-time = "2025-12-05T23:10:58.536Z" },
+ { url = "https://files.pythonhosted.org/packages/81/59/a5aad5b0cc266f5be013db8cde563ac5d2a025e7efc0c328d83b50c72992/statsmodels-0.14.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47ee7af083623d2091954fa71c7549b8443168f41b7c5dce66510274c50fd73e", size = 10072009, upload-time = "2025-12-05T23:11:14.021Z" },
+ { url = "https://files.pythonhosted.org/packages/53/dd/d8cfa7922fc6dc3c56fa6c59b348ea7de829a94cd73208c6f8202dd33f17/statsmodels-0.14.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa60d82e29fcd0a736e86feb63a11d2380322d77a9369a54be8b0965a3985f71", size = 9980018, upload-time = "2025-12-05T23:11:30.907Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/77/0ec96803eba444efd75dba32f2ef88765ae3e8f567d276805391ec2c98c6/statsmodels-0.14.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89ee7d595f5939cc20bf946faedcb5137d975f03ae080f300ebb4398f16a5bd4", size = 10060269, upload-time = "2025-12-05T23:11:46.338Z" },
+ { url = "https://files.pythonhosted.org/packages/10/b9/fd41f1f6af13a1a1212a06bb377b17762feaa6d656947bf666f76300fc05/statsmodels-0.14.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:730f3297b26749b216a06e4327fe0be59b8d05f7d594fb6caff4287b69654589", size = 10324155, upload-time = "2025-12-05T23:12:01.805Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/0f/a6900e220abd2c69cd0a07e3ad26c71984be6061415a60e0f17b152ecf08/statsmodels-0.14.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f1c08befa85e93acc992b72a390ddb7bd876190f1360e61d10cf43833463bc9c", size = 10349765, upload-time = "2025-12-05T23:12:18.018Z" },
+ { url = "https://files.pythonhosted.org/packages/98/08/b79f0c614f38e566eebbdcff90c0bcacf3c6ba7a5bbb12183c09c29ca400/statsmodels-0.14.6-cp313-cp313-win_amd64.whl", hash = "sha256:8021271a79f35b842c02a1794465a651a9d06ec2080f76ebc3b7adce77d08233", size = 9540043, upload-time = "2025-12-05T23:12:33.887Z" },
+ { url = "https://files.pythonhosted.org/packages/71/de/09540e870318e0c7b58316561d417be45eff731263b4234fdd2eee3511a8/statsmodels-0.14.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:00781869991f8f02ad3610da6627fd26ebe262210287beb59761982a8fa88cae", size = 10069403, upload-time = "2025-12-05T23:12:48.424Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/f0/63c1bfda75dc53cee858006e1f46bd6d6f883853bea1b97949d0087766ca/statsmodels-0.14.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:73f305fbf31607b35ce919fae636ab8b80d175328ed38fdc6f354e813b86ee37", size = 9989253, upload-time = "2025-12-05T23:13:05.274Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/98/b0dfb4f542b2033a3341aa5f1bdd97024230a4ad3670c5b0839d54e3dcab/statsmodels-0.14.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e443e7077a6e2d3faeea72f5a92c9f12c63722686eb80bb40a0f04e4a7e267ad", size = 10090802, upload-time = "2025-12-05T23:13:20.653Z" },
+ { url = "https://files.pythonhosted.org/packages/34/0e/2408735aca9e764643196212f9069912100151414dd617d39ffc72d77eee/statsmodels-0.14.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3414e40c073d725007a6603a18247ab7af3467e1af4a5e5a24e4c27bc26673b4", size = 10337587, upload-time = "2025-12-05T23:13:37.597Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/36/4d44f7035ab3c0b2b6a4c4ebb98dedf36246ccbc1b3e2f51ebcd7ac83abb/statsmodels-0.14.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a518d3f9889ef920116f9fa56d0338069e110f823926356946dae83bc9e33e19", size = 10363350, upload-time = "2025-12-05T23:13:53.08Z" },
+ { url = "https://files.pythonhosted.org/packages/26/33/f1652d0c59fa51de18492ee2345b65372550501ad061daa38f950be390b6/statsmodels-0.14.6-cp314-cp314-win_amd64.whl", hash = "sha256:151b73e29f01fe619dbce7f66d61a356e9d1fe5e906529b78807df9189c37721", size = 9588010, upload-time = "2025-12-05T23:14:07.28Z" },
+]
+
+[[package]]
+name = "superqt"
+version = "0.8.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pygments" },
+ { name = "qtpy" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/84/fe/919245507b15b4633cd40292d69c3b6afa8a650bf3ec33f3329d26314a3c/superqt-0.8.2.tar.gz", hash = "sha256:faa3bd00ef1c9b209b1f31b154dd41c596fec3824f61a70c251fb5569acd7ba6", size = 110190, upload-time = "2026-05-18T14:33:47.027Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/01/b9/748c3cbcdba20ef01c055a35905cad5c771cc1df29be11b9a82da6fd8e0d/superqt-0.8.2-py3-none-any.whl", hash = "sha256:ce8400ae7b577d090368aae5bdbabb06cd6bc893f6ca73485bb686835bf20943", size = 101595, upload-time = "2026-05-18T14:33:45.462Z" },
+]
+
+[package.optional-dependencies]
+iconify = [
+ { name = "pyconify" },
+]
+
+[[package]]
+name = "sympy"
+version = "1.14.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mpmath" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" },
+]
+
+[[package]]
+name = "tables"
+version = "3.10.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "blosc2", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numexpr", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "py-cpuinfo", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0d/5d/96708a84e9fcd29d1f684d56d4c38a23d29b1c934599a072a49f27ccfa71/tables-3.10.1.tar.gz", hash = "sha256:4aa07ac734b9c037baeaf44aec64ec902ad247f57811b59f30c4e31d31f126cf", size = 4762413, upload-time = "2024-08-17T09:57:47.127Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ff/69/a768ec8104ada032c9be09f521f548766ddd0351bc941c9d42fa5db001de/tables-3.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bca9d11a570ca1bc57f0845e54e55c3093d5a1ace376faee639e09503a73745b", size = 6823691, upload-time = "2024-08-17T09:56:50.229Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/2d/074bc14b39de9b552eec02ee583eff2997d903da1355f4450506335a6055/tables-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b62881cb682438d1e92b9178db42b160638aef3ca23341f7d98e9b27821b1eb4", size = 5471221, upload-time = "2024-08-17T09:56:54.84Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/30/29411ab804b5ac4bee25c82ba38f4e7a8c0b52c6a1cdbeea7d1db33a53fe/tables-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9cf1bfd8b0e0195196205fc8a134628219cff85d20da537facd67a291e6b347", size = 7170201, upload-time = "2024-08-17T09:56:59.011Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/7d/3165c7538b8e89b22fa17ad68e04106cca7023cf68e94011ae7b3b6d2a78/tables-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77f0e6dd45b91d99bf3976c8655c48fe3816baf390b9098e4fb2f0fdf9da7078", size = 7571035, upload-time = "2024-08-17T09:57:03.115Z" },
+ { url = "https://files.pythonhosted.org/packages/46/b3/985a23d2cf27aad383301a5e99e1851228a1941b868515612b5357bded5f/tables-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:d90542ec172d1d60df0b796c48ad446f2b69a5d5cd3077bd6450891b854d1ffb", size = 6311650, upload-time = "2024-08-17T09:57:06.593Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/04/957264eb35e60251830a965e2d02332eb36ed14fbd8345df06981bbf3ece/tables-3.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8917262a2bb3cd79d37e108557e34ec4b365fdcc806e01dd10765a84c65dab6", size = 6790492, upload-time = "2024-08-17T09:57:10.247Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/19/eb7af9d92aaf6766f5fedfce11a97ab03cf39856561c5f562dc0c769a682/tables-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f93f6db623b484bb6606537c2a71e95ee34fae19b0d891867642dd8c7be05af6", size = 5506835, upload-time = "2024-08-17T09:57:13.883Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/8f/897324e1ad543ca439b2c91f04c406f3eeda6e7ff2f43b4cd939f05043e4/tables-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01ca51624bca1a87e703d6d6b796368bc3460ff007ea8b1341be03bedd863833", size = 7166960, upload-time = "2024-08-17T09:57:17.463Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/5c/3f21d1135bf60af99ac79a17bbffd333d69763df2197ba04f47dd30bbd4e/tables-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9372516c76be3a05a573df63a69ce38315d03b5816d2a1e89c48129ec8b161b0", size = 7568724, upload-time = "2024-08-17T09:57:23.02Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/e3/3ee6b66263902eccadc4e0e23bca7fb480fd190904b7ce0bea4777b5b799/tables-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:09190fb504888aeacafb7739c13d5c5a3e87af3d261f4d2f832b1f8407be133a", size = 6312200, upload-time = "2024-08-17T09:57:26.322Z" },
+ { url = "https://files.pythonhosted.org/packages/95/ec/ea6c476e33602c172c797fe8f8ab96d007d964137068276d142b142a28e5/tables-3.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7090af37909e3bf229d5599fa442633e5a93b6082960b01038dc0106e07a8da", size = 6791597, upload-time = "2024-08-17T09:57:29.598Z" },
+ { url = "https://files.pythonhosted.org/packages/74/02/a967a506e9204e3328a8c03f67e6f3c919defc8df11aba83ae5b2abf7b0f/tables-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:203ed50c0c5f30f007df7633089b2a567b99856cd25d68f19d91624a8db2e7ad", size = 5474779, upload-time = "2024-08-17T09:57:32.43Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/26/925793f753664ec698b2c6315c818269313db143da38150897cf260405c2/tables-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e36ce9f10471c69c1f0b06c6966de762558a35d62592c55df7994a8019adaf0c", size = 7130683, upload-time = "2024-08-17T09:57:36.181Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/79/2b34f22284459e940a84e71dba19b2a34c7cc0ce3cdf685923c50d5b9611/tables-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f233e78cc9fa4157ec4c3ef2abf01a731fe7969bc6ed73539e5f4cd3b94c98b2", size = 7531367, upload-time = "2024-08-17T09:57:39.864Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/27/5a23830f611e26dd7ee104096c6bb82e481b16f3f17ccaed3075f8d48312/tables-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:34357d2f2f75843a44e6fe54d1f11fc2e35a8fd3cb134df3d3362cff78010adb", size = 6295046, upload-time = "2024-08-17T09:57:43.561Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/d4/e7c25df877e054b05f146d6ccb920bcdbe8d39b35a0962868b80547532c7/tables-3.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6fc5b46a4f359249c3ab9a0a0a2448d7e680e68cffd63fdf3fb7171781edd46e", size = 6824253, upload-time = "2024-11-09T19:26:06.428Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/49/091865d75090a24493bd1b66e52d72f4d9627ff42983a13d4dcd89455d02/tables-3.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2ecabd7f459d40b7f9f5256850dd5f43773fda7b789f827de92c3d26df1e320f", size = 5499587, upload-time = "2024-11-09T19:26:12.402Z" },
+ { url = "https://files.pythonhosted.org/packages/23/83/9dac8af333149fa01add439f710d4a312b70faf81c2f59a16b8bfaebb75e/tables-3.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40a4ee18f3c9339d9dd8fd3777c75cda5768f2ff347064a2796f59161a190af8", size = 7128236, upload-time = "2024-11-09T19:26:15.716Z" },
+ { url = "https://files.pythonhosted.org/packages/89/fd/62f31643596f6ab71fc6d2a87acdee0bc01a03fbe1a7f3f6dc0c91e2546d/tables-3.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757c6ea257c174af8036cf8f273ede756bbcd6db5ac7e2a4d64e788b0f371152", size = 7527953, upload-time = "2024-11-09T19:26:20.229Z" },
+]
+
+[[package]]
+name = "tables"
+version = "3.11.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "blosc2", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numexpr", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "py-cpuinfo", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cc/a3/d213ebe7376d48055bd55a29cd9f99061afa0dcece608f94a5025d797b0a/tables-3.11.1.tar.gz", hash = "sha256:78abcf413091bc7c1e4e8c10fbbb438d1ac0b5a87436c5b972c3e8253871b6fb", size = 4790533, upload-time = "2026-03-01T11:43:36.036Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fa/bb/4a9cde6628563388db26fa86c64adb0f2475a757e72af0ec185fd520b72f/tables-3.11.1-cp311-abi3-macosx_10_9_x86_64.whl", hash = "sha256:eb30684c42a77bbecdef2b9c763c4372b0ddc9cc5bd8b2a2055f2042eee67217", size = 7045977, upload-time = "2026-03-01T11:42:48.605Z" },
+ { url = "https://files.pythonhosted.org/packages/78/74/6568c8d3aabf9982ab89fe3e378afbd7aad4894bde4570991a3246169ef4/tables-3.11.1-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:f0367d2e3df0f10ea63ccf4279f3fe58e32ec481767320301a483e2b3cd83efc", size = 6264947, upload-time = "2026-03-01T11:42:53.192Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/a3/ec228901fca4c996306b17f5c60a4105144df0bbd07b3a4a816f91f37b4a/tables-3.11.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56bf6fb9132ead989b7e76695d7613d6d08f071a8019038d6565ba90c66b9f3e", size = 6903733, upload-time = "2026-03-01T11:42:58.349Z" },
+ { url = "https://files.pythonhosted.org/packages/99/29/c2dc674ea70fa9a4819417289a9c0d3e4780835beeed573eb66964cfb763/tables-3.11.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e78fe190fdeb4afe430b79651bae2a4f341904eb85aa8dbafe5f1caee1c7f67", size = 7241357, upload-time = "2026-03-01T11:43:03.938Z" },
+ { url = "https://files.pythonhosted.org/packages/60/b5/a59b62af4127790c618eb11c06c106706e07509a3fb9e346b2a3ffa74419/tables-3.11.1-cp311-abi3-win_amd64.whl", hash = "sha256:7fa6cb03f6fe55ae4f85e89ec5450e5c40cc4c52d8c3b60eb157a445c2219e89", size = 6526565, upload-time = "2026-03-01T11:43:08.58Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/ce/561c82496e7c8c15ebf19b53b12c0ef91b322a66869db762db9711102764/tables-3.11.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a4bbd95036a4d0cc5c86c1f87fbb490b4c53cd70982f1c01b3ed6dcb3085cbb9", size = 7111409, upload-time = "2026-03-01T11:43:13.424Z" },
+ { url = "https://files.pythonhosted.org/packages/84/18/bac920aee8239b572c506459607c6dd8742bc6275a43d51d2dd6ae1a1541/tables-3.11.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e3cfe79484351f7216eb8f3767bfa1217bfd271b04428f79cfa7ef6d7491919d", size = 6380142, upload-time = "2026-03-01T11:43:17.213Z" },
+ { url = "https://files.pythonhosted.org/packages/59/3c/f4a694aa744d2b14d536e172c28dd70c84445f4787083a82d6d44a39e39f/tables-3.11.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a9c35f87fcb6a48c79fbc4e3ab15ca8f6053c4ce13063d6ca2ec36cbb58f40f", size = 7014135, upload-time = "2026-03-01T11:43:22.359Z" },
+ { url = "https://files.pythonhosted.org/packages/45/82/94d4320d6c0fe5bd55230eec90cd142d58cda37b7cce00a318ac2a6abd93/tables-3.11.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4cf3218b76ba78d156d6ee75c19fb757d50682f6c7b4905370441afbfc9d77f3", size = 7349293, upload-time = "2026-03-01T11:43:27.569Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/02/a0f61a602ce2f2be8cc2e6146cc51acdaa8a1bb9b823b3863e70d3e0505d/tables-3.11.1-cp314-cp314t-win_amd64.whl", hash = "sha256:a6f7a3b82dbf0ae0f30de635ca88bb42dd87938b0950369d0ee4289c52ae6de2", size = 6854713, upload-time = "2026-03-01T11:43:31.934Z" },
+]
+
+[[package]]
+name = "tabulate"
+version = "0.10.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754, upload-time = "2026-03-04T18:55:34.402Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814, upload-time = "2026-03-04T18:55:31.284Z" },
+]
+
+[[package]]
+name = "tensorboard"
+version = "2.14.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "absl-py", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "google-auth", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "google-auth-oauthlib", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "grpcio", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "markdown", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "protobuf", version = "4.25.9", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "requests", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "setuptools", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "six", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorboard-data-server", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "werkzeug", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/73/a2/66ed644f6ed1562e0285fcd959af17670ea313c8f331c46f79ee77187eb9/tensorboard-2.14.1-py3-none-any.whl", hash = "sha256:3db108fb58f023b6439880e177743c5f1e703e9eeb5fb7d597871f949f85fd58", size = 5508920, upload-time = "2023-09-27T23:37:16.71Z" },
+]
+
+[[package]]
+name = "tensorboard"
+version = "2.17.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "absl-py", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "grpcio", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "markdown", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "protobuf", version = "4.25.9", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "setuptools", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "six", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorboard-data-server", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "werkzeug", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d4/41/dccba8c5f955bc35b6110ff78574e4e5c8226ad62f08e732096c3861309b/tensorboard-2.17.1-py3-none-any.whl", hash = "sha256:253701a224000eeca01eee6f7e978aea7b408f60b91eb0babdb04e78947b773e", size = 5502989, upload-time = "2024-08-14T18:10:47.657Z" },
+]
+
+[[package]]
+name = "tensorboard"
+version = "2.18.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "absl-py", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "grpcio", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "markdown", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "protobuf", version = "5.29.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "setuptools", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "six", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorboard-data-server", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "werkzeug", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b1/de/021c1d407befb505791764ad2cbd56ceaaa53a746baed01d2e2143f05f18/tensorboard-2.18.0-py3-none-any.whl", hash = "sha256:107ca4821745f73e2aefa02c50ff70a9b694f39f790b11e6f682f7d326745eab", size = 5503036, upload-time = "2024-09-25T21:21:50.169Z" },
+]
+
+[[package]]
+name = "tensorboard"
+version = "2.20.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "absl-py", marker = "python_full_version < '3.13'" },
+ { name = "grpcio", marker = "python_full_version < '3.13'" },
+ { name = "markdown", marker = "python_full_version < '3.13'" },
+ { name = "numpy", marker = "python_full_version < '3.13'" },
+ { name = "packaging", marker = "python_full_version < '3.13'" },
+ { name = "pillow", marker = "python_full_version < '3.13'" },
+ { name = "protobuf", version = "6.33.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
+ { name = "setuptools", marker = "python_full_version < '3.13'" },
+ { name = "tensorboard-data-server", marker = "python_full_version < '3.13'" },
+ { name = "werkzeug", marker = "python_full_version < '3.13'" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9c/d9/a5db55f88f258ac669a92858b70a714bbbd5acd993820b41ec4a96a4d77f/tensorboard-2.20.0-py3-none-any.whl", hash = "sha256:9dc9f978cb84c0723acf9a345d96c184f0293d18f166bb8d59ee098e6cfaaba6", size = 5525680, upload-time = "2025-07-17T19:20:49.638Z" },
+]
+
+[[package]]
+name = "tensorboard-data-server"
+version = "0.7.2"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7a/13/e503968fefabd4c6b2650af21e110aa8466fe21432cd7c43a84577a89438/tensorboard_data_server-0.7.2-py3-none-any.whl", hash = "sha256:7e0610d205889588983836ec05dc098e80f97b7e7bbff7e994ebb78f578d0ddb", size = 2356, upload-time = "2023-10-23T21:23:32.16Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/85/dabeaf902892922777492e1d253bb7e1264cadce3cea932f7ff599e53fea/tensorboard_data_server-0.7.2-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:9fe5d24221b29625dbc7328b0436ca7fc1c23de4acf4d272f1180856e32f9f60", size = 4823598, upload-time = "2023-10-23T21:23:33.714Z" },
+ { url = "https://files.pythonhosted.org/packages/73/c6/825dab04195756cf8ff2e12698f22513b3db2f64925bdd41671bfb33aaa5/tensorboard_data_server-0.7.2-py3-none-manylinux_2_31_x86_64.whl", hash = "sha256:ef687163c24185ae9754ed5650eb5bc4d84ff257aabdc33f0cc6f74d8ba54530", size = 6590363, upload-time = "2023-10-23T21:23:35.583Z" },
+]
+
+[[package]]
+name = "tensorflow"
+version = "2.14.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "absl-py", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "astunparse", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "flatbuffers", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "gast", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "google-pasta", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "grpcio", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "h5py", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "keras", version = "2.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "libclang", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "ml-dtypes", version = "0.2.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "opt-einsum", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "protobuf", version = "4.25.9", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "setuptools", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "six", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorboard", version = "2.14.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow-estimator", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow-io-gcs-filesystem", version = "0.31.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'linux' and extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow-io-gcs-filesystem", version = "0.37.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.12' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version >= '3.12' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version >= '3.12' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "termcolor", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wrapt", version = "1.14.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf') or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/51/ad9ebf4ef29754b813a057d64a0634feb12aef27cabcbdb7433dc5cd4cb4/tensorflow-2.14.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:318b21b18312df6d11f511d0f205d55809d9ad0f46d5f9c13d8325ce4fe3b159", size = 229634719, upload-time = "2023-09-26T22:51:39.857Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/e0/1db7b4b382e7d654dd176ee3e09af201f0735ea1a3233c087c3e63f054e9/tensorflow-2.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:927868c9bd4b3d2026ac77ec65352226a9f25e2d24ec3c7d088c68cff7583c9b", size = 2108, upload-time = "2023-09-26T22:53:05.115Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/40/da089d1cabd9141543dfeb462e16f6c6741a76ac326174f168b7ce53d54f/tensorflow-2.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3870063433aebbd1b8da65ed4dcb09495f9239397f8cb5a8822025b6bb65e04", size = 2122, upload-time = "2023-09-26T22:56:24.729Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/7a/c7762c698fb1ac41a7e3afee51dc72aa3ec74ae8d2f57ce19a9cded3a4af/tensorflow-2.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c9c1101269efcdb63492b45c8e83df0fc30c4454260a252d507dfeaebdf77ff", size = 489833115, upload-time = "2023-09-26T22:53:51.969Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/c3/17c6aa1dd5bc8cea5bf00d0c3a021a5dd1680c250861cc877a7e556e4b9b/tensorflow-2.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:0b7eaab5e034f1695dc968f7be52ce7ccae4621182d1e2bf6d5b3fab583be98c", size = 2099, upload-time = "2023-09-26T22:55:27.537Z" },
+ { url = "https://files.pythonhosted.org/packages/22/50/1e211cbb5e1f52e55eeae1605789c9d24403962d37581cf0deb3e6b33377/tensorflow-2.14.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:00c42e7d8280c660b10cf5d0b3164fdc5e38fd0bf16b3f9963b7cd0e546346d8", size = 229677851, upload-time = "2023-09-26T22:51:59.935Z" },
+ { url = "https://files.pythonhosted.org/packages/de/ea/90267db2c02fb61f4d03b9645c7446d3cbca6d5c08522e889535c88edfcd/tensorflow-2.14.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c92f5526c2029d31a036be06eb229c71f1c1821472876d34d0184d19908e318c", size = 2106, upload-time = "2023-09-26T22:53:08.777Z" },
+ { url = "https://files.pythonhosted.org/packages/92/ba/0b9dc0a69e518cca919587fd32ec22a81c99bcdf94c8482f00440fff72d0/tensorflow-2.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c224c076160ef9f60284e88f59df2bed347d55e64a0ca157f30f9ca57e8495b0", size = 2122, upload-time = "2023-09-26T22:56:28.206Z" },
+ { url = "https://files.pythonhosted.org/packages/09/63/25e76075081ea98ec48f23929cefee58be0b42212e38074a9ec5c19e838c/tensorflow-2.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a80cabe6ab5f44280c05533e5b4a08e5b128f0d68d112564cffa3b96638e28aa", size = 489875759, upload-time = "2023-09-26T22:54:19.219Z" },
+ { url = "https://files.pythonhosted.org/packages/80/6f/57d36f6507e432d7fc1956b2e9e8530c5c2d2bfcd8821bcbfae271cd6688/tensorflow-2.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:0587ece626c4f7c4fcb2132525ea6c77ad2f2f5659a9b0f4451b1000be1b5e16", size = 2099, upload-time = "2023-09-26T22:55:30.95Z" },
+]
+
+[[package]]
+name = "tensorflow"
+version = "2.17.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "absl-py", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "astunparse", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "flatbuffers", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "gast", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "google-pasta", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "grpcio", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "h5py", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "keras", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "libclang", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "opt-einsum", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "protobuf", version = "4.25.9", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "requests", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "setuptools", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "six", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorboard", version = "2.17.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "termcolor", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wrapt", version = "2.1.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e1/81/d658100476affe0c68ccfb0ad72813ef487578187cadba1c896a26cac8e0/tensorflow-2.17.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:61f45ca991cf3dddf0b1069674c455fdbf38edf749dab962bb4bb8a3f99fb25f", size = 236135046, upload-time = "2024-10-24T22:57:23.159Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/18/dc5fa720f2381f6dfeb955dfc330261e183ef91e57128cb39aecb8440d1c/tensorflow-2.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8aa202e17894dcb0582283e5a5c703391d793ccce11c5c02b1fe8f839ae09f3c", size = 223903722, upload-time = "2024-10-24T22:57:37.41Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/39/ca18413eb576ee0231a1fe6e9d9499afcef614fd94154e0aaf14f32ba3eb/tensorflow-2.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618cc21b0adf695fc8b4323f56ccd17c1408379422e1a177481d4fd8523fa8e", size = 601258535, upload-time = "2024-10-24T22:57:50.405Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/0f/28abaa2e4e6df19d77a2d41cc2b62589250c403dcd505c45433919526fd7/tensorflow-2.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:4c23498b370c9d6b2521722b6acc89fab61e6a593e886df16bed3075584bf1c7", size = 7521, upload-time = "2024-10-24T22:58:03.64Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/fd/92db1ecf5550f8937c21c28f84228959adb9ccba5f19e283ec7f22825b11/tensorflow-2.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:595c220b0febe2295a150e0d870c742a75c56b145a63ed878f78d64aa43c6ca6", size = 236176925, upload-time = "2024-10-24T22:58:08.728Z" },
+ { url = "https://files.pythonhosted.org/packages/45/56/d1f227b56a82cf168d4d5b3fa6094857079f36891af4bc851281afa5cc23/tensorflow-2.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc41bc3e31d205dcf6e4b8afc0514de05445303d93fade03549085ef8c45a2b2", size = 223945776, upload-time = "2024-10-24T22:58:18.161Z" },
+ { url = "https://files.pythonhosted.org/packages/df/9a/f5b1f4b2c08295ae1cb8760d1fb6043485459f0d8c107dd900e76a6ba25d/tensorflow-2.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68841e17e573301d8ba9192f929b8096b0b341567cb81414096305c723de68d0", size = 601300475, upload-time = "2024-10-24T22:58:31.181Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/e6/8dbd20c4942b578aa18ef61e8d7858ddbd3650bbea731539c1fdadbaa466/tensorflow-2.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:a6bd9474f1e0dedb7deb331c8e93cf2d5997da8781a1949f75c4a7cf8923d2e3", size = 7518, upload-time = "2024-10-24T22:58:42.649Z" },
+ { url = "https://files.pythonhosted.org/packages/af/29/29bfb68a1cfd61649cd51f5f08474a9eab343c2402f8998af6f1f6120aa5/tensorflow-2.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:d43698039fd057ee6d7c3f908c4e9a6e310be6e77adda419bae3fee5ca7192fe", size = 236283570, upload-time = "2024-10-24T22:58:46.871Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/86/3af00483ff5215e52ebd9d5be2987798a6985f60db413091522a97459c2c/tensorflow-2.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:695741b80ea9b2603a8d3f423051476c02a9cf48f4721d18c4dafbe0a8a5ca91", size = 224039295, upload-time = "2024-10-24T22:58:55.317Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/15/c81ec3b1ca9980cba8a8123a4df0a3f3b96ae4f49d024cd0c4175aa443c0/tensorflow-2.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c3452ab09940cbc3be896641b256855ba92154b79a90c864c5b2be79db78a64", size = 601418583, upload-time = "2024-10-24T22:59:08.262Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/53/0eafd55dd5802c60ac69f6ad01dec61cdb894b591a1f3e471e2a01edbe99/tensorflow-2.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:faf032fae35de0071f20850abd23d78e0e3ae116ce7fbfb9d034da2acc900543", size = 7521, upload-time = "2024-10-24T22:59:21.763Z" },
+]
+
+[[package]]
+name = "tensorflow"
+version = "2.18.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "absl-py", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "astunparse", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "flatbuffers", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "gast", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "google-pasta", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "grpcio", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "h5py", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "keras", version = "3.12.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "keras", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "libclang", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "opt-einsum", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "protobuf", version = "5.29.6", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "requests", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "setuptools", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "six", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorboard", version = "2.18.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow-io-gcs-filesystem", version = "0.37.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.12' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "termcolor", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wrapt", version = "2.1.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0c/e8/d5d54e18ff6fe67c75c3c65415c98ecd31bd0ff7613d47a1390f062993b5/tensorflow-2.18.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:8da90a9388a1f6dd00d626590d2b5810faffbb3e7367f9783d80efff882340ee", size = 239373575, upload-time = "2024-10-25T00:14:11.549Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/58/99ba9d580c218fd866e6044b10915eb415f60af38c03dca6ff2df7f83337/tensorflow-2.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:589342fb9bdcab2e9af0f946da4ca97757677e297d934fcdc087e87db99d6353", size = 231677108, upload-time = "2024-10-25T00:14:25.787Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/80/1567ccc375ccda4d28af28c960cca7f709f7c259463ac1436554697e8868/tensorflow-2.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1eb77fae50d699442726d1b23c7512c97cd688cc7d857b028683d4535bbf3709", size = 615262200, upload-time = "2024-10-25T00:14:39.672Z" },
+ { url = "https://files.pythonhosted.org/packages/59/63/5ca1b06cf17dda9c52927917a7911612953a7d91865b1288c7f9eac2b53b/tensorflow-2.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:46f5a8b4e6273f488dc069fc3ac2211b23acd3d0437d919349c787fa341baa8a", size = 7519, upload-time = "2024-10-25T00:14:52.683Z" },
+ { url = "https://files.pythonhosted.org/packages/26/08/556c4159675c1a30e077ec2a942eeeb81b457cc35c247a5b4a59a1274f05/tensorflow-2.18.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:453cb60638a02fd26316fb36c8cbcf1569d33671f17c658ca0cf2b4626f851e7", size = 239492146, upload-time = "2024-10-25T00:14:56.741Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/3d/45956345442e3a7b335df6f13d068121d8454c243f31b1f44244705ac584/tensorflow-2.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85f1e7369af6d329b117b52e86093cd1e0458dd5404bf5b665853f873dd00b48", size = 231839918, upload-time = "2024-10-25T00:15:05.71Z" },
+ { url = "https://files.pythonhosted.org/packages/84/76/c55967ac9968ddaede25a4dce37aba37e9030656f02c12676151ce1b6f22/tensorflow-2.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b8dd70fa3600bfce66ab529eebb804e1f9d7c863d2f71bc8fe9fc7a1ec3976", size = 615407268, upload-time = "2024-10-25T00:15:20.224Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/24/271e77c22724f370c24c705f394b8035b4d27e4c2c6339f3f45ab9b8258e/tensorflow-2.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e8b0f499ef0b7652480a58e358a73844932047f21c42c56f7f3bdcaf0803edc", size = 7516, upload-time = "2024-10-25T00:15:31.667Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/bf/4cc283db323fd723f630e2454b2857054d2c81ff5012c1857659e72470f1/tensorflow-2.18.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ec4133a215c59314e929e7cbe914579d3afbc7874d9fa924873ee633fe4f71d0", size = 239565465, upload-time = "2024-10-25T00:15:35.566Z" },
+ { url = "https://files.pythonhosted.org/packages/56/e4/55aaac2b15af4dad079e5af329a79d961e5206589d0e02b1e8da221472ed/tensorflow-2.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4822904b3559d8a9c25f0fe5fef191cfc1352ceca42ca64f2a7bc7ae0ff4a1f5", size = 231898760, upload-time = "2024-10-25T00:15:45.725Z" },
+ { url = "https://files.pythonhosted.org/packages/50/29/61ce80da0bfea3948326697dd1d832d28c863c9dacf90a27ee80fd4c1d31/tensorflow-2.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfdd65ea7e064064283dd78d529dd621257ee617218f63681935fd15817c6286", size = 615520727, upload-time = "2024-10-25T00:16:01.386Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/f1/828bbccc84a72db960a7d116f55f3f6aec9f5658f5d32ce9db20142d5742/tensorflow-2.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:a701c2d3dca5f2efcab315b2c217f140ebd3da80410744e87d77016b3aaf53cb", size = 7520, upload-time = "2024-10-25T00:16:12.418Z" },
+]
+
+[[package]]
+name = "tensorflow"
+version = "2.18.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "absl-py", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "astunparse", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "flatbuffers", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "gast", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "google-pasta", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "grpcio", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "h5py", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "keras", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "libclang", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "opt-einsum", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "packaging", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "protobuf", version = "5.29.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "requests", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "setuptools", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "six", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorboard", version = "2.18.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "termcolor", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wrapt", version = "2.1.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dc/5e/955a719c2359430a6fa6ec596bafc903b31285844ef44ae53e83bb91ac62/tensorflow-2.18.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:8baba2b0f9f286f8115a0005d17c020d2febf95e434302eaf758f2020c1c4de5", size = 239430540, upload-time = "2025-03-12T00:11:40.574Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/83/4631df86b7880c18ce221b16e9f6f08e8100143d99d68bd6612d8ec534f8/tensorflow-2.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd7284768f5a6b10e41a700e8141de70756dc62ed5d0b93360d131ccc0a6ba8", size = 231774989, upload-time = "2025-03-12T00:11:50.957Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/f6/43ed0e0accc63747cb9b6250cbef6515a449f848d4eda0af9d591ac1cea9/tensorflow-2.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f929842999d60e7da67743ae5204b477259f3b771c02e5e437d232267e49f18", size = 615365234, upload-time = "2025-03-12T00:12:07.74Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/27/75d313117d8a9f8aadb8b9121cc33d44793a2d704c3b3f5866e632821b82/tensorflow-2.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:db1d186c17b6a7c51813e275d0a83e964669822372aa01d074cf64b853ee76ac", size = 368995257, upload-time = "2025-03-12T00:12:26.63Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/88/57e2acd11a2587cc5c0a6612a389a57f3bda3cd60d132934cb7a9bb00a81/tensorflow-2.18.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:661029cd769b311db910b79a3a6ef50a5a61ecc947172228c777a49989722508", size = 239549037, upload-time = "2025-03-12T00:12:38.202Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/b3/902588dcffbc0603893f1df482840ff9c596430155d62e159bc8fc155230/tensorflow-2.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a6485edd2148f70d011dbd1d8dc2c775e91774a5a159466e83d0d1f21580944", size = 231937898, upload-time = "2025-03-12T00:12:47.544Z" },
+ { url = "https://files.pythonhosted.org/packages/45/c6/05d862ebeaaf63343dffc4f97dab62ac493e8c2bbc6b1a256199bcc78ed4/tensorflow-2.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9f87e5d2a680a4595f5dc30daf6bbaec9d4129b46d7ef1b2af63c46ac7d2828", size = 615510377, upload-time = "2025-03-12T00:13:03.792Z" },
+ { url = "https://files.pythonhosted.org/packages/28/2a/5f5ade4be81e521a16e143234747570ffd0d1a90e001ecc2688aa54bb419/tensorflow-2.18.1-cp311-cp311-win_amd64.whl", hash = "sha256:99223d0dde08aec4ceebb3bf0f80da7802e18462dab0d5048225925c064d2af7", size = 369183850, upload-time = "2025-03-12T00:13:24.786Z" },
+ { url = "https://files.pythonhosted.org/packages/67/8c/1cad54f8634897ad9421de8f558df9aa63d3f2747eb803ce5dbb2db1ef5b/tensorflow-2.18.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:98afa9c7f21481cdc6ccd09507a7878d533150fbb001840cc145e2132eb40942", size = 239622377, upload-time = "2025-03-12T00:13:36.89Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/c2/35a3542a91f4ffd4cf01e72d7f0fb59596cd5f467ff64878b0caef8e0f31/tensorflow-2.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ba52b9c06ab8102b31e50acfaf56899b923171e603c8942f2bfeb181d6bb59e", size = 231996787, upload-time = "2025-03-12T00:13:47.54Z" },
+ { url = "https://files.pythonhosted.org/packages/64/42/812539a8878c242eb0bacf106f5ea8936c2cc4d7f663868bd872a79772ac/tensorflow-2.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:442d2a774811789a8ad948e7286cb950fe3d87d3754e8cc6449d53b03dbfdaa6", size = 615623178, upload-time = "2025-03-12T00:14:03.541Z" },
+ { url = "https://files.pythonhosted.org/packages/20/28/9c5e935b76eebdf46df524980d49700a9c9af56abc8c62bfd93f57709563/tensorflow-2.18.1-cp312-cp312-win_amd64.whl", hash = "sha256:210baf6d421f3e044b6e09efd04494a33b75334922fe6cf11970e2885172620a", size = 369234070, upload-time = "2025-03-12T00:14:23.423Z" },
+]
+
+[[package]]
+name = "tensorflow"
+version = "2.20.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "absl-py", marker = "python_full_version < '3.13'" },
+ { name = "astunparse", marker = "python_full_version < '3.13'" },
+ { name = "flatbuffers", marker = "python_full_version < '3.13'" },
+ { name = "gast", marker = "python_full_version < '3.13'" },
+ { name = "google-pasta", marker = "python_full_version < '3.13'" },
+ { name = "grpcio", marker = "python_full_version < '3.13'" },
+ { name = "h5py", marker = "python_full_version < '3.13'" },
+ { name = "keras", version = "3.12.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "keras", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and python_full_version < '3.13' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.11' and python_full_version < '3.13' and extra != 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "libclang", marker = "python_full_version < '3.13'" },
+ { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
+ { name = "numpy", marker = "python_full_version < '3.13'" },
+ { name = "opt-einsum", marker = "python_full_version < '3.13'" },
+ { name = "packaging", marker = "python_full_version < '3.13'" },
+ { name = "protobuf", version = "6.33.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
+ { name = "requests", marker = "python_full_version < '3.13'" },
+ { name = "setuptools", marker = "python_full_version < '3.13'" },
+ { name = "six", marker = "python_full_version < '3.13'" },
+ { name = "tensorboard", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
+ { name = "termcolor", marker = "python_full_version < '3.13'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { name = "wrapt", version = "2.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/16/0e/9408083cb80d85024829eb78aa0aa799ca9f030a348acac35631b5191d4b/tensorflow-2.20.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e5f169f8f5130ab255bbe854c5f0ae152e93d3d1ac44f42cb1866003b81a5357", size = 200387116, upload-time = "2025-08-13T16:50:38.945Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/07/ea91ac67a9fd36d3372099f5a3e69860ded544f877f5f2117802388f4212/tensorflow-2.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02a0293d94f5c8b7125b66abf622cc4854a33ae9d618a0d41309f95e091bbaea", size = 259307122, upload-time = "2025-08-13T16:50:47.909Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/9e/0d57922cf46b9e91de636cd5b5e0d7a424ebe98f3245380a713f1f6c2a0b/tensorflow-2.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7abd7f3a010e0d354dc804182372779a722d474c4d8a3db8f4a3f5baef2a591e", size = 620425510, upload-time = "2025-08-13T16:51:02.608Z" },
+ { url = "https://files.pythonhosted.org/packages/74/b5/d40e1e389e07de9d113cf8e5d294c04d06124441d57606febfd0fb2cf5a6/tensorflow-2.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:4a69ac2c2ce20720abf3abf917b4e86376326c0976fcec3df330e184b81e4088", size = 331664937, upload-time = "2025-08-13T16:51:17.719Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/69/de33bd90dbddc8eede8f99ddeccfb374f7e18f84beb404bfe2cbbdf8df90/tensorflow-2.20.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5f964016c5035d09b85a246a6b739be89282a7839743f3ea63640224f0c63aee", size = 200507363, upload-time = "2025-08-13T16:51:28.27Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/b7/a3d455db88ab5b35ce53ab885ec0dd9f28d905a86a2250423048bc8cafa0/tensorflow-2.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e9568c8efcb05c0266be223e3269c62ebf7ad3498f156438311735f6fa5ced5", size = 259465882, upload-time = "2025-08-13T16:51:39.546Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0c/7df285ee8a88139fab0b237003634d90690759fae9c18f55ddb7c04656ec/tensorflow-2.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:481499fd0f824583de8945be61d5e827898cdaa4f5ea1bc2cc28ca2ccff8229e", size = 620570129, upload-time = "2025-08-13T16:51:55.104Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/f8/9246d3c7e185a29d7359d8b12b3d70bf2c3150ecf1427ec1382290e71a56/tensorflow-2.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:7551558a48c2e2f6c32a1537f06c654a9df1408a1c18e7b99c3caafbd03edfe3", size = 331845735, upload-time = "2025-08-13T16:52:12.863Z" },
+ { url = "https://files.pythonhosted.org/packages/35/31/47712f425c09cc8b8dba39c6c45aee939c4636a6feb8c81376a4eae653e0/tensorflow-2.20.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:52b122f0232fd7ab10f28d537ce08470d0b6dcac7fff9685432daac7f8a06c8f", size = 200540302, upload-time = "2025-08-13T16:52:22.146Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/b4/f028a5de27d0fda10ba6145bc76e40c37ff6d2d1e95b601adb5ae17d635e/tensorflow-2.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bfbfb3dd0e22bffc45fe1e922390d27753e99261fab8a882e802cf98a0e078f", size = 259533109, upload-time = "2025-08-13T16:52:31.513Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/d1/6aa15085d672056d5f08b5f28b1c7ce01c4e12149a23b0c98e3c79d04441/tensorflow-2.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25265b0bc527e0d54b1e9cc60c44a24f44a809fe27666b905f0466471f9c52ec", size = 620682547, upload-time = "2025-08-13T16:52:46.396Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/37/b97abb360b551fbf5870a0ee07e39ff9c655e6e3e2f839bc88be81361842/tensorflow-2.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:1590cbf87b6bcbd34d8e9ad70d0c696135e0aa71be31803b27358cf7ed63f8fc", size = 331887041, upload-time = "2025-08-13T16:53:05.532Z" },
+ { url = "https://files.pythonhosted.org/packages/04/82/af283f402f8d1e9315644a331a5f0f326264c5d1de08262f3de5a5ade422/tensorflow-2.20.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:197f0b613b38c0da5c6a12a8295ad4a05c78b853835dae8e0f9dfae3ce9ce8a5", size = 200671458, upload-time = "2025-08-13T16:53:16.568Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/4c/c1aa90c5cc92e9f7f9c78421e121ef25bae7d378f8d1d4cbad46c6308836/tensorflow-2.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47c88e05a07f1ead4977b4894b3ecd4d8075c40191065afc4fd9355c9db3d926", size = 259663776, upload-time = "2025-08-13T16:53:24.507Z" },
+ { url = "https://files.pythonhosted.org/packages/43/fb/8be8547c128613d82a2b006004026d86ed0bd672e913029a98153af4ffab/tensorflow-2.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa3729b0126f75a99882b89fb7d536515721eda8014a63e259e780ba0a37372", size = 620815537, upload-time = "2025-08-13T16:53:42.577Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/9e/02e201033f8d6bd5f79240b7262337de44c51a6cfd85c23a86c103c7684d/tensorflow-2.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:c25edad45e8cb9e76366f7a8c835279f9169028d610f3b52ce92d332a1b05438", size = 332012220, upload-time = "2025-08-13T16:53:57.303Z" },
+]
+
+[[package]]
+name = "tensorflow-estimator"
+version = "2.14.0"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/da/4f264c196325bb6e37a6285caec5b12a03def489b57cc1fdac02bb6272cd/tensorflow_estimator-2.14.0-py2.py3-none-any.whl", hash = "sha256:820bf57c24aa631abb1bbe4371739ed77edb11361d61381fd8e790115ac0fd57", size = 440664, upload-time = "2023-09-11T18:41:50.481Z" },
+]
+
+[[package]]
+name = "tensorflow-io-gcs-filesystem"
+version = "0.31.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/81/f1/30c558bf7e795d02415e6e836f6190367130e0dfb8de585b8b81ddde5c7f/tensorflow_io_gcs_filesystem-0.31.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:a71421f8d75a093b6aac65b4c8c8d2f768c3ca6215307cf8c16192e62d992bcf", size = 1642401, upload-time = "2023-02-25T19:31:32.234Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/e0/802488d94fdd14832d9832bd1fbd89cebac2519270d99ce1fe1c75eeb4b2/tensorflow_io_gcs_filesystem-0.31.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:359134ecbd3bf938bb0cf65be4526106c30da461b2e2ce05446a229ed35f6832", size = 2381672, upload-time = "2023-02-25T19:31:34.644Z" },
+ { url = "https://files.pythonhosted.org/packages/be/79/b18c800a17a10abeae3e8aa420fb452646c8f501bac82d61626437c48b0e/tensorflow_io_gcs_filesystem-0.31.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b658b33567552f155af2ed848130f787bfda29381fa78cd905d5ee8254364f3c", size = 2572150, upload-time = "2023-02-25T19:31:36.499Z" },
+ { url = "https://files.pythonhosted.org/packages/78/51/437068ed6b44162d54addb8ac0ddfe9e406d07ac6f9c8a6cf96869ec2262/tensorflow_io_gcs_filesystem-0.31.0-cp310-cp310-win_amd64.whl", hash = "sha256:961353b38c76471fa296bb7d883322c66b91415e7d47087236a6706db3ab2758", size = 1486315, upload-time = "2023-02-25T19:31:38.297Z" },
+ { url = "https://files.pythonhosted.org/packages/84/00/900ca310ff2e46eb3127f8f54af0b0000a6cc786be6a54dc2cfe841f4683/tensorflow_io_gcs_filesystem-0.31.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:8909c4344b0e96aa356230ab460ffafe5900c33c1aaced65fafae71d177a1966", size = 1642401, upload-time = "2023-02-25T19:31:40.204Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/c4/0d44ef93add3432ce43f37fe0c205cc7b6fd685fca80054fb4a646a9dbe3/tensorflow_io_gcs_filesystem-0.31.0-cp311-cp311-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e417faf8755aafe52d8f8c6b5ae5bae6e4fae8326ee3acd5e9181b83bbfbae87", size = 2381673, upload-time = "2023-02-25T19:31:41.992Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/2b/3064195efa016fff942009fe965ecbbbbd7d70bf34ee22d4ff31a0f3443a/tensorflow_io_gcs_filesystem-0.31.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37c40e3c4ee1f8dda3b545deea6b8839192c82037d8021db9f589908034ad975", size = 2572150, upload-time = "2023-02-25T19:31:43.874Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/4e/9566a313927be582ca99455a9523a097c7888fc819695bdc08415432b202/tensorflow_io_gcs_filesystem-0.31.0-cp311-cp311-win_amd64.whl", hash = "sha256:4bb37d23f21c434687b11059cb7ffd094d52a7813368915ba1b7057e3c16e414", size = 1486315, upload-time = "2023-02-25T19:31:45.641Z" },
+]
+
+[[package]]
+name = "tensorflow-io-gcs-filesystem"
+version = "0.37.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/a3/12d7e7326a707919b321e2d6e4c88eb61596457940fd2b8ff3e9b7fac8a7/tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:249c12b830165841411ba71e08215d0e94277a49c551e6dd5d72aab54fe5491b", size = 2470224, upload-time = "2024-07-01T23:44:15.341Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/55/3849a188cc15e58fefde20e9524d124a629a67a06b4dc0f6c881cb3c6e39/tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:257aab23470a0796978efc9c2bcf8b0bc80f22e6298612a4c0a50d3f4e88060c", size = 3479613, upload-time = "2024-07-01T23:44:17.445Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/19/9095c69e22c879cb3896321e676c69273a549a3148c4f62aa4bc5ebdb20f/tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8febbfcc67c61e542a5ac1a98c7c20a91a5e1afc2e14b1ef0cb7c28bc3b6aa70", size = 4842078, upload-time = "2024-07-01T23:44:18.977Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/48/47b7d25572961a48b1de3729b7a11e835b888e41e0203cca82df95d23b91/tensorflow_io_gcs_filesystem-0.37.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9679b36e3a80921876f31685ab6f7270f3411a4cc51bc2847e80d0e4b5291e27", size = 5085736, upload-time = "2024-07-01T23:44:21.034Z" },
+ { url = "https://files.pythonhosted.org/packages/40/9b/b2fb82d0da673b17a334f785fc19c23483165019ddc33b275ef25ca31173/tensorflow_io_gcs_filesystem-0.37.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:32c50ab4e29a23c1f91cd0f9ab8c381a0ab10f45ef5c5252e94965916041737c", size = 2470224, upload-time = "2024-07-01T23:44:23.039Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/cc/16634e76f3647fbec18187258da3ba11184a6232dcf9073dc44579076d36/tensorflow_io_gcs_filesystem-0.37.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b02f9c5f94fd62773954a04f69b68c4d576d076fd0db4ca25d5479f0fbfcdbad", size = 3479613, upload-time = "2024-07-01T23:44:24.399Z" },
+ { url = "https://files.pythonhosted.org/packages/de/bf/ba597d3884c77d05a78050f3c178933d69e3f80200a261df6eaa920656cd/tensorflow_io_gcs_filesystem-0.37.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e1f2796b57e799a8ca1b75bf47c2aaa437c968408cc1a402a9862929e104cda", size = 4842079, upload-time = "2024-07-01T23:44:26.825Z" },
+ { url = "https://files.pythonhosted.org/packages/66/7f/e36ae148c2f03d61ca1bff24bc13a0fef6d6825c966abef73fc6f880a23b/tensorflow_io_gcs_filesystem-0.37.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee7c8ee5fe2fd8cb6392669ef16e71841133041fee8a330eff519ad9b36e4556", size = 5085736, upload-time = "2024-07-01T23:44:28.618Z" },
+ { url = "https://files.pythonhosted.org/packages/70/83/4422804257fe2942ae0af4ea5bcc9df59cb6cb1bd092202ef240751d16aa/tensorflow_io_gcs_filesystem-0.37.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:ffebb6666a7bfc28005f4fbbb111a455b5e7d6cd3b12752b7050863ecb27d5cc", size = 2470224, upload-time = "2024-07-01T23:44:30.232Z" },
+ { url = "https://files.pythonhosted.org/packages/43/9b/be27588352d7bd971696874db92d370f578715c17c0ccb27e4b13e16751e/tensorflow_io_gcs_filesystem-0.37.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fe8dcc6d222258a080ac3dfcaaaa347325ce36a7a046277f6b3e19abc1efb3c5", size = 3479614, upload-time = "2024-07-01T23:44:32.316Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/46/962f47af08bd39fc9feb280d3192825431a91a078c856d17a78ae4884eb1/tensorflow_io_gcs_filesystem-0.37.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fbb33f1745f218464a59cecd9a18e32ca927b0f4d77abd8f8671b645cc1a182f", size = 4842077, upload-time = "2024-07-01T23:44:33.86Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/9b/790d290c232bce9b691391cf16e95a96e469669c56abfb1d9d0f35fa437c/tensorflow_io_gcs_filesystem-0.37.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:286389a203a5aee1a4fa2e53718c661091aa5fea797ff4fa6715ab8436b02e6c", size = 5085733, upload-time = "2024-07-01T23:44:36.663Z" },
+]
+
+[[package]]
+name = "tensorflow-metal"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six", marker = "(platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "wheel", marker = "(platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/39/da/463240cc8ff13a57119db62a676e2edca86fb93905a13527872dadf1926e/tensorflow_metal-1.2.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:bc735e36c97874e41f77ec2e7421ff745d2ec36ee641141c8091e4cc2dbcc819", size = 1357400, upload-time = "2025-01-31T00:52:54.577Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/09/91b253511cd59b9964672567f36b412daf3c70f75fcb5e84468fafa939ac/tensorflow_metal-1.2.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5fa7cee627031c14f45bd97ff0ef422cd6c3866199ff99cf29b94db6674ceb42", size = 1357400, upload-time = "2025-01-31T00:52:56.634Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/bf/988b619322d5617a928e7f31cbb1ed8dd7f375f69dfa73dab26409a00382/tensorflow_metal-1.2.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:4bece0ecb154b713b9f5ad4aec676a366d312822161e3cf0e1dea737c64cec04", size = 1357400, upload-time = "2025-01-31T00:52:57.924Z" },
+]
+
+[[package]]
+name = "tensorpack"
+version = "0.11"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "msgpack", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "msgpack-numpy", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "numpy", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "psutil", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pyzmq", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "six", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tabulate", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "termcolor", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tqdm", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d2/f0/edfda47ca6cc9ece30a893362c336b9121b691529e4cdf3b8732565be790/tensorpack-0.11.tar.gz", hash = "sha256:022b610e416e62e3575424cd08e60af27808a5fb6914294615391caf582cbd4f", size = 223526, upload-time = "2021-01-22T08:44:04.326Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f9/8c/63e5f5a4a04dea36b75850f9daa885ccbfad64bec1fae0ee4ca9f31b3eaa/tensorpack-0.11-py2.py3-none-any.whl", hash = "sha256:afcdaccf6e8e7d61c98970646d57b1c22372ddd712c462477a90f53e3994c4a1", size = 296324, upload-time = "2021-01-22T08:44:03.089Z" },
+]
+
+[[package]]
+name = "termcolor"
+version = "3.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434, upload-time = "2025-12-29T12:55:21.882Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734, upload-time = "2025-12-29T12:55:20.718Z" },
+]
+
+[[package]]
+name = "tf-keras"
+version = "2.14.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/57/e7/883de3afc2032c89d6351fca7eb119ee197de21a2189e3889e9171d64862/tf_keras-2.14.1.tar.gz", hash = "sha256:4ae6871c4989720ea335d771b75b2e520d789ec1600302800bb5ed1a855af2fe", size = 1250245, upload-time = "2023-09-29T01:57:43.17Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/61/c7a98446afd921b7c4a0688e6eb30bf8f48040d069c349c772e7763636e6/tf_keras-2.14.1-py3-none-any.whl", hash = "sha256:903cf3f1739d3f92d2de80e07d2b36b32cc79f60b7affcee09a7f4721c02fca6", size = 1714936, upload-time = "2023-09-29T01:57:41.02Z" },
+]
+
+[[package]]
+name = "tf-keras"
+version = "2.17.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "tensorflow", version = "2.17.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/dc/2b/d647100a2e80d159b020f1dbc2ef2c6787ed33c914951a63b3c88cd805d0/tf_keras-2.17.0.tar.gz", hash = "sha256:fda97c18da30da0f72a5a7e80f3eee343b09f4c206dad6c57c944fb2cd18560e", size = 1260098, upload-time = "2024-07-15T21:31:01.6Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/21/8b/75f7572ec0273ed8da50bc19defe08aaaafcc15fda3407db53f49acec814/tf_keras-2.17.0-py3-none-any.whl", hash = "sha256:cc97717e4dc08487f327b0740a984043a9e0123c7a4e21206711669d3ec41c88", size = 1724905, upload-time = "2024-07-15T21:30:58.941Z" },
+]
+
+[[package]]
+name = "tf-keras"
+version = "2.18.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "tensorflow", version = "2.18.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "tensorflow", version = "2.18.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a9/a4/7d0acc28cde2b29b8114552ce3258dafdc6b2186d24bf8e912de713dd74f/tf_keras-2.18.0.tar.gz", hash = "sha256:ebf744519b322afead33086a2aba872245473294affd40973694f3eb7c7ad77d", size = 1260765, upload-time = "2024-10-24T22:58:06.652Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/ed/e08afca471299b04a34cd548e64e89d0153eda0e6cf9b715356777e24774/tf_keras-2.18.0-py3-none-any.whl", hash = "sha256:c431d04027eef790fcd3261cf7fdf93eb74f3cb32e05078b57b7f5a54bd53262", size = 1725427, upload-time = "2024-10-24T22:58:03.918Z" },
+]
+
+[[package]]
+name = "tf-keras"
+version = "2.20.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "tensorflow", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/42/38/6060f6c7472439bb3890b9094d69d31d9f8d5da123b16c738773e70fff91/tf_keras-2.20.1.tar.gz", hash = "sha256:884be5938fb0b2b53b1583c1ae2b660ef87215377c29b5b6a77fd221b472aeaf", size = 1254487, upload-time = "2025-09-04T21:23:41.81Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/85/6b/d9a8202bfe5c9e3b078cf550bafab962aa9d6b1a1f1180f0065399d4c9b2/tf_keras-2.20.1-py3-none-any.whl", hash = "sha256:3f0e0a34d9a4c8758f24fdc1053e6e335f16ab5534c7d34f1899b8924779760c", size = 1694335, upload-time = "2025-09-04T21:23:40.153Z" },
+]
+
+[[package]]
+name = "tf-slim"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "absl-py", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/02/97/b0f4a64df018ca018cc035d44f2ef08f91e2e8aa67271f6f19633a015ff7/tf_slim-1.1.0-py2.py3-none-any.whl", hash = "sha256:fa2bab63b3925bd42601102e7f178dce997f525742596bf404fa8a6918e146ff", size = 352133, upload-time = "2020-05-07T22:18:55.976Z" },
+]
+
+[[package]]
+name = "threadpoolctl"
+version = "3.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" },
+]
+
+[[package]]
+name = "tifffile"
+version = "2025.5.10"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/44/d0/18fed0fc0916578a4463f775b0fbd9c5fed2392152d039df2fb533bfdd5d/tifffile-2025.5.10.tar.gz", hash = "sha256:018335d34283aa3fd8c263bae5c3c2b661ebc45548fde31504016fcae7bf1103", size = 365290, upload-time = "2025-05-10T19:22:34.386Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5d/06/bd0a6097da704a7a7c34a94cfd771c3ea3c2f405dd214e790d22c93f6be1/tifffile-2025.5.10-py3-none-any.whl", hash = "sha256:e37147123c0542d67bc37ba5cdd67e12ea6fbe6e86c52bee037a9eb6a064e5ad", size = 226533, upload-time = "2025-05-10T19:22:27.279Z" },
+]
+
+[[package]]
+name = "tifffile"
+version = "2026.3.3"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "python_full_version >= '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c5/cb/2f6d79c7576e22c116352a801f4c3c8ace5957e9aced862012430b62e14f/tifffile-2026.3.3.tar.gz", hash = "sha256:d9a1266bed6f2ee1dd0abde2018a38b4f8b2935cb843df381d70ac4eac5458b7", size = 388745, upload-time = "2026-03-03T19:14:38.134Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1a/e4/e804505f87627cd8cdae9c010c47c4485fd8c1ce31a7dd0ab7fcc4707377/tifffile-2026.3.3-py3-none-any.whl", hash = "sha256:e8be15c94273113d31ecb7aa3a39822189dd11c4967e3cc88c178f1ad2fd1170", size = 243960, upload-time = "2026-03-03T19:14:35.808Z" },
+]
+
+[[package]]
+name = "timm"
+version = "1.0.27"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "huggingface-hub" },
+ { name = "pyyaml" },
+ { name = "safetensors" },
+ { name = "torch", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "torch", version = "2.10.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "torch", version = "2.12.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "torchvision", version = "0.15.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "torchvision", version = "0.25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "torchvision", version = "0.27.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/08/54/ece85b0eef3700c90db8271a43669b05a0ebbe2edb1962329c34374a297e/timm-1.0.27.tar.gz", hash = "sha256:315dfe63186ca9fb7ff941268941231fd5be259f2b4bb4afa28560ae1015cb9a", size = 2439861, upload-time = "2026-05-08T19:38:36.844Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1f/2e/26bab7686ff4aed48f8f5f6c23e2aa37b7a37ddd9effe3aa61e908fd518f/timm-1.0.27-py3-none-any.whl", hash = "sha256:5ff07c9ddf53cbada88eab1c93ff175c64cab683b5a2fddf863bcee985926f89", size = 2589280, upload-time = "2026-05-08T19:38:35.034Z" },
+]
+
+[[package]]
+name = "tomli"
+version = "2.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" },
+ { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" },
+ { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" },
+ { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" },
+ { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" },
+ { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" },
+ { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" },
+ { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" },
+ { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" },
+ { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" },
+ { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" },
+ { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" },
+ { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" },
+ { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" },
+ { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" },
+ { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" },
+ { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" },
+ { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" },
+ { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" },
+ { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" },
+ { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" },
+ { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" },
+ { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" },
+ { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" },
+ { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" },
+ { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
+]
+
+[[package]]
+name = "tomli-w"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
+]
+
+[[package]]
+name = "toolz"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/11/d6/114b492226588d6ff54579d95847662fc69196bdeec318eb45393b24c192/toolz-1.1.0.tar.gz", hash = "sha256:27a5c770d068c110d9ed9323f24f1543e83b2f300a687b7891c1a6d56b697b5b", size = 52613, upload-time = "2025-10-17T04:03:21.661Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093, upload-time = "2025-10-17T04:03:20.435Z" },
+]
+
+[[package]]
+name = "torch"
+version = "2.0.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "filelock", marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "jinja2", marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cublas-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cuda-cupti-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cuda-nvrtc-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cuda-runtime-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cudnn-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cufft-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-curand-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cusolver-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cusparse-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-nccl-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-nvtx-cu11", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "sympy", marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "triton", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8c/4d/17e07377c9c3d1a0c4eb3fde1c7c16b5a0ce6133ddbabc08ceef6b7f2645/torch-2.0.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:8ced00b3ba471856b993822508f77c98f48a458623596a4c43136158781e306a", size = 619913492, upload-time = "2023-05-08T16:35:32.915Z" },
+ { url = "https://files.pythonhosted.org/packages/21/33/4925decd863ce88ed9190a4bd872b01c146243ee68db08c72923984fe335/torch-2.0.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:359bfaad94d1cda02ab775dc1cc386d585712329bb47b8741607ef6ef4950747", size = 74032191, upload-time = "2023-05-08T17:03:58.074Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/e7/c216fe520b877cf4fe03858c825cd2031ca3e81e455b89639c9b5ec91981/torch-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:7c84e44d9002182edd859f3400deaa7410f5ec948a519cc7ef512c2f9b34d2c4", size = 172339532, upload-time = "2023-05-08T16:38:31.628Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/27/5c912ccc490ec78585cd463198e80be27b53db77f02e7398b41305606399/torch-2.0.1-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:567f84d657edc5582d716900543e6e62353dbe275e61cdc36eda4929e46df9e7", size = 143409489, upload-time = "2023-05-08T16:41:49.802Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/77/778954c0aad4f7901a1ba02a129bca7467c64a19079108e6b1d6ce8ae575/torch-2.0.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:787b5a78aa7917465e9b96399b883920c88a08f4eb63b5a5d2d1a16e27d2f89b", size = 55830861, upload-time = "2023-05-08T17:01:35.636Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/21/25020cfdd9f564a72f400ee491610e50cb212e8add8031abaa959af6451e/torch-2.0.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:e617b1d0abaf6ced02dbb9486803abfef0d581609b09641b34fa315c9c40766d", size = 619895009, upload-time = "2023-05-08T17:28:02.693Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/61/7273dea60a17c63d9eaef04ae8fee02351e0cb477e76df4ea211896ae124/torch-2.0.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b6019b1de4978e96daa21d6a3ebb41e88a0b474898fe251fd96189587408873e", size = 74042514, upload-time = "2023-05-08T17:29:51.438Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/c8/f0dc8642e3ce0a3ae5f05e5149ab9df5375d569294f7be9a1ab1d95a1d76/torch-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:dbd68cbd1cd9da32fe5d294dd3411509b3d841baecb780b38b3b7b06c7754434", size = 172343262, upload-time = "2023-05-08T17:29:02.117Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/9b/f20686a5ebd09c6feacced771cf4041a521c411c5bb10359580e9e491797/torch-2.0.1-cp311-none-macosx_10_9_x86_64.whl", hash = "sha256:ef654427d91600129864644e35deea761fb1fe131710180b952a6f2e2207075e", size = 143124494, upload-time = "2023-05-08T16:28:47.223Z" },
+ { url = "https://files.pythonhosted.org/packages/85/68/f901437d3e3ef6fe97adb1f372479626d994185b8fa06803f5bdf3bb90fd/torch-2.0.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:25aa43ca80dcdf32f13da04c503ec7afdf8e77e3a0183dd85cd3e53b2842e527", size = 55831241, upload-time = "2023-05-08T17:31:49.294Z" },
+]
+
+[[package]]
+name = "torch"
+version = "2.10.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "cuda-bindings", version = "12.9.4", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "filelock", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "fsspec", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "jinja2", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cublas-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cuda-cupti-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cuda-nvrtc-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cuda-runtime-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cudnn-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cufft-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cufile-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-curand-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cusolver-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cusparse-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cusparselt-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-nccl-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-nvshmem-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-nvtx-cu12", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "setuptools", marker = "(python_full_version >= '3.12' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "sympy", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "triton", version = "3.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5b/30/bfebdd8ec77db9a79775121789992d6b3b75ee5494971294d7b4b7c999bc/torch-2.10.0-2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2b980edd8d7c0a68c4e951ee1856334a43193f98730d97408fbd148c1a933313", size = 79411457, upload-time = "2026-02-10T21:44:59.189Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/8b/4b61d6e13f7108f36910df9ab4b58fd389cc2520d54d81b88660804aad99/torch-2.10.0-2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:418997cb02d0a0f1497cf6a09f63166f9f5df9f3e16c8a716ab76a72127c714f", size = 79423467, upload-time = "2026-02-10T21:44:48.711Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/54/a2ba279afcca44bbd320d4e73675b282fcee3d81400ea1b53934efca6462/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574", size = 79498202, upload-time = "2026-02-10T21:44:52.603Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/23/2c9fe0c9c27f7f6cb865abcea8a4568f29f00acaeadfc6a37f6801f84cb4/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e", size = 79498254, upload-time = "2026-02-10T21:44:44.095Z" },
+ { url = "https://files.pythonhosted.org/packages/16/ee/efbd56687be60ef9af0c9c0ebe106964c07400eade5b0af8902a1d8cd58c/torch-2.10.0-3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a1ff626b884f8c4e897c4c33782bdacdff842a165fee79817b1dd549fdda1321", size = 915510070, upload-time = "2026-03-11T14:16:39.386Z" },
+ { url = "https://files.pythonhosted.org/packages/36/ab/7b562f1808d3f65414cd80a4f7d4bb00979d9355616c034c171249e1a303/torch-2.10.0-3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac5bdcbb074384c66fa160c15b1ead77839e3fe7ed117d667249afce0acabfac", size = 915518691, upload-time = "2026-03-11T14:15:43.147Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/7a/abada41517ce0011775f0f4eacc79659bc9bc6c361e6bfe6f7052a6b9363/torch-2.10.0-3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98c01b8bb5e3240426dcde1446eed6f40c778091c8544767ef1168fc663a05a6", size = 915622781, upload-time = "2026-03-11T14:17:11.354Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/c6/4dfe238342ffdcec5aef1c96c457548762d33c40b45a1ab7033bb26d2ff2/torch-2.10.0-3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:80b1b5bfe38eb0e9f5ff09f206dcac0a87aadd084230d4a36eea5ec5232c115b", size = 915627275, upload-time = "2026-03-11T14:16:11.325Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/f0/72bf18847f58f877a6a8acf60614b14935e2f156d942483af1ffc081aea0/torch-2.10.0-3-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:46b3574d93a2a8134b3f5475cfb98e2eb46771794c57015f6ad1fb795ec25e49", size = 915523474, upload-time = "2026-03-11T14:17:44.422Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/39/590742415c3030551944edc2ddc273ea1fdfe8ffb2780992e824f1ebee98/torch-2.10.0-3-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b1d5e2aba4eb7f8e87fbe04f86442887f9167a35f092afe4c237dfcaaef6e328", size = 915632474, upload-time = "2026-03-11T14:15:13.666Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/8e/34949484f764dde5b222b7fe3fede43e4a6f0da9d7f8c370bb617d629ee2/torch-2.10.0-3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:0228d20b06701c05a8f978357f657817a4a63984b0c90745def81c18aedfa591", size = 915523882, upload-time = "2026-03-11T14:14:46.311Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/1a/c61f36cfd446170ec27b3a4984f072fd06dab6b5d7ce27e11adb35d6c838/torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d", size = 145992962, upload-time = "2026-01-21T16:24:14.04Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/60/6662535354191e2d1555296045b63e4279e5a9dbad49acf55a5d38655a39/torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444", size = 915599237, upload-time = "2026-01-21T16:23:25.497Z" },
+ { url = "https://files.pythonhosted.org/packages/40/b8/66bbe96f0d79be2b5c697b2e0b187ed792a15c6c4b8904613454651db848/torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb", size = 113720931, upload-time = "2026-01-21T16:24:23.743Z" },
+ { url = "https://files.pythonhosted.org/packages/76/bb/d820f90e69cda6c8169b32a0c6a3ab7b17bf7990b8f2c680077c24a3c14c/torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:35e407430795c8d3edb07a1d711c41cc1f9eaddc8b2f1cc0a165a6767a8fb73d", size = 79411450, upload-time = "2026-01-21T16:25:30.692Z" },
+ { url = "https://files.pythonhosted.org/packages/78/89/f5554b13ebd71e05c0b002f95148033e730d3f7067f67423026cc9c69410/torch-2.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3282d9febd1e4e476630a099692b44fdc214ee9bf8ee5377732d9d9dfe5712e4", size = 145992610, upload-time = "2026-01-21T16:25:26.327Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/30/a3a2120621bf9c17779b169fc17e3dc29b230c29d0f8222f499f5e159aa8/torch-2.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a2f9edd8dbc99f62bc4dfb78af7bf89499bca3d753423ac1b4e06592e467b763", size = 915607863, upload-time = "2026-01-21T16:25:06.696Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/3d/c87b33c5f260a2a8ad68da7147e105f05868c281c63d65ed85aa4da98c66/torch-2.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:29b7009dba4b7a1c960260fc8ac85022c784250af43af9fb0ebafc9883782ebd", size = 113723116, upload-time = "2026-01-21T16:25:21.916Z" },
+ { url = "https://files.pythonhosted.org/packages/61/d8/15b9d9d3a6b0c01b883787bd056acbe5cc321090d4b216d3ea89a8fcfdf3/torch-2.10.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:b7bd80f3477b830dd166c707c5b0b82a898e7b16f59a7d9d42778dd058272e8b", size = 79423461, upload-time = "2026-01-21T16:24:50.266Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/af/758e242e9102e9988969b5e621d41f36b8f258bb4a099109b7a4b4b50ea4/torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf", size = 145996088, upload-time = "2026-01-21T16:24:44.171Z" },
+ { url = "https://files.pythonhosted.org/packages/23/8e/3c74db5e53bff7ed9e34c8123e6a8bfef718b2450c35eefab85bb4a7e270/torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb", size = 915711952, upload-time = "2026-01-21T16:23:53.503Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/01/624c4324ca01f66ae4c7cd1b74eb16fb52596dce66dbe51eff95ef9e7a4c/torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547", size = 113757972, upload-time = "2026-01-21T16:24:39.516Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/5c/dee910b87c4d5c0fcb41b50839ae04df87c1cfc663cf1b5fca7ea565eeaa/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294", size = 79498198, upload-time = "2026-01-21T16:24:34.704Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/6f/f2e91e34e3fcba2e3fc8d8f74e7d6c22e74e480bbd1db7bc8900fdf3e95c/torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b", size = 146004247, upload-time = "2026-01-21T16:24:29.335Z" },
+ { url = "https://files.pythonhosted.org/packages/98/fb/5160261aeb5e1ee12ee95fe599d0541f7c976c3701d607d8fc29e623229f/torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738", size = 915716445, upload-time = "2026-01-21T16:22:45.353Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/16/502fb1b41e6d868e8deb5b0e3ae926bbb36dab8ceb0d1b769b266ad7b0c3/torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57", size = 113757050, upload-time = "2026-01-21T16:24:19.204Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/0b/39929b148f4824bc3ad6f9f72a29d4ad865bcf7ebfc2fa67584773e083d2/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382", size = 79851305, upload-time = "2026-01-21T16:24:09.209Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/14/21fbce63bc452381ba5f74a2c0a959fdf5ad5803ccc0c654e752e0dbe91a/torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8", size = 146005472, upload-time = "2026-01-21T16:22:29.022Z" },
+ { url = "https://files.pythonhosted.org/packages/54/fd/b207d1c525cb570ef47f3e9f836b154685011fce11a2f444ba8a4084d042/torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f", size = 915612644, upload-time = "2026-01-21T16:21:47.019Z" },
+ { url = "https://files.pythonhosted.org/packages/36/53/0197f868c75f1050b199fe58f9bf3bf3aecac9b4e85cc9c964383d745403/torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8", size = 113997015, upload-time = "2026-01-21T16:23:00.767Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/13/e76b4d9c160e89fff48bf16b449ea324bda84745d2ab30294c37c2434c0d/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f", size = 79498248, upload-time = "2026-01-21T16:23:09.315Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/93/716b5ac0155f1be70ed81bacc21269c3ece8dba0c249b9994094110bfc51/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a", size = 79464992, upload-time = "2026-01-21T16:23:05.162Z" },
+ { url = "https://files.pythonhosted.org/packages/69/2b/51e663ff190c9d16d4a8271203b71bc73a16aa7619b9f271a69b9d4a936b/torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60", size = 146018567, upload-time = "2026-01-21T16:22:23.393Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/cd/4b95ef7f293b927c283db0b136c42be91c8ec6845c44de0238c8c23bdc80/torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5", size = 915721646, upload-time = "2026-01-21T16:21:16.983Z" },
+ { url = "https://files.pythonhosted.org/packages/56/97/078a007208f8056d88ae43198833469e61a0a355abc0b070edd2c085eb9a/torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c", size = 113752373, upload-time = "2026-01-21T16:22:13.471Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/94/71994e7d0d5238393df9732fdab607e37e2b56d26a746cb59fdb415f8966/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28", size = 79850324, upload-time = "2026-01-21T16:22:09.494Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/65/1a05346b418ea8ccd10360eef4b3e0ce688fba544e76edec26913a8d0ee0/torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63", size = 146006482, upload-time = "2026-01-21T16:22:18.42Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/b9/5f6f9d9e859fc3235f60578fa64f52c9c6e9b4327f0fe0defb6de5c0de31/torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6", size = 915613050, upload-time = "2026-01-21T16:20:49.035Z" },
+ { url = "https://files.pythonhosted.org/packages/66/4d/35352043ee0eaffdeff154fad67cd4a31dbed7ff8e3be1cc4549717d6d51/torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185", size = 113995816, upload-time = "2026-01-21T16:22:05.312Z" },
+]
+
+[[package]]
+name = "torch"
+version = "2.12.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "cuda-bindings", version = "13.2.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "filelock", marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "fsspec", marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "jinja2", marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-fmpose3d') or (python_full_version < '3.11' and extra == 'extra-10-deeplabcut-tf') or (python_full_version < '3.11' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-fmpose3d') or (python_full_version >= '3.11' and extra == 'extra-10-deeplabcut-tf') or (python_full_version >= '3.11' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cublas", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cudnn-cu13", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-cusparselt-cu13", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-nccl-cu13", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "nvidia-nvshmem-cu13", marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "setuptools", marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "sympy", marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "triton", version = "3.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-10-deeplabcut-apple-mchips') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-fmpose3d') or (sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf') or (sys_platform == 'linux' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "typing-extensions", marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/b7/53fe0436586716ab7aecff41e26b9302d57c85ded481fd83a2cd741e6b4e/torch-2.12.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1834bd984f8a2f4f16bdfbeecca9146184b220aa46276bf5756735b5dae12812", size = 87981887, upload-time = "2026-05-13T14:55:53.234Z" },
+ { url = "https://files.pythonhosted.org/packages/34/60/d930eac44c30de06ed16f6d1ba4e785e1632532b50d8f0bf9bf699a4d0c7/torch-2.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d4d029801cb7b6df858804a2a21b00cc2aa0bf0ee5d2ab18d343c9e9e5681f35", size = 426355000, upload-time = "2026-05-13T14:54:31.944Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/0c/c76b6a087820bab55705b94dfc074e520de9ae91f5ef90da2ecbf2a3ef12/torch-2.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d47e7dee68ac4cd7a068b26bcd6b989935427709fae1c8f7bd0019978f829e15", size = 532144998, upload-time = "2026-05-13T14:56:05.523Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/64/8a0d036e166a6aa85ee09bef072f3655d1ba5d5486a68d1b03b6813c01b3/torch-2.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:cf9839790285dd472e7a16aafcb4a4e6bf58ec1b494045044b0eefb0eb4bd1f2", size = 122949877, upload-time = "2026-05-13T14:55:46.841Z" },
+ { url = "https://files.pythonhosted.org/packages/18/62/131124fb95df03811b8260d1d43dcc5ee85ea1a344b964613d7efe77fb08/torch-2.12.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:10802fd383bbfed646212e765a72c37d2185205d4f26eb197a254e8ac7ddcb25", size = 87990344, upload-time = "2026-05-13T14:55:42.154Z" },
+ { url = "https://files.pythonhosted.org/packages/12/9c/dda0dbd547dc549839824135f223792fd0e725f28ed0715dda366b7acaa2/torch-2.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c12592630aef72feaf18bd3f197ef587bbfa21131b31c38b23ab2e55fce92e36", size = 426362932, upload-time = "2026-05-13T14:54:15.295Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/d2/a7dd5a3f9bdaa7842124e8e2359202b317c48d47d2fc5816fafdf2049adb/torch-2.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:415c1b8d0412f67551c8e89a2daca0fb3e56694af0281ba155eaa9da481f58b4", size = 532170085, upload-time = "2026-05-13T14:55:20.788Z" },
+ { url = "https://files.pythonhosted.org/packages/12/1b/a61ce2004f9ab0ea8964a6e6168133a127795667639e2ff4f8f2bdb16a65/torch-2.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd37188ea325042cb1f6cafa56822b11ada2520c04791a52629b0af25bdfbfd9", size = 122953128, upload-time = "2026-05-13T14:54:52.744Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/bb/285d643f254731294c9b595a007eac39db4600a98682d7bca688f42ca164/torch-2.12.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b41339df93d491435e790ff8bcbae1c0ce777175889bfd1281d119862793e6a2", size = 88010197, upload-time = "2026-05-13T14:55:35.414Z" },
+ { url = "https://files.pythonhosted.org/packages/79/81/76debf1db1343bd929bbb5d74c89fb437c2ed88eb144712557e7bd3eea45/torch-2.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8fbef9f108a863e7722a73740998967e3b074742a834fc5be3a535a2befa7057", size = 426376751, upload-time = "2026-05-13T14:55:03.353Z" },
+ { url = "https://files.pythonhosted.org/packages/de/f0/80026028b603c4650ff270fc3785bdef4bd6738765a9cc5a0f5a637d65a2/torch-2.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4b4f64c2c2b11f7510d93dd6412b87025ff6eddd6bb61c3b5a3d892ea20c4756", size = 532261691, upload-time = "2026-05-13T14:52:54.453Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/c2/64b06cbb7830fb3cd9be13e1158b31a3f36b68e6a209105ee3c9d9480be0/torch-2.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:8b958caff4a14d3a3b0b2dfc6a378f64dda9728a9dad28c08a0db9ce4dafb549", size = 122988114, upload-time = "2026-05-13T14:54:42.153Z" },
+ { url = "https://files.pythonhosted.org/packages/86/ca/01896c80ba921676aa45886b2c5b8d774912de2a1f719de48169c6f755cd/torch-2.12.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:90dd587a5f61bfe1307148b581e2084fc5bc4a06e2b90a20e9a36b81087ff16b", size = 88009511, upload-time = "2026-05-13T14:54:47.411Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/04/52bdaf4787eab6ac7d7f5851dff934e4def0bc8ead9c8fd2b69b3e529699/torch-2.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:864392c73b7654f4d2b3ae712f607937d0dbb1101c4555fbb41848106b297f39", size = 426383231, upload-time = "2026-05-13T14:53:32.129Z" },
+ { url = "https://files.pythonhosted.org/packages/49/8a/94bdecd13f5aaa90d45920b89789d9fe7c6f4af8c3cdd7ce01fcb59908fc/torch-2.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5d6b560dfa7d56291c07d615c3bb73e8d9943d9b6d87f76cd0d9d570c4797fa6", size = 532269288, upload-time = "2026-05-13T14:53:49.423Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/2f/bdbaaa267de519ef1b73054bf590d8c93c37a266c9a4e24a01bd38b6918f/torch-2.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:3fee918902090ade827643e758e98363278815de583c75d111fdd665ebffde9f", size = 122987706, upload-time = "2026-05-13T14:54:00.335Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/ad/e95e822f3538171e22640a7fbe839a1fdb666600bf6487025de2ff03b11a/torch-2.12.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:10ee1448a9f304d3b987eb4656f664ba6e4d7b410ca7a5a7c642199777a2cf88", size = 88319556, upload-time = "2026-05-13T14:54:05.574Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/07/055d06d985b445d67422d25b033c11cf55bbb81785d4c4e68e28bca5820e/torch-2.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:af68dbf403439cae9ceaeaaf92f8352b460787dcd27b92aa05c40dd4a19c0f1e", size = 426397656, upload-time = "2026-05-13T14:52:38.84Z" },
+ { url = "https://files.pythonhosted.org/packages/43/94/b0b4fdc3014122e0a7302fb90086d352aa48f2576f0b252561ebb38c01a8/torch-2.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a6a2eebb237d3b1d9ad3b378e86d9b9e0782afdea8b1e0eba6a13646b9b49c07", size = 532183124, upload-time = "2026-05-13T14:53:16.178Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/c8/052405e6ad05d3237bfe5a4df78f917773956f8e17813a2d44c059068b74/torch-2.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2140e373e9a51a3e22ef62e8d14366d0b470d18f0adf19fdc757368077133a34", size = 123232462, upload-time = "2026-05-13T14:52:27.26Z" },
+ { url = "https://files.pythonhosted.org/packages/67/dc/ac069f8d6e8be701535921141055293b0d4819d3d7f224a4612cf157c7f9/torch-2.12.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7dfae4a519197dfa050e98d8e36378a0fb5899625a875c2b54445005a2e404e", size = 88027282, upload-time = "2026-05-13T14:53:05.258Z" },
+ { url = "https://files.pythonhosted.org/packages/33/c3/1c1eb00e34555b536dddf792676026a988d710ed36981aa00499b36b0620/torch-2.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:891c769072637c74e9a5a77a3bc782894696d8ffec83b938df8536dee7f0ba78", size = 426386961, upload-time = "2026-05-13T14:51:28.406Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/d4/7e730dba0c7032a4154dc9056b76cf9625515e030e269cfbf8098fcfee7d/torch-2.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e2ad3eb85d39c3cab62dfa93ed5a73516e6a53c6713cb97d004004fe089f0f1f", size = 532272265, upload-time = "2026-05-13T14:51:59.308Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/b4/92c80d1bbfee1c0036c06d1d2155a3065bd2423134c83bf8a47e65cd6b9b/torch-2.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:c66696857e987efb8bc1777a37357ec4f60ab5e8af6250b83d6034437fa2d8f3", size = 122987138, upload-time = "2026-05-13T14:51:45.942Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/78/2e12b37ce50a19a037d7bc62d652a5a8f27385a7b05859d6bc9204f20cfe/torch-2.12.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:b4556715c8572758625d62b6e0ae3b1f76c440221913a6fb5e100f321fb4fb02", size = 88320100, upload-time = "2026-05-13T14:51:39.955Z" },
+ { url = "https://files.pythonhosted.org/packages/56/5e/83c450ec7b0bb40a7b74611c1b5440f9260e33c54c90d556fd4a1f0fd955/torch-2.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a43ac605a5e13116c72b64c359644cce0229f213dde48d2ae0ae5eb5becf7feb", size = 426391871, upload-time = "2026-05-13T14:52:14.989Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/e9/1a0b575d98d0afedd8f157d23fa3d2759421483660448e60d0a4b10b6daa/torch-2.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6a7512adfdd7f6732e40de1c620831e3c75b39b98cef60b11d0c5f0a76473ec5", size = 532192241, upload-time = "2026-05-13T14:51:07.795Z" },
+ { url = "https://files.pythonhosted.org/packages/88/21/afadd25ecd81b3cea1e11c73cf1ab41a983a50271548c3ec7ec3b9efc3e9/torch-2.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5f96b63f8287f66a005dd1b5a6abba2920f11156c5e5c4d815f3e2050fd1aa16", size = 123231092, upload-time = "2026-05-13T14:51:18.854Z" },
+]
+
+[[package]]
+name = "torchvision"
+version = "0.15.2"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pillow", marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "requests", marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "torch", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/16/e7/3b43cce519d7236bbbdc31f468b43ae2084ff7db8cb162764311028d32a1/torchvision-0.15.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7754088774e810c5672b142a45dcf20b1bd986a5a7da90f8660c43dc43fb850c", size = 1501860, upload-time = "2023-05-08T17:32:32.803Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/bf/4cd5133120e6cbcc2fa5c38c92f2f44a7486a9d2ae851e3d5a7e83f396d5/torchvision-0.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37eb138e13f6212537a3009ac218695483a635c404b6cc1d8e0d0d978026a86d", size = 1410848, upload-time = "2023-05-08T17:34:05.161Z" },
+ { url = "https://files.pythonhosted.org/packages/87/0f/88f023bf6176d9af0f85feedf4be129f9cf2748801c4d9c690739a10c100/torchvision-0.15.2-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:54143f7cc0797d199b98a53b7d21c3f97615762d4dd17ad45a41c7e80d880e73", size = 6032483, upload-time = "2023-05-08T17:33:04.697Z" },
+ { url = "https://files.pythonhosted.org/packages/16/5e/51c5fde550161edcfa3e131c51a8b4261775ebb2b118b3560116fa9f7a73/torchvision-0.15.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:1eefebf5fbd01a95fe8f003d623d941601c94b5cec547b420da89cb369d9cf96", size = 1249097, upload-time = "2023-05-08T17:33:43.726Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/1d/cb1e7f25b6dda4e672ed8a3e7fbd073ec39e2ba6c378c3071ef2cd6100e1/torchvision-0.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:96fae30c5ca8423f4b9790df0f0d929748e32718d88709b7b567d2f630c042e3", size = 1195746, upload-time = "2023-05-08T17:33:33.397Z" },
+ { url = "https://files.pythonhosted.org/packages/69/40/2f3b2392ce7c4b856a5964803c4bc0bf0d5fc75ff7f6cc64cc2058c3e700/torchvision-0.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5f35f6bd5bcc4568e6522e4137fa60fcc72f4fa3e615321c26cd87e855acd398", size = 1501857, upload-time = "2023-05-08T17:33:20.364Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/6d/d713159642b36c42f5b6871330241070797ec89d3f8855eeb91c8baddddd/torchvision-0.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:757505a0ab2be7096cb9d2bf4723202c971cceddb72c7952a7e877f773de0f8a", size = 1410853, upload-time = "2023-05-08T17:33:08.26Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/62/b6ec55347600b02b0a2a6596e673c69424aea7360c48343653866e66aa0d/torchvision-0.15.2-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:012ad25cfd9019ff9b0714a168727e3845029be1af82296ff1e1482931fa4b80", size = 6032450, upload-time = "2023-05-08T17:33:48.437Z" },
+ { url = "https://files.pythonhosted.org/packages/66/e0/cd847d4d22be88a71d5d65f5809342e7ea7ded62230e7bde7420a2105e51/torchvision-0.15.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b02a7ffeaa61448737f39a4210b8ee60234bda0515a0c0d8562f884454105b0f", size = 1249109, upload-time = "2023-05-08T17:33:28.559Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/26/a1e128500fb661d3ee7d99b97fb45d3b83e57091278c9babec859da7b87f/torchvision-0.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:10be76ceded48329d0a0355ac33da131ee3993ff6c125e4a02ab34b5baa2472c", size = 1195759, upload-time = "2023-05-08T17:33:11.008Z" },
+]
+
+[[package]]
+name = "torchvision"
+version = "0.25.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pillow", marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "torch", version = "2.10.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-tf-cu12' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/50/ae/cbf727421eb73f1cf907fbe5788326a08f111b3f6b6ddca15426b53fec9a/torchvision-0.25.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a95c47abb817d4e90ea1a8e57bd0d728e3e6b533b3495ae77d84d883c4d11f56", size = 1874919, upload-time = "2026-01-21T16:27:47.617Z" },
+ { url = "https://files.pythonhosted.org/packages/64/68/dc7a224f606d53ea09f9a85196a3921ec3a801b0b1d17e84c73392f0c029/torchvision-0.25.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:acc339aba4a858192998c2b91f635827e40d9c469d9cf1455bafdda6e4c28ea4", size = 2343220, upload-time = "2026-01-21T16:27:44.26Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/fa/8cce5ca7ffd4da95193232493703d20aa06303f37b119fd23a65df4f239a/torchvision-0.25.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0d9a3f925a081dd2ebb0b791249b687c2ef2c2717d027946654607494b9b64b6", size = 8068106, upload-time = "2026-01-21T16:27:37.805Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/b9/a53bcf8f78f2cd89215e9ded70041765d50ef13bf301f9884ec6041a9421/torchvision-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:b57430fbe9e9b697418a395041bb615124d9c007710a2712fda6e35fb310f264", size = 3697295, upload-time = "2026-01-21T16:27:36.574Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/be/c704bceaf11c4f6b19d64337a34a877fcdfe3bd68160a8c9ae9bea4a35a3/torchvision-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db74a551946b75d19f9996c419a799ffdf6a223ecf17c656f90da011f1d75b20", size = 1874923, upload-time = "2026-01-21T16:27:46.574Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/e9/f143cd71232430de1f547ceab840f68c55e127d72558b1061a71d0b193cd/torchvision-0.25.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:f49964f96644dbac2506dffe1a0a7ec0f2bf8cf7a588c3319fed26e6329ffdf3", size = 2344808, upload-time = "2026-01-21T16:27:43.191Z" },
+ { url = "https://files.pythonhosted.org/packages/43/ae/ad5d6165797de234c9658752acb4fce65b78a6a18d82efdf8367c940d8da/torchvision-0.25.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:153c0d2cbc34b7cf2da19d73450f24ba36d2b75ec9211b9962b5022fb9e4ecee", size = 8070752, upload-time = "2026-01-21T16:27:33.748Z" },
+ { url = "https://files.pythonhosted.org/packages/23/19/55b28aecdc7f38df57b8eb55eb0b14a62b470ed8efeb22cdc74224df1d6a/torchvision-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:ea580ffd6094cc01914ad32f8c8118174f18974629af905cea08cb6d5d48c7b7", size = 4038722, upload-time = "2026-01-21T16:27:41.355Z" },
+ { url = "https://files.pythonhosted.org/packages/56/3a/6ea0d73f49a9bef38a1b3a92e8dd455cea58470985d25635beab93841748/torchvision-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2abe430c90b1d5e552680037d68da4eb80a5852ebb1c811b2b89d299b10573b", size = 1874920, upload-time = "2026-01-21T16:27:45.348Z" },
+ { url = "https://files.pythonhosted.org/packages/51/f8/c0e1ef27c66e15406fece94930e7d6feee4cb6374bbc02d945a630d6426e/torchvision-0.25.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b75deafa2dfea3e2c2a525559b04783515e3463f6e830cb71de0fb7ea36fe233", size = 2344556, upload-time = "2026-01-21T16:27:40.125Z" },
+ { url = "https://files.pythonhosted.org/packages/68/2f/f24b039169db474e8688f649377de082a965fbf85daf4e46c44412f1d15a/torchvision-0.25.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f25aa9e380865b11ea6e9d99d84df86b9cc959f1a007cd966fc6f1ab2ed0e248", size = 8072351, upload-time = "2026-01-21T16:27:21.074Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/16/8f650c2e288977cf0f8f85184b90ee56ed170a4919347fc74ee99286ed6f/torchvision-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:f9c55ae8d673ab493325d1267cbd285bb94d56f99626c00ac4644de32a59ede3", size = 4303059, upload-time = "2026-01-21T16:27:11.08Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/5b/1562a04a6a5a4cf8cf40016a0cdeda91ede75d6962cff7f809a85ae966a5/torchvision-0.25.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:24e11199e4d84ba9c5ee7825ebdf1cd37ce8deec225117f10243cae984ced3ec", size = 1874918, upload-time = "2026-01-21T16:27:39.02Z" },
+ { url = "https://files.pythonhosted.org/packages/36/b1/3d6c42f62c272ce34fcce609bb8939bdf873dab5f1b798fd4e880255f129/torchvision-0.25.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5f271136d2d2c0b7a24c5671795c6e4fd8da4e0ea98aeb1041f62bc04c4370ef", size = 2309106, upload-time = "2026-01-21T16:27:30.624Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/60/59bb9c8b67cce356daeed4cb96a717caa4f69c9822f72e223a0eae7a9bd9/torchvision-0.25.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:855c0dc6d37f462482da7531c6788518baedca1e0847f3df42a911713acdfe52", size = 8071522, upload-time = "2026-01-21T16:27:29.392Z" },
+ { url = "https://files.pythonhosted.org/packages/32/a5/9a9b1de0720f884ea50dbf9acb22cbe5312e51d7b8c4ac6ba9b51efd9bba/torchvision-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:cef0196be31be421f6f462d1e9da1101be7332d91984caa6f8022e6c78a5877f", size = 4321911, upload-time = "2026-01-21T16:27:35.195Z" },
+ { url = "https://files.pythonhosted.org/packages/52/99/dca81ed21ebaeff2b67cc9f815a20fdaa418b69f5f9ea4c6ed71721470db/torchvision-0.25.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a8f8061284395ce31bcd460f2169013382ccf411148ceb2ee38e718e9860f5a7", size = 1896209, upload-time = "2026-01-21T16:27:32.159Z" },
+ { url = "https://files.pythonhosted.org/packages/28/cc/2103149761fdb4eaed58a53e8437b2d716d48f05174fab1d9fcf1e2a2244/torchvision-0.25.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:146d02c9876858420adf41f3189fe90e3d6a409cbfa65454c09f25fb33bf7266", size = 2310735, upload-time = "2026-01-21T16:27:22.327Z" },
+ { url = "https://files.pythonhosted.org/packages/76/ad/f4c985ad52ddd3b22711c588501be1b330adaeaf6850317f66751711b78c/torchvision-0.25.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c4d395cb2c4a2712f6eb93a34476cdf7aae74bb6ea2ea1917f858e96344b00aa", size = 8089557, upload-time = "2026-01-21T16:27:27.666Z" },
+ { url = "https://files.pythonhosted.org/packages/63/cc/0ea68b5802e5e3c31f44b307e74947bad5a38cc655231d845534ed50ddb8/torchvision-0.25.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5e6b449e9fa7d642142c0e27c41e5a43b508d57ed8e79b7c0a0c28652da8678c", size = 4344260, upload-time = "2026-01-21T16:27:17.018Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/1f/fa839532660e2602b7e704d65010787c5bb296258b44fa8b9c1cd6175e7d/torchvision-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:620a236288d594dcec7634c754484542dc0a5c1b0e0b83a34bda5e91e9b7c3a1", size = 1896193, upload-time = "2026-01-21T16:27:24.785Z" },
+ { url = "https://files.pythonhosted.org/packages/80/ed/d51889da7ceaf5ff7a0574fb28f9b6b223df19667265395891f81b364ab3/torchvision-0.25.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:0b5e7f50002a8145a98c5694a018e738c50e2972608310c7e88e1bd4c058f6ce", size = 2309331, upload-time = "2026-01-21T16:27:19.97Z" },
+ { url = "https://files.pythonhosted.org/packages/90/a5/f93fcffaddd8f12f9e812256830ec9c9ca65abbf1bc369379f9c364d1ff4/torchvision-0.25.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:632db02300e83793812eee4f61ae6a2686dab10b4cfd628b620dc47747aa9d03", size = 8088713, upload-time = "2026-01-21T16:27:15.281Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/eb/d0096eed5690d962853213f2ee00d91478dfcb586b62dbbb449fb8abc3a6/torchvision-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:d1abd5ed030c708f5dbf4812ad5f6fbe9384b63c40d6bd79f8df41a4a759a917", size = 4325058, upload-time = "2026-01-21T16:27:26.165Z" },
+ { url = "https://files.pythonhosted.org/packages/97/36/96374a4c7ab50dea9787ce987815614ccfe988a42e10ac1a2e3e5b60319a/torchvision-0.25.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ad9a8a5877782944d99186e4502a614770fe906626d76e9cd32446a0ac3075f2", size = 1896207, upload-time = "2026-01-21T16:27:23.383Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/e2/7abb10a867db79b226b41da419b63b69c0bd5b82438c4a4ed50e084c552f/torchvision-0.25.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:40a122c3cf4d14b651f095e0f672b688dde78632783fc5cd3d4d5e4f6a828563", size = 2310741, upload-time = "2026-01-21T16:27:18.712Z" },
+ { url = "https://files.pythonhosted.org/packages/08/e6/0927784e6ffc340b6676befde1c60260bd51641c9c574b9298d791a9cda4/torchvision-0.25.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:846890161b825b38aa85fc37fb3ba5eea74e7091ff28bab378287111483b6443", size = 8089772, upload-time = "2026-01-21T16:27:14.048Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/37/e7ca4ec820d434c0f23f824eb29f0676a0c3e7a118f1514f5b949c3356da/torchvision-0.25.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f07f01d27375ad89d72aa2b3f2180f07da95dd9d2e4c758e015c0acb2da72977", size = 4425879, upload-time = "2026-01-21T16:27:12.579Z" },
+]
+
+[[package]]
+name = "torchvision"
+version = "0.27.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+dependencies = [
+ { name = "numpy", marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "pillow", marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+ { name = "torch", version = "2.12.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-10-deeplabcut-apple-mchips' or extra == 'extra-10-deeplabcut-fmpose3d' or extra == 'extra-10-deeplabcut-tf' or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/13/15/2df874db140bbfe42f377e05e2dd38f2b9dc88414a6607eecc42073b2baa/torchvision-0.27.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:0822b58d2c5d325cd0c7152b744acbd15f898c07572e2cfb70b075a865a4f6f9", size = 1758817, upload-time = "2026-05-13T14:57:20.113Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/32/10b1ff4087d35b7af7bd85ccb85fbc2573c6f1c2008cf8abfcaf605a10fc/torchvision-0.27.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c9f44e35e6ec01caedacce9e941a5bf21fe424403321efac2507a201273653c5", size = 7830083, upload-time = "2026-05-13T14:57:18.336Z" },
+ { url = "https://files.pythonhosted.org/packages/57/20/97dca91770235028ba7e9c598ca1fc48c297f1843af8102430f2adcd4335/torchvision-0.27.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:419c98a9275b27660cdce6d09080fd5974d1ec1d4a225f71439ebacb3b0c4e64", size = 7573816, upload-time = "2026-05-13T14:57:12.327Z" },
+ { url = "https://files.pythonhosted.org/packages/37/a5/66fbf7f21f292d095a153ee142806646813e2055a69efe5854c28e7c3fb9/torchvision-0.27.0-cp310-cp310-win_amd64.whl", hash = "sha256:2664d06acd64d328aa7689b0d0c81ee31e240e9977d8768816b4be7c66c03211", size = 3435489, upload-time = "2026-05-13T14:57:13.716Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/d6/a7e71e981042d5c573e2e61891b9023b190c88adb75b18bed8594371250c/torchvision-0.27.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:df0c166b6bdf7c47f88e81e8b43bc085451d5c50d0c5d1691bc474c1227d6fed", size = 1758812, upload-time = "2026-05-13T14:57:16.662Z" },
+ { url = "https://files.pythonhosted.org/packages/93/f9/f542fb7e4476603fb237ebdc64369a7d11f18eb5a129aa2559cbdb710aee/torchvision-0.27.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9bb9251f64b854124efed95d02953a89f7e2726c3ca662d7ea0151129157297f", size = 7831148, upload-time = "2026-05-13T14:57:08.37Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/61/7aa7cc2c9e8750027f6fb9ae3a7393ef43860bcdfe3966e2f71fee800e31/torchvision-0.27.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f44453f107c296d5446a79f7ac59733ad8bf5ddfa04c53805dfbae298a42a798", size = 7575519, upload-time = "2026-05-13T14:56:50.552Z" },
+ { url = "https://files.pythonhosted.org/packages/19/aa/929b358b1a643849b81ec95569938044cc37dc65ab10c84eb6d82fe1bfbb/torchvision-0.27.0-cp311-cp311-win_amd64.whl", hash = "sha256:b4aacff70ea4b7377f996f9048989c850d221fef33658ddbcae42aa5bd4ca11a", size = 3749475, upload-time = "2026-05-13T14:57:11.007Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/c8/5cd91932f7f3671b0743dc4ae1a4c16b1d0b45bf4087976277d325bda718/torchvision-0.27.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:1a6dd742a150645126df9e0b2e449874c1d635897c773b322c2e067e98382dfe", size = 1758824, upload-time = "2026-05-13T14:57:15.227Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/36/7fb7d19477b3d93283b52fea11fa8ee30ab9064a08c97b4a6b91445e26cb/torchvision-0.27.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65772ff3ec4f4f5d680e30019835555dd239e7fefee4b0a846375fe1cb1592ef", size = 7831034, upload-time = "2026-05-13T14:57:06.483Z" },
+ { url = "https://files.pythonhosted.org/packages/62/43/dfd894c3f8b01b5b33fde990f0159c1926ebc7b6e2c4193e2efb7da3c4cb/torchvision-0.27.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7a9966a088d06b4cf6c610e03be62de469efa6f2cd2e7c7eed8e925ed6af59ac", size = 7579774, upload-time = "2026-05-13T14:56:59.337Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0c/722e989f9cf026e97ef7cb24a9bb1859e099f72d247ae35388fb89729f73/torchvision-0.27.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c037709072ca9b19750c0cbe9e8bb6f91c9a1be1befa26df33e281deccbd8c7", size = 4021073, upload-time = "2026-05-13T14:57:00.848Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/ae/36547812e6e047c1d80bcacd1b17a340612b08a6e876e0aabf3d0b9228b0/torchvision-0.27.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:41d6dae73e1af09fa82ded597ae57f2a2314285acde54b25890a8f8e51b999d7", size = 1758826, upload-time = "2026-05-13T14:57:05.262Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/30/32c4ea842738728a14e3df8c576c62dedcf5ae5cb6a5c984c6429ebe7524/torchvision-0.27.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:70f071c6f74b60d5fe8851636d8d4cd5f4fa29d57fd9348a87a6f17b990b95ba", size = 7789501, upload-time = "2026-05-13T14:56:57.786Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/24/4d0d48684251bd0673f87d633d5d88ab00227983b00591156eed2f86c8d5/torchvision-0.27.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:aaafa6962c9d91f42503de1957d6fa349907d028c06f335bd95da7a5bc57147d", size = 7579868, upload-time = "2026-05-13T14:56:41.618Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/da/e6edd051d2ba25adf23b120fa97f458dff888d098c51e84724f17d2d1470/torchvision-0.27.0-cp313-cp313-win_amd64.whl", hash = "sha256:aee384a2782c89517c4ab9061d2720ba59fd2ffe5ef89d0a149cc2d43abdf521", size = 4092700, upload-time = "2026-05-13T14:57:09.729Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/23/95dfa40431360f42ca949bf861434bed51164adfa8fb9801e05bf3194f50/torchvision-0.27.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:c5121f1b9ab09a7f73e837871deb8321551f7eaeb19d87aa00de9191968eae44", size = 1845008, upload-time = "2026-05-13T14:57:03.768Z" },
+ { url = "https://files.pythonhosted.org/packages/23/b9/9dbdf76b2b49a75ba8088df6f7c755bdb520afb6c6dbac0102b46cde5e99/torchvision-0.27.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:1c01f0d1091ae22b9dfc082b0a0fe5faaf053686a29b4fb082ba7691375c73cf", size = 7791430, upload-time = "2026-05-13T14:56:56.206Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/6a/e4a16cf2f3310c2ea7760dc5d9054496844391e0f4c1fae87fefac2f3d9e/torchvision-0.27.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dadea3c5ecfd05bbb2a3312ab0374f213c58bf6459cb059122e2f4dfe13d10ed", size = 7668441, upload-time = "2026-05-13T14:57:02.127Z" },
+ { url = "https://files.pythonhosted.org/packages/00/70/01b6461117a6a94b5af3f8ee166bb0f045056f3cf187750c110dabfdfffa/torchvision-0.27.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a49e55055a39a8506fe7e59850522cab004efb2c3839f6057658889c1d69c815", size = 4141602, upload-time = "2026-05-13T14:56:53.449Z" },
+ { url = "https://files.pythonhosted.org/packages/92/22/c0633677b3b3f3e69554a21ac087bf705f829c40cd5e3783507b8c006681/torchvision-0.27.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:c1fac0fc2a7adf29481fc1938a0e7845c57ba1147a986784109c4d98f434ea8c", size = 1758818, upload-time = "2026-05-13T14:56:54.988Z" },
+ { url = "https://files.pythonhosted.org/packages/48/e8/55f9d9667b56dae470e69e31beac9b00d458ea393feec1aae95cc4f3f1c9/torchvision-0.27.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:cbf89764fc76f3f17fbf80c12d5a89c691e91cb9d82c38412aaf0568655ffb19", size = 7789667, upload-time = "2026-05-13T14:56:48.858Z" },
+ { url = "https://files.pythonhosted.org/packages/00/bc/6f8681daf3bbc4c315bb0005110f99d28e3ecd675bf9c8f2c0d393fbac7a/torchvision-0.27.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:91f61b9865423037c327eb56afa207cc72de874e458c361840db9dcf5ce0c0eb", size = 7579848, upload-time = "2026-05-13T14:56:38.209Z" },
+ { url = "https://files.pythonhosted.org/packages/19/6c/8d8020e6bd1e46c53e487c9c4e9457a07f2ee28931028fb5d71e2da40adc/torchvision-0.27.0-cp314-cp314-win_amd64.whl", hash = "sha256:5bb82fc3c55daf1788621e504310b0a286f1069627a8742f692aebb075ef25a7", size = 4119284, upload-time = "2026-05-13T14:56:46.625Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/7e/e78c48662a8d551606efdbe11c6b9c1d6d2391b92cd0e4591b9e6a2412b8/torchvision-0.27.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:2c4099a15150143b9b034730b404a56d572efe0b79489b4c765d929cb4eac7f3", size = 1758828, upload-time = "2026-05-13T14:56:52.293Z" },
+ { url = "https://files.pythonhosted.org/packages/21/dd/d03ee9f9ee7bf11a8c7c776fb8e7fd6102f59c013791a2a4e5175bd6cba7/torchvision-0.27.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b4c6bb0a670dcba017b3643e21902c9b8a1cc1c127d602f1488fa29ec3c6e865", size = 7790618, upload-time = "2026-05-13T14:56:44.721Z" },
+ { url = "https://files.pythonhosted.org/packages/39/08/4002336a74742be70728603ec1769feb2b55e0d19c532c9ec9f92008de76/torchvision-0.27.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1c2db4bde82bc48ebff73436a6adf34d4f809448268a70d9a1285f5c8f92313d", size = 7580217, upload-time = "2026-05-13T14:56:43.274Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/cb/4dd4783eb3565f526ba6e64b6f6ca26c00eacc924cdfe60455db9d91b84b/torchvision-0.27.0-cp314-cp314t-win_amd64.whl", hash = "sha256:72bf547e58ddb948689734eed6f4b6a2031f979dba4fb08e3690688b392e929f", size = 4226392, upload-time = "2026-05-13T14:56:40.235Z" },
+]
+
+[[package]]
+name = "tornado"
+version = "6.5.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f8/f1/3173dfa4a18db4a9b03e5d55325559dab51ee653763bb8745a75af491286/tornado-6.5.5.tar.gz", hash = "sha256:192b8f3ea91bd7f1f50c06955416ed76c6b72f96779b962f07f911b91e8d30e9", size = 516006, upload-time = "2026-03-10T21:31:02.067Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa", size = 445983, upload-time = "2026-03-10T21:30:44.28Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521", size = 444246, upload-time = "2026-03-10T21:30:46.571Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5", size = 447229, upload-time = "2026-03-10T21:30:48.273Z" },
+ { url = "https://files.pythonhosted.org/packages/34/01/74e034a30ef59afb4097ef8659515e96a39d910b712a89af76f5e4e1f93c/tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:435319e9e340276428bbdb4e7fa732c2d399386d1de5686cb331ec8eee754f07", size = 448192, upload-time = "2026-03-10T21:30:51.22Z" },
+ { url = "https://files.pythonhosted.org/packages/be/00/fe9e02c5a96429fce1a1d15a517f5d8444f9c412e0bb9eadfbe3b0fc55bf/tornado-6.5.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3f54aa540bdbfee7b9eb268ead60e7d199de5021facd276819c193c0fb28ea4e", size = 448039, upload-time = "2026-03-10T21:30:53.52Z" },
+ { url = "https://files.pythonhosted.org/packages/82/9e/656ee4cec0398b1d18d0f1eb6372c41c6b889722641d84948351ae19556d/tornado-6.5.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36abed1754faeb80fbd6e64db2758091e1320f6bba74a4cf8c09cd18ccce8aca", size = 447445, upload-time = "2026-03-10T21:30:55.541Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/76/4921c00511f88af86a33de770d64141170f1cfd9c00311aea689949e274e/tornado-6.5.5-cp39-abi3-win32.whl", hash = "sha256:dd3eafaaeec1c7f2f8fdcd5f964e8907ad788fe8a5a32c4426fbbdda621223b7", size = 448582, upload-time = "2026-03-10T21:30:57.142Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl", hash = "sha256:6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b", size = 448990, upload-time = "2026-03-10T21:30:58.857Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/c8/876602cbc96469911f0939f703453c1157b0c826ecb05bdd32e023397d4e/tornado-6.5.5-cp39-abi3-win_arm64.whl", hash = "sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6", size = 448016, upload-time = "2026-03-10T21:31:00.43Z" },
+]
+
+[[package]]
+name = "tqdm"
+version = "4.67.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" },
+]
+
+[[package]]
+name = "traitlets"
+version = "5.15.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1b/22/40f55b26baeab80c2d7b3f1db0682f8954e4617fee7d90ce634022ef05c6/traitlets-5.15.0.tar.gz", hash = "sha256:4fead733f81cf1c4c938e06f8ca4633896833c9d89eff878159457f4d4392971", size = 163197, upload-time = "2026-05-06T08:05:58.016Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/da/98/a9937a969d018a23badfea0b381f66783649d48e0ea6c41923265c3cbeb3/traitlets-5.15.0-py3-none-any.whl", hash = "sha256:fb36a18867a6803deab09f3c5e0fa81bb7b26a5c9e82501c9933f759166eff40", size = 85877, upload-time = "2026-05-06T08:05:55.853Z" },
+]
+
+[[package]]
+name = "triton"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+]
+dependencies = [
+ { name = "cmake", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "filelock", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "lit", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "torch", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-10-deeplabcut-tf-cu11') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (platform_machine != 'x86_64' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform != 'linux' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ca/31/ff6be541195daf77aa5c72303b2354661a69e717967d44d91eb4f3fdce32/triton-2.0.0-1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38806ee9663f4b0f7cd64790e96c579374089e58f49aac4a6608121aa55e2505", size = 63268585, upload-time = "2023-03-20T17:32:17.169Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/cd/4aa0179919306f9c2e3e5308f269d20c094b2a4e2963b656e9405172763f/triton-2.0.0-1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:226941c7b8595219ddef59a1fdb821e8c744289a132415ddd584facedeb475b1", size = 63278135, upload-time = "2023-03-20T17:32:25.22Z" },
+]
+
+[[package]]
+name = "triton"
+version = "3.6.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/44/ba/b1b04f4b291a3205d95ebd24465de0e5bf010a2df27a4e58a9b5f039d8f2/triton-3.6.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c723cfb12f6842a0ae94ac307dba7e7a44741d720a40cf0e270ed4a4e3be781", size = 175972180, upload-time = "2026-01-20T16:15:53.664Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/f7/f1c9d3424ab199ac53c2da567b859bcddbb9c9e7154805119f8bd95ec36f/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea", size = 188105201, upload-time = "2026-01-20T16:00:29.272Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/2c/96f92f3c60387e14cc45aed49487f3486f89ea27106c1b1376913c62abe4/triton-3.6.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49df5ef37379c0c2b5c0012286f80174fcf0e073e5ade1ca9a86c36814553651", size = 176081190, upload-time = "2026-01-20T16:16:00.523Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/12/b05ba554d2c623bffa59922b94b0775673de251f468a9609bc9e45de95e9/triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3", size = 188214640, upload-time = "2026-01-20T16:00:35.869Z" },
+ { url = "https://files.pythonhosted.org/packages/17/5d/08201db32823bdf77a0e2b9039540080b2e5c23a20706ddba942924ebcd6/triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4", size = 176128243, upload-time = "2026-01-20T16:16:07.857Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/12/34d71b350e89a204c2c7777a9bba0dcf2f19a5bfdd70b57c4dbc5ffd7154/triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd", size = 176133521, upload-time = "2026-01-20T16:16:13.321Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/4e/41b0c8033b503fd3cfcd12392cdd256945026a91ff02452bef40ec34bee7/triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6", size = 176276087, upload-time = "2026-01-20T16:16:18.989Z" },
+ { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" },
+ { url = "https://files.pythonhosted.org/packages/49/55/5ecf0dcaa0f2fbbd4420f7ef227ee3cb172e91e5fede9d0ecaddc43363b4/triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43", size = 176138577, upload-time = "2026-01-20T16:16:25.426Z" },
+ { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" },
+ { url = "https://files.pythonhosted.org/packages/48/db/56ee649cab5eaff4757541325aca81f52d02d4a7cd3506776cad2451e060/triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d", size = 176274804, upload-time = "2026-01-20T16:16:31.528Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" },
+]
+
+[[package]]
+name = "triton"
+version = "3.7.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3e/97/dcd1f2a0f8336691bff74abc59b2ed9c69a0c0f8f65cd77109c49e05f068/triton-3.7.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223ac302091491436c248a34ee1e6c47a1026486579103c906ffd805be50cb89", size = 188367104, upload-time = "2026-05-07T19:04:56.68Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/c0/c2ac4fd2d8809b7579d4a820a0f9e5de62a9bc8a757ed4b3abf4f7ee964a/triton-3.7.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c631b65668d4951213b948a413c0564184305b77bb45cc9d686d3e1ecc4701a3", size = 201313191, upload-time = "2026-05-07T18:45:58.444Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/c1/5d842314bb6c78442cc60437928781701c6050b8d479bc2a1aed691d37ca/triton-3.7.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9e71fc392675fac364e0ecf4ef3f76f85b7f5433a16f4c3c5fe5f05a52c85fe", size = 188480277, upload-time = "2026-05-07T19:05:03.231Z" },
+ { url = "https://files.pythonhosted.org/packages/13/31/8315ea5f8dd18e60970b3022e3a8b93fd37e0b784fbbef86e10c8e6e5ca1/triton-3.7.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22bacffce443f54593dd20f05294d5a40622e0ea9ab632816f87154504356221", size = 201415942, upload-time = "2026-05-07T18:46:06.479Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/13/ec05adfcd87311d532ba61e3af143e8be59fcd26675884c4682841406a20/triton-3.7.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4bf49b00a7a377a68a6da603a876e797614e6455a80e9021669c476a953ad9a", size = 188505104, upload-time = "2026-05-07T19:05:09.843Z" },
+ { url = "https://files.pythonhosted.org/packages/62/7b/468a576e35beef1426e0828e28e9ba9e65f5474d496f16ee126c15646324/triton-3.7.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f111161d49bf903c0eaedde3962353a3d841c08a836839b7cc1025b8426efcf", size = 201457567, upload-time = "2026-05-07T18:46:13.505Z" },
+ { url = "https://files.pythonhosted.org/packages/01/e1/a59a583de59b8f62c495d67c80ee3ea97d09e91ac80c4c6e76456ed8d8ac/triton-3.7.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abdf6beaa89b1bcfb9a43cd990536ce66091a997841a4814b260b7bee4c88c3c", size = 188503209, upload-time = "2026-05-07T19:05:17.935Z" },
+ { url = "https://files.pythonhosted.org/packages/30/b1/b7507bb9815d403927c8dd51d4158ed2e11751a92dbc118a044f247b6848/triton-3.7.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a35d7afe3f3f058e7ec49fcce09794049e0ffc5c59019ac25ec3413741b8c4e7", size = 201453566, upload-time = "2026-05-07T18:46:20.427Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/8f/0bea7a6a0c989315c9135a1d7fb37e41905cfb3a17cbc1f10044ebd4cc3a/triton-3.7.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc1d61c172d257db80ddf42595131fb196ad2e9bdd751e90fe2ef13531734e8b", size = 188612899, upload-time = "2026-05-07T19:05:24.955Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/02/d96f57828d0912aec733b9bc7e0e7dbfd2c6f079a8fa433ac25cb93d1a30/triton-3.7.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70fb9bbdc9f400afc54bbf6eb2670af28829a6ae3996863317964783141daf56", size = 201553816, upload-time = "2026-05-07T18:46:27.49Z" },
+ { url = "https://files.pythonhosted.org/packages/40/fb/82a802dac4689f2a2fb2e69302e6a138eecc3e175bbe976ba3cfc717683a/triton-3.7.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a44a8476d0d3571eac4e4d1048e1ff75aad81a09ff4602ccfc56c6dea1672e", size = 188507879, upload-time = "2026-05-07T19:05:32.209Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/af/9904ec6d3c93d9b24e5ec360445bbdf758b7f00bfbeedb89cb0eb64eb8bb/triton-3.7.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9b85e72968a9d8bba5ddb24e9b64aaabaf48affb042f2755cb7cfa92b7531ce", size = 201460637, upload-time = "2026-05-07T18:46:34.749Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/f9/4835a8ea746b88727d8899f4e3ccce4f9cacb38abfc3bb0a638266c53111/triton-3.7.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18a160de426fd99f92b0baf509045360afbd3bfaa0b4a5171dde800ec9f09684", size = 188608706, upload-time = "2026-05-07T19:05:39.218Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/68/fa86e5a39608000f645535b2c124920126327ab731f8c4fafd5b07ff8d4b/triton-3.7.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce061073102714b725f3660ec6939d94a1da7984b3aa99c921417cae273672f5", size = 201546766, upload-time = "2026-05-07T18:46:42.088Z" },
+]
+
+[[package]]
+name = "typer"
+version = "0.25.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-doc" },
+ { name = "click" },
+ { name = "rich" },
+ { name = "shellingham" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.15.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
+]
+
+[[package]]
+name = "typing-inspection"
+version = "0.4.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
+]
+
+[[package]]
+name = "tzdata"
+version = "2026.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" },
+]
+
+[[package]]
+name = "uc-micro-py"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/78/67/9a363818028526e2d4579334460df777115bdec1bb77c08f9db88f6389f2/uc_micro_py-2.0.0.tar.gz", hash = "sha256:c53691e495c8db60e16ffc4861a35469b0ba0821fe409a8a7a0a71864d33a811", size = 6611, upload-time = "2026-03-01T06:31:27.526Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/73/d21edf5b204d1467e06500080a50f79d49ef2b997c79123a536d4a17d97c/uc_micro_py-2.0.0-py3-none-any.whl", hash = "sha256:3603a3859af53e5a39bc7677713c78ea6589ff188d70f4fee165db88e22b242c", size = 6383, upload-time = "2026-03-01T06:31:26.257Z" },
+]
+
+[[package]]
+name = "urllib3"
+version = "2.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" },
+]
+
+[[package]]
+name = "virtualenv"
+version = "20.35.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "distlib" },
+ { name = "filelock" },
+ { name = "platformdirs" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" },
+]
+
+[[package]]
+name = "vispy"
+version = "0.15.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "freetype-py" },
+ { name = "hsluv" },
+ { name = "kiwisolver" },
+ { name = "numpy" },
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1b/2e/ef2697f963111cf1bc83568bbe55f262b7c7c8a72948a6e802a7c236f2c1/vispy-0.15.2.tar.gz", hash = "sha256:d52d10c0697f48990555cea2a2bad3f9f5a772391856fda364ea4bbc69fd075c", size = 2513383, upload-time = "2025-05-19T13:26:41.015Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/21/2b/a483bf80575e047173d55f51115c38f9c43962cfd3247a861ce033913bee/vispy-0.15.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6bc8a49f4e0b27e19be0da318877666d733e1afc7231407e635f948a8aabb095", size = 1478710, upload-time = "2025-05-19T13:26:00.997Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/bc/8c9a3cb402037d6eda521492c04dddf6a00f600c85b650b33342573fe82f/vispy-0.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:89a5d51cc980fed81f16373d515086d5b16da66868df8c1f76e71d4dfc17062c", size = 1472024, upload-time = "2025-05-19T13:26:03.055Z" },
+ { url = "https://files.pythonhosted.org/packages/58/67/46111a528d63ad5308e4a547484c75e1c9982ec6a3709732ba0bcd343a7a/vispy-0.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7cbce48e14e2eeac491688be33b4d422d198515c67a1a87021622790309107e", size = 1845072, upload-time = "2025-05-19T13:26:04.226Z" },
+ { url = "https://files.pythonhosted.org/packages/56/91/5c9d739410427c603fd0aa9e6a29b6c3e51edcc7f83f14bf243974808396/vispy-0.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8b0b6b55a781b6add58f946b95c368bfa203e7915fda234f4993c4db03d3c0", size = 1844568, upload-time = "2025-05-19T13:26:05.511Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/8e/f07f9d7a341b5058a99e0700f7cab7d222b159154a392b335f0e59cbf636/vispy-0.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:0f2639656fd53ee4cbedf3e3caed0cef646f90e7ca4ec777ba18ad247234d8c9", size = 1467701, upload-time = "2025-05-19T13:26:07.193Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/e7/3590e46cec1d51a8bba0b2d5442df698aec41ff5757e9e29e04aea3cbe12/vispy-0.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4f7adab04c0a90ca0a1155f09ecc425ca44bee713e6b2d4970d85fc010b05ee", size = 1478729, upload-time = "2025-05-19T13:26:08.951Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/71/5871fe8d2f612502e5e148224426fb263431870a2f5fa5d2205fb9f2606a/vispy-0.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:725ab77b11bb3a4de5145a25b6e06f4555b0b7b1821baf7ffdd7f674f725dab2", size = 1472015, upload-time = "2025-05-19T13:26:10.246Z" },
+ { url = "https://files.pythonhosted.org/packages/67/d8/067de34eac9aecf71a3291c54a738161caa9cbf30d62314b0500abff4117/vispy-0.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d107b07365bc1bdfa28fe6f8df05004cd73bfd6269230bef26b3862f2016fa64", size = 1872447, upload-time = "2025-05-19T13:26:11.844Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/1e/af88bdb15b20a690b3d5409fc89fbdbb79095a593abb8a37094a42428760/vispy-0.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c67fb06df462b63ba95c77eb00a3d5b7d53e90f69987b0e216ab67ad7d6d00e", size = 1876232, upload-time = "2025-05-19T13:26:13.094Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/4d/236c77644db2427f33330fd6372bac416262fd3de6771c555b969f67bd4f/vispy-0.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:46f8ca742958e19135cfa0f76ef1707666837ae6a9559a3fa3c2a3186967299e", size = 1467601, upload-time = "2025-05-19T13:26:15.199Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/7e/31102425ea26bafab27bb5a675b499c57d29e72b1cc25fe3ae8facdd374e/vispy-0.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c8cbc22bc789f238423abadfcc3ba7589b85729be6b179b321720b511012fbee", size = 1478919, upload-time = "2025-05-19T13:26:16.962Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/63/a601b8f1e8e418d3821d7b4a465c2eb6695ab8eccadfce886b99ffdfe92a/vispy-0.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bf995b86dcc1eab265fa2bf9a8a5ca5a225b86c3ed4ac63900f0c5e90d5e8100", size = 1471915, upload-time = "2025-05-19T13:26:18.441Z" },
+ { url = "https://files.pythonhosted.org/packages/79/1f/ca0deb4a876148c0fa515bf03d03e1dfad4553be8e6f2ab51433c074310b/vispy-0.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0aa64fcab1cd1730a10a1c9188e9cef8aefe6d099a46f2f87e2458bcef719918", size = 1866848, upload-time = "2025-05-19T13:26:19.6Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/a4/dc6f335e54877f5d26ad4e4aac8f49b2d6e7719dee3e08f363d882c8aba4/vispy-0.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9d38228cedd23876cb2359ff568d523a97284d225259d450d5e5e2129e98eb1", size = 1870433, upload-time = "2025-05-19T13:26:20.794Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/73/5ffa34d7300c35e8423d51789d594d35eaa7256d210f9909afa9fdd0aa37/vispy-0.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:a84531c1a89f8b9d3992eb0d0ab74f84884f49d1f46a8be3755c809d7507cb01", size = 1468068, upload-time = "2025-05-19T13:26:22.073Z" },
+ { url = "https://files.pythonhosted.org/packages/85/9a/6664b7f0d28e0008a28111bae847a1fa3eedd409458bf01ca45a6e99da6f/vispy-0.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ac46aab46e208b919dcf0bb26869bcd083a0a1a4927bcaf41631ba38c247639", size = 1477880, upload-time = "2025-05-19T13:26:23.321Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/6f/b9b36f841c5ff7320764f64822e79df3fea8a2c92270cda7f3a634d9a031/vispy-0.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7e197a012503850a77d47177964d572edab964b2a8ea0f9d998c35b81325256c", size = 1471035, upload-time = "2025-05-19T13:26:24.554Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/af/b892a3c9b4be29755ce4d1fc17ecb8249446bdd0e17bb5b2a9cb39bcf0f4/vispy-0.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9eff397ecdbaf0052baae0563caa9e276272d6dc78fbaab3f5e51dbf5f7f92", size = 1860412, upload-time = "2025-05-19T13:26:26.212Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/13/8997d96bdc0a81f331dfd41b368935d79e8ea2917840266567e6dc40d684/vispy-0.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f13ee0cb95bbff6d7b235e1a8f96e620eee15ccb6d5ad74902427ab7e56dc8ab", size = 1865860, upload-time = "2025-05-19T13:26:27.928Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/37/abb30db1853b69aed4c32813cf312f301ec3641b8846193be9a6d892d607/vispy-0.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:f5955821e9b452980490706648a702da8cb2b82e762b6d6589e5872118301b84", size = 1467882, upload-time = "2025-05-19T13:26:29.608Z" },
+]
+
+[[package]]
+name = "wandb"
+version = "0.27.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "gitpython" },
+ { name = "packaging" },
+ { name = "platformdirs" },
+ { name = "protobuf", version = "4.25.9", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or extra == 'extra-10-deeplabcut-tf' or extra == 'extra-10-deeplabcut-tf-cu11' or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "protobuf", version = "5.29.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.13' and extra == 'extra-10-deeplabcut-tf-cu12') or (python_full_version < '3.13' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (python_full_version < '3.13' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.13' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version < '3.13' and extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (python_full_version >= '3.13' and extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf') or (python_full_version >= '3.13' and extra != 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest') or (extra != 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "protobuf", version = "6.33.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (python_full_version < '3.13' and extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-fmpose3d' and extra != 'extra-10-deeplabcut-tf') or (python_full_version < '3.13' and extra != 'extra-10-deeplabcut-apple-mchips' and extra != 'extra-10-deeplabcut-tf' and extra != 'extra-10-deeplabcut-tf-cu11' and extra != 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-fmpose3d' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra == 'extra-10-deeplabcut-tf-cu11' and extra == 'extra-10-deeplabcut-tf-latest') or (extra == 'extra-10-deeplabcut-tf-cu12' and extra == 'extra-10-deeplabcut-tf-latest')" },
+ { name = "pydantic" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "sentry-sdk" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8e/31/fe53d06b75ef0a7f2f0ee5931a89f7aedc27d233840b1839616860fed256/wandb-0.27.0.tar.gz", hash = "sha256:579e75300173059f9334e1f513a79ef15f6d9ea5c74e20d695633648cdd02031", size = 41090732, upload-time = "2026-05-14T03:44:08.894Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ea/5e/2c199e70e636ecfd217cde0bc7469f4511e1d03d0685eb92bfdfce391430/wandb-0.27.0-py3-none-macosx_12_0_arm64.whl", hash = "sha256:c156be4851485f3c4160cb6eb2e8991b4cdeffbccefc5636d33cf5e254847365", size = 24886476, upload-time = "2026-05-14T03:43:27.569Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/cd/a617c871cd304a9804e56a7ec2ec2c65685bf0091a2b9f91910175a149e2/wandb-0.27.0-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:20179f38afb0158859a4141d29ac650d3fdbd0cf801a74ce25565c934f03776c", size = 26045779, upload-time = "2026-05-14T03:43:31.999Z" },
+ { url = "https://files.pythonhosted.org/packages/10/0a/d3f159a201530b84b72ca5f98c68d1f351c2d9a1864558ed76c811407fae/wandb-0.27.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:626497d7975fa898d0a4a239da7a510483495ca3514510dbe75004a25963af4d", size = 25480764, upload-time = "2026-05-14T03:43:35.922Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/6a/8721fcdf71d42639191040a77a585d2982402b1754700cb2ecfc2ca1470a/wandb-0.27.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:f772da7005cc26a2a32b729a16982a583dc68b3d493df6a09d0aa5c5ca5a2060", size = 27256204, upload-time = "2026-05-14T03:43:39.765Z" },
+ { url = "https://files.pythonhosted.org/packages/00/5e/279d167ba79fb7a8a43401c9f25efd0f6663ee9bd1eaf5a8578530198888/wandb-0.27.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:63acfc5b994e4a90e4a2fbdee6d45e664da3dd865bb1419942c8995c06c41cf1", size = 25647469, upload-time = "2026-05-14T03:43:44.817Z" },
+ { url = "https://files.pythonhosted.org/packages/94/51/a69ac59300e3c813939d0764348959ed2a21e14c668cb1cebcb04010da6a/wandb-0.27.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:17aae6e4a88cd05c00ea8f546220918e3ebb6f8c1c36b70ef04a5ac75f0d7160", size = 27599005, upload-time = "2026-05-14T03:43:50.926Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/40/bf510c8758727df020f83b717ebc1fcc1739ed7f6ae1796ebef60bf6f592/wandb-0.27.0-py3-none-win32.whl", hash = "sha256:0bd5659417e386bf6538b5e2ffe6885774c6197f0e4853bfed517d5b0db457f1", size = 25036164, upload-time = "2026-05-14T03:43:54.839Z" },
+ { url = "https://files.pythonhosted.org/packages/54/ff/69f88e7d90c22b79bcb911143c13e59742ee192080b21015ff83a5a1f60a/wandb-0.27.0-py3-none-win_amd64.whl", hash = "sha256:89d584b73166eecee96fb446f18d0e45b1aa45aba6a3696296f3f06d7454516b", size = 25036170, upload-time = "2026-05-14T03:43:59.227Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/38/f7efd7a87297a55c7e9a331a1dbb5b19e54aeacc11fe6f43f8636a73987c/wandb-0.27.0-py3-none-win_arm64.whl", hash = "sha256:a6c129c311edf210a2b4f2f4acc557eff522628125f5f28ed27df19c16c07079", size = 22972710, upload-time = "2026-05-14T03:44:03.275Z" },
+]
+
+[[package]]
+name = "wcwidth"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2c/ee/afaf0f85a9a18fe47a67f1e4422ed6cf1fe642f0ae0a2f81166231303c52/wcwidth-0.7.0.tar.gz", hash = "sha256:90e3a7ea092341c44b99562e75d09e4d5160fe7a3974c6fb842a101a95e7eed0", size = 182132, upload-time = "2026-05-02T16:04:12.653Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/41/52/e465037f5375f43533d1a80b6923955201596a99142ed524d77b571a1418/wcwidth-0.7.0-py3-none-any.whl", hash = "sha256:5d69154c429a82910e241c738cd0e2976fac8a2dd47a1a805f4afed1c0f136f2", size = 110825, upload-time = "2026-05-02T16:04:11.033Z" },
+]
+
+[[package]]
+name = "werkzeug"
+version = "3.1.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markupsafe", marker = "(sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu11') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-cu12') or (sys_platform != 'darwin' and extra == 'extra-10-deeplabcut-tf-latest') or (sys_platform == 'darwin' and extra == 'extra-10-deeplabcut-apple-mchips') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu11') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-cu12') or (extra != 'extra-10-deeplabcut-apple-mchips' and extra == 'extra-10-deeplabcut-tf-latest')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" },
+]
+
+[[package]]
+name = "wheel"
+version = "0.47.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/39/62/75f18a0f03b4219c456652c7780e4d749b929eb605c098ce3a5b6b6bc081/wheel-0.47.0.tar.gz", hash = "sha256:cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3", size = 63854, upload-time = "2026-04-22T15:51:27.727Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/1b/9e33c09813d65e248f7f773119148a612516a4bea93e9c6f545f78455b7c/wheel-0.47.0-py3-none-any.whl", hash = "sha256:212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced", size = 32218, upload-time = "2026-04-22T15:51:26.296Z" },
+]
+
+[[package]]
+name = "wrapt"
+version = "1.14.2"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/23/49/925324e2eaa0c83c45b7a1eb31004af4ad7b37fdc1d9e897ea7d551215da/wrapt-1.14.2.tar.gz", hash = "sha256:19d33781d891c99b5a35f810656d2fe01765b35ccfb6d395007d2edb1058d98f", size = 50701, upload-time = "2025-08-12T06:59:02.334Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ad/3f/12f70730cf9237e07aa5887bdb3e3695fd68e7e0d6dd66b5dd7a8794724a/wrapt-1.14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:734de7e0182d15f03eb4f4fce0ff7f07a072119a19cdcc97c40aee96d10fe851", size = 35836, upload-time = "2025-08-12T06:58:20.535Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/95/5e261ed720bdddf9bbfea2b9bbd30bd03e298b02267333a231592a0cef7b/wrapt-1.14.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:851ded3662733bab093297a0adb844c71e7754b8c144f76bb569ac6544a7c27a", size = 76907, upload-time = "2025-08-12T06:58:39.06Z" },
+ { url = "https://files.pythonhosted.org/packages/18/ed/fcf75886924473ebc11ded6f589f9f715bd605a787201e8b9b1f34b958a7/wrapt-1.14.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f7b9a0364edef5a3033307cde69f7c3775a74eb83caeff8955e72ec3e0963fe", size = 77665, upload-time = "2025-08-12T06:58:29.329Z" },
+ { url = "https://files.pythonhosted.org/packages/16/de/e9205650d0c8401c4e2cc2a01edc89ce3fabda219e2efabcc71d92d02a70/wrapt-1.14.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3066203bf6e35a040c848143636e6fc2aa4ad6fb2b027a956f462083a3b635ea", size = 76401, upload-time = "2025-08-12T06:58:30.309Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/e6/e3b79ba90e7b3a7c1915dbecb84aa491dc0a16238216dae5137bb0d85689/wrapt-1.14.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:072684f6264ca83695d1c7ad601e61b17953351b0950790cd50c327402006492", size = 76360, upload-time = "2025-08-12T06:58:40.019Z" },
+ { url = "https://files.pythonhosted.org/packages/35/07/fe53594e194b133b29b0808c40f1ad92773186c314dbb5b1bf5bb9a4e34a/wrapt-1.14.2-cp310-cp310-win32.whl", hash = "sha256:26c36d922c70de7ae7e933d0f9a10b4017d007d505f60fe02efa8e260a6c7af4", size = 33681, upload-time = "2025-08-12T06:58:52.611Z" },
+ { url = "https://files.pythonhosted.org/packages/12/68/134031baba6e1ba7dbdf2a217e99feb7177a2ad860b19f3ba319d40e0baf/wrapt-1.14.2-cp310-cp310-win_amd64.whl", hash = "sha256:ead0c5373df2c551347c7f71903a6403efb391506e1e349704ec21fe797e59bf", size = 35811, upload-time = "2025-08-12T06:58:51.696Z" },
+ { url = "https://files.pythonhosted.org/packages/16/ea/7ab441022d98da4cd4ff6d2ec6cbd8e4bf69085f261bdf72971a43bd8a45/wrapt-1.14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3913516545546d3ff82ffe5bb4a7eab29dc7f1c71cf7329a1494659dc2a60b7b", size = 35832, upload-time = "2025-08-12T06:58:22.674Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/26/953f79a4233603958234358820434d973bd859d3831b19fde23078e09771/wrapt-1.14.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:122015da6e0a1d09e1f9fbb8c756b9a8802e6cb01138f5256c121602bc7c7399", size = 77384, upload-time = "2025-08-12T06:58:41.195Z" },
+ { url = "https://files.pythonhosted.org/packages/53/7f/4057df32059ea4782cf34b7a9dc08c6b29c4e29ecccfa58b32a12eb77582/wrapt-1.14.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:257a53f69737f340d0ba6e4d5554b56a579ca884de6eada15a7126baae8dacc0", size = 78135, upload-time = "2025-08-12T06:58:31.259Z" },
+ { url = "https://files.pythonhosted.org/packages/37/80/d688dfde8c9d75b41c6f01a71e6f64a77861b6ad67e04d622d78060d638c/wrapt-1.14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e60ca4aa80a85a0bc24cdf0f971d736f6d0957bdbc93874339027cfb0f286c99", size = 76933, upload-time = "2025-08-12T06:58:32.586Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/b0/2b5f8b11c463f75acb6ca710a2ac2e40549f6b5d68bc85c135fc49a7ef19/wrapt-1.14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f23bfecdc0e139f15b195ca3601e71a1388dc058bdbbd160ba9d0f4a17b4a12d", size = 76873, upload-time = "2025-08-12T06:58:42.64Z" },
+ { url = "https://files.pythonhosted.org/packages/28/d4/003f22d726ca8a0bc2170cad7651dad732f3d3a6e4f69d1f13d8244a93e9/wrapt-1.14.2-cp311-cp311-win32.whl", hash = "sha256:cb5aee915d67441bd07acbce9e4cab1d01a110b4e5f02932e80e5ef4c9411ae2", size = 33675, upload-time = "2025-08-12T06:58:54.885Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/61/8eb200c6cf899fedc677a7a01b2f189ad58f1d154d0ef84dab30800b9f01/wrapt-1.14.2-cp311-cp311-win_amd64.whl", hash = "sha256:e9773691347205f7d11e7c4395561633285c8d1d691ebfc73ffd64f9072aa22e", size = 35811, upload-time = "2025-08-12T06:58:53.921Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/c7/7376998449689cf2adbdbeacad47084410d00f3ae04cf73e6127cf52b950/wrapt-1.14.2-py3-none-any.whl", hash = "sha256:82eea3b559f51f22aefc530b747b87406811ef00cb0c4734f48cb139c748db63", size = 21577, upload-time = "2025-08-12T06:59:01.408Z" },
+]
+
+[[package]]
+name = "wrapt"
+version = "2.1.2"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux'",
+ "python_full_version >= '3.13' and sys_platform == 'darwin'",
+ "python_full_version == '3.12.*' and sys_platform == 'darwin'",
+ "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version == '3.11.*' and sys_platform == 'darwin'",
+ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')",
+ "python_full_version < '3.11' and sys_platform == 'darwin'",
+ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'",
+ "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/da/d2/387594fb592d027366645f3d7cc9b4d7ca7be93845fbaba6d835a912ef3c/wrapt-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c", size = 60669, upload-time = "2026-03-06T02:52:40.671Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/18/3f373935bc5509e7ac444c8026a56762e50c1183e7061797437ca96c12ce/wrapt-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f", size = 61603, upload-time = "2026-03-06T02:54:21.032Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/7a/32758ca2853b07a887a4574b74e28843919103194bb47001a304e24af62f/wrapt-2.1.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb", size = 113632, upload-time = "2026-03-06T02:53:54.121Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/d5/eeaa38f670d462e97d978b3b0d9ce06d5b91e54bebac6fbed867809216e7/wrapt-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e", size = 115644, upload-time = "2026-03-06T02:54:53.33Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/09/2a41506cb17affb0bdf9d5e2129c8c19e192b388c4c01d05e1b14db23c00/wrapt-2.1.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba", size = 112016, upload-time = "2026-03-06T02:54:43.274Z" },
+ { url = "https://files.pythonhosted.org/packages/64/15/0e6c3f5e87caadc43db279724ee36979246d5194fa32fed489c73643ba59/wrapt-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f", size = 114823, upload-time = "2026-03-06T02:54:29.392Z" },
+ { url = "https://files.pythonhosted.org/packages/56/b2/0ad17c8248f4e57bedf44938c26ec3ee194715f812d2dbbd9d7ff4be6c06/wrapt-2.1.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394", size = 111244, upload-time = "2026-03-06T02:54:02.149Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/04/bcdba98c26f2c6522c7c09a726d5d9229120163493620205b2f76bd13c01/wrapt-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45", size = 113307, upload-time = "2026-03-06T02:54:12.428Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/1b/5e2883c6bc14143924e465a6fc5a92d09eeabe35310842a481fb0581f832/wrapt-2.1.2-cp310-cp310-win32.whl", hash = "sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d", size = 57986, upload-time = "2026-03-06T02:54:26.823Z" },
+ { url = "https://files.pythonhosted.org/packages/42/5a/4efc997bccadd3af5749c250b49412793bc41e13a83a486b2b54a33e240c/wrapt-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71", size = 60336, upload-time = "2026-03-06T02:54:18Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/f5/a2bb833e20181b937e87c242645ed5d5aa9c373006b0467bfe1a35c727d0/wrapt-2.1.2-cp310-cp310-win_arm64.whl", hash = "sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc", size = 58757, upload-time = "2026-03-06T02:53:51.545Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/81/60c4471fce95afa5922ca09b88a25f03c93343f759aae0f31fb4412a85c7/wrapt-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb", size = 60666, upload-time = "2026-03-06T02:52:58.934Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/be/80e80e39e7cb90b006a0eaf11c73ac3a62bbfb3068469aec15cc0bc795de/wrapt-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d", size = 61601, upload-time = "2026-03-06T02:53:00.487Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/be/d7c88cd9293c859fc74b232abdc65a229bb953997995d6912fc85af18323/wrapt-2.1.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894", size = 114057, upload-time = "2026-03-06T02:52:44.08Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/25/36c04602831a4d685d45a93b3abea61eca7fe35dab6c842d6f5d570ef94a/wrapt-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842", size = 116099, upload-time = "2026-03-06T02:54:56.74Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/4e/98a6eb417ef551dc277bec1253d5246b25003cf36fdf3913b65cb7657a56/wrapt-2.1.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8", size = 112457, upload-time = "2026-03-06T02:53:52.842Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/a6/a6f7186a5297cad8ec53fd7578533b28f795fdf5372368c74bd7e6e9841c/wrapt-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6", size = 115351, upload-time = "2026-03-06T02:53:32.684Z" },
+ { url = "https://files.pythonhosted.org/packages/97/6f/06e66189e721dbebd5cf20e138acc4d1150288ce118462f2fcbff92d38db/wrapt-2.1.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9", size = 111748, upload-time = "2026-03-06T02:53:08.455Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/43/4808b86f499a51370fbdbdfa6cb91e9b9169e762716456471b619fca7a70/wrapt-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15", size = 113783, upload-time = "2026-03-06T02:53:02.02Z" },
+ { url = "https://files.pythonhosted.org/packages/91/2c/a3f28b8fa7ac2cefa01cfcaca3471f9b0460608d012b693998cd61ef43df/wrapt-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b", size = 57977, upload-time = "2026-03-06T02:53:27.844Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/c3/2b1c7bd07a27b1db885a2fab469b707bdd35bddf30a113b4917a7e2139d2/wrapt-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1", size = 60336, upload-time = "2026-03-06T02:54:28.104Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/5c/76ece7b401b088daa6503d6264dd80f9a727df3e6042802de9a223084ea2/wrapt-2.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a", size = 58756, upload-time = "2026-03-06T02:53:16.319Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/b6/1db817582c49c7fcbb7df6809d0f515af29d7c2fbf57eb44c36e98fb1492/wrapt-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9", size = 61255, upload-time = "2026-03-06T02:52:45.663Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/16/9b02a6b99c09227c93cd4b73acc3678114154ec38da53043c0ddc1fba0dc/wrapt-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748", size = 61848, upload-time = "2026-03-06T02:53:48.728Z" },
+ { url = "https://files.pythonhosted.org/packages/af/aa/ead46a88f9ec3a432a4832dfedb84092fc35af2d0ba40cd04aea3889f247/wrapt-2.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e", size = 121433, upload-time = "2026-03-06T02:54:40.328Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/9f/742c7c7cdf58b59085a1ee4b6c37b013f66ac33673a7ef4aaed5e992bc33/wrapt-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8", size = 123013, upload-time = "2026-03-06T02:53:26.58Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/44/2c3dd45d53236b7ed7c646fcf212251dc19e48e599debd3926b52310fafb/wrapt-2.1.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c", size = 117326, upload-time = "2026-03-06T02:53:11.547Z" },
+ { url = "https://files.pythonhosted.org/packages/74/e2/b17d66abc26bd96f89dec0ecd0ef03da4a1286e6ff793839ec431b9fae57/wrapt-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c", size = 121444, upload-time = "2026-03-06T02:54:09.5Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/62/e2977843fdf9f03daf1586a0ff49060b1b2fc7ff85a7ea82b6217c1ae36e/wrapt-2.1.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1", size = 116237, upload-time = "2026-03-06T02:54:03.884Z" },
+ { url = "https://files.pythonhosted.org/packages/88/dd/27fc67914e68d740bce512f11734aec08696e6b17641fef8867c00c949fc/wrapt-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2", size = 120563, upload-time = "2026-03-06T02:53:20.412Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/9f/b750b3692ed2ef4705cb305bd68858e73010492b80e43d2a4faa5573cbe7/wrapt-2.1.2-cp312-cp312-win32.whl", hash = "sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0", size = 58198, upload-time = "2026-03-06T02:53:37.732Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/b2/feecfe29f28483d888d76a48f03c4c4d8afea944dbee2b0cd3380f9df032/wrapt-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63", size = 60441, upload-time = "2026-03-06T02:52:47.138Z" },
+ { url = "https://files.pythonhosted.org/packages/44/e1/e328f605d6e208547ea9fd120804fcdec68536ac748987a68c47c606eea8/wrapt-2.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf", size = 58836, upload-time = "2026-03-06T02:53:22.053Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/7a/d936840735c828b38d26a854e85d5338894cda544cb7a85a9d5b8b9c4df7/wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b", size = 61259, upload-time = "2026-03-06T02:53:41.922Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e", size = 61851, upload-time = "2026-03-06T02:52:48.672Z" },
+ { url = "https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb", size = 121446, upload-time = "2026-03-06T02:54:14.013Z" },
+ { url = "https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca", size = 123056, upload-time = "2026-03-06T02:54:10.829Z" },
+ { url = "https://files.pythonhosted.org/packages/93/b9/ff205f391cb708f67f41ea148545f2b53ff543a7ac293b30d178af4d2271/wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267", size = 117359, upload-time = "2026-03-06T02:53:03.623Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/3d/1ea04d7747825119c3c9a5e0874a40b33594ada92e5649347c457d982805/wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f", size = 121479, upload-time = "2026-03-06T02:53:45.844Z" },
+ { url = "https://files.pythonhosted.org/packages/78/cc/ee3a011920c7a023b25e8df26f306b2484a531ab84ca5c96260a73de76c0/wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8", size = 116271, upload-time = "2026-03-06T02:54:46.356Z" },
+ { url = "https://files.pythonhosted.org/packages/98/fd/e5ff7ded41b76d802cf1191288473e850d24ba2e39a6ec540f21ae3b57cb/wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413", size = 120573, upload-time = "2026-03-06T02:52:50.163Z" },
+ { url = "https://files.pythonhosted.org/packages/47/c5/242cae3b5b080cd09bacef0591691ba1879739050cc7c801ff35c8886b66/wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6", size = 58205, upload-time = "2026-03-06T02:53:47.494Z" },
+ { url = "https://files.pythonhosted.org/packages/12/69/c358c61e7a50f290958809b3c61ebe8b3838ea3e070d7aac9814f95a0528/wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1", size = 60452, upload-time = "2026-03-06T02:53:30.038Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/66/c8a6fcfe321295fd8c0ab1bd685b5a01462a9b3aa2f597254462fc2bc975/wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf", size = 58842, upload-time = "2026-03-06T02:52:52.114Z" },
+ { url = "https://files.pythonhosted.org/packages/da/55/9c7052c349106e0b3f17ae8db4b23a691a963c334de7f9dbd60f8f74a831/wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b", size = 63075, upload-time = "2026-03-06T02:53:19.108Z" },
+ { url = "https://files.pythonhosted.org/packages/09/a8/ce7b4006f7218248dd71b7b2b732d0710845a0e49213b18faef64811ffef/wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18", size = 63719, upload-time = "2026-03-06T02:54:33.452Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/e5/2ca472e80b9e2b7a17f106bb8f9df1db11e62101652ce210f66935c6af67/wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d", size = 152643, upload-time = "2026-03-06T02:52:42.721Z" },
+ { url = "https://files.pythonhosted.org/packages/36/42/30f0f2cefca9d9cbf6835f544d825064570203c3e70aa873d8ae12e23791/wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015", size = 158805, upload-time = "2026-03-06T02:54:25.441Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/67/d08672f801f604889dcf58f1a0b424fe3808860ede9e03affc1876b295af/wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92", size = 145990, upload-time = "2026-03-06T02:53:57.456Z" },
+ { url = "https://files.pythonhosted.org/packages/68/a7/fd371b02e73babec1de6ade596e8cd9691051058cfdadbfd62a5898f3295/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf", size = 155670, upload-time = "2026-03-06T02:54:55.309Z" },
+ { url = "https://files.pythonhosted.org/packages/86/2d/9fe0095dfdb621009f40117dcebf41d7396c2c22dca6eac779f4c007b86c/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67", size = 144357, upload-time = "2026-03-06T02:54:24.092Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/b6/ec7b4a254abbe4cde9fa15c5d2cca4518f6b07d0f1b77d4ee9655e30280e/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a", size = 150269, upload-time = "2026-03-06T02:53:31.268Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/6b/2fabe8ebf148f4ee3c782aae86a795cc68ffe7d432ef550f234025ce0cfa/wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd", size = 59894, upload-time = "2026-03-06T02:54:15.391Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/fb/9ba66fc2dedc936de5f8073c0217b5d4484e966d87723415cc8262c5d9c2/wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f", size = 63197, upload-time = "2026-03-06T02:54:41.943Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/1c/012d7423c95d0e337117723eb8ecf73c622ce15a97847e84cf3f8f26cd7e/wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679", size = 60363, upload-time = "2026-03-06T02:54:48.093Z" },
+ { url = "https://files.pythonhosted.org/packages/39/25/e7ea0b417db02bb796182a5316398a75792cd9a22528783d868755e1f669/wrapt-2.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9", size = 61418, upload-time = "2026-03-06T02:53:55.706Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/0f/fa539e2f6a770249907757eaeb9a5ff4deb41c026f8466c1c6d799088a9b/wrapt-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9", size = 61914, upload-time = "2026-03-06T02:52:53.37Z" },
+ { url = "https://files.pythonhosted.org/packages/53/37/02af1867f5b1441aaeda9c82deed061b7cd1372572ddcd717f6df90b5e93/wrapt-2.1.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e", size = 120417, upload-time = "2026-03-06T02:54:30.74Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/b7/0138a6238c8ba7476c77cf786a807f871672b37f37a422970342308276e7/wrapt-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c", size = 122797, upload-time = "2026-03-06T02:54:51.539Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/ad/819ae558036d6a15b7ed290d5b14e209ca795dd4da9c58e50c067d5927b0/wrapt-2.1.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a", size = 117350, upload-time = "2026-03-06T02:54:37.651Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/2d/afc18dc57a4600a6e594f77a9ae09db54f55ba455440a54886694a84c71b/wrapt-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90", size = 121223, upload-time = "2026-03-06T02:54:35.221Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/5b/5ec189b22205697bc56eb3b62aed87a1e0423e9c8285d0781c7a83170d15/wrapt-2.1.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586", size = 116287, upload-time = "2026-03-06T02:54:19.654Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/2d/f84939a7c9b5e6cdd8a8d0f6a26cabf36a0f7e468b967720e8b0cd2bdf69/wrapt-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19", size = 119593, upload-time = "2026-03-06T02:54:16.697Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/fe/ccd22a1263159c4ac811ab9374c061bcb4a702773f6e06e38de5f81a1bdc/wrapt-2.1.2-cp314-cp314-win32.whl", hash = "sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508", size = 58631, upload-time = "2026-03-06T02:53:06.498Z" },
+ { url = "https://files.pythonhosted.org/packages/65/0a/6bd83be7bff2e7efaac7b4ac9748da9d75a34634bbbbc8ad077d527146df/wrapt-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04", size = 60875, upload-time = "2026-03-06T02:53:50.252Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/c0/0b3056397fe02ff80e5a5d72d627c11eb885d1ca78e71b1a5c1e8c7d45de/wrapt-2.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575", size = 59164, upload-time = "2026-03-06T02:53:59.128Z" },
+ { url = "https://files.pythonhosted.org/packages/71/ed/5d89c798741993b2371396eb9d4634f009ff1ad8a6c78d366fe2883ea7a6/wrapt-2.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb", size = 63163, upload-time = "2026-03-06T02:52:54.873Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/8c/05d277d182bf36b0a13d6bd393ed1dec3468a25b59d01fba2dd70fe4d6ae/wrapt-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22", size = 63723, upload-time = "2026-03-06T02:52:56.374Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/27/6c51ec1eff4413c57e72d6106bb8dec6f0c7cdba6503d78f0fa98767bcc9/wrapt-2.1.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596", size = 152652, upload-time = "2026-03-06T02:53:23.79Z" },
+ { url = "https://files.pythonhosted.org/packages/db/4c/d7dd662d6963fc7335bfe29d512b02b71cdfa23eeca7ab3ac74a67505deb/wrapt-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044", size = 158807, upload-time = "2026-03-06T02:53:35.742Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/4d/1e5eea1a78d539d346765727422976676615814029522c76b87a95f6bcdd/wrapt-2.1.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b", size = 146061, upload-time = "2026-03-06T02:52:57.574Z" },
+ { url = "https://files.pythonhosted.org/packages/89/bc/62cabea7695cd12a288023251eeefdcb8465056ddaab6227cb78a2de005b/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf", size = 155667, upload-time = "2026-03-06T02:53:39.422Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/99/6f2888cd68588f24df3a76572c69c2de28287acb9e1972bf0c83ce97dbc1/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2", size = 144392, upload-time = "2026-03-06T02:54:22.41Z" },
+ { url = "https://files.pythonhosted.org/packages/40/51/1dfc783a6c57971614c48e361a82ca3b6da9055879952587bc99fe1a7171/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3", size = 150296, upload-time = "2026-03-06T02:54:07.848Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/38/cbb8b933a0201076c1f64fc42883b0023002bdc14a4964219154e6ff3350/wrapt-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7", size = 60539, upload-time = "2026-03-06T02:54:00.594Z" },
+ { url = "https://files.pythonhosted.org/packages/82/dd/e5176e4b241c9f528402cebb238a36785a628179d7d8b71091154b3e4c9e/wrapt-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5", size = 63969, upload-time = "2026-03-06T02:54:39Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/99/79f17046cf67e4a95b9987ea129632ba8bcec0bc81f3fb3d19bdb0bd60cd/wrapt-2.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00", size = 60554, upload-time = "2026-03-06T02:53:14.132Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" },
+]
+
+[[package]]
+name = "yacs"
+version = "0.1.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pyyaml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/44/3e/4a45cb0738da6565f134c01d82ba291c746551b5bc82e781ec876eb20909/yacs-0.1.8.tar.gz", hash = "sha256:efc4c732942b3103bea904ee89af98bcd27d01f0ac12d8d4d369f1e7a2914384", size = 11100, upload-time = "2020-08-10T16:37:47.755Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/38/4f/fe9a4d472aa867878ce3bb7efb16654c5d63672b86dc0e6e953a67018433/yacs-0.1.8-py3-none-any.whl", hash = "sha256:99f893e30497a4b66842821bac316386f7bd5c4f47ad35c9073ef089aa33af32", size = 14747, upload-time = "2020-08-10T16:37:46.4Z" },
+]
+
+[[package]]
+name = "zipp"
+version = "4.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214, upload-time = "2026-05-18T20:08:57.967Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238, upload-time = "2026-05-18T20:08:57.045Z" },
+]
|