diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..79dcaa6637c56278d255e0eacd5435d6fdab0e1c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,9 @@ +# LFS/Xet-managed assets +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.gif filter=lfs diff=lfs merge=lfs -text + *.7z filter=lfs diff=lfs merge=lfs -text *.arrow filter=lfs diff=lfs merge=lfs -text *.bin filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a7567464a89a55de149fe25a7d31dc6b1bc6aa0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,234 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[codz] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py.cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +# Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +# poetry.lock +# poetry.toml + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +# pdm.lock +# pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +# pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# Redis +*.rdb +*.aof +*.pid + +# RabbitMQ +mnesia/ +rabbitmq/ +rabbitmq-data/ + +# ActiveMQ +activemq-data/ + +# SageMath parsed files +*.sage.py + +# Environments +.env +.envrc +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +# .idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml + +# pixi environments +.pixi/* +!.pixi/config.toml + +_checkpoints/* + + +# START Ruler Generated Files +/.codex/config.json +/.codex/config.json.bak +/.codex/config.toml +/.codex/config.toml.bak +/.vscode/mcp.json +/.vscode/mcp.json.bak +/AGENTS.md +/AGENTS.md.bak +# END Ruler Generated Files diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000000000000000000000000000000000000..1b22bef9c78ad96ebe90dcd3e565c097acdd2cce --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000000000000000000000000000000000000..cfa161659dde8f3277f7d4ec9654bedf48bc82f6 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2022 Rerun Technologies AB + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index d1735416d0ca1bc52e981e5ba05ba983de3883ad..e0b52c93147334403ad494652386bbd95bfb85c1 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,82 @@ ---- -title: Sam3d Body Rerun -emoji: ๐Ÿ† -colorFrom: gray -colorTo: yellow -sdk: gradio -sdk_version: 6.0.1 -app_file: app.py -pinned: false ---- - -Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference +# SAM3D Body with Rerun +An unofficial playground for Meta's SAM3D Body (DINOv3) with promptable SAM3 masks and live Rerun visualization. Uses **Rerun** for 3D inspection, **Gradio** for the UI, and **Pixi** for one-command setup. + +

+ + Rerun badge + + + Pixi badge + + + CUDA badge + + + GitHub stars + +

+ +

+ + example output +

+ +## Installation +### Using Pixi +Make sure you have the [Pixi](https://pixi.sh/latest/#installation) package manager installed. + +TL;DR install Pixi: +```bash +curl -fsSL https://pixi.sh/install.sh | sh +``` +Restart your shell so the new `pixi` binary is on `PATH`. + +This is Linux only with an NVIDIA GPU. + +The SAM3 and SAM3D Body checkpoints are gated on Hugging Faceโ€”request access for both [facebook/sam-3d-body-dinov3](https://huggingface.co/facebook/sam-3d-body-dinov3) and [facebook/sam3](https://huggingface.co/facebook/sam3), then authenticate either by setting `HF_TOKEN=` or running `huggingface-cli login` before the first download (see Meta's install notes). + +First run will download HF checkpoints for SAM3, SAM3D Body, and the relative-depth model. +```bash +git clone https://github.com/rerun-io/sam3d-body-rerun.git +cd sam3d-body-rerun +pixi run app +``` + +All commands can be listed with `pixi task list`. + +## Usage +### Gradio App +```bash +pixi run app +``` +Opens the Gradio UI with an embedded streaming Rerun viewer. Try the bundled samples in `data/example-data` or upload your own RGB image; toggle โ€œLog relative depthโ€ to stream predicted depth. + +### CLI +From a dev shell (for tyro + dev deps): +``` +pixi run cli +``` + +OR + +```bash +pixi shell -e dev +python tool/demo.py --help +``` +Run on a folder of images and configure Rerun output/recordings via the CLI flags. + +### Promptable SAM3 sandbox +If you just want SAM3 masks without 3D reconstruction: +```bash +pixi run -e dev python tool/gradio_sam3.py +``` + +## Acknowledgements +Thanks to the original projects that make this demo possible: + +- [facebook/sam-3d-body-dinov3](https://huggingface.co/facebook/sam-3d-body-dinov3) โ€” SAM3D Body checkpoints and assets. +- [facebook/sam3](https://huggingface.co/facebook/sam3) โ€” promptable concept segmentation. +- Relative depth/FOV from `MogeV1Predictor` in [monopriors](https://github.com/pablovela5620/monoprior). +- Built with [Rerun](https://rerun.io/), [Gradio](https://www.gradio.app/), and [Pixi](https://pixi.sh/latest/). + +Dual licensed under Apache 2.0 and MIT for the code in this repository; upstream models/assets retain their original licenses (see `LICENSE-APACHE` and `LICENSE-MIT` for this repo). diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..56cd7bdb02844858ae99fe464458c9969f1a98a7 --- /dev/null +++ b/app.py @@ -0,0 +1,43 @@ +import os +import subprocess +from pathlib import Path + +PIXI_PATH = Path("/home/user/.pixi/bin/pixi") +PIXI_VERSION = "0.59.0" +MOCK_CUDA_VERSION = "12.9" + +# Pretend CUDA 12.9 is available so pixi can solve environments on machines without GPUs. +os.environ.setdefault("CONDA_OVERRIDE_CUDA", MOCK_CUDA_VERSION) + + +def check_and_install_pixi() -> None: + try: + subprocess.check_call(f"{PIXI_PATH} --version", shell=True) + except subprocess.CalledProcessError: + print("pixi not found. Installing pixi...") + # Install pixi using the provided installation script + subprocess.check_call( + f"PIXI_VERSION=v{PIXI_VERSION} curl -fsSL https://pixi.sh/install.sh | bash", + shell=True, + ) + subprocess.check_call(f"{PIXI_PATH} self-update --version {PIXI_VERSION}", shell=True) + subprocess.check_call(f"{PIXI_PATH} --version", shell=True) + + +def run_command(command: str) -> None: + try: + subprocess.check_call(command, shell=True) + except subprocess.CalledProcessError as e: + print(f"run command {command}. Error: {e}") + + +if __name__ == "__main__": + check_and_install_pixi() + # install lsof + # run_command(command=f"{PIXI_PATH} global install lsof") + # # kill anything running on port 7860 + # run_command(command=f"{PIXI_PATH.parent}/lsof -t -i:7860 | xargs -r kill") + # clean current environment + run_command(command=f"{PIXI_PATH} clean") + # run spaces app + run_command(command=f"{PIXI_PATH} run app") diff --git a/data/example-data/Amir-Khan-Lamont-Peterson_2689582.jpg b/data/example-data/Amir-Khan-Lamont-Peterson_2689582.jpg new file mode 100644 index 0000000000000000000000000000000000000000..21df096d7de7f90cde9209725635780b9f821d9b --- /dev/null +++ b/data/example-data/Amir-Khan-Lamont-Peterson_2689582.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85013a25f46cad9ba86bc05786b48dfb6e5a2d5dfa9f19328997480ec23226e5 +size 154666 diff --git a/data/example-data/BNAAHPYGMYSE26U6C6T7VA6544.jpg b/data/example-data/BNAAHPYGMYSE26U6C6T7VA6544.jpg new file mode 100644 index 0000000000000000000000000000000000000000..11e01c41b07318b89f0a0ff270aaa0c79d9a28b2 --- /dev/null +++ b/data/example-data/BNAAHPYGMYSE26U6C6T7VA6544.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5d64d944c10ffde20645075b9078b7359899dd019062ceaa6fd54b18be21042 +size 719314 diff --git a/data/example-data/Canelo-Alvarez-b4d59f2080464e4d996177f5ce9792ee.jpg b/data/example-data/Canelo-Alvarez-b4d59f2080464e4d996177f5ce9792ee.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4e06918db7bcb262612f337858937c3b12c218f4 --- /dev/null +++ b/data/example-data/Canelo-Alvarez-b4d59f2080464e4d996177f5ce9792ee.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc029593f9dae5bd0473148fe9b920d6e708220b126c1b0a09bb9b48bfa999be +size 134121 diff --git a/data/example-data/Planche.jpg b/data/example-data/Planche.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65ee2b02bea43a7317a888d8d148b8635258f37c --- /dev/null +++ b/data/example-data/Planche.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:898a2376f2adac0676408cc5c563b8f50df9966caa3299d4013b0476dd5cdbbe +size 216077 diff --git a/data/example-data/yoga-example.jpg b/data/example-data/yoga-example.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dce7bcd19cdaac40996d292b06a344edafbc6ac1 --- /dev/null +++ b/data/example-data/yoga-example.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:260c554cb3e8cc582a37873951f05ee10e99631e2c858d7b77d246554212fdae +size 50590 diff --git a/pixi.lock b/pixi.lock new file mode 100644 index 0000000000000000000000000000000000000000..211ccd6e42944b9db30f267401c176d51c18b623 --- /dev/null +++ b/pixi.lock @@ -0,0 +1,9120 @@ +version: 6 +environments: + cuda128: + channels: + - url: https://conda.anaconda.org/conda-forge/ + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.2-h39aace5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils-2.45-default_h4852527_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.45-default_hfdba357_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.45-default_h4852527_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.11.0-h4d9bdce_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-gcc-specs-14.3.0-he8ccf15_12.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cccl_linux-64-12.9.27-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-compiler-12.9.1-hbad6d8a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-crt-12.9.86-h69a702a_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-crt-dev_linux-64-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-crt-tools-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-dev-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-dev_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-static-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cuobjdump-12.9.82-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cuxxfilt-12.9.82-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-driver-dev-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-driver-dev_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-12.9.86-hcdd1206_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvcc-dev_linux-64-12.9.86-he91c749_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-impl-12.9.86-h85509e4_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-tools-12.9.86-he02047a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc_linux-64-12.9.86-he0b4e1d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvdisasm-12.9.88-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvml-dev-12.9.79-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvprune-12.9.82-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-12.9.86-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-dev-12.9.86-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvtx-12.9.79-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvtx-dev-12.9.79-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-64-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-impl-12.9.86-h4bc722e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-tools-12.9.86-h4bc722e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-profiler-api-12.9.79-h7938cbb_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-12.9-h4f385c5_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cudnn-9.10.2.21-hbcb9cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cusparselt-0.8.1.1-h58dd1b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.11.0-hfcd1e18_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc-14.3.0-h0dff253_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-14.3.0-h0c81b82_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-14.3.0-h298d278_14.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-14.3.0-he448592_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-14.3.0-h2185e75_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-14.3.0-hc876b51_14.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.77-h3ff7636_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-12.9.1.4-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-dev-12.9.1.4-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudnn-9.10.2.21-hf7e9902_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudnn-dev-9.10.2.21-h58dd1b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudss-0.7.1.4-h58dd1b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudss-dev-0.7.1.4-hee47b17_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufft-11.4.1.4-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufft-dev-11.4.1.4-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.14.1.1-hbc026e6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-dev-1.14.1.1-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurand-10.3.10.19-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurand-dev-10.3.10.19-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusolver-11.7.5.82-h676940d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusolver-dev-11.7.5.82-h676940d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusparse-12.5.10.65-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusparse-dev-12.5.10.65-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_12.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-14.3.0-hf649bbc_112.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnl-3.11.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnvjitlink-12.9.86-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnvptxcompiler-dev-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libnvptxcompiler-dev_linux-64-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-14.3.0-h8f1669f_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_12.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-14.3.0-h9f08a49_112.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.10-hd0affe5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-257.10-hd0affe5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-21.1.6-h4922eb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-60.0-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_8.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiofiles-24.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.13.2-py312h27b7581_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/assimp-5.4.3-hecf2907_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.2-h39aace5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/audioop-lts-0.2.2-py312h5253ce2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/av-16.0.1-py312h46547aa_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/beartype-0.22.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils-2.45-default_h4852527_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.45-default_hfdba357_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.45-default_h4852527_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.11.0-h4d9bdce_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cli11-2.6.0-h54a6638_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-gcc-specs-14.3.0-he8ccf15_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312hd9148b4_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.12-py312hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cccl_linux-64-12.9.27-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-compiler-12.9.1-hbad6d8a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-crt-12.9.86-h69a702a_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-crt-dev_linux-64-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-crt-tools-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-dev-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-dev_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-static-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cuobjdump-12.9.82-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cupti-12.9.79-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cuxxfilt-12.9.82-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-driver-dev-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-driver-dev_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-12.9.86-hcdd1206_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvcc-dev_linux-64-12.9.86-he91c749_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-impl-12.9.86-h85509e4_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-tools-12.9.86-he02047a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc_linux-64-12.9.86-he0b4e1d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvdisasm-12.9.88-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvml-dev-12.9.79-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvprune-12.9.82-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-12.9.86-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-dev-12.9.86-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvtx-12.9.79-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvtx-dev-12.9.79-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-64-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-impl-12.9.86-h4bc722e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-tools-12.9.86-h4bc722e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-profiler-api-12.9.79-h7938cbb_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-12.9-h4f385c5_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cudnn-9.10.2.21-hbcb9cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cusparselt-0.8.1.1-h58dd1b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.11.0-hfcd1e18_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dash-3.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.8.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.3.0-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/embree-4.4.0-ha751380_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.122.0-h6dd4f06_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.16-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-core-0.122.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.0.0-gpl_hbbdf940_906.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ffmpy-0.3.0-pyhb6f538c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/flask-3.1.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-11.2.0-h07f6e7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.60.1-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.7.0-py312h447239a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.10.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc-14.3.0-h0dff253_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-14.3.0-h0c81b82_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-14.3.0-h298d278_14.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.4-h2b0a6b4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gl2ps-1.4.2-hae5d5c5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glew-2.2.0-h3abd4de_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glfw-3.4-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glslang-16.0.0-hfd11570_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.2.1-py312hcaba1f9_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gradio-5.49.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gradio-client-1.13.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/groovy-python-0.1.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-14.3.0-he448592_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-14.3.0-h2185e75_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-14.3.0-hc876b51_14.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hf-xet-1.2.1-py310hb823017_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.7.1-py312h4c3975b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-1.1.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/intel-gmmlib-22.8.2-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/intel-media-driver-25.3.4-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/itsdangerous-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jsoncpp-1.9.6-hf42df4d_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py312h0a2e395_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/level-zero-1.26.0-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libass-0.17.4-h96ad9f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.3.0-h6395336_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-2_h5875eb1_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hed09d94_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.77-h3ff7636_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-2_hfef963f_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.6-default_h99862b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.6-default_h746c552_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-12.9.1.4-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-dev-12.9.1.4-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudnn-9.10.2.21-hf7e9902_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudnn-dev-9.10.2.21-h58dd1b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudss-0.7.1.4-h58dd1b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudss-dev-0.7.1.4-hee47b17_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufft-11.4.1.4-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufft-dev-11.4.1.4-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.14.1.1-hbc026e6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-dev-1.14.1.1-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurand-10.3.10.19-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurand-dev-10.3.10.19-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusolver-11.7.5.82-h676940d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusolver-dev-11.7.5.82-h676940d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusparse-12.5.10.65-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusparse-dev-12.5.10.65-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libde265-1.0.15-h00ab1b0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_12.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-14.3.0-hf649bbc_112.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.2-h32235b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglu-9.0.3-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libheif-1.19.7-gpl_hc18d805_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-2_h5e43f62_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-2_hdba1596_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.6-hf7376ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzf-3.6-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmagma-2.9.0-ha7672b3_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnl-3.11.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnvjitlink-12.9.86-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnvjpeg-12.4.0.76-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnvptxcompiler-dev-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libnvptxcompiler-dev_linux-64-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-hd0c01bc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-batch-plugin-2025.2.0-hed573e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-plugin-2025.2.0-hed573e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-hetero-plugin-2025.2.0-hd41364c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-cpu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-gpu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-npu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-ir-frontend-2025.2.0-hd41364c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-onnx-frontend-2025.2.0-h1862bb8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-paddle-frontend-2025.2.0-h1862bb8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-pytorch-frontend-2025.2.0-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-frontend-2025.2.0-h0767aad_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-lite-frontend-2025.2.0-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopus-1.5.2-hd0c01bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.51-h421ea60_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.1-h5c52fec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.60.0-h61e6d4b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-14.3.0-h8f1669f_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.0-hee844dc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_12.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-14.3.0-h9f08a49_112.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.10-hd0affe5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtheora-1.1.1-h4ab18f5_1006.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.8.0-cuda129_mkl_hf53477d_302.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-257.10-hd0affe5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liburing-2.12-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libusb-1.0.29-h73b1eb8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.22.0-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h54a6638_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpl-2.15.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpx-1.14.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.0-hca5e8e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-21.1.6-h4922eb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py312he3d6523_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2025.3.0-h0e700b2_462.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.3.1-h24ddda3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.1-h90cbb55_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.9-hc50e24c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py312hd9148b4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.6.3-py312h178313f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.12.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nccl-2.28.9.1-h4d09622_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/open3d-0.19.0-py312hf1b548d_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openh264-2.6.0-hc22cd8d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.18.0-py312hd9148b4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orjson-3.11.4-py312h868fb18_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py312hf79963d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hadf4263_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py312h7b42cdd_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.3.1-py312h178313f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pugixml-1.15-h3f63f65_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-h9a6aba3_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-2.13.6-pyhc790b64_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-4-hd8ed1ab_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-2.13.6-pyh217bc35_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.10-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.33.2-py312h680f630_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydub-0.25.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.12-hd63d673_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.12-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.8.0-cuda129_mkl_py312_had1c889_302.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-gpu-2.8.0-cuda129_mkl_h43a4b0b_302.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.3-h5c1c036_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.7.1-h8fae777_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-60.0-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/retrying-1.4.2-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.14.6-h813ae00_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/safehttpx-0.1.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2-2.32.56-h54a6638_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl3-3.2.26-h68140b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/semantic_version-2.10.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shaderc-2025.4-h3e344bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sleef-3.9.0-ha0421bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/spirv-tools-2025.4-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.51.0-heff268d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.50.0-pyhfdc7a7d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/svt-av1-3.1.2-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_105.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.3.0-h8d10470_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tinyobjloader-1.0.7-h59595ed_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomlkit-0.12.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/torchvision-0.24.0-cuda129_py312_h9f56bbf_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/torchvision-extra-decoders-0.0.2-py312hd347cc9_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/triton-3.4.0-cuda129py312h811769c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.20.0-pyhefaf540_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.20.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.20.0-h4daf872_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py312h4c3975b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/utfcpp-4.0.8-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.38.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.38.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.22.1-py312h4c3975b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.1-py312h5d43ed7_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.1.1-py312h0ccc70a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wayland-protocols-1.46-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-15.0.1-py312h5253ce2_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wslink-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/x264-1!164.3095-h166bdaf_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/x265-3.5-h924138e_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxscrnsaver-1.2.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.22.0-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + - pypi: https://files.pythonhosted.org/packages/9f/d2/c581486aa6c4fbd7394c23c47b83fa1a919d34194e16944241daf9e762dd/accelerate-1.12.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/00/b08f23b7d7e1e14ce01419a467b583edbb93c6cdb8654e54a9cc579cd61f/addict-2.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/38/7859ff46355f76f8d19459005ca000b6e7012f2f1ca597746cbcd1fbfe5e/antlr4-python3-runtime-4.9.3.tar.gz + - pypi: https://files.pythonhosted.org/packages/dc/95/1ec57df6c52152fb4c776da5fbb2896f19af52de3776899d91e7140e7654/aria2p-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/35/bf/9cad857b630c840738003eb24c1adb63490a1024ec40a9dcc3a753300c38/asciimatics-1.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fa/93/e8c04e80e82391a6e51f218ca49720f64236bc824e92152a2633b74cf7ab/braceexpand-0.1.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/6a/1766f8c163951a3c9aeb30a4e6f5de9b2eed8389e3906c4cf30fcb475be6/casefy-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/2a/2e/38d9824f8c6bb048c5ba21c6d4da54c29c162a46b58b3ef907a360a76d3e/diffusers-0.35.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/11/a8/c6a4b901d17399c77cd81fb001ce8961e9f5e04d3daf27e8925cb012e163/docutils-0.22.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/27/f4/30077733807ca57cc504a6faadcdd067714a7891dad66231b0c1ab373551/fastcore-1.8.16-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ee/1b/00a78aa2e8fbd63f9af08c9c19e6deb3d5d66b4dda677a0f61654680ee89/flatbuffers-25.9.23-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ed/1f/95cb961341bf33e534a472672ef4916c90046dc3c57b1442f5586dda72f1/geffnet-1.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/ba/9ccb80650e5bd61e739f16f33aec3bb290a80f314631be8f7dc0a2b22b5f/glcontext-3.0.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/de/5a/5ebd2acb8f330f4886265c3c13243338075557ccd66a9bbe0b163596797e/gradio_rerun-0.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/d8/f87ea6f42456254b48915970ed98e993110521e9263472840174d32c880d/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/5f/f877d2cfcad41c0db98b120872f67b805fde5e6d407e584e9edfc9dec35c/icecream-2.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b4/09/726f168acad366b11e420df31bf1c702a54d373a83f968d94141a8c3fde0/jaraco_functools-4.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/db/7e/da7b57a1f3af7303a0f3c8594d820fc0d3a9bbe3810a357eb21eb166e76b/jaxtyping-0.2.38-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/de/73/3d757cb3fc16f0f9794dd289bcd0c4a031d9cf54d8137d6b984b2d02edf3/lightning_utilities-0.15.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/2d/2e321a29535b28795aefc1f6658d5c474097cb93ab1dead304a1e41dd532/lovely_numpy-0.2.18-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/bd/5d468c171f201d6169bec848d1116e95736854eec72307299bc68939fe45/mmcv_lite-2.2.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/98/8e/f98332248aad102511bea4ae19c0ddacd2f0a994f3ca4c82b7a369e0af8b/mmengine-0.10.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d7/96/bcb5141eae24474d80b8157b0c3055d25fa75f9804d4abb4a514695bbba9/moderngl-5.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: git+https://github.com/microsoft/MoGe.git#07444410f1e33f402353b99d6ccd26bd31e469e8 + - pypi: git+https://github.com/pablovela5620/monoprior.git#f887b77a3ba006b8e52e3fbf9cd85f90985ac54c + - pypi: https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/b6/5f922792be93b82ec6b5f270bbb1ef031fd0622847070bbcf9da816502cc/multiprocess-0.70.18-py312-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/0f/c76bf3dba22c73c38e9b1113b017cf163f7696f50e003404ec5ecdb1e8a6/nh3-0.3.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e3/94/1843518e420fa3ed6919835845df698c7e27e183cb997394e4a670973a65/omegaconf-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/12/60/17ed502ab8258e36b6caf478974994f15691c73e3c46abf82a2ec63e9eda/omnidata_tools-0.0.23-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/5c/3f9700ba277d52c121dd2cebc8a672fb60b53e888972fc6682b6692a766c/onnxruntime_gpu-1.22.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: 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 + - pypi: git+https://github.com/EasternJournalist/pipeline.git?rev=866f059d2a05cde05e4a52211ec5051fd5f276d6#866f059d2a05cde05e4a52211ec5051fd5f276d6 + - pypi: https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4f/6a/f435b9d12f34e03548949a51c7475775feda4c3e5b5373e180d70fd7fbe4/plum_dispatch-2.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1d/2a/3c5f05a4af06649547027d288747f68525755de692a26a7720dced3652c0/protobuf-6.33.1-cp39-abi3-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/c5/4f/0e22aaa246f96d6ac87fe5ebb9c5a693fbe8877f537a1022527c47ca43c5/psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/13/95/aec81f781c75cd10554dc17a25849c720d54feafb6f7847690478dcf5ef8/pyarrow-22.0.0-cp312-cp312-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/9f/5c/fe9f95abd5eaedfa69f31e450f7e2768bef121dbdf25bcddee2cd3087a16/pyfiglet-1.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ee/c1/0208599382b259251d33b5cbb835b15ce480621791b7a1f86dd09e9a69c5/pyserde-0.23.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/17/e4/32ed2f33c1b634f7c2895369222f4f8cb345044f4642bbff718e7dd1e0b7/pytorch_lightning-2.5.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/bd/9ce9f629fcb714ffc2c3faf62b6766ecb7a585e1e885eb699bcf130a5209/regex-2025.11.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9c/93/5275526c1f925407f1fd69fdf6cd4fa7a0957d863624adf4417258d023ee/rerun_sdk-0.27.0-cp310-abi3-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e8/e8/d571290fa7c1a2beed674d487919efd1fca34d04c3666fdce43eb8bfabed/roma-1.5.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/79/2e/415119c9ab3e62249e18c2b082c07aff907a273741b3f8160414b0e9193c/scipy-1.16.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8e/e1/202a31727b0d096a04380f78e809074d7a1d0a22d9d5a39fea1d2353fd02/shtab-1.8.0-py3-none-any.whl + - pypi: git+https://github.com/pablovela5620/simplecv.git?branch=main#c9e2fa5e1477708431559898da3abb02ec261be7 + - pypi: https://files.pythonhosted.org/packages/d2/0a/97222afd54e2fdb128e1723671a4c2151242cd1464b20adf20797f331495/spaces-0.43.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f9/d5/141f53d7c1eb2a80e6d3e9a390228c3222c27705cbe7f048d3623053f3ca/termcolor-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/14/fc04d491527b774ec7479897f5861959209de1480e4c4cd32ed098ff8bea/timm-1.0.22-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/02/21/aa0f434434c48490f91b65962b1ce863fdcce63febc166ca9fe9d706c2b6/torchmetrics-1.8.2-py3-none-any.whl + - pypi: git+https://github.com/huggingface/transformers.git?rev=d08b98b965176ea9cf8c8e8b24995c955b7e2ec9#d08b98b965176ea9cf8c8e8b24995c955b7e2ec9 + - pypi: https://files.pythonhosted.org/packages/ec/81/951a108396aa850a096be95cd9ccd38ef9f33ae35b40b512be975e2c994d/trimesh-4.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bb/bc/943b35b1a139a602a730e56592a3639bf796a87c91eebcd40cc51043ee5b/tyro-0.9.35-py3-none-any.whl + - pypi: git+https://github.com/EasternJournalist/utils3d.git?rev=3fab839f0be9931dac7c8488eb0e1600c236e183#3fab839f0be9931dac7c8488eb0e1600c236e183 + - pypi: https://files.pythonhosted.org/packages/8d/96/04e7b441807b26b794da5b11e59ed7f83b2cf8af202bd7eba8ad2fa6046e/wadler_lindig-0.1.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/4f/fe9a4d472aa867878ce3bb7efb16654c5d63672b86dc0e6e953a67018433/yacs-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/37/81/6acd6601f61e31cfb8729d3da6d5df966f80f374b78eff83760714487338/yapf-0.43.0-py3-none-any.whl + - pypi: ./ + dev: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiofiles-24.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.13.2-py312h27b7581_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/assimp-5.4.3-hecf2907_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.2-h39aace5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/audioop-lts-0.2.2-py312h5253ce2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/av-16.0.1-py312h46547aa_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/beartype-0.22.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils-2.45-default_h4852527_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.45-default_hfdba357_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.45-default_h4852527_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.11.0-h4d9bdce_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cli11-2.6.0-h54a6638_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-gcc-specs-14.3.0-he8ccf15_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312hd9148b4_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.12-py312hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cccl_linux-64-12.9.27-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-compiler-12.9.1-hbad6d8a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-crt-12.9.86-h69a702a_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-crt-dev_linux-64-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-crt-tools-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-dev-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-dev_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-static-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cuobjdump-12.9.82-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cupti-12.9.79-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cuxxfilt-12.9.82-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-driver-dev-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-driver-dev_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-12.9.86-hcdd1206_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvcc-dev_linux-64-12.9.86-he91c749_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-impl-12.9.86-h85509e4_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-tools-12.9.86-he02047a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc_linux-64-12.9.86-he0b4e1d_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvdisasm-12.9.88-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvml-dev-12.9.79-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvprune-12.9.82-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-12.9.86-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-dev-12.9.86-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvtx-12.9.79-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvtx-dev-12.9.79-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-64-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-impl-12.9.86-h4bc722e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-tools-12.9.86-h4bc722e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-profiler-api-12.9.79-h7938cbb_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-12.9-h4f385c5_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cudnn-9.10.2.21-hbcb9cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cusparselt-0.8.1.1-h58dd1b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.11.0-hfcd1e18_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dash-3.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.8.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.3.0-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/embree-4.4.0-ha751380_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.122.0-h6dd4f06_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.16-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-core-0.122.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.0.0-gpl_hbbdf940_906.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ffmpy-0.3.0-pyhb6f538c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/flask-3.1.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-11.2.0-h07f6e7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.60.1-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.7.0-py312h447239a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.10.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc-14.3.0-h0dff253_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-14.3.0-h0c81b82_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-14.3.0-h298d278_14.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.4-h2b0a6b4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gl2ps-1.4.2-hae5d5c5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glew-2.2.0-h3abd4de_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glfw-3.4-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glslang-16.0.0-hfd11570_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.2.1-py312hcaba1f9_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gradio-5.49.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gradio-client-1.13.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/groovy-python-0.1.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-14.3.0-he448592_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-14.3.0-h2185e75_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-14.3.0-hc876b51_14.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hf-xet-1.2.1-py310hb823017_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.7.1-py312h4c3975b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-1.1.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/intel-gmmlib-22.8.2-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/intel-media-driver-25.3.4-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/itsdangerous-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jsoncpp-1.9.6-hf42df4d_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py312h0a2e395_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/level-zero-1.26.0-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libass-0.17.4-h96ad9f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.3.0-h6395336_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-2_h5875eb1_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hed09d94_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.77-h3ff7636_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-2_hfef963f_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.6-default_h99862b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.6-default_h746c552_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-12.9.1.4-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-dev-12.9.1.4-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudnn-9.10.2.21-hf7e9902_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudnn-dev-9.10.2.21-h58dd1b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudss-0.7.1.4-h58dd1b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudss-dev-0.7.1.4-hee47b17_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufft-11.4.1.4-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufft-dev-11.4.1.4-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.14.1.1-hbc026e6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-dev-1.14.1.1-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurand-10.3.10.19-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurand-dev-10.3.10.19-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusolver-11.7.5.82-h676940d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusolver-dev-11.7.5.82-h676940d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusparse-12.5.10.65-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusparse-dev-12.5.10.65-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libde265-1.0.15-h00ab1b0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_12.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-14.3.0-hf649bbc_112.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.25.1-h3f43e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.2-h32235b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglu-9.0.3-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libheif-1.19.7-gpl_hc18d805_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-2_h5e43f62_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-2_hdba1596_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.6-hf7376ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzf-3.6-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmagma-2.9.0-ha7672b3_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnl-3.11.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnvjitlink-12.9.86-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnvjpeg-12.4.0.76-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnvptxcompiler-dev-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libnvptxcompiler-dev_linux-64-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-hd0c01bc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-batch-plugin-2025.2.0-hed573e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-plugin-2025.2.0-hed573e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-hetero-plugin-2025.2.0-hd41364c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-cpu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-gpu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-npu-plugin-2025.2.0-hb617929_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-ir-frontend-2025.2.0-hd41364c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-onnx-frontend-2025.2.0-h1862bb8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-paddle-frontend-2025.2.0-h1862bb8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-pytorch-frontend-2025.2.0-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-frontend-2025.2.0-h0767aad_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-lite-frontend-2025.2.0-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopus-1.5.2-hd0c01bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.51-h421ea60_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.1-h5c52fec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.60.0-h61e6d4b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-14.3.0-h8f1669f_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.0-hee844dc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_12.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-14.3.0-h9f08a49_112.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.10-hd0affe5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtheora-1.1.1-h4ab18f5_1006.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.8.0-cuda129_mkl_hf53477d_302.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-257.10-hd0affe5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liburing-2.12-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libusb-1.0.29-h73b1eb8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.22.0-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h54a6638_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpl-2.15.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpx-1.14.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.0-hca5e8e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-21.1.6-h4922eb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py312he3d6523_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2025.3.0-h0e700b2_462.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.3.1-h24ddda3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.1-h90cbb55_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.9-hc50e24c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py312hd9148b4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.6.3-py312h178313f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.12.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nccl-2.28.9.1-h4d09622_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/open3d-0.19.0-py312hf1b548d_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openh264-2.6.0-hc22cd8d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.18.0-py312hd9148b4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orjson-3.11.4-py312h868fb18_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py312hf79963d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hadf4263_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py312h7b42cdd_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.3.1-py312h178313f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pugixml-1.15-h3f63f65_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-h9a6aba3_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-2.13.6-pyhc790b64_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-4-hd8ed1ab_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-2.13.6-pyh217bc35_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.10-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.33.2-py312h680f630_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydub-0.25.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyrefly-0.42.3-h2b88eb6_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.12-hd63d673_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.12-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.8.0-cuda129_mkl_py312_had1c889_302.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-gpu-2.8.0-cuda129_mkl_h43a4b0b_302.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.3-h5c1c036_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.7.1-h8fae777_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-60.0-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/retrying-1.4.2-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.14.6-h813ae00_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/safehttpx-0.1.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2-2.32.56-h54a6638_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sdl3-3.2.26-h68140b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/semantic_version-2.10.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shaderc-2025.4-h3e344bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sleef-3.9.0-ha0421bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/spirv-tools-2025.4-hb700be7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.51.0-heff268d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.50.0-pyhfdc7a7d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/svt-av1-3.1.2-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_105.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.3.0-h8d10470_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tinyobjloader-1.0.7-h59595ed_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomlkit-0.12.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/torchvision-0.24.0-cuda129_py312_h9f56bbf_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/torchvision-extra-decoders-0.0.2-py312hd347cc9_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/triton-3.4.0-cuda129py312h811769c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.20.0-pyhefaf540_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.20.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.20.0-h4daf872_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py312h4c3975b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/utfcpp-4.0.8-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.38.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.38.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.22.1-py312h4c3975b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.1-py312h5d43ed7_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.1.1-py312h0ccc70a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wayland-protocols-1.46-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-15.0.1-py312h5253ce2_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wslink-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/x264-1!164.3095-h166bdaf_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/x265-3.5-h924138e_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxscrnsaver-1.2.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.22.0-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + - pypi: https://files.pythonhosted.org/packages/9f/d2/c581486aa6c4fbd7394c23c47b83fa1a919d34194e16944241daf9e762dd/accelerate-1.12.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/00/b08f23b7d7e1e14ce01419a467b583edbb93c6cdb8654e54a9cc579cd61f/addict-2.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/38/7859ff46355f76f8d19459005ca000b6e7012f2f1ca597746cbcd1fbfe5e/antlr4-python3-runtime-4.9.3.tar.gz + - pypi: https://files.pythonhosted.org/packages/dc/95/1ec57df6c52152fb4c776da5fbb2896f19af52de3776899d91e7140e7654/aria2p-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/35/bf/9cad857b630c840738003eb24c1adb63490a1024ec40a9dcc3a753300c38/asciimatics-1.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fa/93/e8c04e80e82391a6e51f218ca49720f64236bc824e92152a2633b74cf7ab/braceexpand-0.1.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/6a/1766f8c163951a3c9aeb30a4e6f5de9b2eed8389e3906c4cf30fcb475be6/casefy-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/2a/2e/38d9824f8c6bb048c5ba21c6d4da54c29c162a46b58b3ef907a360a76d3e/diffusers-0.35.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/11/a8/c6a4b901d17399c77cd81fb001ce8961e9f5e04d3daf27e8925cb012e163/docutils-0.22.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/27/f4/30077733807ca57cc504a6faadcdd067714a7891dad66231b0c1ab373551/fastcore-1.8.16-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ee/1b/00a78aa2e8fbd63f9af08c9c19e6deb3d5d66b4dda677a0f61654680ee89/flatbuffers-25.9.23-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ed/1f/95cb961341bf33e534a472672ef4916c90046dc3c57b1442f5586dda72f1/geffnet-1.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/ba/9ccb80650e5bd61e739f16f33aec3bb290a80f314631be8f7dc0a2b22b5f/glcontext-3.0.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/de/5a/5ebd2acb8f330f4886265c3c13243338075557ccd66a9bbe0b163596797e/gradio_rerun-0.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/d8/f87ea6f42456254b48915970ed98e993110521e9263472840174d32c880d/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/5f/f877d2cfcad41c0db98b120872f67b805fde5e6d407e584e9edfc9dec35c/icecream-2.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b4/09/726f168acad366b11e420df31bf1c702a54d373a83f968d94141a8c3fde0/jaraco_functools-4.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/db/7e/da7b57a1f3af7303a0f3c8594d820fc0d3a9bbe3810a357eb21eb166e76b/jaxtyping-0.2.38-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/de/73/3d757cb3fc16f0f9794dd289bcd0c4a031d9cf54d8137d6b984b2d02edf3/lightning_utilities-0.15.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/2d/2e321a29535b28795aefc1f6658d5c474097cb93ab1dead304a1e41dd532/lovely_numpy-0.2.18-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/bd/5d468c171f201d6169bec848d1116e95736854eec72307299bc68939fe45/mmcv_lite-2.2.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/98/8e/f98332248aad102511bea4ae19c0ddacd2f0a994f3ca4c82b7a369e0af8b/mmengine-0.10.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d7/96/bcb5141eae24474d80b8157b0c3055d25fa75f9804d4abb4a514695bbba9/moderngl-5.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: git+https://github.com/microsoft/MoGe.git#07444410f1e33f402353b99d6ccd26bd31e469e8 + - pypi: git+https://github.com/pablovela5620/monoprior.git#f887b77a3ba006b8e52e3fbf9cd85f90985ac54c + - pypi: https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/b6/5f922792be93b82ec6b5f270bbb1ef031fd0622847070bbcf9da816502cc/multiprocess-0.70.18-py312-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/0f/c76bf3dba22c73c38e9b1113b017cf163f7696f50e003404ec5ecdb1e8a6/nh3-0.3.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e3/94/1843518e420fa3ed6919835845df698c7e27e183cb997394e4a670973a65/omegaconf-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/12/60/17ed502ab8258e36b6caf478974994f15691c73e3c46abf82a2ec63e9eda/omnidata_tools-0.0.23-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/5c/3f9700ba277d52c121dd2cebc8a672fb60b53e888972fc6682b6692a766c/onnxruntime_gpu-1.22.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: 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 + - pypi: git+https://github.com/EasternJournalist/pipeline.git?rev=866f059d2a05cde05e4a52211ec5051fd5f276d6#866f059d2a05cde05e4a52211ec5051fd5f276d6 + - pypi: https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4f/6a/f435b9d12f34e03548949a51c7475775feda4c3e5b5373e180d70fd7fbe4/plum_dispatch-2.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1d/2a/3c5f05a4af06649547027d288747f68525755de692a26a7720dced3652c0/protobuf-6.33.1-cp39-abi3-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/c5/4f/0e22aaa246f96d6ac87fe5ebb9c5a693fbe8877f537a1022527c47ca43c5/psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/13/95/aec81f781c75cd10554dc17a25849c720d54feafb6f7847690478dcf5ef8/pyarrow-22.0.0-cp312-cp312-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/9f/5c/fe9f95abd5eaedfa69f31e450f7e2768bef121dbdf25bcddee2cd3087a16/pyfiglet-1.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ee/c1/0208599382b259251d33b5cbb835b15ce480621791b7a1f86dd09e9a69c5/pyserde-0.23.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/17/e4/32ed2f33c1b634f7c2895369222f4f8cb345044f4642bbff718e7dd1e0b7/pytorch_lightning-2.5.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/bd/9ce9f629fcb714ffc2c3faf62b6766ecb7a585e1e885eb699bcf130a5209/regex-2025.11.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9c/93/5275526c1f925407f1fd69fdf6cd4fa7a0957d863624adf4417258d023ee/rerun_sdk-0.27.0-cp310-abi3-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e8/e8/d571290fa7c1a2beed674d487919efd1fca34d04c3666fdce43eb8bfabed/roma-1.5.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/79/2e/415119c9ab3e62249e18c2b082c07aff907a273741b3f8160414b0e9193c/scipy-1.16.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8e/e1/202a31727b0d096a04380f78e809074d7a1d0a22d9d5a39fea1d2353fd02/shtab-1.8.0-py3-none-any.whl + - pypi: git+https://github.com/pablovela5620/simplecv.git?branch=main#c9e2fa5e1477708431559898da3abb02ec261be7 + - pypi: https://files.pythonhosted.org/packages/d2/0a/97222afd54e2fdb128e1723671a4c2151242cd1464b20adf20797f331495/spaces-0.43.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f9/d5/141f53d7c1eb2a80e6d3e9a390228c3222c27705cbe7f048d3623053f3ca/termcolor-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/14/fc04d491527b774ec7479897f5861959209de1480e4c4cd32ed098ff8bea/timm-1.0.22-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/02/21/aa0f434434c48490f91b65962b1ce863fdcce63febc166ca9fe9d706c2b6/torchmetrics-1.8.2-py3-none-any.whl + - pypi: git+https://github.com/huggingface/transformers.git?rev=d08b98b965176ea9cf8c8e8b24995c955b7e2ec9#d08b98b965176ea9cf8c8e8b24995c955b7e2ec9 + - pypi: https://files.pythonhosted.org/packages/ec/81/951a108396aa850a096be95cd9ccd38ef9f33ae35b40b512be975e2c994d/trimesh-4.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/13/3ff0781445d7c12730befce0fddbbc7a76e56eb0e7029446f2853238360a/types_tqdm-4.67.0.20250809-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bb/bc/943b35b1a139a602a730e56592a3639bf796a87c91eebcd40cc51043ee5b/tyro-0.9.35-py3-none-any.whl + - pypi: git+https://github.com/EasternJournalist/utils3d.git?rev=3fab839f0be9931dac7c8488eb0e1600c236e183#3fab839f0be9931dac7c8488eb0e1600c236e183 + - pypi: https://files.pythonhosted.org/packages/8d/96/04e7b441807b26b794da5b11e59ed7f83b2cf8af202bd7eba8ad2fa6046e/wadler_lindig-0.1.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/4f/fe9a4d472aa867878ce3bb7efb16654c5d63672b86dc0e6e953a67018433/yacs-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/37/81/6acd6601f61e31cfb8729d3da6d5df966f80f374b78eff83760714487338/yapf-0.43.0-py3-none-any.whl + - pypi: ./ +packages: +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-7_kmp_llvm.conda + build_number: 7 + sha256: c0cddb66070dd6355311f7667ce2acccf70d1013edaa6e97f22859502fefdb22 + md5: 887b70e1d607fba7957aa02f9ee0d939 + depends: + - llvm-openmp >=9.0.1 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 8244 + timestamp: 1764092331208 +- conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + sha256: a3967b937b9abf0f2a99f3173fa4630293979bd1644709d89580e7c62a544661 + md5: aaa2a381ccc56eac91d63b6c1240312f + depends: + - cpython + - python-gil + license: MIT + license_family: MIT + purls: [] + size: 8191 + timestamp: 1744137672556 +- pypi: https://files.pythonhosted.org/packages/9f/d2/c581486aa6c4fbd7394c23c47b83fa1a919d34194e16944241daf9e762dd/accelerate-1.12.0-py3-none-any.whl + name: accelerate + version: 1.12.0 + sha256: 3e2091cd341423207e2f084a6654b1efcd250dc326f2a37d6dde446e07cabb11 + requires_dist: + - numpy>=1.17 + - packaging>=20.0 + - psutil + - pyyaml + - torch>=2.0.0 + - huggingface-hub>=0.21.0 + - safetensors>=0.4.3 + - ruff==0.13.1 ; extra == 'quality' + - pytest>=7.2.0 ; extra == 'test-prod' + - pytest-xdist ; extra == 'test-prod' + - pytest-subtests ; extra == 'test-prod' + - parameterized ; extra == 'test-prod' + - pytest-order ; extra == 'test-prod' + - datasets ; extra == 'test-dev' + - diffusers ; extra == 'test-dev' + - evaluate ; extra == 'test-dev' + - torchdata>=0.8.0 ; extra == 'test-dev' + - torchpippy>=0.2.0 ; extra == 'test-dev' + - transformers ; extra == 'test-dev' + - scipy ; extra == 'test-dev' + - scikit-learn ; extra == 'test-dev' + - tqdm ; extra == 'test-dev' + - bitsandbytes ; extra == 'test-dev' + - timm ; extra == 'test-dev' + - pytest>=7.2.0 ; extra == 'testing' + - pytest-xdist ; extra == 'testing' + - pytest-subtests ; extra == 'testing' + - parameterized ; extra == 'testing' + - pytest-order ; extra == 'testing' + - datasets ; extra == 'testing' + - diffusers ; extra == 'testing' + - evaluate ; extra == 'testing' + - torchdata>=0.8.0 ; extra == 'testing' + - torchpippy>=0.2.0 ; extra == 'testing' + - transformers ; extra == 'testing' + - scipy ; extra == 'testing' + - scikit-learn ; extra == 'testing' + - tqdm ; extra == 'testing' + - bitsandbytes ; extra == 'testing' + - timm ; extra == 'testing' + - deepspeed ; extra == 'deepspeed' + - rich ; extra == 'rich' + - torchao ; extra == 'test-fp8' + - wandb ; extra == 'test-trackers' + - comet-ml ; extra == 'test-trackers' + - tensorboard ; extra == 'test-trackers' + - dvclive ; extra == 'test-trackers' + - matplotlib ; extra == 'test-trackers' + - swanlab[dashboard] ; extra == 'test-trackers' + - trackio ; extra == 'test-trackers' + - ruff==0.13.1 ; extra == 'dev' + - pytest>=7.2.0 ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - pytest-subtests ; extra == 'dev' + - parameterized ; extra == 'dev' + - pytest-order ; extra == 'dev' + - datasets ; extra == 'dev' + - diffusers ; extra == 'dev' + - evaluate ; extra == 'dev' + - torchdata>=0.8.0 ; extra == 'dev' + - torchpippy>=0.2.0 ; extra == 'dev' + - transformers ; extra == 'dev' + - scipy ; extra == 'dev' + - scikit-learn ; extra == 'dev' + - tqdm ; extra == 'dev' + - bitsandbytes ; extra == 'dev' + - timm ; extra == 'dev' + - rich ; extra == 'dev' + - sagemaker ; extra == 'sagemaker' + requires_python: '>=3.10.0' +- pypi: https://files.pythonhosted.org/packages/6a/00/b08f23b7d7e1e14ce01419a467b583edbb93c6cdb8654e54a9cc579cd61f/addict-2.4.0-py3-none-any.whl + name: addict + version: 2.4.0 + sha256: 249bb56bbfd3cdc2a004ea0ff4c2b6ddc84d53bc2194761636eb314d5cfa5dfc +- conda: https://conda.anaconda.org/conda-forge/noarch/aiofiles-24.1.0-pyhd8ed1ab_1.conda + sha256: 8e18809f00b0bfe504bc6180b80d844016690925ddf0e61272111eec079774c3 + md5: 7e8045a75e921648c082ba7cd7edd114 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/aiofiles?source=hash-mapping + size: 19712 + timestamp: 1733829125632 +- conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda + sha256: 7842ddc678e77868ba7b92a726b437575b23aaec293bca0d40826f1026d90e27 + md5: 18fd895e0e775622906cdabfc3cf0fb4 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/aiohappyeyeballs?source=hash-mapping + size: 19750 + timestamp: 1741775303303 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.13.2-py312h27b7581_0.conda + sha256: baf2bbf52aeecdbfe6e03a373b2664169cbdc37a92a2ac68bc7ef45353f65d61 + md5: ad84ca57d502eead2df0233090261dfb + depends: + - __glibc >=2.17,<3.0.a0 + - aiohappyeyeballs >=2.5.0 + - aiosignal >=1.4.0 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - libgcc >=14 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yarl >=1.17.0,<2.0 + license: MIT AND Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/aiohttp?source=hash-mapping + size: 1014925 + timestamp: 1761727721839 +- conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda + sha256: 8dc149a6828d19bf104ea96382a9d04dae185d4a03cc6beb1bc7b84c428e3ca2 + md5: 421a865222cd0c9d83ff08bc78bf3a61 + depends: + - frozenlist >=1.1.0 + - python >=3.9 + - typing_extensions >=4.2 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/aiosignal?source=hash-mapping + size: 13688 + timestamp: 1751626573984 +- conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda + sha256: b9214bc17e89bf2b691fad50d952b7f029f6148f4ac4fe7c60c08f093efdf745 + md5: 76df83c2a9035c54df5d04ff81bcc02d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + license_family: GPL + purls: [] + size: 566531 + timestamp: 1744668655747 +- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda + sha256: cc9fbc50d4ee7ee04e49ee119243e6f1765750f0fd0b4d270d5ef35461b643b1 + md5: 52be5139047efadaeeb19c6a5103f92a + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/annotated-doc?source=hash-mapping + size: 14222 + timestamp: 1762868213144 +- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 + md5: 2934f256a8acfe48f6ebb4fce6cde29c + depends: + - python >=3.9 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/annotated-types?source=hash-mapping + size: 18074 + timestamp: 1733247158254 +- pypi: https://files.pythonhosted.org/packages/3e/38/7859ff46355f76f8d19459005ca000b6e7012f2f1ca597746cbcd1fbfe5e/antlr4-python3-runtime-4.9.3.tar.gz + name: antlr4-python3-runtime + version: 4.9.3 + sha256: f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b + requires_dist: + - typing ; python_full_version < '3.5' +- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda + sha256: 7378b5b9d81662d73a906fabfc2fb81daddffe8dc0680ed9cda7a9562af894b0 + md5: 814472b61da9792fae28156cb9ee54f5 + depends: + - exceptiongroup >=1.0.2 + - idna >=2.8 + - python >=3.10 + - sniffio >=1.1 + - typing_extensions >=4.5 + - python + constrains: + - trio >=0.31.0 + - uvloop >=0.21 + license: MIT + license_family: MIT + purls: + - pkg:pypi/anyio?source=hash-mapping + size: 138159 + timestamp: 1758634638734 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + sha256: b08ef033817b5f9f76ce62dfcac7694e7b6b4006420372de22494503decac855 + md5: 346722a0be40f6edc53f12640d301338 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2706396 + timestamp: 1718551242397 +- pypi: https://files.pythonhosted.org/packages/dc/95/1ec57df6c52152fb4c776da5fbb2896f19af52de3776899d91e7140e7654/aria2p-0.12.1-py3-none-any.whl + name: aria2p + version: 0.12.1 + sha256: 96d31c1edd3c9e0edf725b7adeb0b9fb9b6ae102f3c228040d773b43837913a2 + requires_dist: + - loguru>=0.5 + - platformdirs>=4.2 + - requests>=2.19 + - tomli>=2.0 ; python_full_version < '3.11' + - websocket-client>=0.58 + - asciimatics>=1.13 ; extra == 'tui' + - pyperclip>=1.8 ; extra == 'tui' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/35/bf/9cad857b630c840738003eb24c1adb63490a1024ec40a9dcc3a753300c38/asciimatics-1.15.0-py3-none-any.whl + name: asciimatics + version: 1.15.0 + sha256: 0fe068a6bed522929bd04bb5b8a2fb6ebf0aef1b7a9b3843cf71030a34bc38d5 + requires_dist: + - pyfiglet>=0.7.2 + - pillow>=2.7.0 + - wcwidth + - pywin32 ; sys_platform == 'win32' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/assimp-5.4.3-hecf2907_1.conda + sha256: c49e992c1a2978f5a8cdf3cdf9aac0c0a444bbddb7316dbfbf16f5a94ff71c10 + md5: 649115e82c2a9620fcf0d3a846ee365f + depends: + - __glibc >=2.17,<3.0.a0 + - libboost >=1.88.0,<1.89.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - zlib + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3645199 + timestamp: 1753274588181 +- pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl + name: asttokens + version: 3.0.1 + sha256: 15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a + requires_dist: + - astroid>=2,<5 ; extra == 'astroid' + - astroid>=2,<5 ; extra == 'test' + - pytest<9.0 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-xdist ; extra == 'test' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.2-h39aace5_0.conda + sha256: a9c114cbfeda42a226e2db1809a538929d2f118ef855372293bd188f71711c48 + md5: 791365c5f65975051e4e017b5da3abf5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 68072 + timestamp: 1756738968573 +- conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda + sha256: f6c3c19fa599a1a856a88db166c318b148cac3ee4851a9905ed8a04eeec79f45 + md5: c7944d55af26b6d2d7629e27e9a972c1 + depends: + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/attrs?source=hash-mapping + size: 60101 + timestamp: 1759762331492 +- conda: https://conda.anaconda.org/conda-forge/linux-64/audioop-lts-0.2.2-py312h5253ce2_1.conda + sha256: 5749ec7ed6e76f8478c160867681af03fd750b47e146bdeb7d391df28db7215e + md5: 374e17e43a93aeaa9439bae64a0c6645 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.12.* *_cp312 + license: EPL-2.0 + purls: [] + size: 12470 + timestamp: 1757263511335 +- conda: https://conda.anaconda.org/conda-forge/linux-64/av-16.0.1-py312h46547aa_0.conda + sha256: dd397de532acaeb36f4052e8cff5eeea8553b7ec7206d58ea4401f09d5bd5e9d + md5: 0e02a819136baa8414d4dca056ffb5eb + depends: + - __glibc >=2.17,<3.0.a0 + - ffmpeg >=8.0.0,<9.0a0 + - libgcc >=14 + - numpy >=1.22 + - numpy >=1.23,<3 + - pillow + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/av?source=hash-mapping + size: 1109542 + timestamp: 1760362166550 +- conda: https://conda.anaconda.org/conda-forge/noarch/beartype-0.22.6-pyhd8ed1ab_0.conda + sha256: 519ecec8eb3af4c460cf2580c13310198ad058187c3c1398b0b604fecffaded4 + md5: d1c3a850151c6c59c88a9449cada98ad + depends: + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/beartype?source=hash-mapping + size: 1052810 + timestamp: 1763616683515 +- conda: https://conda.anaconda.org/conda-forge/linux-64/binutils-2.45-default_h4852527_104.conda + sha256: 1625ea421e0f44dbdde01e3e7d2c4958bea6c55731e6ac6954ba912d339982c2 + md5: d351e4894d6c4d9d8775bf169a97089d + depends: + - binutils_impl_linux-64 >=2.45,<2.46.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 35316 + timestamp: 1764007880473 +- conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.45-default_hfdba357_104.conda + sha256: 054a77ccab631071a803737ea8e5d04b5b18e57db5b0826a04495bd3fdf39a7c + md5: a7a67bf132a4a2dea92a7cb498cdc5b1 + depends: + - ld_impl_linux-64 2.45 default_hbd61a6d_104 + - sysroot_linux-64 + - zstd >=1.5.7,<1.6.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 3747046 + timestamp: 1764007847963 +- conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.45-default_h4852527_104.conda + sha256: ed23fee4db69ad82320cca400fc77404c3874cd866606651a20bf743acd1a9b1 + md5: e30e71d685e23cc1e5ac1c1990ba1f81 + depends: + - binutils_impl_linux-64 2.45 default_hfdba357_104 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 36180 + timestamp: 1764007883258 +- conda: https://conda.anaconda.org/conda-forge/noarch/blinker-1.9.0-pyhff2d567_0.conda + sha256: f7efd22b5c15b400ed84a996d777b6327e5c402e79e3c534a7e086236f1eb2dc + md5: 42834439227a4551b939beeeb8a4b085 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/blinker?source=hash-mapping + size: 13934 + timestamp: 1731096548765 +- conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + sha256: e7af5d1183b06a206192ff440e08db1c4e8b2ca1f8376ee45fb2f3a85d4ee45d + md5: 2c2fae981fd2afd00812c92ac47d023d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 48427 + timestamp: 1733513201413 +- pypi: https://files.pythonhosted.org/packages/fa/93/e8c04e80e82391a6e51f218ca49720f64236bc824e92152a2633b74cf7ab/braceexpand-0.1.7-py2.py3-none-any.whl + name: braceexpand + version: 0.1.7 + sha256: 91332d53de7828103dcae5773fb43bc34950b0c8160e35e0f44c4427a3b85014 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + sha256: e511644d691f05eb12ebe1e971fd6dc3ae55a4df5c253b4e1788b789bdf2dfa6 + md5: 8ccf913aaba749a5496c17629d859ed1 + depends: + - __glibc >=2.17,<3.0.a0 + - brotli-bin 1.2.0 hb03c661_1 + - libbrotlidec 1.2.0 hb03c661_1 + - libbrotlienc 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 20103 + timestamp: 1764017231353 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + sha256: 64b137f30b83b1dd61db6c946ae7511657eead59fdf74e84ef0ded219605aa94 + md5: af39b9a8711d4a8d437b52c1d78eb6a1 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlidec 1.2.0 hb03c661_1 + - libbrotlienc 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 21021 + timestamp: 1764017221344 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda + sha256: 49df13a1bb5e388ca0e4e87022260f9501ed4192656d23dc9d9a1b4bf3787918 + md5: 64088dffd7413a2dd557ce837b4cbbdb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.2.0 hb03c661_1 + license: MIT + license_family: MIT + purls: + - pkg:pypi/brotli?source=compressed-mapping + size: 368300 + timestamp: 1764017300621 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda + sha256: c30daba32ddebbb7ded490f0e371eae90f51e72db620554089103b4a6934b0d5 + md5: 51a19bba1b8ebfb60df25cde030b7ebc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 260341 + timestamp: 1757437258798 +- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda + sha256: f8003bef369f57396593ccd03d08a8e21966157269426f71e943f96e4b579aeb + md5: f7f0d6cc2dc986d42ac2689ec88192be + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 206884 + timestamp: 1744127994291 +- conda: https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.11.0-h4d9bdce_0.conda + sha256: 8e7a40f16400d7839c82581410aa05c1f8324a693c9d50079f8c50dc9fb241f0 + md5: abd85120de1187b0d1ec305c2173c71b + depends: + - binutils + - gcc + - gcc_linux-64 14.* + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6693 + timestamp: 1753098721814 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda + sha256: b986ba796d42c9d3265602bc038f6f5264095702dd546c14bc684e60c385e773 + md5: f0991f0f84902f6b6009b4d2350a83aa + depends: + - __unix + license: ISC + purls: [] + size: 152432 + timestamp: 1762967197890 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + sha256: 3bd6a391ad60e471de76c0e9db34986c4b5058587fbf2efa5a7f54645e28c2c7 + md5: 09262e66b19567aff4f592fb53b28760 + depends: + - __glibc >=2.17,<3.0.a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - icu >=75.1,<76.0a0 + - libexpat >=2.6.4,<3.0a0 + - libgcc >=13 + - libglib >=2.82.2,<3.0a0 + - libpng >=1.6.47,<1.7.0a0 + - libstdcxx >=13 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pixman >=0.44.2,<1.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.5,<2.0a0 + - xorg-libx11 >=1.8.11,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: LGPL-2.1-only or MPL-1.1 + purls: [] + size: 978114 + timestamp: 1741554591855 +- pypi: https://files.pythonhosted.org/packages/f1/6a/1766f8c163951a3c9aeb30a4e6f5de9b2eed8389e3906c4cf30fcb475be6/casefy-1.1.0-py3-none-any.whl + name: casefy + version: 1.1.0 + sha256: a3dfcb14d85902d90702db1e9835760237f6a73ec0ae3b7e991ad767513a3cbc + requires_python: '>=3.6' +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + sha256: 083a2bdad892ccf02b352ecab38ee86c3e610ba9a4b11b073ea769d55a115d32 + md5: 96a02a5c1a65470a7e4eedb644c872fd + depends: + - python >=3.10 + license: ISC + purls: + - pkg:pypi/certifi?source=compressed-mapping + size: 157131 + timestamp: 1762976260320 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda + sha256: 7dafe8173d5f94e46cf9cd597cc8ff476a8357fbbd4433a8b5697b2864845d9c + md5: 648ee28dcd4e07a1940a17da62eccd40 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 295716 + timestamp: 1761202958833 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + sha256: b32f8362e885f1b8417bac2b3da4db7323faa12d5db62b7fd6691c02d60d6f59 + md5: a22d1fd9bf98827e280a02875d9a007a + depends: + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/charset-normalizer?source=hash-mapping + size: 50965 + timestamp: 1760437331772 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cli11-2.6.0-h54a6638_0.conda + sha256: 324097cd9dde3a4623b0275655ea34641481daa5c1274f9ab994e8a2e6aa26e6 + md5: ddf9fed4661bace13f33f08fe38a5f45 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 98102 + timestamp: 1760975548381 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh707e725_0.conda + sha256: 970b12fb186c3451eee9dd0f10235aeb75fb570b0e9dc83250673c2f0b196265 + md5: 9ba00b39e03a0afb2b1cc0767d4c6175 + depends: + - __unix + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/click?source=compressed-mapping + size: 92604 + timestamp: 1763248639281 +- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/colorama?source=hash-mapping + size: 27011 + timestamp: 1733218222191 +- pypi: https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl + name: coloredlogs + version: 15.0.1 + sha256: 612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934 + requires_dist: + - humanfriendly>=9.1 + - capturer>=2.4 ; extra == 'cron' + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*' +- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-gcc-specs-14.3.0-he8ccf15_12.conda + sha256: 05b200570b3ffc79b8eb72db852c125a0254c2420791a7854b5248c4b2403e7a + md5: 0b40eda76b7401266a6857501016fd5f + depends: + - gcc_impl_linux-64 >=14.3.0,<14.3.1.0a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 33359 + timestamp: 1764036554889 +- conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312hd9148b4_3.conda + sha256: e173ea96fb135b233c7f57c35c0d07f7adc50ebacf814550f3daf1c7ba2ed51e + md5: 86cf7a7d861b79d38e3f0e5097e4965b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - numpy >=1.25 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/contourpy?source=compressed-mapping + size: 295243 + timestamp: 1762525427240 +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.12-py312hd8ed1ab_1.conda + noarch: generic + sha256: b88c76a6d6b45378552ccfd9e88b2a073161fe83fd1294c8fa103ffd32f7934a + md5: 99d689ccc1a360639eec979fd7805be9 + depends: + - python >=3.12,<3.13.0a0 + - python_abi * *_cp312 + license: Python-2.0 + purls: [] + size: 45767 + timestamp: 1761175217281 +- pypi: https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl + name: cryptography + version: 46.0.3 + sha256: a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec + requires_dist: + - cffi>=1.14 ; python_full_version == '3.8.*' and platform_python_implementation != 'PyPy' + - cffi>=2.0.0 ; python_full_version >= '3.9' and platform_python_implementation != 'PyPy' + - typing-extensions>=4.13.2 ; python_full_version < '3.11' + - bcrypt>=3.1.5 ; extra == 'ssh' + - nox[uv]>=2024.4.15 ; extra == 'nox' + - cryptography-vectors==46.0.3 ; extra == 'test' + - pytest>=7.4.0 ; extra == 'test' + - pytest-benchmark>=4.0 ; extra == 'test' + - pytest-cov>=2.10.1 ; extra == 'test' + - pytest-xdist>=3.5.0 ; extra == 'test' + - pretend>=0.7 ; extra == 'test' + - certifi>=2024 ; extra == 'test' + - pytest-randomly ; extra == 'test-randomorder' + - sphinx>=5.3.0 ; extra == 'docs' + - sphinx-rtd-theme>=3.0.0 ; extra == 'docs' + - sphinx-inline-tabs ; extra == 'docs' + - pyenchant>=3 ; extra == 'docstest' + - readme-renderer>=30.0 ; extra == 'docstest' + - sphinxcontrib-spelling>=7.3.1 ; extra == 'docstest' + - build>=1.0.0 ; extra == 'sdist' + - ruff>=0.11.11 ; extra == 'pep8test' + - mypy>=1.14 ; extra == 'pep8test' + - check-sdist ; extra == 'pep8test' + - click>=8.0.1 ; extra == 'pep8test' + requires_python: '>=3.8,!=3.9.0,!=3.9.1' +- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cccl_linux-64-12.9.27-ha770c72_0.conda + sha256: 2ee3b9564ca326226e5cda41d11b251482df8e7c757e333d28ec75213c75d126 + md5: 87ff6381e33b76e5b9b179a2cdd005ec + depends: + - cuda-version >=12.9,<12.10.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 1150650 + timestamp: 1746189825236 +- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-compiler-12.9.1-hbad6d8a_0.conda + sha256: fc25063509a202e0a3e12165fb3a8accecfc15a45b3c2990c99094bb4bb3cad7 + md5: 660b4b6f307be08e0f79231ba7d48203 + depends: + - __linux + - c-compiler + - cuda-cuobjdump 12.9.82.* + - cuda-cuxxfilt 12.9.82.* + - cuda-nvcc 12.9.86.* + - cuda-nvprune 12.9.82.* + - cxx-compiler + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 20648 + timestamp: 1749242335038 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-crt-12.9.86-h69a702a_4.conda + sha256: a98323d4c78b568a7e37cff47b3780cc24f689364f038cd0077a772f50a6d499 + md5: d8de7e76093f5bc8c1a800e3912e745b + depends: + - cuda-crt-dev_linux-64 12.9.86.* + - cuda-crt-tools 12.9.86.* + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 24808 + timestamp: 1761847825768 +- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-crt-dev_linux-64-12.9.86-ha770c72_2.conda + sha256: e6257534c4b4b6b8a1192f84191c34906ab9968c92680fa09f639e7846a87304 + md5: 79d280de61e18010df5997daea4743df + depends: + - cuda-version >=12.9,<12.10.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 94239 + timestamp: 1753975242354 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-crt-tools-12.9.86-ha770c72_2.conda + sha256: 2da9964591af14ba11b2379bed01d56e7185260ee0998d1a939add7fb752db45 + md5: 503a94e20d2690d534d676a764a1852c + depends: + - cuda-version >=12.9,<12.10.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 29138 + timestamp: 1753975252445 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-12.9.79-h5888daf_0.conda + sha256: 57d1294ecfaf9dc8cdb5fc4be3e63ebc7614538bddb5de53cfd9b1b7de43aed5 + md5: cb15315d19b58bd9cd424084e58ad081 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-cudart_linux-64 12.9.79 h3f2d84a_0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 23242 + timestamp: 1749218416505 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-dev-12.9.79-h5888daf_0.conda + sha256: 04d8235cb3cb3510c0492c3515a9d1a6053b50ef39be42b60cafb05044b5f4c6 + md5: ba38a7c3b4c14625de45784b773f0c71 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-cudart 12.9.79 h5888daf_0 + - cuda-cudart-dev_linux-64 12.9.79 h3f2d84a_0 + - cuda-cudart-static 12.9.79 h5888daf_0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 23687 + timestamp: 1749218464010 +- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-dev_linux-64-12.9.79-h3f2d84a_0.conda + sha256: ffe86ed0144315b276f18020d836c8ef05bf971054cf7c3eb167af92494080d5 + md5: 86e40eb67d83f1a58bdafdd44e5a77c6 + depends: + - cuda-cccl_linux-64 + - cuda-cudart-static_linux-64 + - cuda-cudart_linux-64 + - cuda-version >=12.9,<12.10.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 389140 + timestamp: 1749218427266 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-static-12.9.79-h5888daf_0.conda + sha256: 6261e1d9af80e1ec308e3e5e2ff825d189ef922d24093beaf6efca12e67ce060 + md5: d3c4ac48f4967f09dd910d9c15d40c81 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-cudart-static_linux-64 12.9.79 h3f2d84a_0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 23283 + timestamp: 1749218442382 +- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-64-12.9.79-h3f2d84a_0.conda + sha256: d435f8a19b59b52ce460ee3a6bfd877288a0d1d645119a6ba60f1c3627dc5032 + md5: b87bf315d81218dd63eb46cc1eaef775 + depends: + - cuda-version >=12.9,<12.10.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 1148889 + timestamp: 1749218381225 +- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-64-12.9.79-h3f2d84a_0.conda + sha256: 6cde0ace2b995b49d0db2eefb7bc30bf00ffc06bb98ef7113632dec8f8907475 + md5: 64508631775fbbf9eca83c84b1df0cae + depends: + - cuda-version >=12.9,<12.10.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 197249 + timestamp: 1749218394213 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cuobjdump-12.9.82-hffce074_1.conda + sha256: 113c354cb176eee131cc193507214a471bef73e000f5a143f7367c0e48d92959 + md5: 55a83761db33f82d92d7d7a4a61662e5 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-nvdisasm + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 245074 + timestamp: 1761107448598 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cupti-12.9.79-h676940d_1.conda + sha256: f46c13ab4335281a683f428376cb599019dfd25adafabc39c223824daab7ccae + md5: a2ddf359dcb9e6a3d0173b10f58f4db9 + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 1841757 + timestamp: 1761098689894 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cuxxfilt-12.9.82-hffce074_1.conda + sha256: 54e59c7c802e3491ec901011711e79a0dd65e75a12c3e6178c3948f653ac5a59 + md5: 73204e01a30d7d2783de5acd153f6a1d + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 216466 + timestamp: 1761098778582 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-driver-dev-12.9.79-h5888daf_0.conda + sha256: 1aed57d1c2473f989c06a1aee16c7b017875ddf0dc58149192aee47632aab6d4 + md5: 5ad47d2005474ac032502e61cc5ae034 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-driver-dev_linux-64 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=13 + - libstdcxx >=13 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 23076 + timestamp: 1749218453099 +- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-driver-dev_linux-64-12.9.79-h3f2d84a_0.conda + sha256: a15574d966e73135a79d5e6570c87e13accdb44bd432449b5deea71644ad442c + md5: d411828daa36ac84eab210ba3bbe5a64 + depends: + - cuda-version >=12.9,<12.10.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 37714 + timestamp: 1749218405324 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-12.9.86-hcdd1206_4.conda + sha256: 6e7ae2315528ff031a37b2ee35b2bede5f5686304e7a67ea4d9d5df9328bc6b5 + md5: 67cf694a2c7349d0e921c25783a4a642 + depends: + - cuda-nvcc_linux-64 12.9.86.* + - gcc_linux-64 + - gxx_linux-64 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 24793 + timestamp: 1761847836018 +- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvcc-dev_linux-64-12.9.86-he91c749_2.conda + sha256: a1672a34439a72869de9e011e935d41b62fc8dfb1a2700e85ed8a7a129b79981 + md5: 19d4e090217f0ea89d30bedb7461c048 + depends: + - cuda-crt-dev_linux-64 12.9.86 ha770c72_2 + - cuda-nvvm-dev_linux-64 12.9.86 ha770c72_2 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=6 + - libnvptxcompiler-dev_linux-64 12.9.86 ha770c72_2 + constrains: + - gcc_impl_linux-64 >=6,<15.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 28121 + timestamp: 1753975535813 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-impl-12.9.86-h85509e4_2.conda + sha256: 961cf20d411b7685cd744e6c6ed35efea547d095c62151d6f3053d9931bb994d + md5: 67458d2685e7503933efa550f3ee40f3 + depends: + - cuda-cudart >=12.9.79,<13.0a0 + - cuda-cudart-dev + - cuda-nvcc-dev_linux-64 12.9.86 he91c749_2 + - cuda-nvcc-tools 12.9.86 he02047a_2 + - cuda-nvvm-impl 12.9.86 h4bc722e_2 + - cuda-version >=12.9,<12.10.0a0 + - libnvptxcompiler-dev 12.9.86 ha770c72_2 + constrains: + - gcc_impl_linux-64 >=6,<15.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 27215 + timestamp: 1753975546846 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-tools-12.9.86-he02047a_2.conda + sha256: 0e849be7b5e4832ca218ec2c48a9ba3a15a984f629e2e54f38a53f4f57220341 + md5: dc256c9864c2e8e9c817fbca1c84a4bc + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-crt-tools 12.9.86 ha770c72_2 + - cuda-nvvm-tools 12.9.86 h4bc722e_2 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=12 + - libstdcxx >=12 + constrains: + - gcc_impl_linux-64 >=6,<15.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 27380012 + timestamp: 1753975454194 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc_linux-64-12.9.86-he0b4e1d_4.conda + sha256: 876da1de112e849d5aaf158d1be536a41239d6f0ebe3aaa07486aef87ad7d1ce + md5: a19c47e6723125bbc9ad2b807f686c5f + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-cudart-dev_linux-64 12.9.* + - cuda-driver-dev_linux-64 12.9.* + - cuda-nvcc-dev_linux-64 12.9.86.* + - cuda-nvcc-impl 12.9.86.* + - cuda-nvcc-tools 12.9.86.* + - sysroot_linux-64 >=2.17,<3.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 26801 + timestamp: 1761847835326 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvdisasm-12.9.88-hffce074_1.conda + sha256: 6851de88381f2ea0cbc5d18a91ae8a8ff6e682c6ee58c03c922902a0c25eb1a7 + md5: 5e7845d208a5067cb1461a429ff887e0 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 5518360 + timestamp: 1761098730432 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvml-dev-12.9.79-hffce074_1.conda + sha256: 82138fc5a18e0b3204749f8690936976a822d79bac35c525b6fad3554fd19bc3 + md5: bf934cd6425e6fcc02d35815b84947b0 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 143347 + timestamp: 1761098728630 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvprune-12.9.82-hffce074_1.conda + sha256: 3bf91b1c682e698cfe4af1952b4255303ec8949d499e4a40e88fa6e40d96e1bf + md5: 60ff367b93b5ddb5043487c9a3372f3b + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 70948 + timestamp: 1761099226016 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-12.9.86-hecca717_1.conda + sha256: 68f81268c25befa9b70dc49af469ab0eb131960e3700b9a4edb46a32da343a28 + md5: 53f0062e2243b26e43ddac0b5267c6a3 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 67168282 + timestamp: 1760723629347 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-dev-12.9.86-hecca717_1.conda + sha256: ae620051c16eabf7720a47c5115634d64f7703d32124555ad0afccfd4b8d7cf4 + md5: 0d28090f4e63410e20397c7975612837 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-nvrtc 12.9.86 hecca717_1 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - cuda-nvrtc-static >=12.9.86 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 36819 + timestamp: 1760723845601 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvtx-12.9.79-hecca717_1.conda + sha256: b16600e48ef3247366b83d5f195852fcefbc4d52bb245f82a632c7129d1d6283 + md5: b4a3411fa031c409f98cfbd4b2db9ad7 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 29436 + timestamp: 1761098820386 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvtx-dev-12.9.79-ha770c72_1.conda + sha256: 59eb7c81f26d939f1d41e20a322106f2f9bd4f9ff3214caed469cb08528a5b3e + md5: 406f13ffddd1a63934acc44759579a1c + depends: + - cuda-nvtx 12.9.79 hecca717_1 + - cuda-version >=12.9,<12.10.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 69106 + timestamp: 1761098829614 +- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-64-12.9.86-ha770c72_2.conda + sha256: 522722dcaffd133e0c7500c69dc70e21ac34d6762dcbaabfe847439f944028f0 + md5: 7b386291414c7eea113d25ac28a33772 + depends: + - cuda-version >=12.9,<12.10.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 27096 + timestamp: 1753975261562 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-impl-12.9.86-h4bc722e_2.conda + sha256: f4d34556174e4faa9d374ba2244707082870e1bbc1bb441ad3d9d2cea37da6af + md5: 82125dd3c0c4aa009faa00e2829b93d8 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=12 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 21425520 + timestamp: 1753975283188 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-tools-12.9.86-h4bc722e_2.conda + sha256: 45f5e881ed0d973132a5475a0b5c066db6e748ef3a831a14dba8374b252e0067 + md5: f9af26e4079adcd72688a8e8dbecb229 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=12 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 24246736 + timestamp: 1753975332907 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-profiler-api-12.9.79-h7938cbb_1.conda + sha256: 4f679dfbf2bf2d17abb507f31b0176c0e3572337b5005b9e36179948a53988ac + md5: 90d09865fb37d11d510444e34ebe6a09 + depends: + - cuda-cudart-dev + - cuda-version >=12.9,<12.10.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 23668 + timestamp: 1761098836058 +- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-12.9-h4f385c5_3.conda + sha256: 5f5f428031933f117ff9f7fcc650e6ea1b3fef5936cf84aa24af79167513b656 + md5: b6d5d7f1c171cbd228ea06b556cfa859 + constrains: + - cudatoolkit 12.9|12.9.* + - __cuda >=12 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 21578 + timestamp: 1746134436166 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cudnn-9.10.2.21-hbcb9cd8_0.conda + sha256: 5760ad9de2ecff210b018503168d26996497604608cf59f93df90f01ea4eb982 + md5: c8168e26c0a9f50425ac05d6a5201c12 + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-version >=12,<13.0a0 + - libcudnn-dev 9.10.2.21 h58dd1b1_0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - cudnn-jit <0a + license: LicenseRef-cuDNN-Software-License-Agreement + purls: [] + size: 19646 + timestamp: 1762823905292 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cusparselt-0.8.1.1-h58dd1b1_0.conda + sha256: 80432349a54d646c1f38abf1feb4b43c517a35794b56968952fa6a0f9a3d88c2 + md5: eb0b3131dbd80890db9d84d1dcabf768 + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-version >=12,<13.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-cuSPARSELt-Software-License-Agreement + purls: [] + size: 150881343 + timestamp: 1757535886889 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.11.0-hfcd1e18_0.conda + sha256: 3fcc97ae3e89c150401a50a4de58794ffc67b1ed0e1851468fcc376980201e25 + md5: 5da8c935dca9186673987f79cef0b2a5 + depends: + - c-compiler 1.11.0 h4d9bdce_0 + - gxx + - gxx_linux-64 14.* + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6635 + timestamp: 1753098722177 +- conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda + sha256: 9827efa891e507a91a8a2acf64e210d2aff394e1cde432ad08e1f8c66b12293c + md5: 44600c4667a319d67dbe0681fc0bc833 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cycler?source=hash-mapping + size: 13399 + timestamp: 1733332563512 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda + sha256: ee09ad7610c12c7008262d713416d0b58bf365bc38584dce48950025850bdf3f + md5: cae723309a49399d2949362f4ab5c9e4 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libntlm >=1.8,<2.0a0 + - libstdcxx >=13 + - libxcrypt >=4.4.36 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause-Attribution + license_family: BSD + purls: [] + size: 209774 + timestamp: 1750239039316 +- conda: https://conda.anaconda.org/conda-forge/noarch/dash-3.3.0-pyhd8ed1ab_0.conda + sha256: a8e6b531b5fb9582ce2087be74ebd57b095e9db4b1d25d03106b3259fe3259c5 + md5: 1e4dab5c51cef84cf867108cddb09835 + depends: + - flask >=1.0.4 + - importlib-metadata + - nest-asyncio + - plotly >=5.0.0 + - python >=3.10 + - requests + - retrying + - setuptools + - typing_extensions >=4.1.1 + - werkzeug + constrains: + - dash-html-components >=2.0.0 + - dash-core-components >=2.0.0 + - dash_table >=5.0.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/dash?source=hash-mapping + size: 5787240 + timestamp: 1763190015818 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + sha256: 22053a5842ca8ee1cf8e1a817138cdb5e647eb2c46979f84153f6ad7bde73020 + md5: 418c6ca5929a611cbd69204907a83995 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 760229 + timestamp: 1685695754230 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda + sha256: 3b988146a50e165f0fa4e839545c679af88e4782ec284cc7b6d07dd226d6a068 + md5: 679616eb5ad4e521c83da4650860aba7 + depends: + - libstdcxx >=13 + - libgcc >=13 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libexpat >=2.7.0,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + - libglib >=2.84.2,<3.0a0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 437860 + timestamp: 1747855126005 +- pypi: https://files.pythonhosted.org/packages/2a/2e/38d9824f8c6bb048c5ba21c6d4da54c29c162a46b58b3ef907a360a76d3e/diffusers-0.35.2-py3-none-any.whl + name: diffusers + version: 0.35.2 + sha256: d50d5e74fdd6dcf55e5c1d304bc52cc7c2659abd1752740d736d7b54078b4db5 + requires_dist: + - importlib-metadata + - filelock + - huggingface-hub>=0.34.0 + - numpy + - regex!=2019.12.17 + - requests + - safetensors>=0.3.1 + - pillow + - urllib3<=2.0.0 ; extra == 'quality' + - isort>=5.5.4 ; extra == 'quality' + - ruff==0.9.10 ; extra == 'quality' + - hf-doc-builder>=0.3.0 ; extra == 'quality' + - hf-doc-builder>=0.3.0 ; extra == 'docs' + - accelerate>=0.31.0 ; extra == 'training' + - datasets ; extra == 'training' + - protobuf>=3.20.3,<4 ; extra == 'training' + - tensorboard ; extra == 'training' + - jinja2 ; extra == 'training' + - peft>=0.17.0 ; extra == 'training' + - compel==0.1.8 ; extra == 'test' + - gitpython<3.1.19 ; extra == 'test' + - datasets ; extra == 'test' + - jinja2 ; extra == 'test' + - invisible-watermark>=0.2.0 ; extra == 'test' + - k-diffusion==0.0.12 ; extra == 'test' + - librosa ; extra == 'test' + - parameterized ; extra == 'test' + - pytest ; extra == 'test' + - pytest-timeout ; extra == 'test' + - pytest-xdist ; extra == 'test' + - requests-mock==1.10.0 ; extra == 'test' + - safetensors>=0.3.1 ; extra == 'test' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'test' + - scipy ; extra == 'test' + - tiktoken>=0.7.0 ; extra == 'test' + - torchvision ; extra == 'test' + - transformers>=4.41.2 ; extra == 'test' + - phonemizer ; extra == 'test' + - torch>=1.4 ; extra == 'torch' + - accelerate>=0.31.0 ; extra == 'torch' + - bitsandbytes>=0.43.3 ; extra == 'bitsandbytes' + - accelerate>=0.31.0 ; extra == 'bitsandbytes' + - gguf>=0.10.0 ; extra == 'gguf' + - accelerate>=0.31.0 ; extra == 'gguf' + - optimum-quanto>=0.2.6 ; extra == 'optimum-quanto' + - accelerate>=0.31.0 ; extra == 'optimum-quanto' + - torchao>=0.7.0 ; extra == 'torchao' + - accelerate>=0.31.0 ; extra == 'torchao' + - jax>=0.4.1 ; extra == 'flax' + - jaxlib>=0.4.1 ; extra == 'flax' + - flax>=0.4.1 ; extra == 'flax' + - urllib3<=2.0.0 ; extra == 'dev' + - isort>=5.5.4 ; extra == 'dev' + - ruff==0.9.10 ; extra == 'dev' + - hf-doc-builder>=0.3.0 ; extra == 'dev' + - compel==0.1.8 ; extra == 'dev' + - gitpython<3.1.19 ; extra == 'dev' + - datasets ; extra == 'dev' + - jinja2 ; extra == 'dev' + - invisible-watermark>=0.2.0 ; extra == 'dev' + - k-diffusion==0.0.12 ; extra == 'dev' + - librosa ; extra == 'dev' + - parameterized ; extra == 'dev' + - pytest ; extra == 'dev' + - pytest-timeout ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - requests-mock==1.10.0 ; extra == 'dev' + - safetensors>=0.3.1 ; extra == 'dev' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'dev' + - scipy ; extra == 'dev' + - tiktoken>=0.7.0 ; extra == 'dev' + - torchvision ; extra == 'dev' + - transformers>=4.41.2 ; extra == 'dev' + - phonemizer ; extra == 'dev' + - accelerate>=0.31.0 ; extra == 'dev' + - datasets ; extra == 'dev' + - protobuf>=3.20.3,<4 ; extra == 'dev' + - tensorboard ; extra == 'dev' + - jinja2 ; extra == 'dev' + - peft>=0.17.0 ; extra == 'dev' + - hf-doc-builder>=0.3.0 ; extra == 'dev' + - torch>=1.4 ; extra == 'dev' + - accelerate>=0.31.0 ; extra == 'dev' + - jax>=0.4.1 ; extra == 'dev' + - jaxlib>=0.4.1 ; extra == 'dev' + - flax>=0.4.1 ; extra == 'dev' + requires_python: '>=3.8.0' +- pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl + name: dill + version: 0.4.0 + sha256: 44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049 + requires_dist: + - objgraph>=1.7.2 ; extra == 'graph' + - gprof2dot>=2022.7.29 ; extra == 'profile' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.8.0-pyhcf101f3_0.conda + sha256: ef1e7b8405997ed3d6e2b6722bd7088d4a8adf215e7c88335582e65651fb4e05 + md5: d73fdc05f10693b518f52c994d748c19 + depends: + - python >=3.10,<4.0.0 + - sniffio + - python + constrains: + - aioquic >=1.2.0 + - cryptography >=45 + - httpcore >=1.0.0 + - httpx >=0.28.0 + - h2 >=4.2.0 + - idna >=3.10 + - trio >=0.30 + - wmi >=1.5.1 + license: ISC + purls: + - pkg:pypi/dnspython?source=hash-mapping + size: 196500 + timestamp: 1757292856922 +- pypi: https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl + name: docstring-parser + version: 0.17.0 + sha256: cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708 + requires_dist: + - pre-commit>=2.16.0 ; python_full_version >= '3.9' and extra == 'dev' + - pydoctor>=25.4.0 ; extra == 'dev' + - pytest ; extra == 'dev' + - pydoctor>=25.4.0 ; extra == 'docs' + - pytest ; extra == 'test' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/11/a8/c6a4b901d17399c77cd81fb001ce8961e9f5e04d3daf27e8925cb012e163/docutils-0.22.3-py3-none-any.whl + name: docutils + version: 0.22.3 + sha256: bd772e4aca73aff037958d44f2be5229ded4c09927fcf8690c577b66234d6ceb + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + sha256: 1bcc132fbcc13f9ad69da7aa87f60ea41de7ed4d09f3a00ff6e0e70e1c690bc2 + md5: bfd56492d8346d669010eccafe0ba058 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 69544 + timestamp: 1739569648873 +- pypi: https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl + name: einops + version: 0.8.1 + sha256: 919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737 + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.3.0-pyhd8ed1ab_0.conda + sha256: c37320864c35ef996b0e02e289df6ee89582d6c8e233e18dc9983375803c46bb + md5: 3bc0ac31178387e8ed34094d9481bfe8 + depends: + - dnspython >=2.0.0 + - idna >=2.0.0 + - python >=3.10 + license: Unlicense + purls: + - pkg:pypi/email-validator?source=hash-mapping + size: 46767 + timestamp: 1756221480106 +- conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.3.0-hd8ed1ab_0.conda + sha256: 6a518e00d040fcad016fb2dde29672aa3476cd9ae33ea5b7b257222e66037d89 + md5: 2452e434747a6b742adc5045f2182a8e + depends: + - email-validator >=2.3.0,<2.3.1.0a0 + license: Unlicense + purls: [] + size: 7077 + timestamp: 1756221480651 +- conda: https://conda.anaconda.org/conda-forge/linux-64/embree-4.4.0-ha751380_1.conda + sha256: aabfa509404b2e3df9aa28b2304465822d709761fdac0b0a262b07eb6a1b425e + md5: 69603afa88336a2c836721dc980b742e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - tbb >=2022.3.0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 9506863 + timestamp: 1763127235152 +- conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + sha256: ee6cf346d017d954255bbcbdb424cddea4d14e4ed7e9813e429db1d795d01144 + md5: 8e662bd460bda79b1ea39194e3c4c9ab + depends: + - python >=3.10 + - typing_extensions >=4.6.0 + license: MIT and PSF-2.0 + purls: + - pkg:pypi/exceptiongroup?source=compressed-mapping + size: 21333 + timestamp: 1763918099466 +- pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl + name: executing + version: 2.2.1 + sha256: 760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017 + requires_dist: + - asttokens>=2.1.0 ; extra == 'tests' + - ipython ; extra == 'tests' + - pytest ; extra == 'tests' + - coverage ; extra == 'tests' + - coverage-enable-subprocess ; extra == 'tests' + - littleutils ; extra == 'tests' + - rich ; python_full_version >= '3.11' and extra == 'tests' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.122.0-h6dd4f06_0.conda + sha256: 8c55092c9e4b8101ca40f65cfb78cb7cd5d4395f24debbc0eab195bda4e6d352 + md5: 66a865d7b7a9665fbac61addf58520e6 + depends: + - fastapi-core ==0.122.0 pyhcf101f3_0 + - email_validator + - fastapi-cli + - httpx + - jinja2 + - python-multipart + - uvicorn-standard + license: MIT + license_family: MIT + purls: + - pkg:pypi/fastapi?source=compressed-mapping + size: 4788 + timestamp: 1764031271215 +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.16-pyhcf101f3_1.conda + sha256: 4136b0c277188b205332983278c7b278ea946dc1c78a381e0f5bc79204b8ac97 + md5: 4f82a266e2d5b199db16cdb42341d785 + depends: + - python >=3.10 + - rich-toolkit >=0.14.8 + - tomli >=2.0.0 + - typer >=0.15.1 + - uvicorn-standard >=0.15.0 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/fastapi-cli?source=hash-mapping + size: 19029 + timestamp: 1763068963965 +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-core-0.122.0-pyhcf101f3_0.conda + sha256: 91ba4aca4d613f09284a20a0435edcd83a0b4590b2dedd0afce149a7508dd2e6 + md5: b5fb642d0b6e6e6b5dbf6547c4ce7208 + depends: + - python >=3.10 + - annotated-doc >=0.0.2 + - starlette >=0.40.0,<0.51.0 + - typing_extensions >=4.8.0 + - pydantic >=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0 + - python + constrains: + - email_validator >=2.0.0 + - fastapi-cli >=0.0.8 + - httpx >=0.23.0,<1.0.0 + - jinja2 >=3.1.5 + - python-multipart >=0.0.18 + - uvicorn-standard >=0.12.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/fastapi?source=hash-mapping + size: 87651 + timestamp: 1764031271209 +- pypi: https://files.pythonhosted.org/packages/27/f4/30077733807ca57cc504a6faadcdd067714a7891dad66231b0c1ab373551/fastcore-1.8.16-py3-none-any.whl + name: fastcore + version: 1.8.16 + sha256: ce49c2580cfcf416a7ee166f9d84f61a828beb6be1315635c6035ec3f626ed4b + requires_dist: + - packaging + - numpy ; extra == 'dev' + - nbdev>=0.2.39 ; extra == 'dev' + - matplotlib ; extra == 'dev' + - pillow ; extra == 'dev' + - torch ; extra == 'dev' + - pandas ; extra == 'dev' + - nbclassic ; extra == 'dev' + - pysymbol-llm ; extra == 'dev' + - llms-txt ; extra == 'dev' + - plum-dispatch ; extra == 'dev' + - toolslm ; extra == 'dev' + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.0.0-gpl_hbbdf940_906.conda + sha256: 7a62272c6f12e8074d9e6b3e7423c906ed9ee5bb0a66c1256ad341449308e158 + md5: e345ae3d8bc570a3f6ea89f03ff1110a + depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.14,<1.3.0a0 + - aom >=3.9.1,<3.10.0a0 + - bzip2 >=1.0.8,<2.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - gmp >=6.3.0,<7.0a0 + - harfbuzz >=11.5.1 + - lame >=3.100,<3.101.0a0 + - libass >=0.17.4,<0.17.5.0a0 + - libexpat >=2.7.1,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libopenvino >=2025.2.0,<2025.2.1.0a0 + - libopenvino-auto-batch-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-auto-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-hetero-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-intel-cpu-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-intel-gpu-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-intel-npu-plugin >=2025.2.0,<2025.2.1.0a0 + - libopenvino-ir-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-onnx-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-paddle-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-pytorch-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-tensorflow-frontend >=2025.2.0,<2025.2.1.0a0 + - libopenvino-tensorflow-lite-frontend >=2025.2.0,<2025.2.1.0a0 + - libopus >=1.5.2,<2.0a0 + - librsvg >=2.58.4,<3.0a0 + - libstdcxx >=14 + - libva >=2.22.0,<3.0a0 + - libvorbis >=1.3.7,<1.4.0a0 + - libvpl >=2.15.0,<2.16.0a0 + - libvpx >=1.14.1,<1.15.0a0 + - libvulkan-loader >=1.4.313.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - openh264 >=2.6.0,<2.6.1.0a0 + - openssl >=3.5.3,<4.0a0 + - pulseaudio-client >=17.0,<17.1.0a0 + - sdl2 >=2.32.56,<3.0a0 + - shaderc >=2025.4,<2025.5.0a0 + - svt-av1 >=3.1.2,<3.1.3.0a0 + - x264 >=1!164.3095,<1!165 + - x265 >=3.5,<3.6.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + constrains: + - __cuda >=12.8 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 12445427 + timestamp: 1758924965297 +- conda: https://conda.anaconda.org/conda-forge/noarch/ffmpy-0.3.0-pyhb6f538c_0.tar.bz2 + sha256: 19e8500b169244e2ab0df3d086c3040c0cd96631781bb16c9045a0b2e6ea6e61 + md5: 6ac23fb78d7234152221fea00aeb11c8 + depends: + - ffmpeg + - python >=3.6 + license: MIT + license_family: MIT + purls: + - pkg:pypi/ffmpy?source=hash-mapping + size: 10415 + timestamp: 1659475099796 +- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.0-pyhd8ed1ab_0.conda + sha256: 19025a4078ff3940d97eb0da29983d5e0deac9c3e09b0eabf897daeaf9d1114e + md5: 66b8b26023b8efdf8fcb23bac4b6325d + depends: + - python >=3.10 + license: Unlicense + purls: + - pkg:pypi/filelock?source=hash-mapping + size: 17976 + timestamp: 1759948208140 +- conda: https://conda.anaconda.org/conda-forge/noarch/flask-3.1.2-pyhd8ed1ab_0.conda + sha256: 8a97eba37e0723720706d4636cc89c6b07eea1b7cc66fd8994fa8983a81ed988 + md5: ba67a9febeda36948fee26a3dec3d914 + depends: + - blinker >=1.9.0 + - click >=8.1.3 + - importlib-metadata >=3.6.0 + - itsdangerous >=2.2.0 + - jinja2 >=3.1.2 + - markupsafe >=2.1.1 + - python >=3.9 + - werkzeug >=3.1.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/flask?source=hash-mapping + size: 82438 + timestamp: 1755674743256 +- pypi: https://files.pythonhosted.org/packages/ee/1b/00a78aa2e8fbd63f9af08c9c19e6deb3d5d66b4dda677a0f61654680ee89/flatbuffers-25.9.23-py2.py3-none-any.whl + name: flatbuffers + version: 25.9.23 + sha256: 255538574d6cb6d0a79a17ec8bc0d30985913b87513a01cce8bcdb6b4c44d0e2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-11.2.0-h07f6e7f_0.conda + sha256: e0f53b7801d0bcb5d61a1ddcb873479bfe8365e56fd3722a232fbcc372a9ac52 + md5: 0c2f855a88fab6afa92a7aa41217dc8e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + purls: [] + size: 192721 + timestamp: 1751277120358 +- conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + sha256: 58d7f40d2940dd0a8aa28651239adbf5613254df0f75789919c4e6762054403b + md5: 0c96522c6bdaed4b1566d11387caaf45 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 397370 + timestamp: 1566932522327 +- conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + sha256: c52a29fdac682c20d252facc50f01e7c2e7ceac52aa9817aaf0bb83f7559ec5c + md5: 34893075a5c9e55cdafac56607368fc6 + license: OFL-1.1 + license_family: Other + purls: [] + size: 96530 + timestamp: 1620479909603 +- conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + sha256: 00925c8c055a2275614b4d983e1df637245e19058d79fc7dd1a93b8d9fb4b139 + md5: 4d59c254e01d9cde7957100457e2d5fb + license: OFL-1.1 + license_family: Other + purls: [] + size: 700814 + timestamp: 1620479612257 +- conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + sha256: 2821ec1dc454bd8b9a31d0ed22a7ce22422c0aef163c59f49dfdf915d0f0ca14 + md5: 49023d73832ef61042f6a237cb2687e7 + license: LicenseRef-Ubuntu-Font-Licence-Version-1.0 + license_family: Other + purls: [] + size: 1620504 + timestamp: 1727511233259 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda + sha256: 7093aa19d6df5ccb6ca50329ef8510c6acb6b0d8001191909397368b65b02113 + md5: 8f5b0b297b59e1ac160ad4beec99dbee + depends: + - __glibc >=2.17,<3.0.a0 + - freetype >=2.12.1,<3.0a0 + - libexpat >=2.6.3,<3.0a0 + - libgcc >=13 + - libuuid >=2.38.1,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 265599 + timestamp: 1730283881107 +- conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + sha256: a997f2f1921bb9c9d76e6fa2f6b408b7fa549edd349a77639c9fe7a23ea93e61 + md5: fee5683a3f04bd15cbd8318b096a27ab + depends: + - fonts-conda-forge + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3667 + timestamp: 1566974674465 +- conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + sha256: 54eea8469786bc2291cc40bca5f46438d3e062a399e8f53f013b6a9f50e98333 + md5: a7970cd949a077b7cb9696379d338681 + depends: + - font-ttf-ubuntu + - font-ttf-inconsolata + - font-ttf-dejavu-sans-mono + - font-ttf-source-code-pro + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4059 + timestamp: 1762351264405 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.60.1-py312h8a5da7c_0.conda + sha256: 1be46e58f063c1f563f114df9e78bcb70c4b59760104c5456bbe3b0cb17af9cf + md5: b12bb9cc477156ce84038e0be6d0f763 + depends: + - __glibc >=2.17,<3.0.a0 + - brotli + - libgcc >=14 + - munkres + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - unicodedata2 >=15.1.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/fonttools?source=hash-mapping + size: 2888637 + timestamp: 1759187635166 +- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda + sha256: bf8e4dffe46f7d25dc06f31038cacb01672c47b9f45201f065b0f4d00ab0a83e + md5: 4afc585cd97ba8a23809406cd8a9eda8 + depends: + - libfreetype 2.14.1 ha770c72_0 + - libfreetype6 2.14.1 h73754d4_0 + license: GPL-2.0-only OR FTL + purls: [] + size: 173114 + timestamp: 1757945422243 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda + sha256: 858283ff33d4c033f4971bf440cebff217d5552a5222ba994c49be990dacd40d + md5: f9f81ea472684d75b9dd8d0b328cf655 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-or-later + purls: [] + size: 61244 + timestamp: 1757438574066 +- conda: https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.7.0-py312h447239a_0.conda + sha256: f4e0e6cd241bc24afb2d6d08e5d2ba170fad2475e522bdf297b7271bba268be6 + md5: 63e20cf7b7460019b423fc06abb96c60 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/frozenlist?source=hash-mapping + size: 55037 + timestamp: 1752167383781 +- conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.10.0-pyhd8ed1ab_0.conda + sha256: df5cb57bb668cd5b2072d8bd66380ff7acb12e8c337f47dd4b9a75a6a6496a6d + md5: d18004c37182f83b9818b714825a7627 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/fsspec?source=hash-mapping + size: 146592 + timestamp: 1761840236679 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gcc-14.3.0-h0dff253_12.conda + sha256: 9fcba59737983b76a0cf6780f209f23ce069d5f1d74e7a97de1dc64a3ffbeea7 + md5: 8d5f5fea574e437129096e0e72b2aa82 + depends: + - conda-gcc-specs + - gcc_impl_linux-64 14.3.0 h0c81b82_12 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 30919 + timestamp: 1764036684499 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-14.3.0-h0c81b82_12.conda + sha256: 00251250b41542c69dde7d6ef0f2cd143f5db169d42d31b6356dc19964aebbc7 + md5: f02a1f8e059fab4baa5a9cf2a4eb13d4 + depends: + - binutils_impl_linux-64 >=2.45 + - libgcc >=14.3.0 + - libgcc-devel_linux-64 14.3.0 hf649bbc_112 + - libgomp >=14.3.0 + - libsanitizer 14.3.0 h8f1669f_12 + - libstdcxx >=14.3.0 + - sysroot_linux-64 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 75462986 + timestamp: 1764036446089 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-14.3.0-h298d278_14.conda + sha256: 4e99d5903113dfbb36a0a1510450c6a6e3ffc4a4c69d2d1e03c3efa1f1ba4e8f + md5: fe0c2ac970a0b10835f3432a3dfd4542 + depends: + - gcc_impl_linux-64 14.3.0.* + - binutils_linux-64 + - sysroot_linux-64 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 27980 + timestamp: 1763757768300 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.4-h2b0a6b4_0.conda + sha256: f47222f58839bcc77c15f11a8814c1d8cb8080c5ca6ba83398a12b640fd3c85c + md5: c379d67c686fb83475c1a6ed41cc41ff + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libglib >=2.86.0,<3.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libpng >=1.6.50,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 572093 + timestamp: 1761082340749 +- pypi: https://files.pythonhosted.org/packages/ed/1f/95cb961341bf33e534a472672ef4916c90046dc3c57b1442f5586dda72f1/geffnet-1.0.2-py3-none-any.whl + name: geffnet + version: 1.0.2 + sha256: 259013030bf429bc7a4fc4e8dc3fef6364e9750a6cee24d28e1674f8c3048d3a + requires_dist: + - torch>=1.4 + - torchvision + requires_python: '>=3.6' +- conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-0.25.1-h3f43e3d_1.conda + sha256: cbfa8c80771d1842c2687f6016c5e200b52d4ca8f2cc119f6377f64f899ba4ff + md5: c42356557d7f2e37676e121515417e3b + depends: + - __glibc >=2.17,<3.0.a0 + - gettext-tools 0.25.1 h3f43e3d_1 + - libasprintf 0.25.1 h3f43e3d_1 + - libasprintf-devel 0.25.1 h3f43e3d_1 + - libgcc >=14 + - libgettextpo 0.25.1 h3f43e3d_1 + - libgettextpo-devel 0.25.1 h3f43e3d_1 + - libiconv >=1.18,<2.0a0 + - libstdcxx >=14 + license: LGPL-2.1-or-later AND GPL-3.0-or-later + purls: [] + size: 541357 + timestamp: 1753343006214 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.25.1-h3f43e3d_1.conda + sha256: c792729288bdd94f21f25f80802d4c66957b4e00a57f7cb20513f07aadfaff06 + md5: a59c05d22bdcbb4e984bf0c021a2a02f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 3644103 + timestamp: 1753342966311 +- conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda + sha256: aac402a8298f0c0cc528664249170372ef6b37ac39fdc92b40601a6aed1e32ff + md5: 3bf7b9fd5a7136126e0234db4b87c8b6 + depends: + - libgcc-ng >=12 + license: MIT + license_family: MIT + purls: [] + size: 77248 + timestamp: 1712692454246 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gl2ps-1.4.2-hae5d5c5_1.conda + sha256: 68f071ea25e79ee427c0d6c35ccc137d66f093a37660a4e41bafe0c49d64f2d6 + md5: 00e642ec191a19bf806a3915800e9524 + depends: + - libgcc-ng >=12 + - libpng >=1.6.43,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 74102 + timestamp: 1718542981099 +- pypi: https://files.pythonhosted.org/packages/32/ba/9ccb80650e5bd61e739f16f33aec3bb290a80f314631be8f7dc0a2b22b5f/glcontext-3.0.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: glcontext + version: 3.0.0 + sha256: 04921720740438ceea8fb8a38b5665963520c7c8f27bef03df8aeb3ea3cfbfb6 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glew-2.2.0-h3abd4de_0.conda + sha256: fe1232f1a00b671091eff53388ef4dffc1e0e0efeb1c3e7c8ee4cbbbda968c80 + md5: 6ea943ca4f0d01d6eec6a60d24415dc5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libglu >=9.0.3,<9.1.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 544416 + timestamp: 1760370322297 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glfw-3.4-hb03c661_1.conda + sha256: 8586f786e71a52dc37be6a78c37fd8526b80539082352196f6f7440392ce33d0 + md5: bf1df8613072b03a7fb4b77b9fa54048 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxkbcommon >=1.11.0,<2.0a0 + - wayland >=1.24.0,<2.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxi >=1.8.2,<2.0a0 + - xorg-libxinerama >=1.1.5,<1.2.0a0 + - xorg-libxrandr >=1.5.4,<2.0a0 + license: Zlib + purls: [] + size: 166432 + timestamp: 1758809197097 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glslang-16.0.0-hfd11570_0.conda + sha256: 6f35ba185e93b354297a1af138b2e8ede9b125a55acaa03ff14e00947126fc1b + md5: 8fc94e8de494e675c6ca1426b97ed67a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - spirv-tools >=2025,<2026.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1300234 + timestamp: 1758851979910 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda + sha256: 309cf4f04fec0c31b6771a5809a1909b4b3154a2208f52351e1ada006f4c750c + md5: c94a5994ef49749880a8139cf9afcbe1 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: GPL-2.0-or-later OR LGPL-3.0-or-later + purls: [] + size: 460055 + timestamp: 1718980856608 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.2.1-py312hcaba1f9_2.conda + sha256: c9dfe9be6716d992b4ddd2e8219ad8afbe33dc04f69748ac4302954ba2e29e84 + md5: dd6f5de6249439ab97a6e9e873741294 + depends: + - __glibc >=2.17,<3.0.a0 + - gmp >=6.3.0,<7.0a0 + - libgcc >=14 + - mpc >=1.3.1,<2.0a0 + - mpfr >=4.2.1,<5.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: LGPL-3.0-or-later + license_family: LGPL + purls: + - pkg:pypi/gmpy2?source=hash-mapping + size: 214554 + timestamp: 1762946924209 +- conda: https://conda.anaconda.org/conda-forge/noarch/gradio-5.49.1-pyhd8ed1ab_0.conda + sha256: 70c846ec92e8c0ebe1a4c49803bf0aaa097cc13b8cc7ed87f878f57b0e11933a + md5: 47514327f403deae806c2dfdefa1f2f4 + depends: + - aiofiles >=22.0,<25.0 + - anyio >=3.0,<5.0 + - fastapi >=0.115.2,<1.0 + - ffmpy + - gradio-client 1.13.3 + - groovy-python >=0.1,<0.2 + - httpx >=0.24.1 + - huggingface_hub >=0.28.1 + - jinja2 <4.0 + - markupsafe >=2.0,<4.0 + - numpy >=1.0,<3.0 + - orjson >=3.0,<4.0 + - packaging + - pandas >=1.0,<3.0 + - pillow >=8.0,<12.0 + - pydantic >=2.0,<2.12 + - pydub + - python >=3.10,<3.13 + - python-multipart >=0.0.18 + - pyyaml >=5.0,<7.0 + - ruff >=0.9.3 + - safehttpx >=0.1.6,<0.2.0 + - semantic_version >=2.0,<3.0 + - starlette >=0.40.0,<1.0 + - tomlkit >=0.12.0,<0.14.0 + - typer >=0.12,<1.0 + - typing-extensions ~=4.0 + - urllib3 ~=2.0 + - uvicorn >=0.14.0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/gradio?source=hash-mapping + size: 52404150 + timestamp: 1759960936164 +- conda: https://conda.anaconda.org/conda-forge/noarch/gradio-client-1.13.3-pyhd8ed1ab_0.conda + sha256: baa86a104e016f93ad5e27dde455139b499844065b3c2f005075b6391ad719b6 + md5: 690f5098ddd34e5a974cbd0a4c3b5ed9 + depends: + - fsspec + - httpx >=0.24.1 + - huggingface_hub >=0.19.3 + - packaging + - python >=3.10 + - typing_extensions ~=4.0 + - websockets >=10.0,<16.0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/gradio-client?source=hash-mapping + size: 314453 + timestamp: 1758930406107 +- pypi: https://files.pythonhosted.org/packages/de/5a/5ebd2acb8f330f4886265c3c13243338075557ccd66a9bbe0b163596797e/gradio_rerun-0.27.0-py3-none-any.whl + name: gradio-rerun + version: 0.27.0 + sha256: 6a66457778b00c79f4ad7abdaae1c9a1936fb7693abdfd22eadbb8bab77b7f59 + requires_dist: + - gradio>=5.49.1 + - opencv-python + - rerun-sdk==0.27.0 + - twine>=6.1.0 + - build ; extra == 'dev' + - opencv-python>=4.10.0 ; extra == 'dev' + - twine ; extra == 'dev' + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda + sha256: 25ba37da5c39697a77fce2c9a15e48cf0a84f1464ad2aafbe53d8357a9f6cc8c + md5: 2cd94587f3a401ae05e03a6caf09539d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 99596 + timestamp: 1755102025473 +- conda: https://conda.anaconda.org/conda-forge/noarch/groovy-python-0.1.2-pyhd8ed1ab_0.conda + sha256: e6d96279af39a405492288c87c1200f55fb5564992161c261ab3512c6bc37d50 + md5: 069d92df998c58be5492ef8c41e303fe + depends: + - python >=3.10 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/groovy?source=hash-mapping + size: 20065 + timestamp: 1753106860816 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-14.3.0-he448592_7.conda + sha256: 7acf0ee3039453aa69f16da063136335a3511f9c157e222def8d03c8a56a1e03 + md5: 91dc0abe7274ac5019deaa6100643265 + depends: + - gcc 14.3.0.* + - gxx_impl_linux-64 14.3.0.* + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 30403 + timestamp: 1759966121169 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-14.3.0-h2185e75_12.conda + sha256: 1976f6f6e4f93805e7e95f69a00bf8efb5cd07dc9035781a87c198613e03fa1e + md5: eec33cae76a68f98fa3f659da22143a6 + depends: + - gcc_impl_linux-64 14.3.0 h0c81b82_12 + - libstdcxx-devel_linux-64 14.3.0 h9f08a49_112 + - sysroot_linux-64 + - tzdata + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 14739255 + timestamp: 1764036655976 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-14.3.0-hc876b51_14.conda + sha256: d8d6fe7ddd4aa55307ee4fa41860abd0365c29312878f5fb392cd7b50d303711 + md5: 1852de0052b0d6af4294b3ae25a4a450 + depends: + - gxx_impl_linux-64 14.3.0.* + - gcc_linux-64 ==14.3.0 h298d278_14 + - binutils_linux-64 + - sysroot_linux-64 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 27073 + timestamp: 1763757768301 +- conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + sha256: f64b68148c478c3bfc8f8d519541de7d2616bf59d44485a5271041d40c061887 + md5: 4b69232755285701bc86a5afe4d9933a + depends: + - python >=3.9 + - typing_extensions + license: MIT + license_family: MIT + purls: + - pkg:pypi/h11?source=hash-mapping + size: 37697 + timestamp: 1745526482242 +- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + sha256: 84c64443368f84b600bfecc529a1194a3b14c3656ee2e832d15a20e0329b6da3 + md5: 164fc43f0b53b6e3a7bc7dce5e4f1dc9 + depends: + - python >=3.10 + - hyperframe >=6.1,<7 + - hpack >=4.1,<5 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/h2?source=compressed-mapping + size: 95967 + timestamp: 1756364871835 +- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda + sha256: 6bd8b22beb7d40562b2889dc68232c589ff0d11a5ad3addd41a8570d11f039d9 + md5: b8690f53007e9b5ee2c2178dd4ac778c + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.4,<2.0a0 + - graphite2 >=1.3.14,<2.0a0 + - icu >=75.1,<76.0a0 + - libexpat >=2.7.1,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libglib >=2.86.1,<3.0a0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 2411408 + timestamp: 1762372726141 +- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda + sha256: 0d09b6dc1ce5c4005ae1c6a19dc10767932ef9a5e9c755cfdbb5189ac8fb0684 + md5: bd77f8da987968ec3927990495dc22e4 + depends: + - libgcc-ng >=12 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 756742 + timestamp: 1695661547874 +- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda + sha256: 4f173af9e2299de7eee1af3d79e851bca28ee71e7426b377e841648b51d48614 + md5: c74d83614aec66227ae5199d98852aaf + depends: + - __glibc >=2.17,<3.0.a0 + - libaec >=1.1.4,<2.0a0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3710057 + timestamp: 1753357500665 +- pypi: https://files.pythonhosted.org/packages/d6/d8/f87ea6f42456254b48915970ed98e993110521e9263472840174d32c880d/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: hf-transfer + version: 0.1.9 + sha256: cdca9bfb89e6f8f281890cc61a8aff2d3cecaff7e1a4d275574d96ca70098557 + requires_python: '>=3.7' +- conda: https://conda.anaconda.org/conda-forge/linux-64/hf-xet-1.2.1-py310hb823017_0.conda + noarch: python + sha256: 11b840645c4d321d2b33c0e4cf8a1132bd01d1ccd8e019135c01fab7d4b96ca3 + md5: 33bd9525f97a50dc414892cba8996b51 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - _python_abi3_support 1.* + - cpython >=3.10 + - openssl >=3.5.4,<4.0a0 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/hf-xet?source=hash-mapping + size: 2728965 + timestamp: 1763772649407 +- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + sha256: 6ad78a180576c706aabeb5b4c8ceb97c0cb25f1e112d76495bff23e3779948ba + md5: 0a802cb9888dd14eeefc611f05c40b6e + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/hpack?source=hash-mapping + size: 30731 + timestamp: 1737618390337 +- conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + sha256: 04d49cb3c42714ce533a8553986e1642d0549a05dc5cc48e0d43ff5be6679a5b + md5: 4f14640d58e2cc0aa0819d9d8ba125bb + depends: + - python >=3.9 + - h11 >=0.16 + - h2 >=3,<5 + - sniffio 1.* + - anyio >=4.0,<5.0 + - certifi + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/httpcore?source=hash-mapping + size: 49483 + timestamp: 1745602916758 +- conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.7.1-py312h4c3975b_1.conda + sha256: 3579b3765e301ee7106bdf5f506f45ad46cc1568207c0c691aa7b82737ca2e82 + md5: 931af0f1dd27b0072f2865dd55640735 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/httptools?source=compressed-mapping + size: 101089 + timestamp: 1762504091918 +- conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + sha256: cd0f1de3697b252df95f98383e9edb1d00386bfdd03fdf607fa42fe5fcb09950 + md5: d6989ead454181f4f9bc987d3dc4e285 + depends: + - anyio + - certifi + - httpcore 1.* + - idna + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/httpx?source=hash-mapping + size: 63082 + timestamp: 1733663449209 +- conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-1.1.5-pyhd8ed1ab_0.conda + sha256: 2fb409b38281b8bebf4b71c71e6cf12ea65e65355ea24842325d12de98e0d203 + md5: 6e79eaaa2f095f37f11bb25b02ef211a + depends: + - filelock + - fsspec >=2023.5.0 + - hf-xet >=1.2.0,<2.0.0 + - httpx >=0.23.0,<1 + - packaging >=20.9 + - python >=3.10 + - pyyaml >=5.1 + - requests + - shellingham + - tqdm >=4.42.1 + - typer-slim + - typing-extensions >=3.7.4.3 + - typing_extensions >=3.7.4.3 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/huggingface-hub?source=hash-mapping + size: 321246 + timestamp: 1763658063634 +- pypi: https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl + name: humanfriendly + version: '10.0' + sha256: 1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477 + requires_dist: + - monotonic ; python_full_version == '2.7.*' + - pyreadline ; python_full_version < '3.8' and sys_platform == 'win32' + - pyreadline3 ; python_full_version >= '3.8' and sys_platform == 'win32' + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*' +- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + sha256: 77af6f5fe8b62ca07d09ac60127a30d9069fdc3c68d6b256754d0ffb1f7779f8 + md5: 8e6923fc12f1fe8f8c4e5c9f343256ac + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/hyperframe?source=hash-mapping + size: 17397 + timestamp: 1737618427549 +- pypi: https://files.pythonhosted.org/packages/c1/5f/f877d2cfcad41c0db98b120872f67b805fde5e6d407e584e9edfc9dec35c/icecream-2.1.8-py3-none-any.whl + name: icecream + version: 2.1.8 + sha256: 10b1c39dcb54cb28eb487bac56c35dbf9c2b2f406d24340e1a615c3f17274852 + requires_dist: + - colorama>=0.3.9 + - pygments>=2.2.0 + - executing>=2.1.0 + - asttokens>=2.0.1 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + sha256: 71e750d509f5fa3421087ba88ef9a7b9be11c53174af3aa4d06aff4c18b38e8e + md5: 8b189310083baabfb622af68fd9d3ae3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + purls: [] + size: 12129203 + timestamp: 1720853576813 +- pypi: https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl + name: id + version: 1.5.0 + sha256: f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658 + requires_dist: + - requests + - build ; extra == 'dev' + - bump>=1.3.2 ; extra == 'dev' + - id[test,lint] ; extra == 'dev' + - bandit ; extra == 'lint' + - interrogate ; extra == 'lint' + - mypy ; extra == 'lint' + - ruff<0.8.2 ; extra == 'lint' + - types-requests ; extra == 'lint' + - pytest ; extra == 'test' + - pytest-cov ; extra == 'test' + - pretend ; extra == 'test' + - coverage[toml] ; extra == 'test' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + sha256: ae89d0299ada2a3162c2614a9d26557a92aa6a77120ce142f8e0109bbf0342b0 + md5: 53abe63df7e10a6ba605dc5f9f961d36 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/idna?source=compressed-mapping + size: 50721 + timestamp: 1760286526795 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + sha256: c18ab120a0613ada4391b15981d86ff777b5690ca461ea7e9e49531e8f374745 + md5: 63ccfdc3a3ce25b027b8767eb722fca8 + depends: + - python >=3.9 + - zipp >=3.20 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/importlib-metadata?source=hash-mapping + size: 34641 + timestamp: 1747934053147 +- conda: https://conda.anaconda.org/conda-forge/linux-64/intel-gmmlib-22.8.2-hb700be7_0.conda + sha256: 6bc45d77fb625cb9cd154cfb8c0783a3f21123dd9512b91439675c5f6163c29e + md5: 478edf896b4dfca175c27b052d76fbc2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 999849 + timestamp: 1757639263833 +- conda: https://conda.anaconda.org/conda-forge/linux-64/intel-media-driver-25.3.4-hecca717_0.conda + sha256: 286679d4c175e8db2d047be766d1629f1ea5828bff9fe7e6aac2e6f0fad2b427 + md5: 7ae2034a0e2e24eb07468f1a50cdf0bb + depends: + - __glibc >=2.17,<3.0.a0 + - intel-gmmlib >=22.8.1,<23.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libva >=2.22.0,<3.0a0 + license: MIT + license_family: MIT + purls: [] + size: 8424610 + timestamp: 1757591682198 +- conda: https://conda.anaconda.org/conda-forge/noarch/itsdangerous-2.2.0-pyhd8ed1ab_1.conda + sha256: 1684b7b16eec08efef5302ce298c606b163c18272b69a62b666fbaa61516f170 + md5: 7ac5f795c15f288984e32add616cdc59 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/itsdangerous?source=hash-mapping + size: 19180 + timestamp: 1733308353037 +- pypi: https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl + name: jaraco-classes + version: 3.4.0 + sha256: f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790 + requires_dist: + - more-itertools + - sphinx>=3.5 ; extra == 'docs' + - jaraco-packaging>=9.3 ; extra == 'docs' + - rst-linker>=1.9 ; extra == 'docs' + - furo ; extra == 'docs' + - sphinx-lint ; extra == 'docs' + - jaraco-tidelift>=1.4 ; extra == 'docs' + - pytest>=6 ; extra == 'testing' + - pytest-checkdocs>=2.4 ; extra == 'testing' + - pytest-cov ; extra == 'testing' + - pytest-mypy ; extra == 'testing' + - pytest-enabler>=2.2 ; extra == 'testing' + - pytest-ruff>=0.2.1 ; extra == 'testing' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl + name: jaraco-context + version: 6.0.1 + sha256: f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4 + requires_dist: + - backports-tarfile ; python_full_version < '3.12' + - sphinx>=3.5 ; extra == 'doc' + - jaraco-packaging>=9.3 ; extra == 'doc' + - rst-linker>=1.9 ; extra == 'doc' + - furo ; extra == 'doc' + - sphinx-lint ; extra == 'doc' + - jaraco-tidelift>=1.4 ; extra == 'doc' + - pytest>=6,!=8.1.* ; extra == 'test' + - pytest-checkdocs>=2.4 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-mypy ; extra == 'test' + - pytest-enabler>=2.2 ; extra == 'test' + - portend ; extra == 'test' + - pytest-ruff>=0.2.1 ; sys_platform != 'cygwin' and extra == 'test' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/b4/09/726f168acad366b11e420df31bf1c702a54d373a83f968d94141a8c3fde0/jaraco_functools-4.3.0-py3-none-any.whl + name: jaraco-functools + version: 4.3.0 + sha256: 227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8 + requires_dist: + - more-itertools + - pytest>=6,!=8.1.* ; extra == 'test' + - jaraco-classes ; extra == 'test' + - sphinx>=3.5 ; extra == 'doc' + - jaraco-packaging>=9.3 ; extra == 'doc' + - rst-linker>=1.9 ; extra == 'doc' + - furo ; extra == 'doc' + - sphinx-lint ; extra == 'doc' + - jaraco-tidelift>=1.4 ; extra == 'doc' + - pytest-checkdocs>=2.4 ; extra == 'check' + - pytest-ruff>=0.2.1 ; sys_platform != 'cygwin' and extra == 'check' + - pytest-cov ; extra == 'cover' + - pytest-enabler>=2.2 ; extra == 'enabler' + - pytest-mypy ; extra == 'type' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/db/7e/da7b57a1f3af7303a0f3c8594d820fc0d3a9bbe3810a357eb21eb166e76b/jaxtyping-0.2.38-py3-none-any.whl + name: jaxtyping + version: 0.2.38 + sha256: bc209ab8ec29917b6f0c7dec4a8ea1fc276f7d94f25b71c01d1243ec2b21ae12 + requires_dist: + - wadler-lindig>=0.1.3 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl + name: jeepney + version: 0.9.0 + sha256: 97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683 + requires_dist: + - pytest ; extra == 'test' + - pytest-trio ; extra == 'test' + - pytest-asyncio>=0.17 ; extra == 'test' + - testpath ; extra == 'test' + - trio ; extra == 'test' + - async-timeout ; python_full_version < '3.11' and extra == 'test' + - trio ; extra == 'trio' + requires_python: '>=3.7' +- conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda + sha256: f1ac18b11637ddadc05642e8185a851c7fab5998c6f5470d716812fae943b2af + md5: 446bd6c8cb26050d528881df495ce646 + depends: + - markupsafe >=2.0 + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/jinja2?source=hash-mapping + size: 112714 + timestamp: 1741263433881 +- conda: https://conda.anaconda.org/conda-forge/linux-64/jsoncpp-1.9.6-hf42df4d_1.conda + sha256: ed4b1878be103deb2e4c6d0eea3c9bdddfd7fc3178383927dce7578fb1063520 + md5: 7bdc5e2cc11cb0a0f795bdad9732b0f2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: LicenseRef-Public-Domain OR MIT + purls: [] + size: 169093 + timestamp: 1733780223643 +- conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_8.conda + sha256: 305c22a251db227679343fd73bfde121e555d466af86e537847f4c8b9436be0d + md5: ff007ab0f0fdc53d245972bba8a6d40c + constrains: + - sysroot_linux-64 ==2.28 + license: LGPL-2.0-or-later AND LGPL-2.0-or-later WITH exceptions AND GPL-2.0-or-later + license_family: GPL + purls: [] + size: 1272697 + timestamp: 1752669126073 +- pypi: https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl + name: keyring + version: 25.7.0 + sha256: be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f + requires_dist: + - pywin32-ctypes>=0.2.0 ; sys_platform == 'win32' + - secretstorage>=3.2 ; sys_platform == 'linux' + - jeepney>=0.4.2 ; sys_platform == 'linux' + - importlib-metadata>=4.11.4 ; python_full_version < '3.12' + - jaraco-classes + - jaraco-functools + - jaraco-context + - pytest>=6,!=8.1.* ; extra == 'test' + - pyfakefs ; extra == 'test' + - sphinx>=3.5 ; extra == 'doc' + - jaraco-packaging>=9.3 ; extra == 'doc' + - rst-linker>=1.9 ; extra == 'doc' + - furo ; extra == 'doc' + - sphinx-lint ; extra == 'doc' + - jaraco-tidelift>=1.4 ; extra == 'doc' + - pytest-checkdocs>=2.4 ; extra == 'check' + - pytest-ruff>=0.2.1 ; sys_platform != 'cygwin' and extra == 'check' + - pytest-cov ; extra == 'cover' + - pytest-enabler>=3.4 ; extra == 'enabler' + - pytest-mypy>=1.0.1 ; extra == 'type' + - pygobject-stubs ; extra == 'type' + - shtab ; extra == 'type' + - types-pywin32 ; extra == 'type' + - shtab>=1.1.0 ; extra == 'completion' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + sha256: 0960d06048a7185d3542d850986d807c6e37ca2e644342dd0c72feefcf26c2a4 + md5: b38117a3c920364aff79f870c984b4a3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + purls: [] + size: 134088 + timestamp: 1754905959823 +- conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py312h0a2e395_2.conda + sha256: 170d76b7ac7197012bb048e1021482a7b2455f3592a5e8d97c96f285ebad064b + md5: 3a3004fddd39e3bb1a631b08d7045156 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping + size: 77682 + timestamp: 1762488738724 +- conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 + md5: 3f43953b7d3fb3aaa1d0d0723d91e368 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1370023 + timestamp: 1719463201255 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2 + sha256: aad2a703b9d7b038c0f745b853c6bb5f122988fe1a7a096e0e606d9cbec4eaab + md5: a8832b479f93521a9e7b5b743803be51 + depends: + - libgcc-ng >=12 + license: LGPL-2.0-only + license_family: LGPL + purls: [] + size: 508258 + timestamp: 1664996250081 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda + sha256: d6a61830a354da022eae93fa896d0991385a875c6bba53c82263a289deda9db8 + md5: 000e85703f0fd9594c81710dd5066471 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + license: MIT + license_family: MIT + purls: [] + size: 248046 + timestamp: 1739160907615 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda + sha256: 9e191baf2426a19507f1d0a17be0fdb7aa155cdf0f61d5a09c808e0a69464312 + md5: a6abd2796fc332536735f68ba23f7901 + depends: + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-64 2.45 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 725545 + timestamp: 1764007826689 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda + sha256: 412381a43d5ff9bbed82cd52a0bbca5b90623f62e41007c9c42d3870c60945ff + md5: 9344155d33912347b37f0ae6c410a835 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 264243 + timestamp: 1745264221534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/level-zero-1.26.0-hb700be7_0.conda + sha256: 14db841b0ad250cb71ec83814c98a09f02f1402bc2bf75c9811d7a924996cbab + md5: 114cd93e761af141d7f5fa5570048425 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 638517 + timestamp: 1762297603883 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda + sha256: dcd1429a1782864c452057a6c5bc1860f2b637dc20a2b7e6eacd57395bbceff8 + md5: 83b160d4da3e1e847bf044997621ed63 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - libabseil-static =20250512.1=cxx17* + - abseil-cpp =20250512.1 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1310612 + timestamp: 1750194198254 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda + sha256: 410ab78fe89bc869d435de04c9ffa189598ac15bb0fe1ea8ace8fb1b860a2aa3 + md5: 01ba04e414e47f95c03d6ddd81fd37be + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 36825 + timestamp: 1749993532943 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.25.1-h3f43e3d_1.conda + sha256: cb728a2a95557bb6a5184be2b8be83a6f2083000d0c7eff4ad5bbe5792133541 + md5: 3b0d184bc9404516d418d4509e418bdc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: LGPL-2.1-or-later + purls: [] + size: 53582 + timestamp: 1753342901341 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.25.1-h3f43e3d_1.conda + sha256: 2fc95060efc3d76547b7872875af0b7212d4b1407165be11c5f830aeeb57fc3a + md5: fd9cf4a11d07f0ef3e44fc061611b1ed + depends: + - __glibc >=2.17,<3.0.a0 + - libasprintf 0.25.1 h3f43e3d_1 + - libgcc >=14 + license: LGPL-2.1-or-later + purls: [] + size: 34734 + timestamp: 1753342921605 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libass-0.17.4-h96ad9f0_0.conda + sha256: 035eb8b54e03e72e42ef707420f9979c7427776ea99e0f1e3c969f92eb573f19 + md5: d3be7b2870bf7aff45b12ea53165babd + depends: + - libgcc >=13 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.1,<2.0a0 + - libfreetype >=2.13.3 + - libfreetype6 >=2.13.3 + - fribidi >=1.0.10,<2.0a0 + - libiconv >=1.18,<2.0a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - harfbuzz >=11.0.1 + license: ISC + purls: [] + size: 152179 + timestamp: 1749328931930 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.3.0-h6395336_2.conda + sha256: e3a44c0eda23aa15c9a8dfa8c82ecf5c8b073e68a16c29edd0e409e687056d30 + md5: c09c4ac973f7992ba0c6bb1aafd77bd4 + depends: + - __glibc >=2.17,<3.0.a0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - libgcc >=14 + - rav1e >=0.7.1,<0.8.0a0 + - svt-av1 >=3.1.2,<3.1.3.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 139399 + timestamp: 1756124751131 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-2_h5875eb1_mkl.conda + build_number: 2 + sha256: f539ae2aa405175a6dc5781e7d40ed5ecea44340904c834bf4a789785b0e9eac + md5: 6a1a4ec47263069b2dae3cfba106320c + depends: + - mkl >=2025.3.0,<2026.0a0 + constrains: + - liblapack 3.11.0 2*_mkl + - liblapacke 3.11.0 2*_mkl + - blas 2.302 mkl + - libcblas 3.11.0 2*_mkl + track_features: + - blas_mkl + - blas_mkl_2 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18948 + timestamp: 1763828429422 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hed09d94_6.conda + sha256: 40b334d77229fcceb51d911a153d7ab9ff4f6a6f90e938387bf29129ab956c58 + md5: 70675d70a76e1b5539b1f464fd5f02ba + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - liblzma >=5.8.1,<6.0a0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - boost-cpp <0.0a0 + license: BSL-1.0 + purls: [] + size: 2978265 + timestamp: 1763017293494 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + sha256: 318f36bd49ca8ad85e6478bd8506c88d82454cc008c1ac1c6bf00a3c42fa610e + md5: 72c8fd1af66bd67bf580645b426513ed + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 79965 + timestamp: 1764017188531 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + sha256: 12fff21d38f98bc446d82baa890e01fd82e3b750378fedc720ff93522ffb752b + md5: 366b40a69f0ad6072561c1d09301c886 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 34632 + timestamp: 1764017199083 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + sha256: a0c15c79997820bbd3fbc8ecf146f4fe0eca36cc60b62b63ac6cf78857f1dd0d + md5: 4ffbb341c8b616aa2494b6afb26a0c5f + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 298378 + timestamp: 1764017210931 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.77-h3ff7636_0.conda + sha256: 9517cce5193144af0fcbf19b7bd67db0a329c2cc2618f28ffecaa921a1cbe9d3 + md5: 09c264d40c67b82b49a3f3b89037bd2e + depends: + - __glibc >=2.17,<3.0.a0 + - attr >=2.5.2,<2.6.0a0 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 121429 + timestamp: 1762349484074 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-2_hfef963f_mkl.conda + build_number: 2 + sha256: 9c232b9527e8861fb708e30a0c51750ee3f33be393ab3f67b621287a53e456d6 + md5: 62ffd188ee5c953c2d6ac54662c158a7 + depends: + - libblas 3.11.0 2_h5875eb1_mkl + constrains: + - liblapack 3.11.0 2*_mkl + - liblapacke 3.11.0 2*_mkl + - blas 2.302 mkl + track_features: + - blas_mkl + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18557 + timestamp: 1763828437220 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.6-default_h99862b1_0.conda + sha256: 314f4c4980c18138659fdd0c75385c1a88ff6bef2ac7890d1df76f9b2d5e1a5f + md5: 0fcc9b4d3fc5e5010a7098318d9b7971 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm21 >=21.1.6,<21.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 21054536 + timestamp: 1763564022522 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.6-default_h746c552_0.conda + sha256: 83d89825255c0d0153687a74b69c460292d81876f5a71e94e22110702ad3e875 + md5: f5b64315835b284c7eb5332202b1e14b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm21 >=21.1.6,<21.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 12339318 + timestamp: 1763564209593 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-12.9.1.4-h676940d_1.conda + sha256: 671a5204ae983c775d17b3f55b2b0f8ee8cb73b8f0c8b6036070dfadc2770707 + md5: af0df9bc982b5ed2c67e8f5062d1f8c1 + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-nvrtc + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 467746725 + timestamp: 1761086109565 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-dev-12.9.1.4-h676940d_1.conda + sha256: 2cf8b9be18b0d1b2ae39ae51c89f34c74da2af4f8eb97f96327d32095ff986ab + md5: f90f4ff087ac29005c6989ea0fb2735a + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-crt-dev_linux-64 + - cuda-cudart-dev_linux-64 + - cuda-version >=12.9,<12.10.0a0 + - libcublas 12.9.1.4 h676940d_1 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - libcublas-static >=12.9.1.4 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 92793 + timestamp: 1761086831258 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcudnn-9.10.2.21-hf7e9902_0.conda + sha256: dc6b89e874867b2cdf08224059bd1543cbb72ed646da177c1454596469c9a4bb + md5: a178a1f3642521f104ecceeefa138d01 + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-nvrtc + - cuda-version >=12,<13.0a0 + - libcublas + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + constrains: + - libcudnn-jit <0a + license: LicenseRef-cuDNN-Software-License-Agreement + purls: [] + size: 526823453 + timestamp: 1762823414388 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcudnn-dev-9.10.2.21-h58dd1b1_0.conda + sha256: e9fef18b943a8181427734bc9fada8a594e3a8391fa2a8d59d980acfe1c2cf04 + md5: 7d7a47d067261531c3089dcec326d6fa + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-version >=12,<13.0a0 + - libcudnn 9.10.2.21 hf7e9902_0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - libcudnn-jit-dev <0a + license: LicenseRef-cuDNN-Software-License-Agreement + purls: [] + size: 44188 + timestamp: 1762823889020 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcudss-0.7.1.4-h58dd1b1_0.conda + sha256: 4b4658e53e8479c6f88961ea0c7a009805a0f12d89ace2434d7de4c3832244f5 + md5: d71b1cf78714f31f1264591cdb7ce97d + depends: + - __glibc >=2.28,<3.0.a0 + - _openmp_mutex >=4.5 + - cuda-version >=12,<13.0a0 + - libcublas + - libgcc >=14 + - libstdcxx >=14 + constrains: + - libcudss0 <0.0.0a0 + - libcudss-commlayer-nccl 0.7.1.4 h4d09622_0 + - libcudss-commlayer-mpi 0.7.1.4 h09b4041_0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 59962710 + timestamp: 1761105490979 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcudss-dev-0.7.1.4-hee47b17_0.conda + sha256: bd5d36f774e3e6615ed79c1748cdb59f43c09138f6937ffea6aa044c9d544850 + md5: f35b7e71241f803740fa6934ba7e1752 + depends: + - libcudss 0.7.1.4 h58dd1b1_0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 26244 + timestamp: 1761105560044 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcufft-11.4.1.4-hecca717_1.conda + sha256: 62d4214c182c89cfb02271a42eaac56a41f50bbbea3b0d795a8e33f167a39a4e + md5: 75ae571353ec92c8f34d4cf6ec6ba264 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 162080769 + timestamp: 1761098842719 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcufft-dev-11.4.1.4-hecca717_1.conda + sha256: 1cec99c995b321f4184075194bf0306584092e5ac4bdd39c48a22b56b0ab3cee + md5: a5da289982801cc89244633a4775f055 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libcufft 11.4.1.4 hecca717_1 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - libcufft-static >=11.4.1.4 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 35188 + timestamp: 1761099156569 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.14.1.1-hbc026e6_1.conda + sha256: 5fa43e8a8d335fc0c3a6aeb2e7b0debc7d8495b8a60a56ac30f23b0e852ab74a + md5: cab1818eada3952ed09c8dcbb7c26af7 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + - rdma-core >=59.0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 969845 + timestamp: 1761098818759 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-dev-1.14.1.1-hecca717_1.conda + sha256: 3a7c726419017319df149ff67ea3efae37d668ecfc1aa2ac3273b21c4c76d68b + md5: 74a93e4c8fd24d535abc546c6fee2155 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libcufile 1.14.1.1 hbc026e6_1 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - libcufile-static >=1.14.1.1 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 36377 + timestamp: 1761098841141 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda + sha256: cb83980c57e311783ee831832eb2c20ecb41e7dee6e86e8b70b8cef0e43eab55 + md5: d4a250da4737ee127fb1fa6452a9002e + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 4523621 + timestamp: 1749905341688 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurand-10.3.10.19-h676940d_1.conda + sha256: 3d40daf956b220cc367a6306ede1e259446fb844051bcfed87c46539cc1aaf03 + md5: 2a91559a9345bedf09af8b7903deb6e6 + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 46221876 + timestamp: 1761098855347 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurand-dev-10.3.10.19-h676940d_1.conda + sha256: b506f93e7bea6d0e060f09f4bac6db3f57586084ac309db0d44b3756f5b0bc80 + md5: fc716aaff5af15b80ccbd28b3e67672c + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libcurand 10.3.10.19 h676940d_1 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - libcurand-static >=10.3.10.19 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 249874 + timestamp: 1761098955940 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_0.conda + sha256: 100e29ca864c32af15a5cc354f502d07b2600218740fdf2439fa7d66b50b3529 + md5: 01e149d4a53185622dc2e788281961f2 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=14 + - libnghttp2 >=1.67.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + purls: [] + size: 460366 + timestamp: 1762333743748 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcusolver-11.7.5.82-h676940d_2.conda + sha256: 8691cf6b1585cf6251663029e00485da5a912f6ca0ff7e5c31a6d8d604b29253 + md5: bb6e31a0daa64ede76fe8d3fff01c06f + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libcublas >=12.9.1.4,<12.10.0a0 + - libcusparse >=12.5.10.65,<12.6.0a0 + - libgcc >=14 + - libnvjitlink >=12.9.86,<13.0a0 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 205149446 + timestamp: 1761098826989 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcusolver-dev-11.7.5.82-h676940d_2.conda + sha256: f2a974af90ecf6e47c2780741b5351c5f21d20bf6b9fb4448966f07d23ad27b8 + md5: 0fe12e558abf507458bcec839e29778d + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libcusolver 11.7.5.82 h676940d_2 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - libcusolver-static >=11.7.5.82 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 61710 + timestamp: 1761099187356 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcusparse-12.5.10.65-hecca717_2.conda + sha256: 7b511549a22df408d36dadbeabdfd9c35b124d9d6f000b29ffcbe4b38b7faeb7 + md5: 890ebfaad48c887d3d82847ec9d6bc79 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libnvjitlink >=12.9.86,<13.0a0 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 208846028 + timestamp: 1761069913328 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcusparse-dev-12.5.10.65-hecca717_2.conda + sha256: c6d7ec3ccef6dce988c3acc93198973ec9ff5aa9ffe99e07dd953c2d3b409a3b + md5: db94469fbd554c107acc3afd0af5d8ec + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libcusparse 12.5.10.65 hecca717_2 + - libgcc >=14 + - libnvjitlink >=12.9.86,<13.0a0 + - libstdcxx >=14 + constrains: + - libcusparse-static >=12.5.10.65 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 52779 + timestamp: 1761070300821 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libde265-1.0.15-h00ab1b0_0.conda + sha256: 7cf7e294e1a7c8219065885e186d8f52002fb900bf384d815f159b5874204e3d + md5: 407fee7a5d7ab2dca12c9ca7f62310ad + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: LGPL-3.0-or-later + license_family: LGPL + purls: [] + size: 411814 + timestamp: 1703088639063 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + sha256: aa8e8c4be9a2e81610ddf574e05b64ee131fab5e0e3693210c9d6d2fba32c680 + md5: 6c77a605a7a689d17d4819c0f8ac9a00 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 73490 + timestamp: 1761979956660 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda + sha256: c076a213bd3676cc1ef22eeff91588826273513ccc6040d9bea68bccdc849501 + md5: 9314bc5a1fe7d1044dc9dfd3ef400535 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpciaccess >=0.18,<0.19.0a0 + license: MIT + license_family: MIT + purls: [] + size: 310785 + timestamp: 1757212153962 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + sha256: d789471216e7aba3c184cd054ed61ce3f6dac6f87a50ec69291b9297f8c18724 + md5: c277e0a4d549b03ac1e9d6cbbe3d017b + depends: + - ncurses + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - ncurses >=6.5,<7.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 134676 + timestamp: 1738479519902 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda + sha256: 7fd5408d359d05a969133e47af580183fbf38e2235b562193d427bb9dad79723 + md5: c151d5eb730e9b7480e6d48c0fc44048 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + license: LicenseRef-libglvnd + purls: [] + size: 44840 + timestamp: 1731330973553 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 + md5: 172bf1cd1ff8629f2b1179945ed45055 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 112766 + timestamp: 1702146165126 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda + sha256: 1e1b08f6211629cbc2efe7a5bca5953f8f6b3cae0eeb04ca4dacee1bd4e2db2f + md5: 8b09ae86839581147ef2e5c5e229d164 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - expat 2.7.3.* + license: MIT + license_family: MIT + purls: [] + size: 76643 + timestamp: 1763549731408 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda + sha256: 25cbdfa65580cfab1b8d15ee90b4c9f1e0d72128f1661449c9a999d341377d54 + md5: 35f29eec58405aaf55e01cb470d8c26a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 57821 + timestamp: 1760295480630 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda + sha256: 65908b75fa7003167b8a8f0001e11e58ed5b1ef5e98b96ab2ba66d7c1b822c7d + md5: ee48bf17cc83a00f59ca1494d5646869 + depends: + - gettext >=0.21.1,<1.0a0 + - libgcc-ng >=12 + - libogg 1.3.* + - libogg >=1.3.4,<1.4.0a0 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 394383 + timestamp: 1687765514062 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda + sha256: 4641d37faeb97cf8a121efafd6afd040904d4bca8c46798122f417c31d5dfbec + md5: f4084e4e6577797150f9b04a4560ceb0 + depends: + - libfreetype6 >=2.14.1 + license: GPL-2.0-only OR FTL + purls: [] + size: 7664 + timestamp: 1757945417134 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda + sha256: 4a7af818a3179fafb6c91111752954e29d3a2a950259c14a2fc7ba40a8b03652 + md5: 8e7251989bca326a28f4a5ffbd74557a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpng >=1.6.50,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - freetype >=2.14.1 + license: GPL-2.0-only OR FTL + purls: [] + size: 386739 + timestamp: 1757945416744 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_12.conda + sha256: b2c57cebdcf243f71d96a6c934c643aebb5a38093eb61d8d1aa67dc2e03c9244 + md5: b3137606149c607becd89faed5ee4ec6 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgomp 15.2.0 he0feb66_12 + - libgcc-ng ==15.2.0=*_12 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 1043771 + timestamp: 1764036113005 +- conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-14.3.0-hf649bbc_112.conda + sha256: 437e1c25abfa006b75430ed771c5bd52b045a79ce4e98bd57d5040afbb538458 + md5: 910016d7438c380f0c583e043b4d2f3a + depends: + - __unix + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 3082754 + timestamp: 1764036255074 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_12.conda + sha256: b45f45c2362f9e8aaa5b875a7e612f4b4562bd136611a93b7642b45f7d1eaec3 + md5: 3c4b621138fcfc95ba219344b8a0d91f + depends: + - libgcc 15.2.0 he0feb66_12 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 29191 + timestamp: 1764036122114 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.25.1-h3f43e3d_1.conda + sha256: 50a9e9815cf3f5bce1b8c5161c0899cc5b6c6052d6d73a4c27f749119e607100 + md5: 2f4de899028319b27eb7a4023be5dfd2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 188293 + timestamp: 1753342911214 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.25.1-h3f43e3d_1.conda + sha256: c7ea10326fd450a2a21955987db09dde78c99956a91f6f05386756a7bfe7cc04 + md5: 3f7a43b3160ec0345c9535a9f0d7908e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgettextpo 0.25.1 h3f43e3d_1 + - libiconv >=1.18,<2.0a0 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 37407 + timestamp: 1753342931100 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_13.conda + sha256: bf0a0698318561a0027af6fc2aede086799414952c84d43be5c2ac720658f247 + md5: 0d1067fda8911da519d7fc15bced6e3e + depends: + - libgfortran5 15.2.0 h68bc16d_13 + constrains: + - libgfortran-ng ==15.2.0=*_13 + license: GPL-3.0-only WITH GCC-exception-3.1 + purls: [] + size: 26951 + timestamp: 1764187818201 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_13.conda + sha256: 0f0b0f2b7a345eee5cdc7864a7794e196006b2b45d377a1ad2039afed054eb11 + md5: f5cd1c419503abab5196946dd9e7d99c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15.2.0 + constrains: + - libgfortran 15.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + purls: [] + size: 2482657 + timestamp: 1764187793388 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda + sha256: dc2752241fa3d9e40ce552c1942d0a4b5eeb93740c9723873f6fcf8d39ef8d2d + md5: 928b8be80851f5d8ffb016f9c81dae7a + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + - libglx 1.7.0 ha4b6fd6_2 + license: LicenseRef-libglvnd + purls: [] + size: 134712 + timestamp: 1731330998354 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.2-h32235b2_0.conda + sha256: 918306d6ed211ab483e4e19368e5748b265d24e75c88a1c66a61f72b9fa30b29 + md5: 0cb0612bc9cb30c62baf41f9d600611b + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pcre2 >=10.46,<10.47.0a0 + constrains: + - glib 2.86.2 *_0 + license: LGPL-2.1-or-later + purls: [] + size: 3974801 + timestamp: 1763672326986 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglu-9.0.3-h5888daf_1.conda + sha256: a0105eb88f76073bbb30169312e797ed5449ebb4e964a756104d6e54633d17ef + md5: 8422fcc9e5e172c91e99aef703b3ce65 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libopengl >=1.7.0,<2.0a0 + - libstdcxx >=13 + license: SGI-B-2.0 + purls: [] + size: 325262 + timestamp: 1748692137626 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda + sha256: 1175f8a7a0c68b7f81962699751bb6574e6f07db4c9f72825f978e3016f46850 + md5: 434ca7e50e40f4918ab701e3facd59a0 + depends: + - __glibc >=2.17,<3.0.a0 + license: LicenseRef-libglvnd + purls: [] + size: 132463 + timestamp: 1731330968309 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda + sha256: 2d35a679624a93ce5b3e9dd301fff92343db609b79f0363e6d0ceb3a6478bfa7 + md5: c8013e438185f33b13814c5c488acd5c + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + - xorg-libx11 >=1.8.10,<2.0a0 + license: LicenseRef-libglvnd + purls: [] + size: 75504 + timestamp: 1731330988898 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_12.conda + sha256: 49c313bb040d04512c5e29da169dec58c51c6535dc97cc5808d3614bc048723e + md5: 4881b9b732ee8b673cd46875d7d36fc6 + depends: + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 605231 + timestamp: 1764036022611 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libheif-1.19.7-gpl_hc18d805_100.conda + sha256: ec9797d57088aeed7ca4905777d4f3e70a4dbe90853590eef7006b0ab337af3f + md5: 1db2693fa6a50bef58da2df97c5204cb + depends: + - __glibc >=2.17,<3.0.a0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - libavif16 >=1.2.0,<2.0a0 + - libde265 >=1.0.15,<1.0.16.0a0 + - libgcc >=13 + - libstdcxx >=13 + - x265 >=3.5,<3.6.0a0 + license: LGPL-3.0-or-later + license_family: LGPL + purls: [] + size: 596714 + timestamp: 1741306859216 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda + sha256: f7fbc792dbcd04bf27219c765c10c239937b34c6c1a1f77a5827724753e02da1 + md5: c01021ae525a76fe62720c7346212d74 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 2450642 + timestamp: 1757624375958 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f + md5: 915f5995e94f60e9a4826e0b0920ee88 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-only + purls: [] + size: 790176 + timestamp: 1754908768807 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda + sha256: cc9aba923eea0af8e30e0f94f2ad7156e2984d80d1e8e7fe6be5a1f257f0eb32 + md5: 8397539e3a0bbd1695584fb4f927485a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 633710 + timestamp: 1762094827865 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-2_h5e43f62_mkl.conda + build_number: 2 + sha256: 18166b664d39650d3482420a7349082d4a8d79cee5ca69562160795ab6b0a808 + md5: 4f33d79eda3c82c95a54e8c2981adddb + depends: + - libblas 3.11.0 2_h5875eb1_mkl + constrains: + - liblapacke 3.11.0 2*_mkl + - blas 2.302 mkl + - libcblas 3.11.0 2*_mkl + track_features: + - blas_mkl + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18542 + timestamp: 1763828444709 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-2_hdba1596_mkl.conda + build_number: 2 + sha256: fb9d2c1ec2e92f6cd6cc303f322ba8a96b526702ac5713f3c640925ca1792534 + md5: 96dea51ff1435bd823020e25fd02da59 + depends: + - libblas 3.11.0 2_h5875eb1_mkl + - libcblas 3.11.0 2_hfef963f_mkl + - liblapack 3.11.0 2_h5e43f62_mkl + constrains: + - blas 2.302 mkl + track_features: + - blas_mkl + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18595 + timestamp: 1763828452171 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.6-hf7376ad_0.conda + sha256: 23010386efb545d68acbc4f9216c45f2b70a2f5398a6f389d70c9fee103648c4 + md5: 8aa154f30e0bc616cbde9794710e0be2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 44320002 + timestamp: 1763523422320 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzf-3.6-hb9d3cd8_3.conda + sha256: f4a4c1a0945dc97e22ddf495cbd38bc230b76a5fe6cec9f54ba0b00960329fb5 + md5: 5fff3766d959c2a6fd424ad325602383 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 18687 + timestamp: 1728095215498 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + sha256: f2591c0069447bbe28d4d696b7fcb0c5bd0b4ac582769b89addbcf26fb3430d8 + md5: 1a580f7796c7bf6393fddb8bbbde58dc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - xz 5.8.1.* + license: 0BSD + purls: [] + size: 112894 + timestamp: 1749230047870 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmagma-2.9.0-ha7672b3_4.conda + sha256: 34da7d270ee89143679afcc1f6b8f32289b954bfadfb621482d6d8e23113b108 + md5: 7bbb8b3e5530cc049b2e2b2e2797e09d + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - cuda-cudart >=12.9.79,<13.0a0 + - cuda-version >=12.9,<13 + - libblas >=3.9.0,<4.0a0 + - libcublas >=12.9.1.4,<13.0a0 + - libcusparse >=12.5.10.65,<13.0a0 + - libgcc >=14 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 554495629 + timestamp: 1764086162638 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda + sha256: e9a8668212719a91a6b0348db05188dfc59de5a21888db13ff8510918a67b258 + md5: 3ccff1066c05a1e6c221356eecc40581 + depends: + - __glibc >=2.17,<3.0.a0 + - attr >=2.5.2,<2.6.0a0 + - blosc >=1.21.6,<2.0a0 + - bzip2 >=1.0.8,<2.0a0 + - hdf4 >=4.2.15,<4.2.16.0a0 + - hdf5 >=1.14.6,<1.14.7.0a0 + - libaec >=1.1.4,<2.0a0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - libzip >=1.11.2,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.2,<4.0a0 + - zlib + - zstd >=1.5.7,<1.6.0a0 + license: MIT + license_family: MIT + purls: [] + size: 871447 + timestamp: 1757977084313 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + sha256: a4a7dab8db4dc81c736e9a9b42bdfd97b087816e029e221380511960ac46c690 + md5: b499ce4b026493a13774bcf0f4c33849 + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.5,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.2,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 666600 + timestamp: 1756834976695 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnl-3.11.0-hb9d3cd8_0.conda + sha256: ba7c5d294e3d80f08ac5a39564217702d1a752e352e486210faff794ac5001b4 + md5: db63358239cbe1ff86242406d440e44a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 741323 + timestamp: 1731846827427 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + sha256: 927fe72b054277cde6cb82597d0fcf6baf127dcbce2e0a9d8925a68f1265eef5 + md5: d864d34357c3b65a4b731f78c0801dc4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-only + license_family: GPL + purls: [] + size: 33731 + timestamp: 1750274110928 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda + sha256: 3b3f19ced060013c2dd99d9d46403be6d319d4601814c772a3472fe2955612b0 + md5: 7c7927b404672409d9917d49bff5f2d6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + purls: [] + size: 33418 + timestamp: 1734670021371 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnvjitlink-12.9.86-hecca717_2.conda + sha256: 3b1c851f4fc42d347ce1c1606bdd195343a47f121e0fceb7a1f1e5aa1d497da9 + md5: 3461b0f2d5cbb7973d361f9e85241d98 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 30515495 + timestamp: 1760723776293 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnvjpeg-12.4.0.76-hecca717_1.conda + sha256: 14a57af0552fc9a4c929b4e278d05f9d431ee733264fac8e72eac9086509fad0 + md5: 91d7130481d3b78d3c19126e7c9465e5 + depends: + - __glibc >=2.17,<3.0.a0 + - cuda-version >=12.9,<12.10.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 3582778 + timestamp: 1761098854056 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnvptxcompiler-dev-12.9.86-ha770c72_2.conda + sha256: 1e7a7b34f8639a5feb75ba864127059e4d83edfe1a516547f0dbb9941e7b8f8b + md5: 3fd926c321c6dbf386aa14bd8b125bfb + depends: + - cuda-version >=12.9,<12.10.0a0 + - libnvptxcompiler-dev_linux-64 12.9.86 ha770c72_2 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 27046 + timestamp: 1753975516342 +- conda: https://conda.anaconda.org/conda-forge/noarch/libnvptxcompiler-dev_linux-64-12.9.86-ha770c72_2.conda + sha256: 17952c32eac197a59c119fdf3fb6f08c6a29c225a80bae141ac904ad212b87dd + md5: a66a909acf08924aced622903832a937 + depends: + - cuda-version >=12.9,<12.10.0a0 + license: LicenseRef-NVIDIA-End-User-License-Agreement + purls: [] + size: 14422867 + timestamp: 1753975387297 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-hd0c01bc_1.conda + sha256: ffb066ddf2e76953f92e06677021c73c85536098f1c21fcd15360dbc859e22e4 + md5: 68e52064ed3897463c0e958ab5c8f91b + depends: + - libgcc >=13 + - __glibc >=2.17,<3.0.a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 218500 + timestamp: 1745825989535 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda + sha256: 215086c108d80349e96051ad14131b751d17af3ed2cb5a34edd62fa89bfe8ead + md5: 7df50d44d4a14d6c31a2c54f2cd92157 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_2 + license: LicenseRef-libglvnd + purls: [] + size: 50757 + timestamp: 1731330993524 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-2025.2.0-hb617929_1.conda + sha256: 235e7d474c90ad9d8955401b8a91dbe373aa1dc65db3c8232a5e22e4eaf41976 + md5: 1da20cc4ff32dc74424dec68ec087dba + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - pugixml >=1.15,<1.16.0a0 + - tbb >=2021.13.0 + purls: [] + size: 6244771 + timestamp: 1753211097492 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-batch-plugin-2025.2.0-hed573e4_1.conda + sha256: 193f760e828b0dd5168dd1d28580d4bf429c5f14a4eee5e0c02ff4c6d4cf8093 + md5: 94f9d17be1d658213b66b22f63cc6578 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - tbb >=2021.13.0 + purls: [] + size: 114760 + timestamp: 1753211116381 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-auto-plugin-2025.2.0-hed573e4_1.conda + sha256: a6f9f996e64e6d2f295f017a833eda7018ff58b6894503272d72f0002dfd6f33 + md5: 071b3a82342715a411f216d379ab6205 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - tbb >=2021.13.0 + purls: [] + size: 250500 + timestamp: 1753211127339 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-hetero-plugin-2025.2.0-hd41364c_1.conda + sha256: f43f9049338ef9735b6815bac3f483d1e3adddecbfdeb13be365bc3f601fe156 + md5: 77c0c7028a8110076d40314dc7b1fa98 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - pugixml >=1.15,<1.16.0a0 + purls: [] + size: 194815 + timestamp: 1753211138624 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-cpu-plugin-2025.2.0-hb617929_1.conda + sha256: a4a1cd320fa010a45d01f438dc3431b7a60271ee19188a901f884399fe744268 + md5: e4cc6db5bdc8b554c06bf569de57f85f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - pugixml >=1.15,<1.16.0a0 + - tbb >=2021.13.0 + purls: [] + size: 12377488 + timestamp: 1753211149903 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-gpu-plugin-2025.2.0-hb617929_1.conda + sha256: 03ebf700586775144ca5913f401393a386b9a1d7a7cfcba4494830063ca5eb92 + md5: b846fe6c158ca417e246122172d68d3a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - ocl-icd >=2.3.3,<3.0a0 + - pugixml >=1.15,<1.16.0a0 + - tbb >=2021.13.0 + purls: [] + size: 10815480 + timestamp: 1753211182626 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-intel-npu-plugin-2025.2.0-hb617929_1.conda + sha256: b6dbc342293d6ce0c7b37c9f29f734b3e1856cff9405a02fb33cedd1b36528e6 + md5: 86fd4c25f6accaf646c86adf0f1382d3 + depends: + - __glibc >=2.17,<3.0.a0 + - level-zero >=1.23.1,<2.0a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - pugixml >=1.15,<1.16.0a0 + - tbb >=2021.13.0 + purls: [] + size: 1261488 + timestamp: 1753211212823 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-ir-frontend-2025.2.0-hd41364c_1.conda + sha256: 334733396d4c9a9b2b2d7d7d850e8ee8deca1f9becd0368d106010076ceb20ca + md5: 75e595d9f2019a60f6dcb500266da615 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + - pugixml >=1.15,<1.16.0a0 + purls: [] + size: 204890 + timestamp: 1753211224567 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-onnx-frontend-2025.2.0-h1862bb8_1.conda + sha256: 3937b028e7192ed3805581ac0ea171725843056c8544537754fad45a1791e864 + md5: 68f5ad9d8e3979362bb9dfc9388980aa + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + purls: [] + size: 1724503 + timestamp: 1753211235981 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-paddle-frontend-2025.2.0-h1862bb8_1.conda + sha256: c7ac3d4187323ab37ef62ec0896a41c8ca7da426c7f587494c72fe74852269e5 + md5: a032d03468dee9fb5b8eaf635b4571c2 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + purls: [] + size: 744746 + timestamp: 1753211248776 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-pytorch-frontend-2025.2.0-hecca717_1.conda + sha256: 2d4a680a16509b8dd06ccd7a236655e46cc7c242bb5b6e88b83a834b891658db + md5: cd40cf2d10a3279654c9769f3bc8caf5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + purls: [] + size: 1243134 + timestamp: 1753211260154 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-frontend-2025.2.0-h0767aad_1.conda + sha256: 311ec1118448a28e76f0359c4393c7f7f5e64761c48ac7b169bf928a391eae77 + md5: f71c6b4e342b560cc40687063ef62c50 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + - snappy >=1.2.2,<1.3.0a0 + purls: [] + size: 1325059 + timestamp: 1753211272484 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-lite-frontend-2025.2.0-hecca717_1.conda + sha256: 581f4951e645e820c4a6ffe40fb0174b56d6e31fb1fefd2d64913fea01f8f69e + md5: fd9dacd7101f80ff1110ea6b76adb95d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libopenvino 2025.2.0 hb617929_1 + - libstdcxx >=14 + purls: [] + size: 497047 + timestamp: 1753211285617 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopus-1.5.2-hd0c01bc_0.conda + sha256: 786d43678d6d1dc5f88a6bad2d02830cfd5a0184e84a8caa45694049f0e3ea5f + md5: b64523fb87ac6f87f0790f324ad43046 + depends: + - libgcc >=13 + - __glibc >=2.17,<3.0.a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 312472 + timestamp: 1744330953241 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda + sha256: 0bd91de9b447a2991e666f284ae8c722ffb1d84acb594dbd0c031bd656fa32b2 + md5: 70e3400cbbfa03e96dcde7fc13e38c7b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 28424 + timestamp: 1749901812541 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.51-h421ea60_0.conda + sha256: 1eb769c0f2778d07428947f64272592cc2d3b9bce63b41600abe5dc2b683d829 + md5: d8b81203d08435eb999baa249427884e + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 317576 + timestamp: 1763764145606 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.1-h5c52fec_1.conda + sha256: 22a2e4a5054143641545e87ec42aa7da745c6823587aa319dc86c3486698fa2c + md5: 638350cf5da41f3651958876a2104992 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=14 + - openldap >=2.6.10,<2.7.0a0 + - openssl >=3.5.4,<4.0a0 + license: PostgreSQL + purls: [] + size: 2907791 + timestamp: 1763518791620 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_2.conda + sha256: 1679f16c593d769f3dab219adb1117cbaaddb019080c5a59f79393dc9f45b84f + md5: 94cb88daa0892171457d9fdc69f43eca + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4645876 + timestamp: 1760550892361 +- conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.60.0-h61e6d4b_0.conda + sha256: 960b137673b2b8293e2a12d194add72967b3bf12fcdf691e7ad8bd5c8318cec3 + md5: 91e6d4d684e237fba31b9815c4b40edf + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.4,<2.0a0 + - gdk-pixbuf >=2.44.3,<3.0a0 + - libgcc >=14 + - libglib >=2.86.0,<3.0a0 + - libxml2-16 >=2.14.6 + - pango >=1.56.4,<2.0a0 + constrains: + - __glibc >=2.17 + license: LGPL-2.1-or-later + purls: [] + size: 3421977 + timestamp: 1759327942156 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-14.3.0-h8f1669f_12.conda + sha256: dfdf41caf1035455023c272f4ccc80deb32fe8098cd7d0d89cf8066b20466d17 + md5: 97895f6884a3abbb910b813c1420fbc4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14.3.0 + - libstdcxx >=14.3.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 7574061 + timestamp: 1764036385910 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda + sha256: f709cbede3d4f3aee4e2f8d60bd9e256057f410bd60b8964cb8cf82ec1457573 + md5: ef1910918dd895516a769ed36b5b3a4e + depends: + - lame >=3.100,<3.101.0a0 + - libflac >=1.4.3,<1.5.0a0 + - libgcc-ng >=12 + - libogg >=1.3.4,<1.4.0a0 + - libopus >=1.3.1,<2.0a0 + - libstdcxx-ng >=12 + - libvorbis >=1.3.7,<1.4.0a0 + - mpg123 >=1.32.1,<1.33.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 354372 + timestamp: 1695747735668 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + sha256: 0105bd108f19ea8e6a78d2d994a6d4a8db16d19a41212070d2d1d48a63c34161 + md5: a587892d3c13b6621a6091be690dbca2 + depends: + - libgcc-ng >=12 + license: ISC + purls: [] + size: 205978 + timestamp: 1716828628198 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.0-hee844dc_0.conda + sha256: 4c992dcd0e34b68f843e75406f7f303b1b97c248d18f3c7c330bdc0bc26ae0b3 + md5: 729a572a3ebb8c43933b30edcc628ceb + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: blessing + purls: [] + size: 945576 + timestamp: 1762299687230 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + sha256: fa39bfd69228a13e553bd24601332b7cfeb30ca11a3ca50bb028108fe90a7661 + md5: eecce068c7e4eddeb169591baac20ac4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 304790 + timestamp: 1745608545575 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_12.conda + sha256: 2954f7b21ad6f0f1b9b5eabf0595039c425f6f6267087e58310dc4855fee8383 + md5: b8ef46cab65ab6676c7d5c9581b17ebf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 15.2.0 he0feb66_12 + constrains: + - libstdcxx-ng ==15.2.0=*_12 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 5854408 + timestamp: 1764036151142 +- conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-14.3.0-h9f08a49_112.conda + sha256: 6755267e98707f12c1f1b38badfc86fbe880d84a9ac55410125c157cf449adde + md5: 827e707207b2b7b66b598db9dc7b082a + depends: + - __unix + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 19704733 + timestamp: 1764036282587 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_12.conda + sha256: 7540d3b3577b058962d110dfa08ec2a278254dd6f9397d33ad0ede7bf222094e + md5: ac15e685fa88f7d070b60b396dd91017 + depends: + - libstdcxx 15.2.0 h934c35e_12 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 29230 + timestamp: 1764036201717 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.10-hd0affe5_2.conda + sha256: b30c06f60f03c2cf101afeb3452f48f12a2553b4cb631c9460c8a8ccf0813ae5 + md5: b04e0a2163a72588a40cde1afd6f2d18 + depends: + - __glibc >=2.17,<3.0.a0 + - libcap >=2.77,<2.78.0a0 + - libgcc >=14 + license: LGPL-2.1-or-later + purls: [] + size: 491211 + timestamp: 1763011323224 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtheora-1.1.1-h4ab18f5_1006.conda + sha256: 50c8cd416ac8425e415264de167b41ae8442de22a91098dfdd993ddbf9f13067 + md5: 553281a034e9cf8693c9df49f6c78ea1 + depends: + - libgcc-ng >=12 + - libogg 1.3.* + - libogg >=1.3.5,<1.4.0a0 + - libvorbis 1.3.* + - libvorbis >=1.3.7,<1.4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 328924 + timestamp: 1719667859099 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + sha256: e5f8c38625aa6d567809733ae04bb71c161a42e44a9fa8227abe61fa5c60ebe0 + md5: cd5a90476766d53e901500df9215e927 + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libgcc >=14 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libstdcxx >=14 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + purls: [] + size: 435273 + timestamp: 1762022005702 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.8.0-cuda129_mkl_hf53477d_302.conda + sha256: cefb16d5f87c0e99cfc940c465c0384620d2fc4934b408c73ac74be540f82673 + md5: 9e7f8bdbfb88efa1d74fd16dc6723f16 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex * *_llvm + - _openmp_mutex >=4.5 + - cuda-cudart >=12.9.79,<13.0a0 + - cuda-cupti >=12.9.79,<13.0a0 + - cuda-nvrtc >=12.9.86,<13.0a0 + - cuda-nvtx >=12.9.79,<13.0a0 + - cuda-version >=12.9,<13 + - cudnn >=9.10.1.4,<10.0a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libblas * *mkl + - libcblas >=3.9.0,<4.0a0 + - libcublas >=12.9.1.4,<13.0a0 + - libcudss >=0.7.1.4,<0.7.2.0a0 + - libcufft >=11.4.1.4,<12.0a0 + - libcufile >=1.14.1.1,<2.0a0 + - libcurand >=10.3.10.19,<11.0a0 + - libcusolver >=11.7.5.82,<12.0a0 + - libcusparse >=12.5.10.65,<13.0a0 + - libgcc >=14 + - libmagma >=2.9.0,<2.9.1.0a0 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + - libuv >=1.51.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - llvm-openmp >=21.1.4 + - mkl >=2025.3.0,<2026.0a0 + - nccl >=2.28.7.1,<3.0a0 + - pybind11-abi 4 + - sleef >=3.9.0,<4.0a0 + constrains: + - pytorch 2.8.0 cuda129_mkl_*_302 + - pytorch-cpu <0.0a0 + - pytorch-gpu 2.8.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 876816719 + timestamp: 1762140104716 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-257.10-hd0affe5_2.conda + sha256: 751cf346f0f56cc9bfa43f7b5c9c30df2fcec8d84d164ac0cd74a27a3af79f30 + md5: 2f6b30acaa0d6e231d01166549108e2c + depends: + - __glibc >=2.17,<3.0.a0 + - libcap >=2.77,<2.78.0a0 + - libgcc >=14 + license: LGPL-2.1-or-later + purls: [] + size: 144395 + timestamp: 1763011330153 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda + sha256: 71c8b9d5c72473752a0bb6e91b01dd209a03916cb71f36cc6a564e3a2a132d7a + md5: e179a69edd30d75c0144d7a380b88f28 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 75995 + timestamp: 1757032240102 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liburing-2.12-hb700be7_0.conda + sha256: 880b1f76b24814c9f07b33402e82fa66d5ae14738a35a943c21c4434eef2403d + md5: f0531fc1ebc0902555670e9cb0127758 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 127967 + timestamp: 1756125594973 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libusb-1.0.29-h73b1eb8_0.conda + sha256: 89c84f5b26028a9d0f5c4014330703e7dff73ba0c98f90103e9cef6b43a5323c + md5: d17e3fb595a9f24fa9e149239a33475d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libudev1 >=257.4 + license: LGPL-2.1-or-later + purls: [] + size: 89551 + timestamp: 1748856210075 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda + sha256: e5ec6d2ad7eef538ddcb9ea62ad4346fde70a4736342c4ad87bd713641eb9808 + md5: 80c07c68d2f6870250959dcc95b209d1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 37135 + timestamp: 1758626800002 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda + sha256: c180f4124a889ac343fc59d15558e93667d894a966ec6fdb61da1604481be26b + md5: 0f03292cc56bf91a077a134ea8747118 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 895108 + timestamp: 1753948278280 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.22.0-h4f16b4b_2.conda + sha256: e0df324fb02fa05a05824b8db886b06659432b5cff39495c59e14a37aa23d40f + md5: 2c65566e79dc11318ce689c656fb551c + depends: + - __glibc >=2.17,<3.0.a0 + - libdrm >=2.4.124,<2.5.0a0 + - libegl >=1.7.0,<2.0a0 + - libgcc >=13 + - libgl >=1.7.0,<2.0a0 + - libglx >=1.7.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - wayland >=1.23.1,<2.0a0 + - wayland-protocols + - xorg-libx11 >=1.8.11,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 217567 + timestamp: 1740897682004 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h54a6638_2.conda + sha256: ca494c99c7e5ecc1b4cd2f72b5584cef3d4ce631d23511184411abcbb90a21a5 + md5: b4ecbefe517ed0157c37f8182768271c + depends: + - libogg + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - libogg >=1.3.5,<1.4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 285894 + timestamp: 1753879378005 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvpl-2.15.0-h54a6638_1.conda + sha256: bf0010d93f5b154c59bd9d3cc32168698c1d24f2904729f4693917cce5b27a9f + md5: a41a299c157cc6d0eff05e5fc298cc45 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - intel-media-driver >=25.3.3,<25.4.0a0 + - libva >=2.22.0,<3.0a0 + license: MIT + license_family: MIT + purls: [] + size: 287944 + timestamp: 1757278954789 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvpx-1.14.1-hac33072_0.conda + sha256: e7d2daf409c807be48310fcc8924e481b62988143f582eb3a58c5523a6763b13 + md5: cde393f461e0c169d9ffb2fc70f81c33 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1022466 + timestamp: 1717859935011 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda + sha256: bbabc5c48b63ff03f440940a11d4648296f5af81bb7630d98485405cd32ac1ce + md5: 372a62464d47d9e966b630ffae3abe73 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxrandr >=1.5.4,<2.0a0 + constrains: + - libvulkan-headers 1.4.328.1.* + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 197672 + timestamp: 1759972155030 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda + sha256: 3aed21ab28eddffdaf7f804f49be7a7d701e8f0e46c856d801270b470820a37b + md5: aea31d2e5b1091feca96fcfe945c3cf9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 429011 + timestamp: 1752159441324 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa + md5: 92ed62436b625154323d40d5f2f11dd7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + purls: [] + size: 395888 + timestamp: 1727278577118 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + purls: [] + size: 100393 + timestamp: 1702724383534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.0-hca5e8e5_0.conda + sha256: 576ce5378cc6a2b722ff33d2359ccb74dea1e6465daa45116e57550f1eb4ba7e + md5: aa65b4add9574bb1d23c76560c5efd4c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxcb >=1.17.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - xkeyboard-config + - xorg-libxau >=1.0.12,<2.0a0 + license: MIT/X11 Derivative + license_family: MIT + purls: [] + size: 843995 + timestamp: 1762341607312 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + sha256: ec0735ae56c3549149eebd7dc22c0bed91fd50c02eaa77ff418613ddda190aa8 + md5: e512be7dc1f84966d50959e900ca121f + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libxml2-16 2.15.1 ha9997c6_0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 45283 + timestamp: 1761015644057 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + sha256: 71436e72a286ef8b57d6f4287626ff91991eb03c7bdbe835280521791efd1434 + md5: e7733bc6785ec009e47a224a71917e84 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - libxml2 2.15.1 + license: MIT + license_family: MIT + purls: [] + size: 556302 + timestamp: 1761015637262 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda + sha256: 991e7348b0f650d495fb6d8aa9f8c727bdf52dabf5853c0cc671439b160dce48 + md5: a7b27c075c9b7f459f1c022090697cba + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.2,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 109043 + timestamp: 1730442108429 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + purls: [] + size: 60963 + timestamp: 1727963148474 +- pypi: https://files.pythonhosted.org/packages/de/73/3d757cb3fc16f0f9794dd289bcd0c4a031d9cf54d8137d6b984b2d02edf3/lightning_utilities-0.15.2-py3-none-any.whl + name: lightning-utilities + version: 0.15.2 + sha256: ad3ab1703775044bbf880dbf7ddaaac899396c96315f3aa1779cec9d618a9841 + requires_dist: + - importlib-metadata>=4.0.0 ; python_full_version < '3.8' + - packaging>=17.1 + - setuptools + - typing-extensions + - requests>=2.0.0 ; extra == 'docs' + - mypy>=1.0.0 ; extra == 'typing' + - types-setuptools ; extra == 'typing' + - jsonargparse[signatures]>=4.38.0 ; extra == 'cli' + - tomlkit ; extra == 'cli' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-21.1.6-h4922eb0_0.conda + sha256: d7b534285d4abe0042ca985149df4888e808a5c1731f4a87c5552dc725d8a1d8 + md5: 7a0b9ce502e0ed62195e02891dfcd704 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - openmp 21.1.6|21.1.6.* + - intel-openmp <0.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + purls: [] + size: 3209134 + timestamp: 1763529474187 +- conda: https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh707e725_0.conda + sha256: e4a07f357a4cf195a2345dabd98deab80f4d53574abe712a9cc7f22d3f2cc2c3 + md5: 49647ac1de4d1e4b49124aedf3934e02 + depends: + - __unix + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/loguru?source=hash-mapping + size: 59696 + timestamp: 1746634858826 +- pypi: https://files.pythonhosted.org/packages/4b/2d/2e321a29535b28795aefc1f6658d5c474097cb93ab1dead304a1e41dd532/lovely_numpy-0.2.18-py3-none-any.whl + name: lovely-numpy + version: 0.2.18 + sha256: f5da3ae3f1edda879169e018248042c17779cba50d29060754d7294ed1a56a3c + requires_dist: + - packaging + - numpy>=1.17 + - fastcore + - matplotlib + - scipy ; extra == 'dev' + - ipython ; extra == 'dev' + requires_python: '>=3.7' +- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 + md5: 9de5350a85c4a20c685259b889aa6393 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 167055 + timestamp: 1733741040117 +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + sha256: 7b1da4b5c40385791dbc3cc85ceea9fad5da680a27d5d3cb8bfaa185e304a89e + md5: 5b5203189eb668f042ac2b0826244964 + depends: + - mdurl >=0.1,<1 + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/markdown-it-py?source=hash-mapping + size: 64736 + timestamp: 1754951288511 +- conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_0.conda + sha256: f77f9f1a4da45cbc8792d16b41b6f169f649651a68afdc10b2da9da12b9aa42b + md5: f775a43412f7f3d7ed218113ad233869 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=compressed-mapping + size: 25321 + timestamp: 1759055268795 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py312he3d6523_0.conda + sha256: 70cf0e7bfd50ef50eb712a6ca1eef0ef0d63b7884292acc81353327b434b548c + md5: b8dc157bbbb69c1407478feede8b7b42 + depends: + - __glibc >=2.17,<3.0.a0 + - contourpy >=1.0.1 + - cycler >=0.10 + - fonttools >=4.22.0 + - freetype + - kiwisolver >=1.3.1 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libstdcxx >=14 + - numpy >=1.23 + - numpy >=1.23,<3 + - packaging >=20.0 + - pillow >=8 + - pyparsing >=2.3.1 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.7 + - python_abi 3.12.* *_cp312 + - qhull >=2020.2,<2020.3.0a0 + - tk >=8.6.13,<8.7.0a0 + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping + size: 8442149 + timestamp: 1763055517581 +- conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 + md5: 592132998493b3ff25fd7479396e8351 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/mdurl?source=hash-mapping + size: 14465 + timestamp: 1733255681319 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2025.3.0-h0e700b2_462.conda + sha256: b5ddfb4378c19d0d69e751478a7733dee035d1dd1f206e7a88a5df4ee71345e0 + md5: a2e8e73f7132ea5ea70fda6f3cf05578 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex * *_llvm + - _openmp_mutex >=4.5 + - libgcc >=14 + - libstdcxx >=14 + - llvm-openmp >=21.1.4 + - tbb >=2022.2.0 + license: LicenseRef-IntelSimplifiedSoftwareOct2022 + license_family: Proprietary + purls: [] + size: 125177250 + timestamp: 1761668323993 +- pypi: https://files.pythonhosted.org/packages/59/bd/5d468c171f201d6169bec848d1116e95736854eec72307299bc68939fe45/mmcv_lite-2.2.0-py2.py3-none-any.whl + name: mmcv-lite + version: 2.2.0 + sha256: a24ee8dd3df7556dfced282dbfe8c3f87df6de2d4dcaf1207e83e9a2d58455a6 + requires_dist: + - addict + - mmengine>=0.3.0 + - numpy + - packaging + - pillow + - pyyaml + - yapf + - opencv-python>=3 + - regex ; sys_platform == 'win32' + - pytest-runner ; extra == 'all' + - ninja ; extra == 'all' + - psutil ; extra == 'all' + - addict ; extra == 'all' + - mmengine>=0.3.0 ; extra == 'all' + - numpy ; extra == 'all' + - packaging ; extra == 'all' + - pillow ; extra == 'all' + - pyyaml ; extra == 'all' + - yapf ; extra == 'all' + - coverage ; extra == 'all' + - lmdb ; extra == 'all' + - onnx ; extra == 'all' + - onnxoptimizer ; extra == 'all' + - onnxruntime ; extra == 'all' + - pytest ; extra == 'all' + - pyturbojpeg ; extra == 'all' + - scipy ; extra == 'all' + - tifffile ; extra == 'all' + - regex ; sys_platform == 'win32' and extra == 'all' + - pytest-runner ; extra == 'build' + - ninja ; extra == 'optional' + - psutil ; extra == 'optional' + - coverage ; extra == 'tests' + - lmdb ; extra == 'tests' + - onnx ; extra == 'tests' + - onnxoptimizer ; extra == 'tests' + - onnxruntime ; extra == 'tests' + - pytest ; extra == 'tests' + - pyturbojpeg ; extra == 'tests' + - scipy ; extra == 'tests' + - tifffile ; extra == 'tests' + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/98/8e/f98332248aad102511bea4ae19c0ddacd2f0a994f3ca4c82b7a369e0af8b/mmengine-0.10.7-py3-none-any.whl + name: mmengine + version: 0.10.7 + sha256: 262ac976a925562f78cd5fd14dd1bc9b680ed0aa81f0d85b723ef782f99c54ee + requires_dist: + - addict + - matplotlib + - numpy + - pyyaml + - rich + - termcolor + - yapf + - opencv-python>=3 + - regex ; sys_platform == 'win32' + - addict ; extra == 'all' + - matplotlib ; extra == 'all' + - numpy ; extra == 'all' + - pyyaml ; extra == 'all' + - rich ; extra == 'all' + - termcolor ; extra == 'all' + - yapf ; extra == 'all' + - bitsandbytes ; extra == 'all' + - clearml ; extra == 'all' + - coverage ; extra == 'all' + - dadaptation ; extra == 'all' + - dvclive ; extra == 'all' + - lion-pytorch ; extra == 'all' + - lmdb ; extra == 'all' + - mlflow ; extra == 'all' + - parameterized ; extra == 'all' + - pydantic==1.10.9 ; extra == 'all' + - pytest ; extra == 'all' + - transformers ; extra == 'all' + - aim<=3.17.5 ; sys_platform != 'win32' and extra == 'all' + - regex ; sys_platform == 'win32' and extra == 'all' + - bitsandbytes ; extra == 'tests' + - clearml ; extra == 'tests' + - coverage ; extra == 'tests' + - dadaptation ; extra == 'tests' + - dvclive ; extra == 'tests' + - lion-pytorch ; extra == 'tests' + - lmdb ; extra == 'tests' + - mlflow ; extra == 'tests' + - parameterized ; extra == 'tests' + - pydantic==1.10.9 ; extra == 'tests' + - pytest ; extra == 'tests' + - transformers ; extra == 'tests' + - aim<=3.17.5 ; sys_platform != 'win32' and extra == 'tests' + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/d7/96/bcb5141eae24474d80b8157b0c3055d25fa75f9804d4abb4a514695bbba9/moderngl-5.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: moderngl + version: 5.12.0 + sha256: b6c4972f3ddd10a3de6c30311da2c25bc493d023796e16c5d4e0f8bd6d5770be + requires_dist: + - glcontext>=3.0.0 + - glcontext>=3.0.0 ; extra == 'headless' + requires_python: '>=3.7' +- pypi: git+https://github.com/microsoft/MoGe.git#07444410f1e33f402353b99d6ccd26bd31e469e8 + name: moge + version: 2.0.0 + requires_dist: + - click + - opencv-python + - scipy + - matplotlib + - trimesh + - pillow + - huggingface-hub + - numpy + - torch>=2.0.0 + - torchvision + - gradio + - utils3d @ git+https://github.com/EasternJournalist/utils3d.git@3fab839f0be9931dac7c8488eb0e1600c236e183 + - pipeline @ git+https://github.com/EasternJournalist/pipeline.git@866f059d2a05cde05e4a52211ec5051fd5f276d6 + requires_python: '>=3.9' +- pypi: git+https://github.com/pablovela5620/monoprior.git#f887b77a3ba006b8e52e3fbf9cd85f90985ac54c + name: monopriors + version: 0.3.2 + requires_dist: + - geffnet>=1.0.2 + - omnidata-tools>=0.0.23 + - mmcv-lite>=2.2.0 + - pytorch-lightning>=2.5.1,<3 + - transformers + - diffusers[torch]>=0.32.2 + - timm>=1.0.15,<2 + - gradio-rerun>=0.24.0 + - onnxruntime-gpu<1.23.0 + - gradio==5.33.1 + requires_python: '>=3.11.0' +- pypi: https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl + name: more-itertools + version: 10.8.0 + sha256: 52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.3.1-h24ddda3_1.conda + sha256: 1bf794ddf2c8b3a3e14ae182577c624fa92dea975537accff4bc7e5fea085212 + md5: aa14b9a5196a6d8dd364164b7ce56acf + depends: + - __glibc >=2.17,<3.0.a0 + - gmp >=6.3.0,<7.0a0 + - libgcc >=13 + - mpfr >=4.2.1,<5.0a0 + license: LGPL-3.0-or-later + license_family: LGPL + purls: [] + size: 116777 + timestamp: 1725629179524 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.1-h90cbb55_3.conda + sha256: f25d2474dd557ca66c6231c8f5ace5af312efde1ba8290a6ea5e1732a4e669c0 + md5: 2eeb50cab6652538eee8fc0bc3340c81 + depends: + - __glibc >=2.17,<3.0.a0 + - gmp >=6.3.0,<7.0a0 + - libgcc >=13 + license: LGPL-3.0-only + license_family: LGPL + purls: [] + size: 634751 + timestamp: 1725746740014 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.9-hc50e24c_0.conda + sha256: 39c4700fb3fbe403a77d8cc27352fa72ba744db487559d5d44bf8411bb4ea200 + md5: c7f302fd11eeb0987a6a5e1f3aed6a21 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: LGPL-2.1-only + license_family: LGPL + purls: [] + size: 491140 + timestamp: 1730581373280 +- conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_1.conda + sha256: 7d7aa3fcd6f42b76bd711182f3776a02bef09a68c5f117d66b712a6d81368692 + md5: 3585aa87c43ab15b167b574cd73b057b + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/mpmath?source=hash-mapping + size: 439705 + timestamp: 1733302781386 +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py312hd9148b4_1.conda + sha256: 94068fd39d1a672f8799e3146a18ba4ef553f0fcccefddb3c07fbdabfd73667a + md5: 2e489969e38f0b428c39492619b5e6e5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/msgpack?source=hash-mapping + size: 102525 + timestamp: 1762504116832 +- conda: https://conda.anaconda.org/conda-forge/linux-64/multidict-6.6.3-py312h178313f_0.conda + sha256: c703d148a85ffb4f11001d31b7c4c686a46ad554eeeaa02c69da59fbf0e00dbb + md5: f4e246ec4ccdf73e50eefb0fa359a64e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/multidict?source=hash-mapping + size: 97272 + timestamp: 1751310833783 +- pypi: https://files.pythonhosted.org/packages/bf/b6/5f922792be93b82ec6b5f270bbb1ef031fd0622847070bbcf9da816502cc/multiprocess-0.70.18-py312-none-any.whl + name: multiprocess + version: 0.70.18 + sha256: 9b78f8e5024b573730bfb654783a13800c2c0f2dfc0c25e70b40d184d64adaa2 + requires_dist: + - dill>=0.4.0 + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + sha256: d09c47c2cf456de5c09fa66d2c3c5035aa1fa228a1983a433c47b876aa16ce90 + md5: 37293a85a0f4f77bbd9cf7aaefc62609 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/munkres?source=hash-mapping + size: 15851 + timestamp: 1749895533014 +- pypi: https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl + name: mypy-extensions + version: 1.1.0 + sha256: 1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.12.0-pyhcf101f3_0.conda + sha256: 1bdb3210db397315c7334c0432a07199b5db28f3c79adcafb6a7436f93423a92 + md5: 02cab382663872083b7e8675f09d9c21 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/narwhals?source=compressed-mapping + size: 267826 + timestamp: 1763381298763 +- conda: https://conda.anaconda.org/conda-forge/linux-64/nccl-2.28.9.1-h4d09622_0.conda + sha256: 12de75cf9b11c1e975f26f84ee1ac6a77f7b65f26eb819d1d45c3007dad1ef85 + md5: d41326b02eb74cf875803c265dde8210 + depends: + - __glibc >=2.28,<3.0.a0 + - cuda-version >=12.2a.0,<13.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 193073223 + timestamp: 1762821007597 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 + md5: 47e340acb35de30501a76c7c799c41d7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: X11 AND BSD-3-Clause + purls: [] + size: 891641 + timestamp: 1738195959188 +- conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda + sha256: bb7b21d7fd0445ddc0631f64e66d91a179de4ba920b8381f29b9d006a42788c0 + md5: 598fd7d4d0de2455fb74f56063969a97 + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/nest-asyncio?source=hash-mapping + size: 11543 + timestamp: 1733325673691 +- conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6-pyhcf101f3_0.conda + sha256: 57b0bbb72ed5647438a81e7caf4890075390f80030c1333434467f9366762db7 + md5: 6725bfdf8ea7a8bf6415f096f3f1ffa5 + depends: + - python >=3.11 + - python + constrains: + - numpy >=1.25 + - scipy >=1.11.2 + - matplotlib-base >=3.8 + - pandas >=2.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/networkx?source=compressed-mapping + size: 1584885 + timestamp: 1763962034867 +- pypi: https://files.pythonhosted.org/packages/42/0f/c76bf3dba22c73c38e9b1113b017cf163f7696f50e003404ec5ecdb1e8a6/nh3-0.3.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: nh3 + version: 0.3.2 + sha256: 7bb18403f02b655a1bbe4e3a4696c2ae1d6ae8f5991f7cacb684b1ae27e6c9f7 + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + sha256: fd2cbd8dfc006c72f45843672664a8e4b99b2f8137654eaae8c3d46dca776f63 + md5: 16c2a0e9c4a166e53632cfca4f68d020 + constrains: + - nlohmann_json-abi ==3.12.0 + license: MIT + license_family: MIT + purls: [] + size: 136216 + timestamp: 1758194284857 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py312h33ff503_0.conda + sha256: 68b5dd7e4d12295c44130e3a777462dbc8886ca0a7d141f1ff5ab0375df5da30 + md5: 1570db96376f9f01cf495afe203672e5 + depends: + - python + - libgcc >=14 + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libcblas >=3.9.0,<4.0a0 + - libblas >=3.9.0,<4.0a0 + - python_abi 3.12.* *_cp312 + - liblapack >=3.9.0,<4.0a0 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=compressed-mapping + size: 8820654 + timestamp: 1763351074641 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda + sha256: 2254dae821b286fb57c61895f2b40e3571a070910fdab79a948ff703e1ea807b + md5: 56f8947aa9d5cf37b0b3d43b83f34192 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - opencl-headers >=2024.10.24 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 106742 + timestamp: 1743700382939 +- pypi: https://files.pythonhosted.org/packages/e3/94/1843518e420fa3ed6919835845df698c7e27e183cb997394e4a670973a65/omegaconf-2.3.0-py3-none-any.whl + name: omegaconf + version: 2.3.0 + sha256: 7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b + requires_dist: + - antlr4-python3-runtime==4.9.* + - pyyaml>=5.1.0 + - dataclasses ; python_full_version == '3.6.*' + requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/12/60/17ed502ab8258e36b6caf478974994f15691c73e3c46abf82a2ec63e9eda/omnidata_tools-0.0.23-py3-none-any.whl + name: omnidata-tools + version: 0.0.23 + sha256: 13810718aa5ffac6057b0f981c19db51b389fc572c598bac05cda3ddfb5a2bbb + requires_dist: + - fastcore>=1.3.21 + - multiprocess>=0.70.12.2 + - aria2p[tui]>=0.10.4 + - tqdm + requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/b5/5c/3f9700ba277d52c121dd2cebc8a672fb60b53e888972fc6682b6692a766c/onnxruntime_gpu-1.22.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: onnxruntime-gpu + version: 1.22.0 + sha256: 86b064c8f6cbe6da03f51f46351237d985f8fd5eb907d3f9997ea91881131a13 + requires_dist: + - coloredlogs + - flatbuffers + - numpy>=1.21.6 + - packaging + - protobuf + - sympy + - nvidia-cuda-nvrtc-cu12~=12.0 ; extra == 'cuda' + - nvidia-cuda-runtime-cu12~=12.0 ; extra == 'cuda' + - nvidia-cufft-cu12~=11.0 ; extra == 'cuda' + - nvidia-curand-cu12~=10.0 ; extra == 'cuda' + - nvidia-cudnn-cu12~=9.0 ; extra == 'cudnn' + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/open3d-0.19.0-py312hf1b548d_6.conda + sha256: 146e706e5b000b232b667c87fe5aea30c0cdbb03f28d4295eba91cb4c2347d1d + md5: af27e1cc663b249caad22d1b567e4ddb + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - assimp >=5.4.3,<5.4.4.0a0 + - dash + - embree >=4.4.0,<4.5.0a0 + - fmt >=11.2.0,<11.3.0a0 + - glew >=2.2.0,<2.3.0a0 + - glfw >=3.4,<4.0a0 + - jsoncpp >=1.9.6,<1.9.7.0a0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libglx >=1.7.0,<2.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblapacke >=3.9.0,<4.0a0 + - liblzf >=3.6,<3.7.0a0 + - libopengl >=1.7.0,<2.0a0 + - libpng >=1.6.50,<1.7.0a0 + - libstdcxx >=14 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - numpy + - plotly + - pybind11-abi 4 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - qhull >=2020.2,<2020.3.0a0 + - qt6-main >=6.9.3,<6.10.0a0 + - tbb >=2021.13.0 + - tinyobjloader >=1.0.7,<2.0a0 + - vtk-base >=9.5.1,<9.5.2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + - zeromq >=4.3.5,<4.4.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/open3d-cpu?source=hash-mapping + size: 10377847 + timestamp: 1760688402623 +- conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda + sha256: 2b6ce54174ec19110e1b3c37455f7cd138d0e228a75727a9bba443427da30a36 + md5: 45c3d2c224002d6d0d7769142b29f986 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 55357 + timestamp: 1749853464518 +- pypi: 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 + name: opencv-python + version: 4.11.0.86 + sha256: 6b02611523803495003bd87362db3e1d2a0454a6a63025dc6658a9830570aa0d + requires_dist: + - numpy>=1.13.3 ; python_full_version < '3.7' + - numpy>=1.21.0 ; python_full_version < '3.10' and platform_machine == 'arm64' and sys_platform == 'darwin' + - numpy>=1.21.2 ; python_full_version >= '3.10' + - numpy>=1.21.4 ; python_full_version >= '3.10' and sys_platform == 'darwin' + - numpy>=1.23.5 ; python_full_version >= '3.11' + - numpy>=1.26.0 ; python_full_version >= '3.12' + - numpy>=1.19.3 ; python_full_version >= '3.6' and platform_machine == 'aarch64' and sys_platform == 'linux' + - numpy>=1.17.0 ; python_full_version >= '3.7' + - numpy>=1.17.3 ; python_full_version >= '3.8' + - numpy>=1.19.3 ; python_full_version >= '3.9' + requires_python: '>=3.6' +- conda: https://conda.anaconda.org/conda-forge/linux-64/openh264-2.6.0-hc22cd8d_0.conda + sha256: 3f231f2747a37a58471c82a9a8a80d92b7fece9f3fce10901a5ac888ce00b747 + md5: b28cf020fd2dead0ca6d113608683842 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 731471 + timestamp: 1739400677213 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + sha256: 3900f9f2dbbf4129cf3ad6acf4e4b6f7101390b53843591c53b00f034343bc4d + md5: 11b3379b191f63139e29c0d19dee24cd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpng >=1.6.50,<1.7.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 355400 + timestamp: 1758489294972 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda + sha256: cb0b07db15e303e6f0a19646807715d28f1264c6350309a559702f4f34f37892 + md5: 2e5bf4f1da39c0b32778561c3c4e5878 + depends: + - __glibc >=2.17,<3.0.a0 + - cyrus-sasl >=2.1.27,<3.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.5.0,<4.0a0 + license: OLDAP-2.8 + license_family: BSD + purls: [] + size: 780253 + timestamp: 1748010165522 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda + sha256: a47271202f4518a484956968335b2521409c8173e123ab381e775c358c67fe6d + md5: 9ee58d5c534af06558933af3c845a780 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3165399 + timestamp: 1762839186699 +- conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.18.0-py312hd9148b4_0.conda + sha256: f34a33825d0e925c6b0e09c81632657935e2c0cc595d2a70d9902c7b9f891a51 + md5: 4d4148297810361256ebf85b17693dff + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - typing-extensions >=4.12 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/optree?source=hash-mapping + size: 444884 + timestamp: 1763124490090 +- conda: https://conda.anaconda.org/conda-forge/linux-64/orjson-3.11.4-py312h868fb18_0.conda + sha256: d7e494b31917f58abcf66c3176b400c92d36d26a2a8313434fd08beabc833d09 + md5: ddbda4ead588f088ed961271bd87a3e8 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/orjson?source=hash-mapping + size: 316971 + timestamp: 1761331249144 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + sha256: 289861ed0c13a15d7bbb408796af4de72c2fe67e2bcb0de98f4c3fce259d7991 + md5: 58335b26c38bf4a20f399384c33cbcf9 + depends: + - python >=3.8 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/packaging?source=hash-mapping + size: 62477 + timestamp: 1745345660407 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py312hf79963d_1.conda + sha256: f633d5f9b28e4a8f66a6ec9c89ef1b6743b880b0511330184b4ab9b7e2dda247 + md5: e597b3e812d9613f659b7d87ad252d18 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - numpy >=1.22.4 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.8.2 + - python-tzdata >=2022.7 + - python_abi 3.12.* *_cp312 + - pytz >=2020.1 + constrains: + - xarray >=2022.12.0 + - qtpy >=2.3.0 + - html5lib >=1.1 + - pandas-gbq >=0.19.0 + - tzdata >=2022.7 + - fsspec >=2022.11.0 + - fastparquet >=2022.12.0 + - odfpy >=1.4.1 + - pyxlsb >=1.0.10 + - scipy >=1.10.0 + - sqlalchemy >=2.0.0 + - pytables >=3.8.0 + - bottleneck >=1.3.6 + - pyarrow >=10.0.1 + - numexpr >=2.8.4 + - pyqt5 >=5.15.9 + - xlsxwriter >=3.0.5 + - openpyxl >=3.1.0 + - blosc >=1.21.3 + - matplotlib >=3.6.3 + - lxml >=4.9.2 + - numba >=0.56.4 + - s3fs >=2022.11.0 + - tabulate >=0.9.0 + - xlrd >=2.0.1 + - gcsfs >=2022.11.0 + - pyreadstat >=1.2.0 + - python-calamine >=0.1.7 + - zstandard >=0.19.0 + - psycopg2 >=2.9.6 + - beautifulsoup4 >=4.11.2 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pandas?source=compressed-mapping + size: 15099922 + timestamp: 1759266031115 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hadf4263_0.conda + sha256: 3613774ad27e48503a3a6a9d72017087ea70f1426f6e5541dbdb59a3b626eaaf + md5: 79f71230c069a287efe3a8614069ddf1 + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.4,<2.0a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - fribidi >=1.0.10,<2.0a0 + - harfbuzz >=11.0.1 + - libexpat >=2.7.0,<3.0a0 + - libfreetype >=2.13.3 + - libfreetype6 >=2.13.3 + - libgcc >=13 + - libglib >=2.84.2,<3.0a0 + - libpng >=1.6.49,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + license: LGPL-2.1-or-later + purls: [] + size: 455420 + timestamp: 1751292466873 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda + sha256: 5c7380c8fd3ad5fc0f8039069a45586aa452cf165264bc5a437ad80397b32934 + md5: 7fa07cb0fb1b625a089ccc01218ee5b1 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1209177 + timestamp: 1756742976157 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py312h7b42cdd_3.conda + sha256: ad4a22899819a2bb86550d1fc3833a44e073aac80ea61529676b5e73220fcc2b + md5: 1d7f05c3f8bb4e98d02fca45f0920b23 + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - lcms2 >=2.17,<3.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - tk >=8.6.13,<8.7.0a0 + - libxcb >=1.17.0,<2.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - python_abi 3.12.* *_cp312 + - openjpeg >=2.5.3,<3.0a0 + license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping + size: 1028547 + timestamp: 1758208668856 +- pypi: git+https://github.com/EasternJournalist/pipeline.git?rev=866f059d2a05cde05e4a52211ec5051fd5f276d6#866f059d2a05cde05e4a52211ec5051fd5f276d6 + name: pipeline + version: 1.0.0 + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda + sha256: 43d37bc9ca3b257c5dd7bf76a8426addbdec381f6786ff441dc90b1a49143b6a + md5: c01af13bdc553d1a8fbfff6e8db075f0 + depends: + - libgcc >=14 + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 450960 + timestamp: 1754665235234 +- pypi: https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl + name: platformdirs + version: 4.5.0 + sha256: e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3 + requires_dist: + - furo>=2025.9.25 ; extra == 'docs' + - proselint>=0.14 ; extra == 'docs' + - sphinx-autodoc-typehints>=3.2 ; extra == 'docs' + - sphinx>=8.2.3 ; extra == 'docs' + - appdirs==1.4.4 ; extra == 'test' + - covdefaults>=2.3 ; extra == 'test' + - pytest-cov>=7 ; extra == 'test' + - pytest-mock>=3.15.1 ; extra == 'test' + - pytest>=8.4.2 ; extra == 'test' + - mypy>=1.18.2 ; extra == 'type' + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.5.0-pyhd8ed1ab_0.conda + sha256: 13b06d2380fc46c299d2ae3465f90f156929b7f98597fc22b0e7ac0cfd40c20d + md5: 6d4c79b604d50c1140c32164f7eca72a + depends: + - narwhals >=1.15.1 + - packaging + - python >=3.10 + constrains: + - ipywidgets >=7.6 + license: MIT + license_family: MIT + purls: + - pkg:pypi/plotly?source=hash-mapping + size: 5179039 + timestamp: 1763430425844 +- pypi: https://files.pythonhosted.org/packages/4f/6a/f435b9d12f34e03548949a51c7475775feda4c3e5b5373e180d70fd7fbe4/plum_dispatch-2.6.0-py3-none-any.whl + name: plum-dispatch + version: 2.6.0 + sha256: 8e9b8f20c5119f944720fa5b93f84338a9f604329f016a5132e419e4894cddf1 + requires_dist: + - beartype>=0.16.2 + - rich>=10.0 + - typing-extensions>=4.9.0 + - black==23.9.0 ; extra == 'dev' + - build ; extra == 'dev' + - coveralls ; extra == 'dev' + - ghp-import ; extra == 'dev' + - ipython ; extra == 'dev' + - jupyter-book ; extra == 'dev' + - mypy ; extra == 'dev' + - numpy ; extra == 'dev' + - pre-commit ; extra == 'dev' + - pyright>=1.1.331 ; extra == 'dev' + - pytest-cov ; extra == 'dev' + - pytest>=6 ; extra == 'dev' + - ruff>=0.2.1 ; extra == 'dev' + - sybil ; extra == 'dev' + - tox ; extra == 'dev' + - wheel ; extra == 'dev' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda + sha256: f1c5e1cc0de088fd3458009be68095f561fa74b5ca6293dcca266f1854d859df + md5: 438e75abf4d8c9c1d9e483b6c3f36282 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libsqlite >=3.50.4,<4.0a0 + - libstdcxx >=14 + - libtiff >=4.7.0,<4.8.0a0 + - sqlite + constrains: + - proj4 ==999999999999 + license: MIT + license_family: MIT + purls: [] + size: 3259440 + timestamp: 1757929968903 +- conda: https://conda.anaconda.org/conda-forge/linux-64/propcache-0.3.1-py312h178313f_0.conda + sha256: d0ff67d89cf379a9f0367f563320621f0bc3969fe7f5c85e020f437de0927bb4 + md5: 0cf580c1b73146bb9ff1bbdb4d4c8cf9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/propcache?source=hash-mapping + size: 54233 + timestamp: 1744525107433 +- pypi: https://files.pythonhosted.org/packages/1d/2a/3c5f05a4af06649547027d288747f68525755de692a26a7720dced3652c0/protobuf-6.33.1-cp39-abi3-manylinux2014_x86_64.whl + name: protobuf + version: 6.33.1 + sha256: 8fd7d5e0eb08cd5b87fd3df49bc193f5cfd778701f47e11d127d0afc6c39f1d1 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/c5/4f/0e22aaa246f96d6ac87fe5ebb9c5a693fbe8877f537a1022527c47ca43c5/psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: psutil + version: 5.9.8 + sha256: d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4 + requires_dist: + - ipaddress ; python_full_version < '3' and extra == 'test' + - mock ; python_full_version < '3' and extra == 'test' + - enum34 ; python_full_version < '3.5' and extra == 'test' + - pywin32 ; sys_platform == 'win32' and extra == 'test' + - wmi ; sys_platform == 'win32' and extra == 'test' + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*' +- conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 + md5: b3c17d95b5a10c6e64a21fa17573e70e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 8252 + timestamp: 1726802366959 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pugixml-1.15-h3f63f65_0.conda + sha256: 23c98a5000356e173568dc5c5770b53393879f946f3ace716bbdefac2a8b23d2 + md5: b11a4c6bf6f6f44e5e143f759ffa2087 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + purls: [] + size: 118488 + timestamp: 1736601364156 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-h9a6aba3_3.conda + sha256: 0a0858c59805d627d02bdceee965dd84fde0aceab03a2f984325eec08d822096 + md5: b8ea447fdf62e3597cb8d2fae4eb1a90 + depends: + - __glibc >=2.17,<3.0.a0 + - dbus >=1.16.2,<2.0a0 + - libgcc >=14 + - libglib >=2.86.1,<3.0a0 + - libiconv >=1.18,<2.0a0 + - libsndfile >=1.2.2,<1.3.0a0 + - libsystemd0 >=257.10 + - libxcb >=1.17.0,<2.0a0 + constrains: + - pulseaudio 17.0 *_3 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 750785 + timestamp: 1763148198088 +- pypi: https://files.pythonhosted.org/packages/13/95/aec81f781c75cd10554dc17a25849c720d54feafb6f7847690478dcf5ef8/pyarrow-22.0.0-cp312-cp312-manylinux_2_28_x86_64.whl + name: pyarrow + version: 22.0.0 + sha256: c6c791b09c57ed76a18b03f2631753a4960eefbbca80f846da8baefc6491fcfe + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-2.13.6-pyhc790b64_3.conda + sha256: d429f6f255fbe49f09b9ae1377aa8cbc4d9285b8b220c17ae2ad9c4894c91317 + md5: 1594696beebf1ecb6d29a1136f859a74 + depends: + - pybind11-global 2.13.6 *_3 + - python >=3.9 + constrains: + - pybind11-abi ==4 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pybind11?source=hash-mapping + size: 186821 + timestamp: 1747935138653 +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-4-hd8ed1ab_3.tar.bz2 + sha256: d4fb485b79b11042a16dc6abfb0c44c4f557707c2653ac47c81e5d32b24a3bb0 + md5: 878f923dd6acc8aeb47a75da6c4098be + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 9906 + timestamp: 1610372835205 +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-2.13.6-pyh217bc35_3.conda + sha256: c044cfcbe6ef0062d0960e9f9f0de5f8818cec84ed901219ff9994b9a9e57237 + md5: 730a5284e26d6bdb73332dafb26aec82 + depends: + - __unix + - python >=3.9 + constrains: + - pybind11-abi ==4 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pybind11-global?source=hash-mapping + size: 180116 + timestamp: 1747934418811 +- conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 + md5: 12c566707c80111f9799308d9e265aef + depends: + - python >=3.9 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pycparser?source=hash-mapping + size: 110100 + timestamp: 1733195786147 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.10-pyh3cfb1c2_0.conda + sha256: 26779821ba83b896f319837d7c5301cc244dee41b311d2bd57cbd693ed9e43ef + md5: 918d9adfc81cb14ab4cced31d22c7711 + depends: + - annotated-types >=0.6.0 + - pydantic-core 2.33.2 + - python >=3.10 + - typing-extensions >=4.6.1 + - typing-inspection >=0.4.0 + - typing_extensions >=4.12.2 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pydantic?source=hash-mapping + size: 307863 + timestamp: 1759584847417 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.33.2-py312h680f630_0.conda + sha256: 4d14d7634c8f351ff1e63d733f6bb15cba9a0ec77e468b0de9102014a4ddc103 + md5: cfbd96e5a0182dfb4110fc42dda63e57 + depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping + size: 1890081 + timestamp: 1746625309715 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydub-0.25.1-pyhd8ed1ab_1.conda + sha256: 8a52c6937a1e0f27f5cd71d55572597c8b21e11956c2da974dddc16c17119633 + md5: a2fae5e32bb95478386c58f9c2cac980 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pydub?source=hash-mapping + size: 33535 + timestamp: 1734638151010 +- pypi: https://files.pythonhosted.org/packages/9f/5c/fe9f95abd5eaedfa69f31e450f7e2768bef121dbdf25bcddee2cd3087a16/pyfiglet-1.0.4-py3-none-any.whl + name: pyfiglet + version: 1.0.4 + sha256: 65b57b7a8e1dff8a67dc8e940a117238661d5e14c3e49121032bd404d9b2b39f + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + sha256: 5577623b9f6685ece2697c6eb7511b4c9ac5fb607c9babc2646c811b428fd46a + md5: 6b6ece66ebcae2d5f326c77ef2c5a066 + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/pygments?source=hash-mapping + size: 889287 + timestamp: 1750615908735 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda + sha256: 6814b61b94e95ffc45ec539a6424d8447895fef75b0fec7e1be31f5beee883fb + md5: 6c8979be6d7a17692793114fa26916e8 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyparsing?source=compressed-mapping + size: 104044 + timestamp: 1758436411254 +- pypi: https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl + name: pyperclip + version: 1.11.0 + sha256: 299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyrefly-0.42.3-h2b88eb6_0.conda + sha256: 28ffe7a595262ad28a88a053b3a0899f9b6688bfbbc26d069646d0eb324a5877 + md5: 671d79c83700da071f74f5b56e36d7db + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + purls: [] + size: 8703695 + timestamp: 1763773792307 +- pypi: https://files.pythonhosted.org/packages/ee/c1/0208599382b259251d33b5cbb835b15ce480621791b7a1f86dd09e9a69c5/pyserde-0.23.0-py3-none-any.whl + name: pyserde + version: 0.23.0 + sha256: 72ba7cc30bad1e0d6cfe3c9900ce078559c295c782a19759aa950d6009554031 + requires_dist: + - beartype>=0.18.4 + - casefy + - jaxtyping ; extra == 'all' or extra == 'jaxtyping' + - jinja2 + - msgpack ; extra == 'all' or extra == 'msgpack' + - numpy>1.21.0,<2.0.0 ; (python_full_version == '3.9.*' and extra == 'all') or (python_full_version == '3.9.*' and extra == 'numpy') + - numpy>1.22.0,<2.0.0 ; (python_full_version >= '3.10' and python_full_version < '4' and extra == 'all') or (python_full_version >= '3.10' and python_full_version < '4' and extra == 'numpy') + - numpy>1.22.0,<2.0.0 ; (python_full_version >= '3.11' and python_full_version < '4' and extra == 'all') or (python_full_version >= '3.11' and python_full_version < '4' and extra == 'numpy') + - numpy>1.22.0,<2.0.0 ; (python_full_version >= '3.12' and python_full_version < '4' and extra == 'all') or (python_full_version >= '3.12' and python_full_version < '4' and extra == 'numpy') + - numpy>1.22.0,<2.0.0 ; (python_full_version >= '3.13' and python_full_version < '4' and extra == 'all') or (python_full_version >= '3.13' and python_full_version < '4' and extra == 'numpy') + - orjson ; extra == 'all' or extra == 'orjson' + - plum-dispatch>=2.3 + - pyyaml ; extra == 'all' or extra == 'yaml' + - sqlalchemy>2 ; extra == 'all' or extra == 'sqlalchemy' + - tomli ; extra == 'all' or extra == 'toml' + - tomli-w ; extra == 'all' or extra == 'toml' + - typing-extensions>=4.1.0 + - typing-inspect>=0.4.0 + requires_python: '>=3.9.0,<4.0.0' +- conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pysocks?source=hash-mapping + size: 21085 + timestamp: 1733217331982 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.12-hd63d673_1_cpython.conda + build_number: 1 + sha256: 39898d24769a848c057ab861052e50bdc266310a7509efa3514b840e85a2ae98 + md5: 5c00c8cea14ee8d02941cab9121dce41 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.7.1,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - liblzma >=5.8.1,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.50.4,<4.0a0 + - libuuid >=2.41.2,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.4,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + purls: [] + size: 31537229 + timestamp: 1761176876216 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + sha256: d6a17ece93bbd5139e02d2bd7dbfa80bee1a4261dced63f65f679121686bf664 + md5: 5b8d21249ff20967101ffa321cab24e8 + depends: + - python >=3.9 + - six >=1.5 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/python-dateutil?source=hash-mapping + size: 233310 + timestamp: 1751104122689 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.1-pyhcf101f3_0.conda + sha256: aa98e0b1f5472161318f93224f1cfec1355ff69d2f79f896c0b9e033e4a6caf9 + md5: 083725d6cd3dc007f06d04bcf1e613a2 + depends: + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/python-dotenv?source=hash-mapping + size: 26922 + timestamp: 1761503229008 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.12-hd8ed1ab_1.conda + sha256: 59f17182813f8b23709b7d4cfda82c33b72dd007cb729efa0033c609fbd92122 + md5: c20172b4c59fbe288fa50cdc1b693d73 + depends: + - cpython 3.12.12.* + - python_abi * *_cp312 + license: Python-2.0 + purls: [] + size: 45888 + timestamp: 1761175248278 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + sha256: 1b03678d145b1675b757cba165a0d9803885807792f7eb4495e48a38858c3cca + md5: a28c984e0429aff3ab7386f7de56de6f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/python-multipart?source=hash-mapping + size: 27913 + timestamp: 1734420869885 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + sha256: e8392a8044d56ad017c08fec2b0eb10ae3d1235ac967d0aab8bd7b41c4a5eaf0 + md5: 88476ae6ebd24f39261e0854ac244f33 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/tzdata?source=hash-mapping + size: 144160 + timestamp: 1742745254292 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + build_number: 8 + sha256: 80677180dd3c22deb7426ca89d6203f1c7f1f256f2d5a94dc210f6e758229809 + md5: c3efd25ac4d74b1584d2f7a57195ddf1 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6958 + timestamp: 1752805918820 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.8.0-cuda129_mkl_py312_had1c889_302.conda + sha256: f589327e0cb5d8658c77df9c3f87e0798c2775485682c35b2bd6a0ea0b528ee7 + md5: ec360db89efeedc51c34c7b3473b55af + depends: + - __cuda + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex * *_llvm + - _openmp_mutex >=4.5 + - cuda-cudart >=12.9.79,<13.0a0 + - cuda-cupti >=12.9.79,<13.0a0 + - cuda-nvrtc >=12.9.86,<13.0a0 + - cuda-nvtx >=12.9.79,<13.0a0 + - cuda-version >=12.9,<13 + - cudnn >=9.10.1.4,<10.0a0 + - filelock + - fsspec + - jinja2 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libblas * *mkl + - libcblas >=3.9.0,<4.0a0 + - libcublas >=12.9.1.4,<13.0a0 + - libcudss >=0.7.1.4,<0.7.2.0a0 + - libcufft >=11.4.1.4,<12.0a0 + - libcufile >=1.14.1.1,<2.0a0 + - libcurand >=10.3.10.19,<11.0a0 + - libcusolver >=11.7.5.82,<12.0a0 + - libcusparse >=12.5.10.65,<13.0a0 + - libgcc >=14 + - libmagma >=2.9.0,<2.9.1.0a0 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + - libtorch 2.8.0 cuda129_mkl_hf53477d_302 + - libuv >=1.51.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - llvm-openmp >=21.1.4 + - mkl >=2025.3.0,<2026.0a0 + - nccl >=2.28.7.1,<3.0a0 + - networkx + - numpy >=1.23,<3 + - optree >=0.13.0 + - pybind11 + - pybind11-abi 4 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - setuptools + - sleef >=3.9.0,<4.0a0 + - sympy >=1.13.3 + - triton 3.4.0 + - typing_extensions >=4.10.0 + constrains: + - pytorch-cpu <0.0a0 + - pytorch-gpu 2.8.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/torch?source=hash-mapping + size: 24877844 + timestamp: 1762141499938 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-gpu-2.8.0-cuda129_mkl_h43a4b0b_302.conda + sha256: 8781dfd1df1f1a80b28aa87ac34574c1841a533a88c721cb31591fa011eae3f6 + md5: 8f435becd46ff815cf48773bfdeeb699 + depends: + - pytorch 2.8.0 cuda*_mkl*302 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 47651 + timestamp: 1762145032202 +- pypi: https://files.pythonhosted.org/packages/17/e4/32ed2f33c1b634f7c2895369222f4f8cb345044f4642bbff718e7dd1e0b7/pytorch_lightning-2.5.6-py3-none-any.whl + name: pytorch-lightning + version: 2.5.6 + sha256: 037bad1e2fd94d5eb6c5144f045fd4c1070c3d38fc9c14d9f3774a3a9be54dff + requires_dist: + - torch>=2.1.0 + - tqdm>=4.57.0 + - pyyaml>5.4 + - fsspec[http]>=2022.5.0 + - torchmetrics>0.7.0 + - packaging>=20.0 + - typing-extensions>4.5.0 + - lightning-utilities>=0.10.0 + - matplotlib>3.1 ; extra == 'extra' + - omegaconf>=2.2.3 ; extra == 'extra' + - hydra-core>=1.2.0 ; extra == 'extra' + - jsonargparse[jsonnet,signatures]>=4.39.0 ; extra == 'extra' + - rich>=12.3.0 ; extra == 'extra' + - tensorboardx>=2.2 ; extra == 'extra' + - bitsandbytes>=0.45.2 ; sys_platform != 'darwin' and extra == 'extra' + - deepspeed>=0.14.1,<=0.15.0 ; sys_platform != 'darwin' and sys_platform != 'win32' and extra == 'strategies' + - requests<2.33.0 ; extra == 'examples' + - torchvision>=0.16.0 ; extra == 'examples' + - ipython[all]>=8.0.0 ; extra == 'examples' + - torchmetrics>=0.10.0 ; extra == 'examples' + - coverage==7.10.6 ; extra == 'test' + - pytest==8.4.1 ; extra == 'test' + - pytest-cov==6.2.1 ; extra == 'test' + - pytest-timeout==2.4.0 ; extra == 'test' + - pytest-rerunfailures==16.0 ; extra == 'test' + - pytest-random-order==1.2.0 ; extra == 'test' + - cloudpickle>=1.3 ; extra == 'test' + - scikit-learn>0.22.1 ; extra == 'test' + - numpy>1.20.0 ; extra == 'test' + - onnx>1.12.0 ; extra == 'test' + - onnxruntime>=1.12.0 ; extra == 'test' + - onnxscript>=0.1.0 ; extra == 'test' + - psutil<7.0.1 ; extra == 'test' + - pandas>2.0 ; extra == 'test' + - fastapi ; extra == 'test' + - uvicorn ; extra == 'test' + - tensorboard>=2.11 ; extra == 'test' + - deepspeed>=0.14.1,<=0.15.0 ; sys_platform != 'darwin' and sys_platform != 'win32' and extra == 'deepspeed' + - matplotlib>3.1 ; extra == 'all' + - omegaconf>=2.2.3 ; extra == 'all' + - hydra-core>=1.2.0 ; extra == 'all' + - jsonargparse[jsonnet,signatures]>=4.39.0 ; extra == 'all' + - rich>=12.3.0 ; extra == 'all' + - tensorboardx>=2.2 ; extra == 'all' + - bitsandbytes>=0.45.2 ; sys_platform != 'darwin' and extra == 'all' + - deepspeed>=0.14.1,<=0.15.0 ; sys_platform != 'darwin' and sys_platform != 'win32' and extra == 'all' + - requests<2.33.0 ; extra == 'all' + - torchvision>=0.16.0 ; extra == 'all' + - ipython[all]>=8.0.0 ; extra == 'all' + - torchmetrics>=0.10.0 ; extra == 'all' + - matplotlib>3.1 ; extra == 'dev' + - omegaconf>=2.2.3 ; extra == 'dev' + - hydra-core>=1.2.0 ; extra == 'dev' + - jsonargparse[jsonnet,signatures]>=4.39.0 ; extra == 'dev' + - rich>=12.3.0 ; extra == 'dev' + - tensorboardx>=2.2 ; extra == 'dev' + - bitsandbytes>=0.45.2 ; sys_platform != 'darwin' and extra == 'dev' + - deepspeed>=0.14.1,<=0.15.0 ; sys_platform != 'darwin' and sys_platform != 'win32' and extra == 'dev' + - requests<2.33.0 ; extra == 'dev' + - torchvision>=0.16.0 ; extra == 'dev' + - ipython[all]>=8.0.0 ; extra == 'dev' + - torchmetrics>=0.10.0 ; extra == 'dev' + - coverage==7.10.6 ; extra == 'dev' + - pytest==8.4.1 ; extra == 'dev' + - pytest-cov==6.2.1 ; extra == 'dev' + - pytest-timeout==2.4.0 ; extra == 'dev' + - pytest-rerunfailures==16.0 ; extra == 'dev' + - pytest-random-order==1.2.0 ; extra == 'dev' + - cloudpickle>=1.3 ; extra == 'dev' + - scikit-learn>0.22.1 ; extra == 'dev' + - numpy>1.20.0 ; extra == 'dev' + - onnx>1.12.0 ; extra == 'dev' + - onnxruntime>=1.12.0 ; extra == 'dev' + - onnxscript>=0.1.0 ; extra == 'dev' + - psutil<7.0.1 ; extra == 'dev' + - pandas>2.0 ; extra == 'dev' + - fastapi ; extra == 'dev' + - uvicorn ; extra == 'dev' + - tensorboard>=2.11 ; extra == 'dev' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + sha256: 8d2a8bf110cc1fc3df6904091dead158ba3e614d8402a83e51ed3a8aa93cdeb0 + md5: bc8e3267d44011051f2eb14d22fb0960 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pytz?source=hash-mapping + size: 189015 + timestamp: 1742920947249 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_0.conda + sha256: 1b3dc4c25c83093fff08b86a3574bc6b94ba355c8eba1f35d805c5e256455fc7 + md5: fba10c2007c8b06f77c5a23ce3a635ad + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping + size: 204539 + timestamp: 1758892248166 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda + sha256: 776363493bad83308ba30bcb88c2552632581b143e8ee25b1982c8c743e73abc + md5: 353823361b1d27eb3960efb076dfcaf6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: LicenseRef-Qhull + purls: [] + size: 552937 + timestamp: 1720813982144 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.3-h5c1c036_1.conda + sha256: 51537408ce1493d267b375b33ec02a060d77c4e00c7bef5e2e1c6724e08a23e3 + md5: 762af6d08fdfa7a45346b1466740bacd + depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.14,<1.3.0a0 + - dbus >=1.16.2,<2.0a0 + - double-conversion >=3.3.1,<3.4.0a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - harfbuzz >=12.1.0 + - icu >=75.1,<76.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libclang-cpp21.1 >=21.1.4,<21.2.0a0 + - libclang13 >=21.1.4 + - libcups >=2.3.3,<2.4.0a0 + - libdrm >=2.4.125,<2.5.0a0 + - libegl >=1.7.0,<2.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libglib >=2.86.0,<3.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - libllvm21 >=21.1.4,<21.2.0a0 + - libpng >=1.6.50,<1.7.0a0 + - libpq >=18.0,<19.0a0 + - libsqlite >=3.50.4,<4.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libvulkan-loader >=1.4.328.1,<2.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libxkbcommon >=1.12.2,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - pcre2 >=10.46,<10.47.0a0 + - wayland >=1.24.0,<2.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + - xcb-util-cursor >=0.1.5,<0.2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-keysyms >=0.4.1,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + - xcb-util-wm >=0.4.2,<0.5.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxcomposite >=0.4.6,<1.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrandr >=1.5.4,<2.0a0 + - xorg-libxtst >=1.2.5,<2.0a0 + - xorg-libxxf86vm >=1.1.6,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - qt 6.9.3 + license: LGPL-3.0-only + license_family: LGPL + purls: [] + size: 54785664 + timestamp: 1761308850008 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.7.1-h8fae777_3.conda + sha256: 6e5e704c1c21f820d760e56082b276deaf2b53cf9b751772761c3088a365f6f4 + md5: 2c42649888aac645608191ffdc80d13a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - __glibc >=2.17 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 5176669 + timestamp: 1746622023242 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-60.0-hecca717_0.conda + sha256: 5c09b833b698ecd19da14f5ff903063cf174382d6b32c86166984a93d427d681 + md5: fe7412835a65cd99eacf3afbb124c7ac + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libnl >=3.11.0,<4.0a0 + - libstdcxx >=14 + - libsystemd0 >=257.9 + - libudev1 >=257.9 + license: Linux-OpenIB + license_family: BSD + purls: [] + size: 1244282 + timestamp: 1761557737114 +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + sha256: 2d6d0c026902561ed77cd646b5021aef2d4db22e57a5b0178dfc669231e06d2c + md5: 283b96675859b20a825f8fa30f311446 + depends: + - libgcc >=13 + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 282480 + timestamp: 1740379431762 +- pypi: https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl + name: readme-renderer + version: '44.0' + sha256: 2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151 + requires_dist: + - nh3>=0.2.14 + - docutils>=0.21.2 + - pygments>=2.5.1 + - cmarkgfm>=0.8.0 ; extra == 'md' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/84/bd/9ce9f629fcb714ffc2c3faf62b6766ecb7a585e1e885eb699bcf130a5209/regex-2025.11.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: regex + version: 2025.11.3 + sha256: a12ab1f5c29b4e93db518f5e3872116b7e9b1646c9f9f426f777b50d44a09e8c + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda + sha256: 8dc54e94721e9ab545d7234aa5192b74102263d3e704e6d0c8aa7008f2da2a7b + md5: db0c6b99149880c8ba515cf4abe93ee4 + depends: + - certifi >=2017.4.17 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - python >=3.9 + - urllib3 >=1.21.1,<3 + constrains: + - chardet >=3.0.2,<6 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/requests?source=hash-mapping + size: 59263 + timestamp: 1755614348400 +- pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + name: requests-toolbelt + version: 1.0.0 + sha256: cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06 + requires_dist: + - requests>=2.0.1,<3.0.0 + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*' +- pypi: https://files.pythonhosted.org/packages/9c/93/5275526c1f925407f1fd69fdf6cd4fa7a0957d863624adf4417258d023ee/rerun_sdk-0.27.0-cp310-abi3-manylinux_2_28_x86_64.whl + name: rerun-sdk + version: 0.27.0 + sha256: d85857dee2f5010b4f4423b03c7154b73293a13ba47fa3ceeaba69cd31b96f26 + requires_dist: + - attrs>=23.1.0 + - numpy>=2 + - pillow>=8.0.0 + - pyarrow>=18.0.0 + - typing-extensions>=4.5 + - pytest==8.4.2 ; extra == 'tests' + - syrupy==5.0.0 ; extra == 'tests' + - rerun-notebook==0.27.0 ; extra == 'notebook' + - datafusion==50.1.0 ; extra == 'datafusion' + - rerun-sdk[notebook] ; extra == 'all' + - rerun-sdk[datafusion] ; extra == 'all' + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/noarch/retrying-1.4.2-pyhe01879c_0.conda + sha256: 7a10527962d2ca2cf936872ef58d4b622b1d1d1703e1d6396d0673fd9f883c7f + md5: 128b46a47ea164f9a8659cb6da2f3555 + depends: + - python >=3.9 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/retrying?source=hash-mapping + size: 20907 + timestamp: 1754219562310 +- pypi: https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl + name: rfc3986 + version: 2.0.0 + sha256: 50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd + requires_dist: + - idna ; extra == 'idna2008' + requires_python: '>=3.7' +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda + sha256: edfb44d0b6468a8dfced728534c755101f06f1a9870a7ad329ec51389f16b086 + md5: a247579d8a59931091b16a1e932bbed6 + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.10 + - typing_extensions >=4.0.0,<5.0.0 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/rich?source=compressed-mapping + size: 200840 + timestamp: 1760026188268 +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.16.0-pyhcf101f3_0.conda + sha256: 76857d1dfaf2bae7ace02f9cdb8ad9f2d654b0cbba291514bfd9785351ff470f + md5: 4d6a58603c5bfdffa4d4e93176c46e73 + depends: + - python >=3.10 + - rich >=13.7.1 + - click >=8.1.7 + - typing_extensions >=4.12.2 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/rich-toolkit?source=compressed-mapping + size: 30208 + timestamp: 1763570584515 +- pypi: https://files.pythonhosted.org/packages/e8/e8/d571290fa7c1a2beed674d487919efd1fca34d04c3666fdce43eb8bfabed/roma-1.5.4-py3-none-any.whl + name: roma + version: 1.5.4 + sha256: f895bec0be87ab37ce58b65307aa10efa1a7472f42e5b5bff6c0ad74664363f5 + requires_python: '>=3.6' +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.14.6-h813ae00_0.conda + noarch: python + sha256: 2c811e3c15b343a7cf4f3d3985710d63d36310d7c21a2db4c00a42e0e3c9fc1a + md5: 406217e531c7261aa6ea8cba649693b4 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + purls: + - pkg:pypi/ruff?source=compressed-mapping + size: 11216432 + timestamp: 1763741549592 +- conda: https://conda.anaconda.org/conda-forge/noarch/safehttpx-0.1.7-pyhd8ed1ab_0.conda + sha256: 90100bc3d562c7706a2f2233c418abdafe4f2449e5427145a9d8eee9f38a33ff + md5: 15b161a21aabf816ab2d4303bb9fbeff + depends: + - httpx + - python >=3.10 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/safehttpx?source=hash-mapping + size: 15488 + timestamp: 1761342674001 +- pypi: https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: safetensors + version: 0.7.0 + sha256: dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48 + requires_dist: + - numpy>=1.21.6 ; extra == 'numpy' + - packaging ; extra == 'torch' + - safetensors[numpy] ; extra == 'torch' + - torch>=1.10 ; extra == 'torch' + - safetensors[numpy] ; extra == 'tensorflow' + - tensorflow>=2.11.0 ; extra == 'tensorflow' + - safetensors[numpy] ; extra == 'pinned-tf' + - tensorflow==2.18.0 ; extra == 'pinned-tf' + - safetensors[numpy] ; extra == 'jax' + - flax>=0.6.3 ; extra == 'jax' + - jax>=0.3.25 ; extra == 'jax' + - jaxlib>=0.3.25 ; extra == 'jax' + - mlx>=0.0.9 ; extra == 'mlx' + - safetensors[numpy] ; extra == 'paddlepaddle' + - paddlepaddle>=2.4.1 ; extra == 'paddlepaddle' + - ruff ; extra == 'quality' + - safetensors[numpy] ; extra == 'testing' + - h5py>=3.7.0 ; extra == 'testing' + - huggingface-hub>=0.12.1 ; extra == 'testing' + - setuptools-rust>=1.5.2 ; extra == 'testing' + - pytest>=7.2.0 ; extra == 'testing' + - pytest-benchmark>=4.0.0 ; extra == 'testing' + - hypothesis>=6.70.2 ; extra == 'testing' + - safetensors[numpy] ; extra == 'testingfree' + - huggingface-hub>=0.12.1 ; extra == 'testingfree' + - setuptools-rust>=1.5.2 ; extra == 'testingfree' + - pytest>=7.2.0 ; extra == 'testingfree' + - pytest-benchmark>=4.0.0 ; extra == 'testingfree' + - hypothesis>=6.70.2 ; extra == 'testingfree' + - safetensors[torch] ; extra == 'all' + - safetensors[numpy] ; extra == 'all' + - safetensors[pinned-tf] ; extra == 'all' + - safetensors[jax] ; extra == 'all' + - safetensors[paddlepaddle] ; extra == 'all' + - safetensors[quality] ; extra == 'all' + - safetensors[testing] ; extra == 'all' + - safetensors[all] ; extra == 'dev' + requires_python: '>=3.9' +- pypi: ./ + name: sam3d-body + version: 0.1.0 + sha256: 7c410588d14ed4e106a0d2dded8ec72f9dfd4bc58dceea0731b17e4beb983b6f + requires_dist: + - jaxtyping<0.3.0 + - numpy>=2.0 + - einops>=0.8.0 + - icecream>=2.1.3 + - opencv-python>=4.10.0 + - pyserde>=0.20.0 + - rerun-sdk>=0.27.0 + - tyro>=0.9.1 + - tqdm + - hf-transfer>=0.1.9 + - lovely-numpy>=0.2.13,<0.3 + - pandas>=2.3.3 + - braceexpand>=0.1.7,<0.2 + - roma>=1.5.4,<2 + - pytorch-lightning>=2.5.6,<3 + - yacs>=0.1.8,<0.2 + - omegaconf>=2.3.0,<3 + - termcolor>=3.2.0,<4 + - gradio-rerun>=0.27.0 + - spaces>=0.43.0 + requires_python: '>=3.12' + editable: true +- pypi: https://files.pythonhosted.org/packages/79/2e/415119c9ab3e62249e18c2b082c07aff907a273741b3f8160414b0e9193c/scipy-1.16.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: scipy + version: 1.16.3 + sha256: 72d1717fd3b5e6ec747327ce9bda32d5463f472c9dce9f54499e81fbd50245a1 + requires_dist: + - numpy>=1.25.2,<2.6 + - pytest>=8.0.0 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-timeout ; extra == 'test' + - pytest-xdist ; extra == 'test' + - asv ; extra == 'test' + - mpmath ; extra == 'test' + - gmpy2 ; extra == 'test' + - threadpoolctl ; extra == 'test' + - scikit-umfpack ; extra == 'test' + - pooch ; extra == 'test' + - hypothesis>=6.30 ; extra == 'test' + - array-api-strict>=2.3.1 ; extra == 'test' + - cython ; extra == 'test' + - meson ; extra == 'test' + - ninja ; sys_platform != 'emscripten' and extra == 'test' + - sphinx>=5.0.0,<8.2.0 ; extra == 'doc' + - intersphinx-registry ; extra == 'doc' + - pydata-sphinx-theme>=0.15.2 ; extra == 'doc' + - sphinx-copybutton ; extra == 'doc' + - sphinx-design>=0.4.0 ; extra == 'doc' + - matplotlib>=3.5 ; extra == 'doc' + - numpydoc ; extra == 'doc' + - jupytext ; extra == 'doc' + - myst-nb>=1.2.0 ; extra == 'doc' + - pooch ; extra == 'doc' + - jupyterlite-sphinx>=0.19.1 ; extra == 'doc' + - jupyterlite-pyodide-kernel ; extra == 'doc' + - linkify-it-py ; extra == 'doc' + - mypy==1.10.0 ; extra == 'dev' + - typing-extensions ; extra == 'dev' + - types-psutil ; extra == 'dev' + - pycodestyle ; extra == 'dev' + - ruff>=0.0.292 ; extra == 'dev' + - cython-lint>=0.12.2 ; extra == 'dev' + - rich-click ; extra == 'dev' + - doit>=0.36.0 ; extra == 'dev' + - pydevtool ; extra == 'dev' + requires_python: '>=3.11' +- conda: https://conda.anaconda.org/conda-forge/linux-64/sdl2-2.32.56-h54a6638_0.conda + sha256: 987ad072939fdd51c92ea8d3544b286bb240aefda329f9b03a51d9b7e777f9de + md5: cdd138897d94dc07d99afe7113a07bec + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libgl >=1.7.0,<2.0a0 + - sdl3 >=3.2.22,<4.0a0 + - libegl >=1.7.0,<2.0a0 + license: Zlib + purls: [] + size: 589145 + timestamp: 1757842881 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sdl3-3.2.26-h68140b3_0.conda + sha256: 31bfb3db00feb74dab5c3420b6b7529cd9a044fda381c422adc817df09ae17b1 + md5: 8d9c193907f1c9defb46322f06990105 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libxscrnsaver >=1.2.4,<2.0a0 + - libvulkan-loader >=1.4.328.1,<2.0a0 + - liburing >=2.12,<2.13.0a0 + - libusb >=1.0.29,<2.0a0 + - libegl >=1.7.0,<2.0a0 + - libunwind >=1.8.3,<1.9.0a0 + - libgl >=1.7.0,<2.0a0 + - dbus >=1.16.2,<2.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - libudev1 >=257.9 + - pulseaudio-client >=17.0,<17.1.0a0 + - libxkbcommon >=1.12.3,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + - libdrm >=2.4.125,<2.5.0a0 + - wayland >=1.24.0,<2.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: Zlib + purls: [] + size: 1936312 + timestamp: 1761847993797 +- pypi: https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl + name: secretstorage + version: 3.5.0 + sha256: 0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137 + requires_dist: + - cryptography>=2.0 + - jeepney>=0.6 + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/noarch/semantic_version-2.10.0-pyhd8ed1ab_0.tar.bz2 + sha256: f1cad72270a3de65107120d68d74d0bc944591fd6a56710fbcaa6651eea717ed + md5: 7d5de798a497fbcd6720b862478975ed + depends: + - python >=2.7 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/semantic-version?source=hash-mapping + size: 17923 + timestamp: 1653579450384 +- conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda + sha256: 972560fcf9657058e3e1f97186cc94389144b46dbdf58c807ce62e83f977e863 + md5: 4de79c071274a53dcaf2a8c749d1499e + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/setuptools?source=hash-mapping + size: 748788 + timestamp: 1748804951958 +- conda: https://conda.anaconda.org/conda-forge/linux-64/shaderc-2025.4-h3e344bc_0.conda + sha256: 638cf498db667ea449be531aba2f1c3b7784bd57dd44dacdfcd033f0e3deec8d + md5: d3b1d75357bce20e29a5ba4072603889 + depends: + - __glibc >=2.17,<3.0.a0 + - glslang >=16,<17.0a0 + - libgcc >=14 + - libstdcxx >=14 + - spirv-tools >=2025,<2026.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 112823 + timestamp: 1758916816431 +- conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_2.conda + sha256: 1d6534df8e7924d9087bd388fbac5bd868c5bf8971c36885f9f016da0657d22b + md5: 83ea3a2ddb7a75c1b09cea582aa4f106 + depends: + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/shellingham?source=compressed-mapping + size: 15018 + timestamp: 1762858315311 +- pypi: https://files.pythonhosted.org/packages/8e/e1/202a31727b0d096a04380f78e809074d7a1d0a22d9d5a39fea1d2353fd02/shtab-1.8.0-py3-none-any.whl + name: shtab + version: 1.8.0 + sha256: f0922a82174b4007e06ac0bac4f79abd826c5cca88e201bfd927f889803c571d + requires_dist: + - pytest>=6 ; extra == 'dev' + - pytest-cov ; extra == 'dev' + - pytest-timeout ; extra == 'dev' + requires_python: '>=3.9' +- pypi: git+https://github.com/pablovela5620/simplecv.git?branch=main#c9e2fa5e1477708431559898da3abb02ec261be7 + name: simplecv + version: 0.6.0 + requires_dist: + - jaxtyping<0.3.0 + - numpy>=2.0 + - einops>=0.8.0 + - icecream>=2.1.3 + - opencv-python>=4.10.0 + - pyserde>=0.20.0 + - rerun-sdk>=0.27.0 + - tyro>=0.9.1 + - tqdm + - hf-transfer>=0.1.9 + - lovely-numpy>=0.2.13,<0.3 + - pandas>=2.3.3 + requires_python: '>=3.12' +- conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + sha256: 458227f759d5e3fcec5d9b7acce54e10c9e1f4f4b7ec978f3bfd54ce4ee9853d + md5: 3339e3b65d58accf4ca4fb8748ab16b3 + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/six?source=hash-mapping + size: 18455 + timestamp: 1753199211006 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sleef-3.9.0-ha0421bc_0.conda + sha256: 57afc2ab5bdb24cf979964018dddbc5dfaee130b415e6863765e45aed2175ee4 + md5: e8a0b4f5e82ecacffaa5e805020473cb + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - libgcc >=14 + - libstdcxx >=14 + license: BSL-1.0 + purls: [] + size: 1951720 + timestamp: 1756274576844 +- conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + sha256: 48f3f6a76c34b2cfe80de9ce7f2283ecb55d5ed47367ba91e8bb8104e12b8f11 + md5: 98b6c9dc80eb87b2519b97bcf7e578dd + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 45829 + timestamp: 1762948049098 +- conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda + sha256: dce518f45e24cd03f401cb0616917773159a210c19d601c5f2d4e0e5879d30ad + md5: 03fe290994c5e4ec17293cfb6bdce520 + depends: + - python >=3.10 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/sniffio?source=compressed-mapping + size: 15698 + timestamp: 1762941572482 +- pypi: https://files.pythonhosted.org/packages/d2/0a/97222afd54e2fdb128e1723671a4c2151242cd1464b20adf20797f331495/spaces-0.43.0-py3-none-any.whl + name: spaces + version: 0.43.0 + sha256: 8b3e61c82405cc73f83e4da45db7d45bd4e2603a08d437e379c0ea08040a941f + requires_dist: + - gradio + - httpx>=0.20 + - packaging + - psutil>=2,<6 + - pydantic>=1,<3 + - requests>=2.19,<3.0 + - typing-extensions>=4,<5 + requires_python: '>=3.10,<3.14' +- conda: https://conda.anaconda.org/conda-forge/linux-64/spirv-tools-2025.4-hb700be7_0.conda + sha256: aa0f0fc41646ef5a825d5725a2d06659df1c1084f15155936319e1909ac9cd16 + md5: aace50912e0f7361d0d223e7f7cfa6e5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - spirv-headers >=1.4.328.0,<1.4.328.1.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 2248062 + timestamp: 1759805790709 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.51.0-heff268d_0.conda + sha256: 5cece58ca7353705ea47bbe44088baee70d2dfa8bdf2bbcd211698f60ab5e7cd + md5: 5422f0e1b59d2aa29329d5b3e36d57e5 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libsqlite 3.51.0 hee844dc_0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - readline >=8.2,<9.0a0 + license: blessing + purls: [] + size: 182985 + timestamp: 1762299697693 +- conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.50.0-pyhfdc7a7d_0.conda + sha256: ab9ab67faa3cf12f45f5ced316e2c50dc72b4046cd275612fae756fe9d4cf82c + md5: 68bcb398c375177cf117cf608c274f9d + depends: + - anyio >=3.6.2,<5 + - python >=3.10 + - typing_extensions >=4.10.0 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/starlette?source=hash-mapping + size: 64760 + timestamp: 1762016292582 +- conda: https://conda.anaconda.org/conda-forge/linux-64/svt-av1-3.1.2-hecca717_0.conda + sha256: 34e2e9c505cd25dba0a9311eb332381b15147cf599d972322a7c197aedfc8ce2 + md5: 9859766c658e78fec9afa4a54891d920 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2741200 + timestamp: 1756086702093 +- conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_105.conda + sha256: 09d3b6ac51d437bc996ad006d9f749ca5c645c1900a854a6c8f193cbd13f03a8 + md5: 8c09fac3785696e1c477156192d64b91 + depends: + - __unix + - cpython + - gmpy2 >=2.0.8 + - mpmath >=0.19 + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/sympy?source=hash-mapping + size: 4616621 + timestamp: 1745946173026 +- conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_8.conda + sha256: 0053c17ffbd9f8af1a7f864995d70121c292e317804120be4667f37c92805426 + md5: 1bad93f0aa428d618875ef3a588a889e + depends: + - __glibc >=2.28 + - kernel-headers_linux-64 4.18.0 he073ed8_8 + - tzdata + license: LGPL-2.0-or-later AND LGPL-2.0-or-later WITH exceptions AND GPL-2.0-or-later + license_family: GPL + purls: [] + size: 24210909 + timestamp: 1752669140965 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.3.0-h8d10470_1.conda + sha256: 2e3238234ae094d5a5f7c559410ea8875351b6bac0d9d0e576bf64b732b8029e + md5: e3259be3341da4bc06c5b7a78c8bf1bd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libhwloc >=2.12.1,<2.12.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 181262 + timestamp: 1762509955687 +- pypi: https://files.pythonhosted.org/packages/f9/d5/141f53d7c1eb2a80e6d3e9a390228c3222c27705cbe7f048d3623053f3ca/termcolor-3.2.0-py3-none-any.whl + name: termcolor + version: 3.2.0 + sha256: a10343879eba4da819353c55cb8049b0933890c2ebf9ad5d3ecd2bb32ea96ea6 + requires_dist: + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/d6/14/fc04d491527b774ec7479897f5861959209de1480e4c4cd32ed098ff8bea/timm-1.0.22-py3-none-any.whl + name: timm + version: 1.0.22 + sha256: 888981753e65cbaacfc07494370138b1700a27b1f0af587f4f9b47bc024161d0 + requires_dist: + - torch + - torchvision + - pyyaml + - huggingface-hub + - safetensors + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/tinyobjloader-1.0.7-h59595ed_2.conda + sha256: 72628067e594f1431968143aed6a5d4e6f0f5d6d1aac0c5165b04143b4f03119 + md5: 3d75225b64bcbdc86128e693104a409d + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + purls: [] + size: 71746 + timestamp: 1704450371417 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda + sha256: 1544760538a40bcd8ace2b1d8ebe3eb5807ac268641f8acdc18c69c5ebfeaf64 + md5: 86bc20552bf46075e3d92b67f089172d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + constrains: + - xorg-libx11 >=1.8.12,<2.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3284905 + timestamp: 1763054914403 +- pypi: https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: tokenizers + version: 0.22.1 + sha256: e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4 + requires_dist: + - huggingface-hub>=0.16.4,<2.0 + - pytest ; extra == 'testing' + - pytest-asyncio ; extra == 'testing' + - requests ; extra == 'testing' + - numpy ; extra == 'testing' + - datasets ; extra == 'testing' + - black==22.3 ; extra == 'testing' + - ruff ; extra == 'testing' + - sphinx ; extra == 'docs' + - sphinx-rtd-theme ; extra == 'docs' + - setuptools-rust ; extra == 'docs' + - tokenizers[testing] ; extra == 'dev' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda + sha256: cb77c660b646c00a48ef942a9e1721ee46e90230c7c570cdeb5a893b5cce9bff + md5: d2732eb636c264dc9aa4cbee404b1a53 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/tomli?source=compressed-mapping + size: 20973 + timestamp: 1760014679845 +- conda: https://conda.anaconda.org/conda-forge/noarch/tomlkit-0.12.0-pyha770c72_0.conda + sha256: d56f30962d6ff3dad8468b8709dc5a7a4809535433a44bf51cbc034058de6c9d + md5: e2d5f8153c4d4b5f4919ad1492775152 + depends: + - python >=3.7 + license: MIT + license_family: MIT + purls: + - pkg:pypi/tomlkit?source=hash-mapping + size: 37086 + timestamp: 1690458478787 +- pypi: https://files.pythonhosted.org/packages/02/21/aa0f434434c48490f91b65962b1ce863fdcce63febc166ca9fe9d706c2b6/torchmetrics-1.8.2-py3-none-any.whl + name: torchmetrics + version: 1.8.2 + sha256: 08382fd96b923e39e904c4d570f3d49e2cc71ccabd2a94e0f895d1f0dac86242 + requires_dist: + - numpy>1.20.0 + - packaging>17.1 + - torch>=2.0.0 + - lightning-utilities>=0.8.0 + - onnxruntime>=1.12.0 ; extra == 'audio' + - requests>=2.19.0 ; extra == 'audio' + - torchaudio>=2.0.1 ; extra == 'audio' + - gammatone>=1.0.0 ; extra == 'audio' + - pystoi>=0.4.0 ; extra == 'audio' + - pesq>=0.0.4 ; extra == 'audio' + - librosa>=0.10.0 ; extra == 'audio' + - torch-linear-assignment>=0.0.2 ; extra == 'clustering' + - pycocotools>2.0.0 ; extra == 'detection' + - torchvision>=0.15.1 ; extra == 'detection' + - torch-fidelity<=0.4.0 ; extra == 'image' + - torchvision>=0.15.1 ; extra == 'image' + - scipy>1.0.0 ; extra == 'image' + - piq<=0.8.0 ; extra == 'multimodal' + - einops>=0.7.0 ; extra == 'multimodal' + - transformers>=4.43.0 ; extra == 'multimodal' + - timm>=0.9.0 ; extra == 'multimodal' + - transformers>=4.43.0 ; extra == 'text' + - regex>=2021.9.24 ; extra == 'text' + - sentencepiece>=0.2.0 ; extra == 'text' + - nltk>3.8.1 ; extra == 'text' + - tqdm<4.68.0 ; extra == 'text' + - mecab-python3>=1.0.6 ; extra == 'text' + - ipadic>=1.0.0 ; extra == 'text' + - mypy==1.17.1 ; extra == 'typing' + - types-six ; extra == 'typing' + - torch==2.8.0 ; extra == 'typing' + - types-emoji ; extra == 'typing' + - types-protobuf ; extra == 'typing' + - types-setuptools ; extra == 'typing' + - types-requests ; extra == 'typing' + - types-tabulate ; extra == 'typing' + - types-pyyaml ; extra == 'typing' + - einops>=0.7.0 ; extra == 'video' + - vmaf-torch>=1.1.0 ; extra == 'video' + - scienceplots>=2.0.0 ; extra == 'visual' + - matplotlib>=3.6.0 ; extra == 'visual' + - onnxruntime>=1.12.0 ; extra == 'all' + - requests>=2.19.0 ; extra == 'all' + - torchaudio>=2.0.1 ; extra == 'all' + - gammatone>=1.0.0 ; extra == 'all' + - pystoi>=0.4.0 ; extra == 'all' + - pesq>=0.0.4 ; extra == 'all' + - librosa>=0.10.0 ; extra == 'all' + - torch-linear-assignment>=0.0.2 ; extra == 'all' + - pycocotools>2.0.0 ; extra == 'all' + - torchvision>=0.15.1 ; extra == 'all' + - torch-fidelity<=0.4.0 ; extra == 'all' + - torchvision>=0.15.1 ; extra == 'all' + - scipy>1.0.0 ; extra == 'all' + - piq<=0.8.0 ; extra == 'all' + - einops>=0.7.0 ; extra == 'all' + - transformers>=4.43.0 ; extra == 'all' + - timm>=0.9.0 ; extra == 'all' + - transformers>=4.43.0 ; extra == 'all' + - regex>=2021.9.24 ; extra == 'all' + - sentencepiece>=0.2.0 ; extra == 'all' + - nltk>3.8.1 ; extra == 'all' + - tqdm<4.68.0 ; extra == 'all' + - mecab-python3>=1.0.6 ; extra == 'all' + - ipadic>=1.0.0 ; extra == 'all' + - mypy==1.17.1 ; extra == 'all' + - types-six ; extra == 'all' + - torch==2.8.0 ; extra == 'all' + - types-emoji ; extra == 'all' + - types-protobuf ; extra == 'all' + - types-setuptools ; extra == 'all' + - types-requests ; extra == 'all' + - types-tabulate ; extra == 'all' + - types-pyyaml ; extra == 'all' + - einops>=0.7.0 ; extra == 'all' + - vmaf-torch>=1.1.0 ; extra == 'all' + - scienceplots>=2.0.0 ; extra == 'all' + - matplotlib>=3.6.0 ; extra == 'all' + - onnxruntime>=1.12.0 ; extra == 'dev' + - requests>=2.19.0 ; extra == 'dev' + - torchaudio>=2.0.1 ; extra == 'dev' + - gammatone>=1.0.0 ; extra == 'dev' + - pystoi>=0.4.0 ; extra == 'dev' + - pesq>=0.0.4 ; extra == 'dev' + - librosa>=0.10.0 ; extra == 'dev' + - torch-linear-assignment>=0.0.2 ; extra == 'dev' + - pycocotools>2.0.0 ; extra == 'dev' + - torchvision>=0.15.1 ; extra == 'dev' + - torch-fidelity<=0.4.0 ; extra == 'dev' + - torchvision>=0.15.1 ; extra == 'dev' + - scipy>1.0.0 ; extra == 'dev' + - piq<=0.8.0 ; extra == 'dev' + - einops>=0.7.0 ; extra == 'dev' + - transformers>=4.43.0 ; extra == 'dev' + - timm>=0.9.0 ; extra == 'dev' + - transformers>=4.43.0 ; extra == 'dev' + - regex>=2021.9.24 ; extra == 'dev' + - sentencepiece>=0.2.0 ; extra == 'dev' + - nltk>3.8.1 ; extra == 'dev' + - tqdm<4.68.0 ; extra == 'dev' + - mecab-python3>=1.0.6 ; extra == 'dev' + - ipadic>=1.0.0 ; extra == 'dev' + - mypy==1.17.1 ; extra == 'dev' + - types-six ; extra == 'dev' + - torch==2.8.0 ; extra == 'dev' + - types-emoji ; extra == 'dev' + - types-protobuf ; extra == 'dev' + - types-setuptools ; extra == 'dev' + - types-requests ; extra == 'dev' + - types-tabulate ; extra == 'dev' + - types-pyyaml ; extra == 'dev' + - einops>=0.7.0 ; extra == 'dev' + - vmaf-torch>=1.1.0 ; extra == 'dev' + - scienceplots>=2.0.0 ; extra == 'dev' + - matplotlib>=3.6.0 ; extra == 'dev' + - properscoring==0.1 ; extra == 'dev' + - mir-eval>=0.6 ; extra == 'dev' + - pytorch-msssim==1.0.0 ; extra == 'dev' + - scikit-image>=0.19.0 ; extra == 'dev' + - sacrebleu>=2.3.0 ; extra == 'dev' + - dists-pytorch==0.1 ; extra == 'dev' + - torch-complex<0.5.0 ; extra == 'dev' + - pytdc==0.4.1 ; (python_full_version < '3.10' and extra == 'dev') or (python_full_version < '3.12' and sys_platform == 'win32' and extra == 'dev') + - netcal>1.0.0 ; extra == 'dev' + - lpips<=0.1.4 ; extra == 'dev' + - jiwer>=2.3.0 ; extra == 'dev' + - fairlearn ; extra == 'dev' + - monai==1.4.0 ; extra == 'dev' + - statsmodels>0.13.5 ; extra == 'dev' + - mecab-ko-dic>=1.0.0 ; python_full_version < '3.12' and extra == 'dev' + - sewar>=0.4.4 ; extra == 'dev' + - mecab-ko>=1.0.0,<1.1.0 ; python_full_version < '3.12' and extra == 'dev' + - faster-coco-eval>=1.6.3 ; extra == 'dev' + - huggingface-hub<0.35 ; extra == 'dev' + - numpy<2.4.0 ; extra == 'dev' + - permetrics==2.0.0 ; extra == 'dev' + - bert-score==0.3.13 ; extra == 'dev' + - scipy>1.0.0 ; extra == 'dev' + - kornia>=0.6.7 ; extra == 'dev' + - rouge-score>0.1.0 ; extra == 'dev' + - fast-bss-eval>=0.1.0 ; extra == 'dev' + - aeon>=1.0.0 ; python_full_version >= '3.11' and extra == 'dev' + - pandas>1.4.0 ; extra == 'dev' + - dython==0.7.9 ; extra == 'dev' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/torchvision-0.24.0-cuda129_py312_h9f56bbf_2.conda + sha256: e117ba916cb92f8cf1df89b8da7733dfff84dd4e81b4e20a4eab430793def02d + md5: 3762ca04bd1fd5e3334b3cc10da8aee6 + depends: + - python + - pytorch * cuda* + - cudnn >=9.10.1.4,<10.0a0 + - pillow >=5.3.0,!=8.3.0,!=8.3.1 + - numpy >=1.23.5 + - torchvision-extra-decoders + - cuda-version >=12.9,<13 + - libgcc >=14 + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libwebp-base >=1.6.0,<2.0a0 + - libtorch >=2.8.0,<2.9.0a0 + - cudnn >=9.10.1.4,<10.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - giflib >=5.2.2,<5.3.0a0 + - python_abi 3.12.* *_cp312 + - libcusolver >=11.7.5.82,<12.0a0 + - libnvjpeg >=12.4.0.76,<13.0a0 + - libpng >=1.6.50,<1.7.0a0 + - pytorch >=2.8.0,<2.9.0a0 + - libtorch >=2.8.0,<2.9.0a0 + - libcublas >=12.9.1.4,<13.0a0 + - libcusparse >=12.5.10.65,<13.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/torchvision?source=hash-mapping + size: 3192054 + timestamp: 1762096288078 +- conda: https://conda.anaconda.org/conda-forge/linux-64/torchvision-extra-decoders-0.0.2-py312hd347cc9_4.conda + sha256: 70d1985d420ccfa1f358af4cfb67264b964a81b21e74bfa394ed38498608a4c8 + md5: 80b0cbbeb536bc609028eb9416edefd8 + depends: + - python + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - pytorch >=2.8.0,<2.9.0a0 + - libtorch >=2.8.0,<2.9.0a0 + - python_abi 3.12.* *_cp312 + - libheif >=1.19.7,<1.20.0a0 + - libavif16 >=1.3.0,<2.0a0 + - libtorch >=2.8.0,<2.9.0a0 + license: LGPL-2.1-only + purls: + - pkg:pypi/torchvision-extra-decoders?source=hash-mapping + size: 65356 + timestamp: 1759392542188 +- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda + sha256: 11e2c85468ae9902d24a27137b6b39b4a78099806e551d390e394a8c34b48e40 + md5: 9efbfdc37242619130ea42b1cc4ed861 + depends: + - colorama + - python >=3.9 + license: MPL-2.0 or MIT + purls: + - pkg:pypi/tqdm?source=hash-mapping + size: 89498 + timestamp: 1735661472632 +- pypi: git+https://github.com/huggingface/transformers.git?rev=d08b98b965176ea9cf8c8e8b24995c955b7e2ec9#d08b98b965176ea9cf8c8e8b24995c955b7e2ec9 + name: transformers + version: 5.0.0.dev0 + requires_dist: + - filelock + - huggingface-hub>=1.0.0,<2.0 + - numpy>=1.17 + - packaging>=20.0 + - pyyaml>=5.1 + - regex!=2019.12.17 + - requests + - tokenizers>=0.22.0,<=0.23.0 + - typer-slim + - safetensors>=0.4.3 + - tqdm>=4.27 + - fugashi>=1.0 ; extra == 'ja' + - ipadic>=1.0.0,<2.0 ; extra == 'ja' + - unidic-lite>=1.0.7 ; extra == 'ja' + - unidic>=1.0.2 ; extra == 'ja' + - sudachipy>=0.6.6 ; extra == 'ja' + - sudachidict-core>=20220729 ; extra == 'ja' + - rhoknp>=1.1.0,<1.3.1 ; extra == 'ja' + - scikit-learn ; extra == 'sklearn' + - torch>=2.2 ; extra == 'torch' + - accelerate>=1.1.0 ; extra == 'torch' + - accelerate>=1.1.0 ; extra == 'accelerate' + - hf-xet ; extra == 'hf-xet' + - faiss-cpu ; extra == 'retrieval' + - datasets>=2.15.0 ; extra == 'retrieval' + - tokenizers>=0.22.0,<=0.23.0 ; extra == 'tokenizers' + - ftfy ; extra == 'ftfy' + - cookiecutter==1.7.3 ; extra == 'modelcreation' + - sagemaker>=2.31.0 ; extra == 'sagemaker' + - deepspeed>=0.9.3 ; extra == 'deepspeed' + - accelerate>=1.1.0 ; extra == 'deepspeed' + - optuna ; extra == 'optuna' + - ray[tune]>=2.7.0 ; extra == 'ray' + - kernels>=0.10.2,<0.11 ; extra == 'hub-kernels' + - kernels>=0.10.2,<0.11 ; extra == 'integrations' + - optuna ; extra == 'integrations' + - ray[tune]>=2.7.0 ; extra == 'integrations' + - openai>=1.98.0 ; extra == 'serving' + - pydantic>=2 ; extra == 'serving' + - uvicorn ; extra == 'serving' + - fastapi ; extra == 'serving' + - starlette ; extra == 'serving' + - rich ; extra == 'serving' + - torch>=2.2 ; extra == 'serving' + - accelerate>=1.1.0 ; extra == 'serving' + - librosa ; extra == 'audio' + - pyctcdecode>=0.4.0 ; extra == 'audio' + - phonemizer ; extra == 'audio' + - kenlm ; extra == 'audio' + - torchaudio ; extra == 'speech' + - librosa ; extra == 'speech' + - pyctcdecode>=0.4.0 ; extra == 'speech' + - phonemizer ; extra == 'speech' + - kenlm ; extra == 'speech' + - torchaudio ; extra == 'torch-speech' + - librosa ; extra == 'torch-speech' + - pyctcdecode>=0.4.0 ; extra == 'torch-speech' + - phonemizer ; extra == 'torch-speech' + - kenlm ; extra == 'torch-speech' + - pillow>=10.0.1,<=15.0 ; extra == 'vision' + - timm!=1.0.18,<=1.0.19 ; extra == 'timm' + - torchvision ; extra == 'torch-vision' + - pillow>=10.0.1,<=15.0 ; extra == 'torch-vision' + - natten>=0.14.6,<0.15.0 ; extra == 'natten' + - codecarbon>=2.8.1 ; extra == 'codecarbon' + - av ; extra == 'video' + - num2words ; extra == 'num2words' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'sentencepiece' + - protobuf ; extra == 'sentencepiece' + - tiktoken ; extra == 'tiktoken' + - blobfile ; extra == 'tiktoken' + - mistral-common[opencv]>=1.6.3 ; extra == 'mistral-common' + - jinja2>=3.1.0 ; extra == 'chat-template' + - jmespath>=1.0.1 ; extra == 'chat-template' + - pytest>=7.2.0,<9.0.0 ; extra == 'testing' + - pytest-asyncio>=1.2.0 ; extra == 'testing' + - pytest-rich ; extra == 'testing' + - pytest-xdist ; extra == 'testing' + - pytest-order ; extra == 'testing' + - pytest-rerunfailures<16.0 ; extra == 'testing' + - timeout-decorator ; extra == 'testing' + - parameterized>=0.9 ; extra == 'testing' + - psutil ; extra == 'testing' + - datasets>=2.15.0 ; extra == 'testing' + - dill<0.3.5 ; extra == 'testing' + - evaluate>=0.4.6 ; extra == 'testing' + - pytest-timeout ; extra == 'testing' + - ruff==0.13.1 ; extra == 'testing' + - rouge-score!=0.0.7,!=0.0.8,!=0.1,!=0.1.1 ; extra == 'testing' + - nltk<=3.8.1 ; extra == 'testing' + - gitpython<3.1.19 ; extra == 'testing' + - sacremoses ; extra == 'testing' + - rjieba ; extra == 'testing' + - beautifulsoup4 ; extra == 'testing' + - tensorboard ; extra == 'testing' + - pydantic>=2 ; extra == 'testing' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'testing' + - sacrebleu>=1.4.12,<2.0.0 ; extra == 'testing' + - libcst ; extra == 'testing' + - faiss-cpu ; extra == 'testing' + - datasets>=2.15.0 ; extra == 'testing' + - cookiecutter==1.7.3 ; extra == 'testing' + - mistral-common[opencv]>=1.6.3 ; extra == 'testing' + - openai>=1.98.0 ; extra == 'testing' + - pydantic>=2 ; extra == 'testing' + - uvicorn ; extra == 'testing' + - fastapi ; extra == 'testing' + - starlette ; extra == 'testing' + - rich ; extra == 'testing' + - torch>=2.2 ; extra == 'testing' + - accelerate>=1.1.0 ; extra == 'testing' + - deepspeed>=0.9.3 ; extra == 'deepspeed-testing' + - accelerate>=1.1.0 ; extra == 'deepspeed-testing' + - pytest>=7.2.0,<9.0.0 ; extra == 'deepspeed-testing' + - pytest-asyncio>=1.2.0 ; extra == 'deepspeed-testing' + - pytest-rich ; extra == 'deepspeed-testing' + - pytest-xdist ; extra == 'deepspeed-testing' + - pytest-order ; extra == 'deepspeed-testing' + - pytest-rerunfailures<16.0 ; extra == 'deepspeed-testing' + - timeout-decorator ; extra == 'deepspeed-testing' + - parameterized>=0.9 ; extra == 'deepspeed-testing' + - psutil ; extra == 'deepspeed-testing' + - datasets>=2.15.0 ; extra == 'deepspeed-testing' + - dill<0.3.5 ; extra == 'deepspeed-testing' + - evaluate>=0.4.6 ; extra == 'deepspeed-testing' + - pytest-timeout ; extra == 'deepspeed-testing' + - ruff==0.13.1 ; extra == 'deepspeed-testing' + - rouge-score!=0.0.7,!=0.0.8,!=0.1,!=0.1.1 ; extra == 'deepspeed-testing' + - nltk<=3.8.1 ; extra == 'deepspeed-testing' + - gitpython<3.1.19 ; extra == 'deepspeed-testing' + - sacremoses ; extra == 'deepspeed-testing' + - rjieba ; extra == 'deepspeed-testing' + - beautifulsoup4 ; extra == 'deepspeed-testing' + - tensorboard ; extra == 'deepspeed-testing' + - pydantic>=2 ; extra == 'deepspeed-testing' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'deepspeed-testing' + - sacrebleu>=1.4.12,<2.0.0 ; extra == 'deepspeed-testing' + - libcst ; extra == 'deepspeed-testing' + - faiss-cpu ; extra == 'deepspeed-testing' + - datasets>=2.15.0 ; extra == 'deepspeed-testing' + - cookiecutter==1.7.3 ; extra == 'deepspeed-testing' + - mistral-common[opencv]>=1.6.3 ; extra == 'deepspeed-testing' + - openai>=1.98.0 ; extra == 'deepspeed-testing' + - pydantic>=2 ; extra == 'deepspeed-testing' + - uvicorn ; extra == 'deepspeed-testing' + - fastapi ; extra == 'deepspeed-testing' + - starlette ; extra == 'deepspeed-testing' + - rich ; extra == 'deepspeed-testing' + - torch>=2.2 ; extra == 'deepspeed-testing' + - accelerate>=1.1.0 ; extra == 'deepspeed-testing' + - optuna ; extra == 'deepspeed-testing' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'deepspeed-testing' + - protobuf ; extra == 'deepspeed-testing' + - ruff==0.13.1 ; extra == 'ruff' + - datasets>=2.15.0 ; extra == 'quality' + - ruff==0.13.1 ; extra == 'quality' + - gitpython<3.1.19 ; extra == 'quality' + - urllib3<2.0.0 ; extra == 'quality' + - libcst ; extra == 'quality' + - rich ; extra == 'quality' + - pandas<2.3.0 ; extra == 'quality' + - torch>=2.2 ; extra == 'all' + - accelerate>=1.1.0 ; extra == 'all' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'all' + - protobuf ; extra == 'all' + - tokenizers>=0.22.0,<=0.23.0 ; extra == 'all' + - torchaudio ; extra == 'all' + - librosa ; extra == 'all' + - pyctcdecode>=0.4.0 ; extra == 'all' + - phonemizer ; extra == 'all' + - kenlm ; extra == 'all' + - pillow>=10.0.1,<=15.0 ; extra == 'all' + - kernels>=0.10.2,<0.11 ; extra == 'all' + - optuna ; extra == 'all' + - ray[tune]>=2.7.0 ; extra == 'all' + - timm!=1.0.18,<=1.0.19 ; extra == 'all' + - torchvision ; extra == 'all' + - pillow>=10.0.1,<=15.0 ; extra == 'all' + - codecarbon>=2.8.1 ; extra == 'all' + - accelerate>=1.1.0 ; extra == 'all' + - av ; extra == 'all' + - num2words ; extra == 'all' + - mistral-common[opencv]>=1.6.3 ; extra == 'all' + - jinja2>=3.1.0 ; extra == 'all' + - jmespath>=1.0.1 ; extra == 'all' + - pytest>=7.2.0,<9.0.0 ; extra == 'dev-torch' + - pytest-asyncio>=1.2.0 ; extra == 'dev-torch' + - pytest-rich ; extra == 'dev-torch' + - pytest-xdist ; extra == 'dev-torch' + - pytest-order ; extra == 'dev-torch' + - pytest-rerunfailures<16.0 ; extra == 'dev-torch' + - timeout-decorator ; extra == 'dev-torch' + - parameterized>=0.9 ; extra == 'dev-torch' + - psutil ; extra == 'dev-torch' + - datasets>=2.15.0 ; extra == 'dev-torch' + - dill<0.3.5 ; extra == 'dev-torch' + - evaluate>=0.4.6 ; extra == 'dev-torch' + - pytest-timeout ; extra == 'dev-torch' + - ruff==0.13.1 ; extra == 'dev-torch' + - rouge-score!=0.0.7,!=0.0.8,!=0.1,!=0.1.1 ; extra == 'dev-torch' + - nltk<=3.8.1 ; extra == 'dev-torch' + - gitpython<3.1.19 ; extra == 'dev-torch' + - sacremoses ; extra == 'dev-torch' + - rjieba ; extra == 'dev-torch' + - beautifulsoup4 ; extra == 'dev-torch' + - tensorboard ; extra == 'dev-torch' + - pydantic>=2 ; extra == 'dev-torch' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'dev-torch' + - sacrebleu>=1.4.12,<2.0.0 ; extra == 'dev-torch' + - libcst ; extra == 'dev-torch' + - faiss-cpu ; extra == 'dev-torch' + - datasets>=2.15.0 ; extra == 'dev-torch' + - cookiecutter==1.7.3 ; extra == 'dev-torch' + - mistral-common[opencv]>=1.6.3 ; extra == 'dev-torch' + - openai>=1.98.0 ; extra == 'dev-torch' + - pydantic>=2 ; extra == 'dev-torch' + - uvicorn ; extra == 'dev-torch' + - fastapi ; extra == 'dev-torch' + - starlette ; extra == 'dev-torch' + - rich ; extra == 'dev-torch' + - torch>=2.2 ; extra == 'dev-torch' + - accelerate>=1.1.0 ; extra == 'dev-torch' + - torch>=2.2 ; extra == 'dev-torch' + - accelerate>=1.1.0 ; extra == 'dev-torch' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'dev-torch' + - protobuf ; extra == 'dev-torch' + - tokenizers>=0.22.0,<=0.23.0 ; extra == 'dev-torch' + - torchaudio ; extra == 'dev-torch' + - librosa ; extra == 'dev-torch' + - pyctcdecode>=0.4.0 ; extra == 'dev-torch' + - phonemizer ; extra == 'dev-torch' + - kenlm ; extra == 'dev-torch' + - pillow>=10.0.1,<=15.0 ; extra == 'dev-torch' + - kernels>=0.10.2,<0.11 ; extra == 'dev-torch' + - optuna ; extra == 'dev-torch' + - ray[tune]>=2.7.0 ; extra == 'dev-torch' + - timm!=1.0.18,<=1.0.19 ; extra == 'dev-torch' + - torchvision ; extra == 'dev-torch' + - pillow>=10.0.1,<=15.0 ; extra == 'dev-torch' + - codecarbon>=2.8.1 ; extra == 'dev-torch' + - datasets>=2.15.0 ; extra == 'dev-torch' + - ruff==0.13.1 ; extra == 'dev-torch' + - gitpython<3.1.19 ; extra == 'dev-torch' + - urllib3<2.0.0 ; extra == 'dev-torch' + - libcst ; extra == 'dev-torch' + - rich ; extra == 'dev-torch' + - pandas<2.3.0 ; extra == 'dev-torch' + - fugashi>=1.0 ; extra == 'dev-torch' + - ipadic>=1.0.0,<2.0 ; extra == 'dev-torch' + - unidic-lite>=1.0.7 ; extra == 'dev-torch' + - unidic>=1.0.2 ; extra == 'dev-torch' + - sudachipy>=0.6.6 ; extra == 'dev-torch' + - sudachidict-core>=20220729 ; extra == 'dev-torch' + - rhoknp>=1.1.0,<1.3.1 ; extra == 'dev-torch' + - scikit-learn ; extra == 'dev-torch' + - cookiecutter==1.7.3 ; extra == 'dev-torch' + - num2words ; extra == 'dev-torch' + - torch>=2.2 ; extra == 'dev' + - accelerate>=1.1.0 ; extra == 'dev' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'dev' + - protobuf ; extra == 'dev' + - tokenizers>=0.22.0,<=0.23.0 ; extra == 'dev' + - torchaudio ; extra == 'dev' + - librosa ; extra == 'dev' + - pyctcdecode>=0.4.0 ; extra == 'dev' + - phonemizer ; extra == 'dev' + - kenlm ; extra == 'dev' + - pillow>=10.0.1,<=15.0 ; extra == 'dev' + - kernels>=0.10.2,<0.11 ; extra == 'dev' + - optuna ; extra == 'dev' + - ray[tune]>=2.7.0 ; extra == 'dev' + - timm!=1.0.18,<=1.0.19 ; extra == 'dev' + - torchvision ; extra == 'dev' + - pillow>=10.0.1,<=15.0 ; extra == 'dev' + - codecarbon>=2.8.1 ; extra == 'dev' + - accelerate>=1.1.0 ; extra == 'dev' + - av ; extra == 'dev' + - num2words ; extra == 'dev' + - mistral-common[opencv]>=1.6.3 ; extra == 'dev' + - jinja2>=3.1.0 ; extra == 'dev' + - jmespath>=1.0.1 ; extra == 'dev' + - pytest>=7.2.0,<9.0.0 ; extra == 'dev' + - pytest-asyncio>=1.2.0 ; extra == 'dev' + - pytest-rich ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - pytest-order ; extra == 'dev' + - pytest-rerunfailures<16.0 ; extra == 'dev' + - timeout-decorator ; extra == 'dev' + - parameterized>=0.9 ; extra == 'dev' + - psutil ; extra == 'dev' + - datasets>=2.15.0 ; extra == 'dev' + - dill<0.3.5 ; extra == 'dev' + - evaluate>=0.4.6 ; extra == 'dev' + - pytest-timeout ; extra == 'dev' + - ruff==0.13.1 ; extra == 'dev' + - rouge-score!=0.0.7,!=0.0.8,!=0.1,!=0.1.1 ; extra == 'dev' + - nltk<=3.8.1 ; extra == 'dev' + - gitpython<3.1.19 ; extra == 'dev' + - sacremoses ; extra == 'dev' + - rjieba ; extra == 'dev' + - beautifulsoup4 ; extra == 'dev' + - tensorboard ; extra == 'dev' + - pydantic>=2 ; extra == 'dev' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'dev' + - sacrebleu>=1.4.12,<2.0.0 ; extra == 'dev' + - libcst ; extra == 'dev' + - faiss-cpu ; extra == 'dev' + - datasets>=2.15.0 ; extra == 'dev' + - cookiecutter==1.7.3 ; extra == 'dev' + - mistral-common[opencv]>=1.6.3 ; extra == 'dev' + - openai>=1.98.0 ; extra == 'dev' + - pydantic>=2 ; extra == 'dev' + - uvicorn ; extra == 'dev' + - fastapi ; extra == 'dev' + - starlette ; extra == 'dev' + - rich ; extra == 'dev' + - torch>=2.2 ; extra == 'dev' + - accelerate>=1.1.0 ; extra == 'dev' + - datasets>=2.15.0 ; extra == 'dev' + - ruff==0.13.1 ; extra == 'dev' + - gitpython<3.1.19 ; extra == 'dev' + - urllib3<2.0.0 ; extra == 'dev' + - libcst ; extra == 'dev' + - rich ; extra == 'dev' + - pandas<2.3.0 ; extra == 'dev' + - fugashi>=1.0 ; extra == 'dev' + - ipadic>=1.0.0,<2.0 ; extra == 'dev' + - unidic-lite>=1.0.7 ; extra == 'dev' + - unidic>=1.0.2 ; extra == 'dev' + - sudachipy>=0.6.6 ; extra == 'dev' + - sudachidict-core>=20220729 ; extra == 'dev' + - rhoknp>=1.1.0,<1.3.1 ; extra == 'dev' + - scikit-learn ; extra == 'dev' + - cookiecutter==1.7.3 ; extra == 'dev' + - filelock ; extra == 'torchhub' + - huggingface-hub>=1.0.0,<2.0 ; extra == 'torchhub' + - importlib-metadata ; extra == 'torchhub' + - numpy>=1.17 ; extra == 'torchhub' + - packaging>=20.0 ; extra == 'torchhub' + - protobuf ; extra == 'torchhub' + - regex!=2019.12.17 ; extra == 'torchhub' + - requests ; extra == 'torchhub' + - sentencepiece>=0.1.91,!=0.1.92 ; extra == 'torchhub' + - torch>=2.2 ; extra == 'torchhub' + - tokenizers>=0.22.0,<=0.23.0 ; extra == 'torchhub' + - tqdm>=4.27 ; extra == 'torchhub' + - optimum-benchmark>=0.3.0 ; extra == 'benchmark' + - opentelemetry-api ; extra == 'open-telemetry' + - opentelemetry-exporter-otlp ; extra == 'open-telemetry' + - opentelemetry-sdk ; extra == 'open-telemetry' + requires_python: '>=3.10.0' +- pypi: https://files.pythonhosted.org/packages/ec/81/951a108396aa850a096be95cd9ccd38ef9f33ae35b40b512be975e2c994d/trimesh-4.10.0-py3-none-any.whl + name: trimesh + version: 4.10.0 + sha256: bdafda66d5c8a9564d7d82f5673c06f8b4fc51c86de959fafee0e169fcfea12a + requires_dist: + - numpy>=1.20 + - colorlog ; extra == 'easy' + - manifold3d>=2.3.0 ; python_full_version < '3.14' and extra == 'easy' + - charset-normalizer ; extra == 'easy' + - lxml ; extra == 'easy' + - jsonschema ; extra == 'easy' + - networkx ; extra == 'easy' + - svg-path ; extra == 'easy' + - pycollada<=0.9.0 ; python_full_version < '3.9' and extra == 'easy' + - pycollada ; python_full_version >= '3.9' and extra == 'easy' + - shapely ; extra == 'easy' + - xxhash ; extra == 'easy' + - rtree ; extra == 'easy' + - httpx ; extra == 'easy' + - scipy ; extra == 'easy' + - embreex ; platform_machine == 'x86_64' and extra == 'easy' + - pillow ; extra == 'easy' + - vhacdx ; python_full_version >= '3.9' and extra == 'easy' + - mapbox-earcut>=1.0.2 ; python_full_version >= '3.9' and extra == 'easy' + - sympy ; extra == 'recommend' + - pyglet<2 ; extra == 'recommend' + - scikit-image ; extra == 'recommend' + - fast-simplification ; extra == 'recommend' + - python-fcl ; extra == 'recommend' + - cascadio ; extra == 'recommend' + - manifold3d>=2.3.0 ; python_full_version >= '3.14' and extra == 'recommend' + - pytest-cov ; extra == 'test' + - pytest ; extra == 'test' + - pyinstrument ; extra == 'test' + - ruff ; extra == 'test' + - coveralls ; extra == 'test-more' + - ezdxf ; extra == 'test-more' + - meshio ; extra == 'test-more' + - xatlas ; extra == 'test-more' + - pytest-beartype ; python_full_version >= '3.10' and extra == 'test-more' + - matplotlib ; extra == 'test-more' + - pymeshlab ; python_full_version < '3.14' and extra == 'test-more' + - triangle ; python_full_version < '3.14' and extra == 'test-more' + - ipython ; extra == 'test-more' + - marimo ; extra == 'test-more' + - openctm ; extra == 'deprecated' + - trimesh[deprecated,easy,recommend,test,test-more] ; extra == 'all' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/triton-3.4.0-cuda129py312h811769c_1.conda + sha256: 679cd856750442f8901914e1db96d504f038ef7d897e89db53a28c731d70d158 + md5: 34d933687688ffeeb4b828a9edd0b079 + depends: + - python + - setuptools + - cuda-nvcc-tools + - cuda-cuobjdump + - cuda-cudart + - cuda-cupti + - libstdcxx >=14 + - libgcc >=14 + - cuda-version >=12.9,<13 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 + - cuda-cupti >=12.9.79,<13.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/triton?source=hash-mapping + size: 189193540 + timestamp: 1759344409322 +- pypi: https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl + name: twine + version: 6.2.0 + sha256: 418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8 + requires_dist: + - readme-renderer>=35.0 + - requests>=2.20 + - requests-toolbelt>=0.8.0,!=0.9.0 + - urllib3>=1.26.0 + - importlib-metadata>=3.6 ; python_full_version < '3.10' + - keyring>=21.2.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' + - rfc3986>=1.4.0 + - rich>=12.0.0 + - packaging>=24.0 + - id + - keyring>=21.2.0 ; extra == 'keyring' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl + name: typeguard + version: 4.4.4 + sha256: b5f562281b6bfa1f5492470464730ef001646128b180769880468bd84b68b09e + requires_dist: + - importlib-metadata>=3.6 ; python_full_version < '3.10' + - typing-extensions>=4.14.0 + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.20.0-pyhefaf540_1.conda + sha256: 17a1e572939af33d709248170871d4da74f7e32b48f2e9b5abca613e201c6e64 + md5: 23a53fdefc45ba3f4e075cc0997fd13b + depends: + - typer-slim-standard ==0.20.0 h4daf872_1 + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/typer?source=compressed-mapping + size: 79829 + timestamp: 1762984042927 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.20.0-pyhcf101f3_1.conda + sha256: 4b5ded929080b91367f128e7299619f6116f08bc77d9924a2f8766e2a1b18161 + md5: 4b02a515f3e882dcfe9cfbf0a1f5cd3a + depends: + - python >=3.10 + - click >=8.0.0 + - typing_extensions >=3.7.4.3 + - python + constrains: + - typer 0.20.0.* + - rich >=10.11.0 + - shellingham >=1.3.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/typer-slim?source=compressed-mapping + size: 47951 + timestamp: 1762984042920 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.20.0-h4daf872_1.conda + sha256: 5027768bc9a580c8ffbf25872bb2208c058cbb79ae959b1cf2cc54b5d32c0377 + md5: 37b26aafb15a6687b31a3d8d7a1f04e7 + depends: + - typer-slim ==0.20.0 pyhcf101f3_1 + - rich + - shellingham + license: MIT + license_family: MIT + purls: [] + size: 5322 + timestamp: 1762984042927 +- pypi: https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl + name: types-requests + version: 2.32.4.20250913 + sha256: 78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1 + requires_dist: + - urllib3>=2 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/3f/13/3ff0781445d7c12730befce0fddbbc7a76e56eb0e7029446f2853238360a/types_tqdm-4.67.0.20250809-py3-none-any.whl + name: types-tqdm + version: 4.67.0.20250809 + sha256: 1a73053b31fcabf3c1f3e2a9d5ecdba0f301bde47a418cd0e0bdf774827c5c57 + requires_dist: + - types-requests + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + sha256: 7c2df5721c742c2a47b2c8f960e718c930031663ac1174da67c1ed5999f7938c + md5: edd329d7d3a4ab45dcf905899a7a6115 + depends: + - typing_extensions ==4.15.0 pyhcf101f3_0 + license: PSF-2.0 + license_family: PSF + purls: [] + size: 91383 + timestamp: 1756220668932 +- pypi: https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl + name: typing-inspect + version: 0.9.0 + sha256: 9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f + requires_dist: + - mypy-extensions>=0.3.0 + - typing-extensions>=3.7.4 + - typing>=3.7.4 ; python_full_version < '3.5' +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_1.conda + sha256: 70db27de58a97aeb7ba7448366c9853f91b21137492e0b4430251a1870aa8ff4 + md5: a0a4a3035667fc34f29bfbd5c190baa6 + depends: + - python >=3.10 + - typing_extensions >=4.12.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/typing-inspection?source=compressed-mapping + size: 18923 + timestamp: 1764158430324 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 + md5: 0caa1af407ecff61170c9437a808404d + depends: + - python >=3.10 + - python + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/typing-extensions?source=hash-mapping + size: 51692 + timestamp: 1756220668932 +- pypi: https://files.pythonhosted.org/packages/bb/bc/943b35b1a139a602a730e56592a3639bf796a87c91eebcd40cc51043ee5b/tyro-0.9.35-py3-none-any.whl + name: tyro + version: 0.9.35 + sha256: 82d656389d3041c04dd68542f8ba9b77baf91a4aaaa627cd5c88af3b3dc6a2cd + requires_dist: + - colorama>=0.4.0 ; sys_platform == 'win32' + - docstring-parser>=0.15 + - eval-type-backport>=0.1.3 ; python_full_version < '3.10' + - rich>=11.1.0 + - shtab>=1.5.6 + - typeguard>=4.0.0 + - typing-extensions>=4.13.0 + - attrs>=21.4.0 ; extra == 'dev' + - coverage[toml]>=6.5.0 ; extra == 'dev' + - eval-type-backport>=0.1.3 ; extra == 'dev' + - ml-collections>=0.1.0 ; extra == 'dev' + - msgspec>=0.18.6 ; extra == 'dev' + - mypy>=1.4.1 ; extra == 'dev' + - omegaconf>=2.2.2 ; extra == 'dev' + - pydantic>=2.5.2,!=2.10.0 ; extra == 'dev' + - pyright>=1.1.349,!=1.1.379 ; extra == 'dev' + - pytest-cov>=3.0.0 ; extra == 'dev' + - pytest-xdist>=3.5.0 ; extra == 'dev' + - pytest>=7.1.2 ; extra == 'dev' + - pyyaml>=6.0 ; extra == 'dev' + - ruff>=0.1.13 ; extra == 'dev' + - attrs>=21.4.0 ; extra == 'dev-nn' + - coverage[toml]>=6.5.0 ; extra == 'dev-nn' + - eval-type-backport>=0.1.3 ; extra == 'dev-nn' + - flax>=0.6.9 ; python_full_version < '3.14' and extra == 'dev-nn' + - ml-collections>=0.1.0 ; extra == 'dev-nn' + - msgspec>=0.18.6 ; extra == 'dev-nn' + - mypy>=1.4.1 ; extra == 'dev-nn' + - numpy>=1.20.0 ; extra == 'dev-nn' + - omegaconf>=2.2.2 ; extra == 'dev-nn' + - pydantic>=2.5.2,!=2.10.0 ; extra == 'dev-nn' + - pyright>=1.1.349,!=1.1.379 ; extra == 'dev-nn' + - pytest-cov>=3.0.0 ; extra == 'dev-nn' + - pytest-xdist>=3.5.0 ; extra == 'dev-nn' + - pytest>=7.1.2 ; extra == 'dev-nn' + - pyyaml>=6.0 ; extra == 'dev-nn' + - ruff>=0.1.13 ; extra == 'dev-nn' + - torch>=1.10.0 ; python_full_version < '3.14' and extra == 'dev-nn' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + sha256: 5aaa366385d716557e365f0a4e9c3fca43ba196872abbbe3d56bb610d131e192 + md5: 4222072737ccff51314b5ece9c7d6f5a + license: LicenseRef-Public-Domain + purls: [] + size: 122968 + timestamp: 1742727099393 +- conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py312h4c3975b_1.conda + sha256: 3c812c634e78cec74e224cc6adf33aed533d9fe1ee1eff7f692e1f338efb8c5b + md5: a0b8efbe73c90f810a171a6c746be087 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=compressed-mapping + size: 408399 + timestamp: 1763054875733 +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + sha256: 4fb9789154bd666ca74e428d973df81087a697dbb987775bc3198d2215f240f8 + md5: 436c165519e140cb08d246a4472a9d6a + depends: + - brotli-python >=1.0.9 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.9 + - zstandard >=0.18.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/urllib3?source=hash-mapping + size: 101735 + timestamp: 1750271478254 +- conda: https://conda.anaconda.org/conda-forge/linux-64/utfcpp-4.0.8-ha770c72_0.conda + sha256: bbfbfc43bc028ec8acc5c9c2bb9a52c7652140cef91fdb6219a52d91d773a474 + md5: a480ee3eb9c95364a229673a28384899 + license: BSL-1.0 + purls: [] + size: 14169 + timestamp: 1758003868824 +- pypi: git+https://github.com/EasternJournalist/utils3d.git?rev=3fab839f0be9931dac7c8488eb0e1600c236e183#3fab839f0be9931dac7c8488eb0e1600c236e183 + name: utils3d + version: '1.3' + requires_dist: + - moderngl + - numpy + - scipy + - matplotlib + requires_python: '>=3.7' +- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.38.0-pyh31011fe_0.conda + sha256: 32e637726fd7cfeb74058e829b116e17514d001846fef56d8c763ec9ec5ac887 + md5: d3aa78bc38d9478e9eed5f128ba35f41 + depends: + - __unix + - click >=7.0 + - h11 >=0.8 + - python >=3.10 + - typing_extensions >=4.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/uvicorn?source=hash-mapping + size: 51717 + timestamp: 1760803935306 +- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.38.0-h31011fe_0.conda + sha256: 3629a349257c0e129cbb84fd593759a31d68ac1219c0af8b8ed89b95b9574c9b + md5: 1ce870d7537376362672f5ff57109529 + depends: + - __unix + - httptools >=0.6.3 + - python-dotenv >=0.13 + - pyyaml >=5.1 + - uvicorn 0.38.0 pyh31011fe_0 + - uvloop >=0.14.0,!=0.15.0,!=0.15.1 + - watchfiles >=0.13 + - websockets >=10.4 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 7719 + timestamp: 1760803936446 +- conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.22.1-py312h4c3975b_1.conda + sha256: 15714d471fcba83c76930f49c4de0ebb5589f9b90d9eab52b1e7fc1478474891 + md5: bdfc3f5345f9a16c63ba18e50c292e08 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libuv >=1.51.0,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT OR Apache-2.0 + purls: + - pkg:pypi/uvloop?source=hash-mapping + size: 596425 + timestamp: 1762472840090 +- conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.1-py312h5d43ed7_7.conda + sha256: 5b3697c55047ba4df07ecfa1172f758b1ef714a44c2edae36c133c0543632cbf + md5: dc25deb8a5cd4e101a64cda08b7eceee + depends: + - __glibc >=2.17,<3.0.a0 + - cli11 + - double-conversion >=3.3.1,<3.4.0a0 + - fmt >=11.2.0,<11.3.0a0 + - gl2ps >=1.4.2,<1.4.3.0a0 + - hdf5 >=1.14.6,<1.14.7.0a0 + - jsoncpp >=1.9.6,<1.9.7.0a0 + - libexpat >=2.7.1,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libglu >=9.0.3,<9.1.0a0 + - libglvnd >=1.7.0,<2.0a0 + - libglx >=1.7.0,<2.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libnetcdf >=4.9.3,<4.9.4.0a0 + - libogg >=1.3.5,<1.4.0a0 + - libopengl >=1.7.0,<2.0a0 + - libpng >=1.6.50,<1.7.0a0 + - libsqlite >=3.50.4,<4.0a0 + - libstdcxx >=14 + - libtheora >=1.1.1,<1.2.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - loguru + - lz4-c >=1.10.0,<1.11.0a0 + - matplotlib-base >=2.0.0 + - nlohmann_json + - numpy + - proj >=9.7.0,<9.8.0a0 + - pugixml >=1.15,<1.16.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - qt6-main >=6.9.3,<6.10.0a0 + - tbb >=2021.13.0 + - utfcpp + - wslink + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + constrains: + - libboost-headers >=1.88.0,<1.89.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/vtk?source=hash-mapping + size: 63074465 + timestamp: 1760147895246 +- pypi: https://files.pythonhosted.org/packages/8d/96/04e7b441807b26b794da5b11e59ed7f83b2cf8af202bd7eba8ad2fa6046e/wadler_lindig-0.1.7-py3-none-any.whl + name: wadler-lindig + version: 0.1.7 + sha256: e3ec83835570fd0a9509f969162aeb9c65618f998b1f42918cfc8d45122fe953 + requires_dist: + - numpy ; extra == 'dev' + - pre-commit ; extra == 'dev' + - pytest ; extra == 'dev' + - hippogriffe==0.1.0 ; extra == 'docs' + - mkdocs-include-exclude-files==0.1.0 ; extra == 'docs' + - mkdocs-ipynb==0.1.0 ; extra == 'docs' + - mkdocs-material==9.6.7 ; extra == 'docs' + - mkdocs==1.6.1 ; extra == 'docs' + - mkdocstrings[python]==0.28.3 ; extra == 'docs' + - pymdown-extensions==10.14.3 ; extra == 'docs' + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.1.1-py312h0ccc70a_0.conda + sha256: 5cc839dafe34e5f7b612e1d4d97bb11546eae8b1842e5b7870b3c6adbe9097e8 + md5: d8ecac58c1cb180296a1dd7de058dbc5 + depends: + - __glibc >=2.17,<3.0.a0 + - anyio >=3.0.0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + purls: + - pkg:pypi/watchfiles?source=hash-mapping + size: 419919 + timestamp: 1760456820374 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda + sha256: 3aa04ae8e9521d9b56b562376d944c3e52b69f9d2a0667f77b8953464822e125 + md5: 035da2e4f5770f036ff704fa17aace24 + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.7.1,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 329779 + timestamp: 1761174273487 +- conda: https://conda.anaconda.org/conda-forge/noarch/wayland-protocols-1.46-hd8ed1ab_0.conda + sha256: 8094050a5146dadc4e94d71351b70ec0f54803ef3999afa6640e599a0b3b43a8 + md5: 967e4d37eaad18d4add66aaa394d8de8 + license: MIT + license_family: MIT + purls: [] + size: 139554 + timestamp: 1764021418156 +- pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl + name: wcwidth + version: 0.2.14 + sha256: a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1 + requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl + name: websocket-client + version: 1.9.0 + sha256: af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef + requires_dist: + - pytest ; extra == 'test' + - websockets ; extra == 'test' + - python-socks ; extra == 'optional' + - wsaccel ; extra == 'optional' + - sphinx>=6.0 ; extra == 'docs' + - sphinx-rtd-theme>=1.1.0 ; extra == 'docs' + - myst-parser>=2.0.0 ; extra == 'docs' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-15.0.1-py312h5253ce2_2.conda + sha256: 550e082eb189cf1a6dea57e544259152704759524f373ba2bd773cb8214a0c23 + md5: 3fed1ea2c74091df72ad4e893e55c905 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/websockets?source=hash-mapping + size: 356215 + timestamp: 1756476348289 +- conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.3-pyhd8ed1ab_1.conda + sha256: cd9a603beae0b237be7d9dfae8ae0b36ad62666ac4bb073969bce7da6f55157c + md5: 0a9b57c159d56b508613cc39022c1b9e + depends: + - markupsafe >=2.1.1 + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/werkzeug?source=hash-mapping + size: 243546 + timestamp: 1733160561258 +- conda: https://conda.anaconda.org/conda-forge/noarch/wslink-2.5.0-pyhd8ed1ab_0.conda + sha256: e9ac3caa3b17bed9bc301a67d3950f84fa37fb34002d2878c46cafb87978401d + md5: 8fa415e696acd9af59ce0a4425fd1b38 + depends: + - aiohttp <4 + - msgpack-python >=1,<2 + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/wslink?source=hash-mapping + size: 35839 + timestamp: 1760984848678 +- conda: https://conda.anaconda.org/conda-forge/linux-64/x264-1!164.3095-h166bdaf_2.tar.bz2 + sha256: 175315eb3d6ea1f64a6ce470be00fa2ee59980108f246d3072ab8b977cb048a5 + md5: 6c99772d483f566d59e25037fea2c4b1 + depends: + - libgcc-ng >=12 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 897548 + timestamp: 1660323080555 +- conda: https://conda.anaconda.org/conda-forge/linux-64/x265-3.5-h924138e_3.tar.bz2 + sha256: 76c7405bcf2af639971150f342550484efac18219c0203c5ee2e38b8956fe2a0 + md5: e7f6ed84d4623d52ee581325c1587a6b + depends: + - libgcc-ng >=10.3.0 + - libstdcxx-ng >=10.3.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 3357188 + timestamp: 1646609687141 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + sha256: ad8cab7e07e2af268449c2ce855cbb51f43f4664936eff679b1f3862e6e4b01d + md5: fdc27cb255a7a2cc73b7919a968b48f0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20772 + timestamp: 1750436796633 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + sha256: c2be9cae786fdb2df7c2387d2db31b285cf90ab3bfabda8fa75a596c3d20fc67 + md5: 4d1fc190b99912ed557a8236e958c559 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxcb >=1.13 + - libxcb >=1.17.0,<2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20829 + timestamp: 1763366954390 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + sha256: 94b12ff8b30260d9de4fd7a28cca12e028e572cbc504fd42aa2646ec4a5bded7 + md5: a0901183f08b6c7107aab109733a3c91 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + license: MIT + license_family: MIT + purls: [] + size: 24551 + timestamp: 1718880534789 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + sha256: 546e3ee01e95a4c884b6401284bb22da449a2f4daf508d038fdfa0712fe4cc69 + md5: ad748ccca349aec3e91743e08b5e2b50 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 14314 + timestamp: 1718846569232 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + sha256: 2d401dadc43855971ce008344a4b5bd804aca9487d8ebd83328592217daca3df + md5: 0e0cbe0564d03a99afd5fd7b362feecd + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 16978 + timestamp: 1718848865819 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + sha256: 31d44f297ad87a1e6510895740325a635dd204556aa7e079194a0034cdd7e66a + md5: 608e0ef8256b81d04456e8d211eee3e8 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 51689 + timestamp: 1718844051451 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda + sha256: aa03b49f402959751ccc6e21932d69db96a65a67343765672f7862332aa32834 + md5: 71ae752a748962161b4740eaff510258 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 396975 + timestamp: 1759543819846 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + sha256: c12396aabb21244c212e488bbdc4abcdef0b7404b15761d9329f5a4a39113c4b + md5: fb901ff28063514abb6046c9ec2c4a45 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 58628 + timestamp: 1734227592886 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda + sha256: 277841c43a39f738927145930ff963c5ce4c4dacf66637a3d95d802a64173250 + md5: 1c74ff8c35dcadf952a16f752ca5aa49 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libuuid >=2.38.1,<3.0a0 + - xorg-libice >=1.1.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 27590 + timestamp: 1741896361728 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda + sha256: 51909270b1a6c5474ed3978628b341b4d4472cd22610e5f22b506855a5e20f67 + md5: db038ce880f100acc74dba10302b5630 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 835896 + timestamp: 1741901112627 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + sha256: 6bc6ab7a90a5d8ac94c7e300cc10beb0500eeba4b99822768ca2f2ef356f731b + md5: b2895afaf55bf96a8c8282a2e47a5de0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 15321 + timestamp: 1762976464266 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda + sha256: 753f73e990c33366a91fd42cc17a3d19bb9444b9ca5ff983605fa9e953baf57f + md5: d3c295b50f092ab525ffe3c2aa4b7413 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 13603 + timestamp: 1727884600744 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + sha256: 832f538ade441b1eee863c8c91af9e69b356cd3e9e1350fff4fe36cc573fc91a + md5: 2ccd714aa2242315acaf0a67faea780b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + - xorg-libxrender >=0.9.11,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 32533 + timestamp: 1730908305254 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + sha256: 43b9772fd6582bf401846642c4635c47a9b0e36ca08116b3ec3df36ab96e0ec0 + md5: b5fcc7172d22516e1f965490e65e33a4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 13217 + timestamp: 1727891438799 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda + sha256: 25d255fb2eef929d21ff660a0c687d38a6d2ccfbcbf0cc6aa738b12af6e9d142 + md5: 1dafce8548e38671bea82e3f5c6ce22f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 20591 + timestamp: 1762976546182 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda + sha256: da5dc921c017c05f38a38bd75245017463104457b63a1ce633ed41f214159c14 + md5: febbab7d15033c913d53c7a2c102309d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 50060 + timestamp: 1727752228921 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda + sha256: 83c4c99d60b8784a611351220452a0a85b080668188dce5dfa394b723d7b64f4 + md5: ba231da7fccf9ea1e768caf5c7099b84 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20071 + timestamp: 1759282564045 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda + sha256: 1a724b47d98d7880f26da40e45f01728e7638e6ec69f35a3e11f92acd05f9e7a + md5: 17dcc85db3c7886650b8908b183d6876 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 47179 + timestamp: 1727799254088 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda + sha256: 1b9141c027f9d84a9ee5eb642a0c19457c788182a5a73c5a9083860ac5c20a8c + md5: 5e2eb9bf77394fc2e5918beefec9f9ab + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 13891 + timestamp: 1727908521531 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda + sha256: ac0f037e0791a620a69980914a77cb6bb40308e26db11698029d6708f5aa8e0d + md5: 2de7f99d6581a4a7adbff607b5c278ca + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrender >=0.9.11,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 29599 + timestamp: 1727794874300 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + sha256: 044c7b3153c224c6cedd4484dd91b389d2d7fd9c776ad0f4a34f099b3389f4a1 + md5: 96d57aba173e878a2089d5638016dc5e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 33005 + timestamp: 1734229037766 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxscrnsaver-1.2.4-hb9d3cd8_0.conda + sha256: 58e8fc1687534124832d22e102f098b5401173212ac69eb9fd96b16a3e2c8cb2 + md5: 303f7a0e9e0cd7d250bb6b952cecda90 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 14412 + timestamp: 1727899730073 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + sha256: 752fdaac5d58ed863bbf685bb6f98092fe1a488ea8ebb7ed7b606ccfce08637a + md5: 7bbe9a0cc0df0ac5f5a8ad6d6a11af2f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxi >=1.7.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 32808 + timestamp: 1727964811275 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda + sha256: 8a4e2ee642f884e6b78c20c0892b85dd9b2a6e64a6044e903297e616be6ca35b + md5: 5efa5fa6243a622445fdfd72aee15efa + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 17819 + timestamp: 1734214575628 +- pypi: https://files.pythonhosted.org/packages/38/4f/fe9a4d472aa867878ce3bb7efb16654c5d63672b86dc0e6e953a67018433/yacs-0.1.8-py3-none-any.whl + name: yacs + version: 0.1.8 + sha256: 99f893e30497a4b66842821bac316386f7bd5c4f47ad35c9073ef089aa33af32 + requires_dist: + - pyyaml +- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + sha256: 6d9ea2f731e284e9316d95fa61869fe7bbba33df7929f82693c121022810f4ad + md5: a77f85f77be52ff59391544bfe73390a + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 85189 + timestamp: 1753484064210 +- pypi: https://files.pythonhosted.org/packages/37/81/6acd6601f61e31cfb8729d3da6d5df966f80f374b78eff83760714487338/yapf-0.43.0-py3-none-any.whl + name: yapf + version: 0.43.0 + sha256: 224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca + requires_dist: + - platformdirs>=3.5.1 + - tomli>=2.0.1 ; python_full_version < '3.11' + requires_python: '>=3.7' +- conda: https://conda.anaconda.org/conda-forge/linux-64/yarl-1.22.0-py312h8a5da7c_0.conda + sha256: 6e3f2db09387fc982b5400b842745084825cd2d4621e8278e4af8fb0dc2b55d8 + md5: 6a3fd177315aaafd4366930d440e4430 + depends: + - __glibc >=2.17,<3.0.a0 + - idna >=2.0 + - libgcc >=14 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/yarl?source=hash-mapping + size: 151549 + timestamp: 1761337128623 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda + sha256: 47cfe31255b91b4a6fa0e9dbaf26baa60ac97e033402dbc8b90ba5fee5ffe184 + md5: 8035e5b54c08429354d5d64027041cad + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libsodium >=1.0.20,<1.0.21.0a0 + - krb5 >=1.21.3,<1.22.0a0 + license: MPL-2.0 + license_family: MOZILLA + purls: [] + size: 310648 + timestamp: 1757370847287 +- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda + sha256: 7560d21e1b021fd40b65bfb72f67945a3fcb83d78ad7ccf37b8b3165ec3b68ad + md5: df5e78d904988eb55042c0c97446079f + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/zipp?source=hash-mapping + size: 22963 + timestamp: 1749421737203 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + sha256: 5d7c0e5f0005f74112a34a7425179f4eb6e73c92f5d109e6af4ddeca407c92ab + md5: c9f075ab2f33b3bbee9e62d4ad0a6cd8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib 1.3.1 hb9d3cd8_2 + license: Zlib + license_family: Other + purls: [] + size: 92286 + timestamp: 1727963153079 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda + sha256: c2bcb8aa930d6ea3c9c7a64fc4fab58ad7bcac483a9a45de294f67d2f447f413 + md5: 02738ff9855946075cbd1b5274399a41 + depends: + - python + - cffi >=1.11 + - zstd >=1.5.7,<1.5.8.0a0 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/zstandard?source=compressed-mapping + size: 467133 + timestamp: 1762512686069 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + sha256: a4166e3d8ff4e35932510aaff7aa90772f84b4d07e9f6f83c614cba7ceefe0eb + md5: 6432cb5d4ac0046c3ac0a8a0f95842f9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 567578 + timestamp: 1742433379869 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..987247bea2d5c00c66de16856e61c13dcf67b703 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,149 @@ +[project] +authors = [{ name = "pablo vela", email = "pablovela5620@gmail.com" }] +dependencies = [ + "jaxtyping<0.3.0", + "numpy>=2.0", + "einops>=0.8.0", + "icecream>=2.1.3", + "opencv-python>=4.10.0", + "pyserde>=0.20.0", + "rerun-sdk>=0.27.0", + "tyro>=0.9.1", + "tqdm", + "hf-transfer>=0.1.9", + "lovely-numpy>=0.2.13,<0.3", + "pandas>=2.3.3", + "braceexpand>=0.1.7,<0.2", + "roma>=1.5.4,<2", + "pytorch-lightning>=2.5.6,<3", + "yacs>=0.1.8,<0.2", + "omegaconf>=2.3.0,<3", + "termcolor>=3.2.0,<4", + "gradio-rerun>=0.27.0", + "spaces>=0.43.0", +] +name = "sam3d_body" +requires-python = ">= 3.12" +version = "0.1.0" + + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.metadata] +allow-direct-references = true + +[tool.pixi.workspace] +channels = ["conda-forge"] +platforms = ["linux-64"] +preview = ["pixi-build"] + +[tool.pixi.pypi-options] +no-build-isolation = ["detectron2", "moge"] +[tool.pixi.pypi-options.dependency-overrides] +# Allow iopath >=0.1.10 even though detectron2 pins <0.1.10, so it can satisfy sam-2. +iopath = ">=0.1.10" +gradio = ">=5.45.0,<6" +[tool.pixi.pypi-dependencies] +sam3d_body = { path = ".", editable = true } +moge = { git = "https://github.com/microsoft/MoGe.git" } +simplecv = { git = "https://github.com/pablovela5620/simplecv.git", branch = "main" } +timm = ">=0.9" +transformers = { git = "https://github.com/huggingface/transformers.git", rev = "d08b98b965176ea9cf8c8e8b24995c955b7e2ec9" } +monopriors = { git = "https://github.com/pablovela5620/monoprior.git" } + +[tool.pixi.tasks] +app = "python tool/gradio_sam3d_body.py" +cli = "python tool/demo.py --image-folder data/example-data" + +[tool.pixi.feature.cuda129.system-requirements] +cuda = "12.9" + +[tool.pixi.feature.cuda129.dependencies] +# CUDA Build Tools +cuda-compiler = "*" +cuda-version = "12.9.*" +cuda-cudart-dev = "*" +cuda-crt = "*" +libcusparse-dev = "*" +cuda-driver-dev = "*" +cuda-nvcc = "*" +cuda-nvrtc-dev = "*" +cuda-nvtx = "*" +cuda-nvtx-dev = "*" +cuda-nvml-dev = "*" +cuda-profiler-api = "*" + +# CUDA Libraries +cudnn = "*" +libcublas-dev = "*" +libcudss-dev = "*" +libcufile-dev = "*" +libcufft-dev = "*" +libcurand-dev = "*" +libcusolver-dev = "*" +cusparselt = "*" +libnvjitlink = "*" +# cuda129 end + +[tool.pixi.feature.gpu.dependencies] +pytorch-gpu = ">=2.8.0" +torchvision = "*" + + +[tool.pixi.feature.dev.dependencies] +beartype = "*" +pyrefly = ">=0.42.2,<0.43" +ruff = ">=0.14.5,<0.15" + +[tool.pixi.feature.dev.pypi-dependencies] +types-tqdm = "*" + +[tool.pixi.environments] +cuda128 = { features = [ + "cuda129", +], solve-group = "cuda129", no-default-feature = true } +default = { features = ["gpu", "cuda129"], solve-group = "cuda129" } +dev = { features = ["dev", "gpu", "cuda129"], solve-group = "cuda129" } + +[tool.pixi.dependencies] +av = ">=16.0.1,<17" +gradio = ">=5.45.0,<6" +huggingface_hub = ">=1.0,<2" +tomlkit = "==0.12.0" +audioop-lts = "*" +pydub = "*" +open3d = ">=0.19.0,<0.20" + +[tool.ruff] +line-length = 150 + +[tool.ruff.lint] +select = [ + # pycodestyle + "E", + # Pyflakes + "F", + # pyupgrade + "UP", + # flake8-bugbear + "B", + # flake8-simplify + "SIM", + # isort + "I", +] + +ignore = [ + "E501", # Line too long. + "F722", # Forward annotation false positive from jaxtyping. Should be caught by pyright. + "F821", # Forward annotation false positive from jaxtyping. Should be caught by pyright. + "UP037", # Remove quotes from type, false positive when using jaxtyping + "UP040", # Beartype fails if not using this for typealias + +] + +[tool.pyrefly] +project-includes = ["**/*"] +project-excludes = ["**/node_modules", "**/__pycache__", "**/*venv/**/*"] diff --git a/src/sam3d_body/__init__.py b/src/sam3d_body/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ef40b5aa945d47b9503b686129dffc29b39816dc --- /dev/null +++ b/src/sam3d_body/__init__.py @@ -0,0 +1,12 @@ +import os + +# Only enable beartype when running in the 'dev' environment +# Check the PIXI_ENVIRONMENT_NAME environment variable set by pixi +if os.environ.get("PIXI_ENVIRONMENT_NAME") == "dev": + try: + from beartype.claw import beartype_this_package + + beartype_this_package() + except ImportError: + # beartype not available even in dev environment + pass diff --git a/src/sam3d_body/api/demo.py b/src/sam3d_body/api/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..364b345926b5d34207650e60ea5b69e6fbb93de1 --- /dev/null +++ b/src/sam3d_body/api/demo.py @@ -0,0 +1,241 @@ +"""Minimal standalone demo wiring for SAM 3D Body with Rerun visualization.""" + +import os +from dataclasses import dataclass +from glob import glob +from pathlib import Path +from typing import Literal, TypedDict + +import cv2 +import numpy as np +import rerun as rr +import rerun.blueprint as rrb +import torch +from jaxtyping import Float32, UInt8 +from monopriors.relative_depth_models import BaseRelativePredictor, RelativeDepthPrediction, get_relative_predictor +from numpy import ndarray +from serde import serde +from simplecv.rerun_log_utils import RerunTyroConfig +from torch import Tensor +from tqdm import tqdm +from transformers.models.sam3 import Sam3Model, Sam3Processor +from yacs.config import CfgNode + +from sam3d_body.api.visualization import create_view, set_annotation_context, visualize_sample +from sam3d_body.build_models import load_sam_3d_body, load_sam_3d_body_hf +from sam3d_body.models.meta_arch import SAM3DBody +from sam3d_body.sam_3d_body_estimator import FinalPosePrediction, SAM3DBodyEstimator + + +class SAM3ResultsDict(TypedDict): + """Torch-format outputs returned directly by ``Sam3Processor`` post-processing.""" + + scores: Float32[Tensor, "n"] + boxes: Float32[Tensor, "n 4"] + masks: Float32[Tensor, "n h w"] + + +@serde() +class SAM3Results: + scores: Float32[ndarray, "n"] + """Per-instance confidence scores ``[N]``.""" + boxes: Float32[ndarray, "n 4"] + """Bounding boxes in XYXY pixel coordinates ``[N, 4]``.""" + masks: Float32[ndarray, "n h w"] + """Probability masks for each detection ``[N, H, W]`` (float32 in ``[0, 1]``).""" + + +@dataclass +class SAM3Config: + """Configuration for loading a SAM3 checkpoint and selecting device.""" + + device: Literal["cpu", "cuda"] = "cuda" + """Computation device passed to the Hugging Face SAM3 model.""" + sam3_checkpoint: str = "facebook/sam3" + """Model identifier or path accepted by ``Sam3Model.from_pretrained``.""" + + +class SAM3Predictor: + """Lightweight wrapper around the SAM3 model for single-image inference.""" + + def __init__(self, config: SAM3Config): + self.config = config + self.sam3_model = Sam3Model.from_pretrained(config.sam3_checkpoint).to(config.device) + self.sam3_processor = Sam3Processor.from_pretrained(config.sam3_checkpoint) + + def predict_single_image(self, rgb_hw3: UInt8[ndarray, "h w 3"], text: str = "person") -> SAM3Results: + """Run SAM3 instance segmentation on one RGB image. + + Args: + rgb_hw3: Input image in RGB order with dtype ``uint8`` and shape ``[H, W, 3]``. + text: Optional prompt used by SAM3's text-conditioned decoder (default: ``"person"``). + + Returns: + ``SAM3Results`` with NumPy copies of scores, XYXY boxes, and binary masks. + """ + inputs = self.sam3_processor( + images=rgb_hw3, + text=text, + return_tensors="pt", + ).to(self.config.device) + + with torch.no_grad(): + outputs = self.sam3_model(**inputs) + + results: SAM3ResultsDict = self.sam3_processor.post_process_instance_segmentation( + outputs, threshold=0.5, mask_threshold=0.5, target_sizes=inputs.get("original_sizes").tolist() + )[0] + + mask_probs: Float32[ndarray, "n h w"] = results["masks"].detach().cpu().numpy().astype(np.float32, copy=False) + + return SAM3Results( + scores=results["scores"].detach().cpu().numpy().astype(np.float32, copy=False), + boxes=results["boxes"].detach().cpu().numpy().astype(np.float32, copy=False), + masks=mask_probs, + ) + + +@dataclass +class SAM3DBodyE2EConfig: + """Bundle of sub-configurations required for the end-to-end demo.""" + + sam3_config: SAM3Config + """Settings for the underlying SAM3 detector.""" + fov_estimator: Literal["MogeV1Predictor"] = "MogeV1Predictor" + """Identifier of the relative depth/FOV estimator to load.""" + mhr_path: Path = Path("checkpoints/sam-3d-body-dinov3/assets/mhr_model.pt") + """Path to the MHR mesh/pose asset file required by the head network.""" + checkpoint_path: Path = Path("checkpoints/sam-3d-body-dinov3/model.ckpt") + """Core SAM 3D Body model checkpoint (.ckpt).""" + + +class SAM3DBodyE2E: + """Convenience facade that chains detection, FOV estimation, and 3D reconstruction.""" + + def __init__(self, config: SAM3DBodyE2EConfig): + self.sam3_predictor = SAM3Predictor(config.sam3_config) + self.fov_predictor: BaseRelativePredictor = get_relative_predictor(config.fov_estimator)(device="cuda") + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + # load_output: tuple[SAM3DBody, CfgNode] = load_sam_3d_body( + # config.checkpoint_path, + # device=device, + # mhr_path=config.mhr_path, + # ) + load_output: tuple[SAM3DBody, CfgNode] = load_sam_3d_body_hf(repo_id="facebook/sam-3d-body-dinov3") + model: SAM3DBody = load_output[0] + self.sam3d_body_estimator = SAM3DBodyEstimator( + sam_3d_body_model=model, + ) + + def predict_single_image( + self, rgb_hw3: UInt8[ndarray, "h w 3"] + ) -> tuple[list[FinalPosePrediction], RelativeDepthPrediction]: + """Estimate 3D poses for a single frame. + + Pipeline: + 1. Use the configured relative-depth predictor to derive camera intrinsics ``K_33``. + 2. Run SAM3 to obtain person masks and boxes. + 3. Feed detections and intrinsics into ``SAM3DBodyEstimator`` for per-person 3D bodies. + + Args: + rgb_hw3: RGB image with shape ``[H, W, 3]`` and dtype ``uint8``. + + Returns: + A list of ``FinalPosePrediction`` entriesโ€”one per detected person. + """ + # estimate the camera intrinsics + relative_pred: RelativeDepthPrediction = self.fov_predictor(rgb=rgb_hw3, K_33=None) + K_33: Float32[ndarray, "3 3"] = relative_pred.K_33 + + sam3_results: SAM3Results = self.sam3_predictor.predict_single_image(rgb_hw3) + + outputs: list[FinalPosePrediction] = self.sam3d_body_estimator.process_one_image( + rgb_hw3, + xyxy=sam3_results.boxes, + masks=sam3_results.masks, + masks_score=sam3_results.scores, + K_33=K_33, + ) + return outputs, relative_pred + + +@dataclass(slots=True) +class Sam3DBodyDemoConfig: + """Configuration for the standalone demo runner.""" + + rr_config: RerunTyroConfig + """Viewer/runtime options for Rerun (window layout, recording, etc.).""" + + sam3_e2e_config: SAM3DBodyE2EConfig + """Configuration for the end-to-end SAM 3D Body model.""" + + image_folder: Path | None = None + """Directory containing input images to process.""" + + image_path: Path | None = None + """Path to a single input image to process.""" + + max_frames: int | None = None + """Optional limit on the number of images to process; ``None`` processes all images.""" + + +def main(cfg: Sam3DBodyDemoConfig): + """Run the Rerun-enabled demo on a folder or single image. + + Args: + cfg: Aggregated configuration containing Rerun settings, SAM3 model options, + and input image selection. + """ + # Setup Rerun + parent_log_path = Path("/world") + set_annotation_context() + view: rrb.ContainerLike = create_view() + blueprint = rrb.Blueprint(view, collapse_panels=True) + rr.send_blueprint(blueprint) + rr.log("/", rr.ViewCoordinates.RDF, static=True) + + if cfg.image_path is not None: + images_list = [str(cfg.image_path)] + elif cfg.image_folder is not None: + image_extensions: list[str] = [ + "*.jpg", + "*.jpeg", + "*.png", + "*.gif", + "*.bmp", + "*.tiff", + "*.webp", + ] + images_list: list[str] = sorted( + [image for ext in image_extensions for image in glob(os.path.join(cfg.image_folder, ext))] + ) + else: + raise ValueError("Either image_path or image_folder must be specified.") + + # load end to end model + sam3D_body_e2e = SAM3DBodyE2E(cfg.sam3_e2e_config) + + for idx, image_path in enumerate(tqdm(images_list)): + rr.set_time(timeline="image_sequence", sequence=idx) + # load image and convert to RGB + bgr_hw3: UInt8[ndarray, "h w 3"] = cv2.imread(image_path) + rgb_hw3: UInt8[ndarray, "h w 3"] = cv2.cvtColor(bgr_hw3, cv2.COLOR_BGR2RGB) + + outputs: tuple[list[FinalPosePrediction], RelativeDepthPrediction] = sam3D_body_e2e.predict_single_image( + rgb_hw3 + ) + pred_list: list[FinalPosePrediction] = outputs[0] + relative_pred: RelativeDepthPrediction = outputs[1] + + if len(pred_list) == 0: + # Detector/FOV failed on this frame; avoid crashing the visualization step. + print(f"[warn] No detections for {image_path}; skipping.") + continue + + visualize_sample( + pred_list=pred_list, + rgb_hw3=rgb_hw3, + parent_log_path=parent_log_path, + faces=sam3D_body_e2e.sam3d_body_estimator.faces, + relative_depth_pred=relative_pred, + ) diff --git a/src/sam3d_body/api/visualization.py b/src/sam3d_body/api/visualization.py new file mode 100644 index 0000000000000000000000000000000000000000..2ad750a223e2282e72c95f98d9b12dcaa9ae5c7e --- /dev/null +++ b/src/sam3d_body/api/visualization.py @@ -0,0 +1,425 @@ +from pathlib import Path + +import cv2 +import numpy as np +import open3d as o3d +import rerun as rr +import rerun.blueprint as rrb +from jaxtyping import Bool, Float32, Int, UInt8 +from monopriors.depth_utils import depth_edges_mask +from monopriors.relative_depth_models import RelativeDepthPrediction +from numpy import ndarray +from simplecv.camera_parameters import Extrinsics, Intrinsics, PinholeParameters +from simplecv.ops.pc_utils import estimate_voxel_size +from simplecv.rerun_log_utils import log_pinhole + +from sam3d_body.metadata.mhr70 import MHR70_ID2NAME, MHR70_IDS, MHR70_LINKS +from sam3d_body.sam_3d_body_estimator import FinalPosePrediction + +BOX_PALETTE: UInt8[np.ndarray, "n_colors 4"] = np.array( + [ + [255, 99, 71, 255], # tomato + [65, 105, 225, 255], # royal blue + [60, 179, 113, 255], # medium sea green + [255, 215, 0, 255], # gold + [138, 43, 226, 255], # blue violet + [255, 140, 0, 255], # dark orange + [220, 20, 60, 255], # crimson + [70, 130, 180, 255], # steel blue + ], + dtype=np.uint8, +) + +# Use a separate id range for segmentation classes to avoid clobbering the person class (id=0). +SEG_CLASS_OFFSET = 1000 # background = 1000, persons start at 1001 +MAX_POINT_CLOUD_POINTS = 50_000 +MIN_DEPTH_CONFIDENCE = 0.5 + + +def filter_out_of_bounds( + uv: Float32[ndarray, "n_points 2"], + h: int, + w: int, + xyz_cam: Float32[ndarray, "n_points 3"] | None = None, +) -> Float32[ndarray, "n_points 2"]: + """Return a copy of ``uv`` with off-screen (and optional behind-camera) points masked. + + Args: + uv: Pixel coordinates ``[N, 2]`` in (u, v) order. + h: Image height in pixels. + w: Image width in pixels. + xyz_cam: Optional camera-frame coordinates ``[N, 3]`` to mask points with negative ``z``. + + Returns: + Copy of ``uv`` where out-of-bounds rows are set to ``NaN`` so Rerun hides them. + """ + + uv_filtered: Float32[ndarray, "n_points 2"] = np.asarray(uv, dtype=np.float32).copy() + + out_of_bounds: Bool[ndarray, "n_points"] = np.logical_or(uv_filtered[:, 0] >= float(w), uv_filtered[:, 0] < 0.0) + out_of_bounds = np.logical_or(out_of_bounds, uv_filtered[:, 1] >= float(h)) + out_of_bounds = np.logical_or(out_of_bounds, uv_filtered[:, 1] < 0.0) + + if xyz_cam is not None: + out_of_bounds = np.logical_or(out_of_bounds, xyz_cam[:, 2] < 0.0) + + uv_filtered[out_of_bounds, :] = np.nan + return uv_filtered + + +def compute_vertex_normals( + verts: Float32[ndarray, "n_verts 3"], + faces: Int[ndarray, "n_faces 3"], + eps: float = 1e-12, +) -> Float32[ndarray, "n_verts 3"]: + """Compute per-vertex normals for a single mesh. + + Args: + verts: Float32 array of vertex positions with shape ``(n_verts, 3)``. + faces: Int array of triangle indices with shape ``(n_faces, 3)``. + eps: Small epsilon to avoid division by zero when normalizing. + + Returns: + Float32 array of unit vertex normals with shape ``(n_verts, 3)``; zeros for degenerate vertices. + """ + + # Expand faces to vertex triplets and fetch their positions. + faces_i: Int[ndarray, "n_faces 3"] = faces.astype(np.int64) + v0: Float32[ndarray, "n_faces 3"] = verts[faces_i[:, 0]] + v1: Float32[ndarray, "n_faces 3"] = verts[faces_i[:, 1]] + v2: Float32[ndarray, "n_faces 3"] = verts[faces_i[:, 2]] + + # Face normal = cross(edge1, edge2). + e1: Float32[ndarray, "n_faces 3"] = v1 - v0 + e2: Float32[ndarray, "n_faces 3"] = v2 - v0 + face_normals: Float32[ndarray, "n_faces 3"] = np.cross(e1, e2) + + # Accumulate each face normal into its three vertices with a vectorized scatter-add. + vertex_normals: Float32[ndarray, "n_verts 3"] = np.zeros_like(verts, dtype=np.float32) + flat_indices: Int[ndarray, "n_faces3"] = faces_i.reshape(-1) + face_normals_repeated: Float32[ndarray, "n_faces3 3"] = np.repeat(face_normals, 3, axis=0) + np.add.at(vertex_normals, flat_indices, face_normals_repeated) + + norms: Float32[ndarray, "n_verts 1"] = np.linalg.norm(vertex_normals, axis=-1, keepdims=True) + denom: Float32[ndarray, "n_verts 1"] = np.maximum(norms, eps).astype(np.float32) + vn_unit: Float32[ndarray, "n_verts 3"] = (vertex_normals / denom).astype(np.float32) + mask: ndarray = norms > eps + vn_unit = np.where(mask, vn_unit, np.float32(0.0)) + return vn_unit + + +def export_meshes_to_glb( + pred_list: list[FinalPosePrediction], + faces: Int[ndarray, "n_faces 3"], + output_dir: Path, + box_palette: UInt8[ndarray, "n_colors 4"] = BOX_PALETTE, + center_mesh: bool = True, +) -> list[Path]: + """Write one GLB per predicted mesh and return the file paths.""" + + output_dir.mkdir(parents=True, exist_ok=True) + written_paths: list[Path] = [] + faces_int: Int[ndarray, "n_faces 3"] = np.ascontiguousarray(faces, dtype=np.int32) + + for idx, output in enumerate(pred_list): + verts_cam: Float32[ndarray, "n_verts 3"] = np.ascontiguousarray(output.pred_vertices, dtype=np.float32) + cam_t: Float32[ndarray, "3"] = np.ascontiguousarray(output.pred_cam_t, dtype=np.float32) + # Convert to world coordinates to mirror the viewer logging convention (cam โ†’ world via translation). + verts_world: Float32[ndarray, "n_verts 3"] = np.ascontiguousarray(verts_cam + cam_t, dtype=np.float32) + verts_export: Float32[ndarray, "n_verts 3"] + verts_export = verts_world - np.mean(verts_world, axis=0, keepdims=True) if center_mesh else verts_world + + vertex_normals: Float32[ndarray, "n_verts 3"] = compute_vertex_normals(verts_export, faces_int) + + mesh = o3d.geometry.TriangleMesh() + mesh.vertices = o3d.utility.Vector3dVector(verts_export.astype(np.float64)) + mesh.triangles = o3d.utility.Vector3iVector(faces_int.astype(np.int32)) + mesh.vertex_normals = o3d.utility.Vector3dVector(vertex_normals.astype(np.float64)) + + color: Float32[ndarray, "3"] = box_palette[idx % len(box_palette), :3].astype(np.float32) / 255.0 + vertex_colors: Float32[ndarray, "n_verts 3"] = np.repeat(color[np.newaxis, :], verts_export.shape[0], axis=0) + mesh.vertex_colors = o3d.utility.Vector3dVector(vertex_colors.astype(np.float64)) + + glb_path: Path = output_dir / f"person_{idx:02d}.glb" + success: bool = bool( + o3d.io.write_triangle_mesh( + str(glb_path), + mesh, + write_ascii=False, + write_vertex_normals=True, + write_vertex_colors=True, + ) + ) + if not success: + fallback_path: Path = output_dir / f"person_{idx:02d}.ply" + success = bool( + o3d.io.write_triangle_mesh( + str(fallback_path), + mesh, + write_ascii=False, + write_vertex_normals=True, + write_vertex_colors=True, + ) + ) + if success: + glb_path = fallback_path + + if success: + written_paths.append(glb_path) + + return written_paths + + +def set_annotation_context() -> None: + """Register MHR-70 semantic metadata so subsequent logs show names/edges and mask colors.""" + # Base person class (for keypoints / boxes) uses id=0 (original), segmentation uses 1000+ to avoid clashes. + person_class = rr.ClassDescription( + info=rr.AnnotationInfo(id=0, label="Person", color=(0, 0, 255)), + keypoint_annotations=[rr.AnnotationInfo(id=idx, label=name) for idx, name in MHR70_ID2NAME.items()], + keypoint_connections=MHR70_LINKS, + ) + + # Segmentation classes: id=SEG_CLASS_OFFSET background, ids SEG_CLASS_OFFSET+1..n for each instance color. + seg_classes: list[rr.ClassDescription] = [ + rr.ClassDescription(info=rr.AnnotationInfo(id=SEG_CLASS_OFFSET, label="Background", color=(64, 64, 64))), + ] + for idx, color in enumerate(BOX_PALETTE[:, :3].tolist(), start=1): + seg_classes.append( + rr.ClassDescription( + info=rr.AnnotationInfo( + id=SEG_CLASS_OFFSET + idx, label=f"Person-{idx}", color=tuple(int(c) for c in color) + ), + ) + ) + + rr.log( + "/", + rr.AnnotationContext([person_class, *seg_classes]), + static=True, + ) + + +def visualize_sample( + pred_list: list[FinalPosePrediction], + rgb_hw3: UInt8[ndarray, "h w 3"], + parent_log_path: Path, + faces: Int[ndarray, "n_faces 3"], + relative_depth_pred: RelativeDepthPrediction | None = None, +) -> None: + h: int = rgb_hw3.shape[0] + w: int = rgb_hw3.shape[1] + cam_log_path: Path = parent_log_path / "cam" + pinhole_log_path: Path = cam_log_path / "pinhole" + image_log_path: Path = pinhole_log_path / "image" + pred_log_path: Path = pinhole_log_path / "pred" + # log the pinhole camera parameters (assume fx=fy and center at image center) + focal_length: float = float(pred_list[0].focal_length) + intri: Intrinsics = Intrinsics( + camera_conventions="RDF", + fl_x=focal_length, + fl_y=focal_length, + cx=float(w) / 2.0, + cy=float(h) / 2.0, + height=h, + width=w, + ) + world_T_cam: Float32[ndarray, "4 4"] = np.eye(4, dtype=np.float32) + extri: Extrinsics = Extrinsics( + world_R_cam=world_T_cam[:3, :3], + world_t_cam=world_T_cam[:3, 3], + ) + + pinhole_params: PinholeParameters = PinholeParameters(intrinsics=intri, extrinsics=extri, name="pinhole") + log_pinhole(camera=pinhole_params, cam_log_path=cam_log_path) + # clear the previous pred logs + rr.log(f"{pred_log_path}", rr.Clear(recursive=True)) + rr.log(f"{image_log_path}", rr.Image(rgb_hw3, color_model=rr.ColorModel.RGB).compress(jpeg_quality=90)) + + # Build per-pixel maps (SEG_CLASS_OFFSET = background). Also build RGBA overlay with transparent background. + seg_map: Int[ndarray, "h w"] = np.full((h, w), SEG_CLASS_OFFSET, dtype=np.int32) + seg_overlay: UInt8[ndarray, "h w 4"] = np.zeros((h, w, 4), dtype=np.uint8) + human_mask: Bool[ndarray, "h w"] = np.zeros((h, w), dtype=bool) + + mesh_root_path: Path = parent_log_path / "pred" + rr.log(str(mesh_root_path), rr.Clear(recursive=True)) + + for i, output in enumerate(pred_list): + box_color: UInt8[ndarray, "1 4"] = BOX_PALETTE[i % len(BOX_PALETTE)].reshape(1, 4) + rr.log( + f"{pred_log_path}/bbox_{i}", + rr.Boxes2D( + array=output.bbox, + array_format=rr.Box2DFormat.XYXY, + class_ids=0, + colors=box_color, + show_labels=True, + ), + ) + + kpts_cam: Float32[ndarray, "n_kpts 3"] = np.ascontiguousarray(output.pred_keypoints_3d, dtype=np.float32) + kpts_uv: Float32[ndarray, "n_kpts 2"] = np.ascontiguousarray(output.pred_keypoints_2d, dtype=np.float32) + kpts_uv_in_bounds: Float32[ndarray, "n_kpts 2"] = filter_out_of_bounds( + uv=kpts_uv, + h=h, + w=w, + xyz_cam=None, # Depth sign from the model can be negative; only cull by image bounds. + ) + rr.log( + f"{pred_log_path}/uv_{i}", + rr.Points2D( + positions=kpts_uv_in_bounds, + keypoint_ids=MHR70_IDS, + class_ids=0, + colors=(0, 255, 0), + ), + ) + + # Accumulate segmentation masks (if present) into a single segmentation image. + mask = output.mask + if mask is not None: + mask_arr: ndarray = np.asarray(mask).squeeze() + if mask_arr.shape != seg_map.shape: + mask_arr = cv2.resize( + mask_arr.astype(np.uint8), (seg_map.shape[1], seg_map.shape[0]), interpolation=cv2.INTER_NEAREST + ) + mask_bool = mask_arr.astype(bool) + human_mask = np.logical_or(human_mask, mask_bool) + seg_id = SEG_CLASS_OFFSET + i + 1 # keep person class (0) separate from seg classes + seg_map = np.where(mask_bool, np.uint16(seg_id), seg_map) + + # Color overlay for this instance, background stays transparent. + color = BOX_PALETTE[i % len(BOX_PALETTE), :3] + seg_overlay[mask_bool] = np.array([color[0], color[1], color[2], 120], dtype=np.uint8) + + # Log 3D keypoints in world coordinates + cam_t: Float32[ndarray, "3"] = np.ascontiguousarray(output.pred_cam_t, dtype=np.float32) + kpts_world: Float32[ndarray, "n_kpts 3"] = np.ascontiguousarray(kpts_cam + cam_t, dtype=np.float32) + rr.log( + f"{parent_log_path}/pred/kpts3d_{i}", + rr.Points3D( + positions=kpts_world, + keypoint_ids=MHR70_IDS, + class_ids=0, + colors=(0, 255, 0), + ), + ) + + # Log the full-body mesh in world coordinates so it shows in 3D + verts_cam: Float32[ndarray, "n_verts 3"] = np.ascontiguousarray(output.pred_vertices, dtype=np.float32) + verts_world: Float32[ndarray, "n_verts 3"] = np.ascontiguousarray(verts_cam + cam_t, dtype=np.float32) + faces_int: Int[ndarray, "n_faces 3"] = np.ascontiguousarray(faces, dtype=np.int32) + vertex_normals: Float32[ndarray, "n_verts 3"] = compute_vertex_normals(verts_world, faces_int) + rr.log( + f"{parent_log_path}/pred/mesh_{i}", + rr.Mesh3D( + vertex_positions=verts_world, + triangle_indices=faces_int, + vertex_normals=vertex_normals, + albedo_factor=( + float(box_color[0, 0]) / 255.0, + float(box_color[0, 1]) / 255.0, + float(box_color[0, 2]) / 255.0, + 0.35, + ), + ), + ) + + # Log segmentation ids (full map) and an RGBA overlay with transparent background. + if np.any(seg_map != SEG_CLASS_OFFSET): + rr.log(f"{pred_log_path}/segmentation_ids", rr.SegmentationImage(seg_map)) + rr.log(f"{pred_log_path}/segmentation_overlay", rr.Image(seg_overlay, color_model=rr.ColorModel.RGBA)) + + # Optionally log depth and a background-only point cloud (for 3D view only). + if relative_depth_pred is not None: + depth_hw: Float32[ndarray, "h w"] = np.asarray(relative_depth_pred.depth, dtype=np.float32) + conf_hw: Float32[ndarray, "h w"] = np.asarray(relative_depth_pred.confidence, dtype=np.float32) + if depth_hw.shape != (h, w): + depth_hw = cv2.resize(depth_hw, (w, h), interpolation=cv2.INTER_NEAREST) + if conf_hw.shape != (h, w): + conf_hw = cv2.resize(conf_hw, (w, h), interpolation=cv2.INTER_NEAREST) + depth_hw = np.nan_to_num(depth_hw, nan=0.0, posinf=0.0, neginf=0.0) + + # Remove flying pixels along depth discontinuities. + edges_mask: Bool[ndarray, "h w"] = depth_edges_mask(depth_hw, threshold=0.01) + depth_hw = depth_hw * np.logical_not(edges_mask) + + # Remove low-confidence pixels. + conf_mask: Bool[ndarray, "h w"] = conf_hw >= MIN_DEPTH_CONFIDENCE + depth_hw = depth_hw * conf_mask + + background_mask: Bool[ndarray, "h w"] = np.logical_not(human_mask) + depth_bg: Float32[ndarray, "h w"] = depth_hw * background_mask + + # Log depth image (not referenced by the 2D blueprint). + # rr.log(f"{pinhole_log_path}/depth", rr.DepthImage(depth_bg, meter=1.0)) + + fx: float = float(relative_depth_pred.K_33[0, 0]) + fy: float = float(relative_depth_pred.K_33[1, 1]) + cx: float = float(relative_depth_pred.K_33[0, 2]) + cy: float = float(relative_depth_pred.K_33[1, 2]) + + u: Float32[ndarray, "w"] = np.arange(w, dtype=np.float32) + v: Float32[ndarray, "h"] = np.arange(h, dtype=np.float32) + uu: Float32[ndarray, "h w"] + vv: Float32[ndarray, "h w"] + uu, vv = np.meshgrid(u, v) + + z_cam: Float32[ndarray, "h w"] = depth_bg + valid: Bool[ndarray, "h w"] = np.logical_and(z_cam > 0.0, np.isfinite(z_cam)) + if np.any(valid): + x_cam: Float32[ndarray, "h w"] = (uu - cx) * z_cam / fx + y_cam: Float32[ndarray, "h w"] = (vv - cy) * z_cam / fy + points_cam: Float32[ndarray, "h w 3"] = np.stack([x_cam, y_cam, z_cam], axis=-1) + + points_flat: Float32[ndarray, "n_valid 3"] = points_cam[valid] + colors_flat: UInt8[ndarray, "n_valid 3"] = rgb_hw3[valid] + + if points_flat.shape[0] > MAX_POINT_CLOUD_POINTS: + voxel_size: float = estimate_voxel_size( + points_flat, target_points=MAX_POINT_CLOUD_POINTS, tolerance=0.25 + ) + pcd: o3d.geometry.PointCloud = o3d.geometry.PointCloud() + pcd.points = o3d.utility.Vector3dVector(points_flat) + pcd.colors = o3d.utility.Vector3dVector(colors_flat.astype(np.float32) / 255.0) + pcd_ds: o3d.geometry.PointCloud = pcd.voxel_down_sample(voxel_size) + points_flat = np.asarray(pcd_ds.points, dtype=np.float32) + colors_flat = (np.asarray(pcd_ds.colors, dtype=np.float32) * 255.0).astype(np.uint8) + + rr.log( + f"{parent_log_path}/depth_point_cloud", + rr.Points3D( + positions=points_flat, + colors=colors_flat, + ), + ) + + +def create_view() -> rrb.ContainerLike: + view_2d = rrb.Vertical( + contents=[ + # Top: people-only overlay on the RGB image. + rrb.Spatial2DView( + name="image", + origin="/world/cam/pinhole", + contents=[ + "/world/cam/pinhole/image", + "/world/cam/pinhole/pred/segmentation_overlay", + ], + ), + # Bottom: 2D boxes + keypoints; segmentation hidden. + rrb.Spatial2DView( + name="mhr", + origin="/world/cam/pinhole", + contents=[ + "/world/cam/pinhole/image", + "/world/cam/pinhole/pred/**", + "- /world/cam/pinhole/pred/segmentation_overlay/**", + "- /world/cam/pinhole/pred/segmentation_ids/**", + ], + ), + ], + ) + view_3d = rrb.Spatial3DView(name="mhr_3d", line_grid=rrb.LineGrid3D(visible=False)) + main_view = rrb.Horizontal(contents=[view_2d, view_3d], column_shares=[2, 3]) + view = rrb.Tabs(contents=[main_view], name="sam-3d-body-demo") + return view diff --git a/src/sam3d_body/build_models.py b/src/sam3d_body/build_models.py new file mode 100644 index 0000000000000000000000000000000000000000..6a22d73ab1a2972991d858f4eae062511ded0029 --- /dev/null +++ b/src/sam3d_body/build_models.py @@ -0,0 +1,56 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +import os +from os import PathLike + +import torch + +from .models.meta_arch import SAM3DBody +from .utils.checkpoint import load_state_dict +from .utils.config import CN, get_config + + +def load_sam_3d_body( + checkpoint_path: str | PathLike[str] = "", + device: str | torch.device = "cuda", + mhr_path: str | PathLike[str] = "", +) -> tuple[SAM3DBody, CN]: + print("Loading SAM 3D Body model...") + + checkpoint_path = os.fspath(checkpoint_path) + mhr_path = os.fspath(mhr_path) + + # Check the current directory, and if not present check the parent dir. + model_cfg = os.path.join(os.path.dirname(checkpoint_path), "model_config.yaml") + if not os.path.exists(model_cfg): + # Looks at parent dir + model_cfg = os.path.join(os.path.dirname(os.path.dirname(checkpoint_path)), "model_config.yaml") + + model_cfg = get_config(model_cfg) + + # Disable face for inference + model_cfg.defrost() + model_cfg.MODEL.MHR_HEAD.MHR_MODEL_PATH = mhr_path + model_cfg.freeze() + + # Initialze the model + model = SAM3DBody(model_cfg) + + checkpoint = torch.load(checkpoint_path, map_location="cpu", weights_only=False) + state_dict = checkpoint.get("state_dict", checkpoint) + load_state_dict(model, state_dict, strict=False) + + model = model.to(device) + model.eval() + return model, model_cfg + + +def _hf_download(repo_id): + from huggingface_hub import snapshot_download + + local_dir = snapshot_download(repo_id=repo_id) + return os.path.join(local_dir, "model.ckpt"), os.path.join(local_dir, "assets", "mhr_model.pt") + + +def load_sam_3d_body_hf(repo_id, **kwargs): + ckpt_path, mhr_path = _hf_download(repo_id) + return load_sam_3d_body(checkpoint_path=ckpt_path, mhr_path=mhr_path) diff --git a/src/sam3d_body/data/__init__.py b/src/sam3d_body/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..71ca4b12c770afea62d06f97064cdf0c97d40ed7 --- /dev/null +++ b/src/sam3d_body/data/__init__.py @@ -0,0 +1 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. diff --git a/src/sam3d_body/data/transforms/__init__.py b/src/sam3d_body/data/transforms/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a4830beaed1e5c2ac655dd6f5f9a0ad3042478cb --- /dev/null +++ b/src/sam3d_body/data/transforms/__init__.py @@ -0,0 +1,21 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from .bbox_utils import ( + bbox_cs2xywh, + bbox_cs2xyxy, + bbox_xywh2cs, + bbox_xywh2xyxy, + bbox_xyxy2cs, + bbox_xyxy2xywh, + flip_bbox, + get_udp_warp_matrix, + get_warp_matrix, +) +from .common import ( + Compose, + GetBBoxCenterScale, + NormalizeKeypoint, + SquarePad, + TopdownAffine, + VisionTransformWrapper, +) diff --git a/src/sam3d_body/data/transforms/bbox_utils.py b/src/sam3d_body/data/transforms/bbox_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..66c06c4ab37330b98773e2fe7bb4dc876e3a3981 --- /dev/null +++ b/src/sam3d_body/data/transforms/bbox_utils.py @@ -0,0 +1,380 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import math + +import cv2 +import numpy as np + + +def bbox_xyxy2xywh(bbox_xyxy: np.ndarray) -> np.ndarray: + """Transform the bbox format from x1y1x2y2 to xywh. + + Args: + bbox_xyxy (np.ndarray): Bounding boxes (with scores), shaped (n, 4) or + (n, 5). (left, top, right, bottom, [score]) + + Returns: + np.ndarray: Bounding boxes (with scores), + shaped (n, 4) or (n, 5). (left, top, width, height, [score]) + """ + bbox_xywh = bbox_xyxy.copy() + bbox_xywh[:, 2] = bbox_xywh[:, 2] - bbox_xywh[:, 0] + bbox_xywh[:, 3] = bbox_xywh[:, 3] - bbox_xywh[:, 1] + + return bbox_xywh + + +def bbox_xywh2xyxy(bbox_xywh: np.ndarray) -> np.ndarray: + """Transform the bbox format from xywh to x1y1x2y2. + + Args: + bbox_xywh (ndarray): Bounding boxes (with scores), + shaped (n, 4) or (n, 5). (left, top, width, height, [score]) + Returns: + np.ndarray: Bounding boxes (with scores), shaped (n, 4) or + (n, 5). (left, top, right, bottom, [score]) + """ + bbox_xyxy = bbox_xywh.copy() + bbox_xyxy[:, 2] = bbox_xyxy[:, 2] + bbox_xyxy[:, 0] + bbox_xyxy[:, 3] = bbox_xyxy[:, 3] + bbox_xyxy[:, 1] + + return bbox_xyxy + + +def bbox_xyxy2cs(bbox: np.ndarray, padding: float = 1.0) -> tuple[np.ndarray, np.ndarray]: + """Transform the bbox format from (x,y,w,h) into (center, scale) + + Args: + bbox (ndarray): Bounding box(es) in shape (4,) or (n, 4), formatted + as (left, top, right, bottom) + padding (float): BBox padding factor that will be multilied to scale. + Default: 1.0 + + Returns: + tuple: A tuple containing center and scale. + - np.ndarray[float32]: Center (x, y) of the bbox in shape (2,) or + (n, 2) + - np.ndarray[float32]: Scale (w, h) of the bbox in shape (2,) or + (n, 2) + """ + # convert single bbox from (4, ) to (1, 4) + dim = bbox.ndim + if dim == 1: + bbox = bbox[None, :] + + x1, y1, x2, y2 = np.hsplit(bbox, [1, 2, 3]) + center = np.hstack([x1 + x2, y1 + y2]) * 0.5 + scale = np.hstack([x2 - x1, y2 - y1]) * padding + + if dim == 1: + center = center[0] + scale = scale[0] + + return center, scale + + +def bbox_xywh2cs(bbox: np.ndarray, padding: float = 1.0) -> tuple[np.ndarray, np.ndarray]: + """Transform the bbox format from (x,y,w,h) into (center, scale) + + Args: + bbox (ndarray): Bounding box(es) in shape (4,) or (n, 4), formatted + as (x, y, h, w) + padding (float): BBox padding factor that will be multilied to scale. + Default: 1.0 + + Returns: + tuple: A tuple containing center and scale. + - np.ndarray[float32]: Center (x, y) of the bbox in shape (2,) or + (n, 2) + - np.ndarray[float32]: Scale (w, h) of the bbox in shape (2,) or + (n, 2) + """ + + # convert single bbox from (4, ) to (1, 4) + dim = bbox.ndim + if dim == 1: + bbox = bbox[None, :] + + x, y, w, h = np.hsplit(bbox, [1, 2, 3]) + center = np.hstack([x + w * 0.5, y + h * 0.5]) + scale = np.hstack([w, h]) * padding + + if dim == 1: + center = center[0] + scale = scale[0] + + return center, scale + + +def bbox_cs2xyxy(center: np.ndarray, scale: np.ndarray, padding: float = 1.0) -> np.ndarray: + """Transform the bbox format from (center, scale) to (x1,y1,x2,y2). + + Args: + center (ndarray): BBox center (x, y) in shape (2,) or (n, 2) + scale (ndarray): BBox scale (w, h) in shape (2,) or (n, 2) + padding (float): BBox padding factor that will be multilied to scale. + Default: 1.0 + + Returns: + ndarray[float32]: BBox (x1, y1, x2, y2) in shape (4, ) or (n, 4) + """ + + dim = center.ndim + assert scale.ndim == dim + + if dim == 1: + center = center[None, :] + scale = scale[None, :] + + wh = scale / padding + xy = center - 0.5 * wh + bbox = np.hstack((xy, xy + wh)) + + if dim == 1: + bbox = bbox[0] + + return bbox + + +def bbox_cs2xywh(center: np.ndarray, scale: np.ndarray, padding: float = 1.0) -> np.ndarray: + """Transform the bbox format from (center, scale) to (x,y,w,h). + + Args: + center (ndarray): BBox center (x, y) in shape (2,) or (n, 2) + scale (ndarray): BBox scale (w, h) in shape (2,) or (n, 2) + padding (float): BBox padding factor that will be multilied to scale. + Default: 1.0 + + Returns: + ndarray[float32]: BBox (x, y, w, h) in shape (4, ) or (n, 4) + """ + + dim = center.ndim + assert scale.ndim == dim + + if dim == 1: + center = center[None, :] + scale = scale[None, :] + + wh = scale / padding + xy = center - 0.5 * wh + bbox = np.hstack((xy, wh)) + + if dim == 1: + bbox = bbox[0] + + return bbox + + +def flip_bbox( + bbox: np.ndarray, + image_size: tuple[int, int], + bbox_format: str = "xywh", + direction: str = "horizontal", +) -> np.ndarray: + """Flip the bbox in the given direction. + + Args: + bbox (np.ndarray): The bounding boxes. The shape should be (..., 4) + if ``bbox_format`` is ``'xyxy'`` or ``'xywh'``, and (..., 2) if + ``bbox_format`` is ``'center'`` + image_size (tuple): The image shape in [w, h] + bbox_format (str): The bbox format. Options are ``'xywh'``, ``'xyxy'`` + and ``'center'``. + direction (str): The flip direction. Options are ``'horizontal'``, + ``'vertical'`` and ``'diagonal'``. Defaults to ``'horizontal'`` + + Returns: + np.ndarray: The flipped bounding boxes. + """ + direction_options = {"horizontal", "vertical", "diagonal"} + assert direction in direction_options, f'Invalid flipping direction "{direction}". Options are {direction_options}' + + format_options = {"xywh", "xyxy", "center"} + assert bbox_format in format_options, f'Invalid bbox format "{bbox_format}". Options are {format_options}' + + bbox_flipped = bbox.copy() + w, h = image_size + + if direction == "horizontal": + if bbox_format == "xywh" or bbox_format == "center": + bbox_flipped[..., 0] = w - bbox[..., 0] - 1 + elif bbox_format == "xyxy": + bbox_flipped[..., ::2] = w - bbox[..., ::2] - 1 + elif direction == "vertical": + if bbox_format == "xywh" or bbox_format == "center": + bbox_flipped[..., 1] = h - bbox[..., 1] - 1 + elif bbox_format == "xyxy": + bbox_flipped[..., 1::2] = h - bbox[..., 1::2] - 1 + elif direction == "diagonal": + if bbox_format == "xywh" or bbox_format == "center": + bbox_flipped[..., :2] = [w, h] - bbox[..., :2] - 1 + elif bbox_format == "xyxy": + bbox_flipped[...] = [w, h, w, h] - bbox - 1 + + return bbox_flipped + + +def fix_aspect_ratio(bbox_scale: np.ndarray, aspect_ratio: float): + """Reshape the bbox to a fixed aspect ratio. + + Args: + bbox_scale (np.ndarray): The bbox scales (w, h) in shape (n, 2) + aspect_ratio (float): The ratio of ``w/h`` + + Returns: + np.darray: The reshaped bbox scales in (n, 2) + """ + dim = bbox_scale.ndim + if dim == 1: + bbox_scale = bbox_scale[None, :] + + w, h = np.hsplit(bbox_scale, [1]) + bbox_scale = np.where( + w > h * aspect_ratio, + np.hstack([w, w / aspect_ratio]), + np.hstack([h * aspect_ratio, h]), + ) + if dim == 1: + bbox_scale = bbox_scale[0] + + return bbox_scale + + +def get_udp_warp_matrix( + center: np.ndarray, + scale: np.ndarray, + rot: float, + output_size: tuple[int, int], +) -> np.ndarray: + """Calculate the affine transformation matrix under the unbiased + constraint. See `UDP (CVPR 2020)`_ for details. + + Note: + + - The bbox number: N + + Args: + center (np.ndarray[2, ]): Center of the bounding box (x, y). + scale (np.ndarray[2, ]): Scale of the bounding box + wrt [width, height]. + rot (float): Rotation angle (degree). + output_size (tuple): Size ([w, h]) of the output image + + Returns: + np.ndarray: A 2x3 transformation matrix + + .. _`UDP (CVPR 2020)`: https://arxiv.org/abs/1911.07524 + """ + assert len(center) == 2 + assert len(scale) == 2 + assert len(output_size) == 2 + + input_size = center * 2 + rot_rad = np.deg2rad(rot) + warp_mat = np.zeros((2, 3), dtype=np.float32) + scale_x = (output_size[0] - 1) / scale[0] + scale_y = (output_size[1] - 1) / scale[1] + warp_mat[0, 0] = math.cos(rot_rad) * scale_x + warp_mat[0, 1] = -math.sin(rot_rad) * scale_x + warp_mat[0, 2] = scale_x * ( + -0.5 * input_size[0] * math.cos(rot_rad) + 0.5 * input_size[1] * math.sin(rot_rad) + 0.5 * scale[0] + ) + warp_mat[1, 0] = math.sin(rot_rad) * scale_y + warp_mat[1, 1] = math.cos(rot_rad) * scale_y + warp_mat[1, 2] = scale_y * ( + -0.5 * input_size[0] * math.sin(rot_rad) - 0.5 * input_size[1] * math.cos(rot_rad) + 0.5 * scale[1] + ) + return warp_mat + + +def get_warp_matrix( + center: np.ndarray, + scale: np.ndarray, + rot: float, + output_size: tuple[int, int], + shift: tuple[float, float] = (0.0, 0.0), + inv: bool = False, +) -> np.ndarray: + """Calculate the affine transformation matrix that can warp the bbox area + in the input image to the output size. + + Args: + center (np.ndarray[2, ]): Center of the bounding box (x, y). + scale (np.ndarray[2, ]): Scale of the bounding box + wrt [width, height]. + rot (float): Rotation angle (degree). + output_size (np.ndarray[2, ] | list(2,)): Size of the + destination heatmaps. + shift (0-100%): Shift translation ratio wrt the width/height. + Default (0., 0.). + inv (bool): Option to inverse the affine transform direction. + (inv=False: src->dst or inv=True: dst->src) + + Returns: + np.ndarray: A 2x3 transformation matrix + """ + assert len(center) == 2 + assert len(scale) == 2 + assert len(output_size) == 2 + assert len(shift) == 2 + + shift = np.array(shift) + src_w = scale[0] + dst_w = output_size[0] + dst_h = output_size[1] + + rot_rad = np.deg2rad(rot) + src_dir = _rotate_point(np.array([0.0, src_w * -0.5]), rot_rad) + dst_dir = np.array([0.0, dst_w * -0.5]) + + src = np.zeros((3, 2), dtype=np.float32) + src[0, :] = center + scale * shift + src[1, :] = center + src_dir + scale * shift + src[2, :] = _get_3rd_point(src[0, :], src[1, :]) + + dst = np.zeros((3, 2), dtype=np.float32) + dst[0, :] = [dst_w * 0.5, dst_h * 0.5] + dst[1, :] = np.array([dst_w * 0.5, dst_h * 0.5]) + dst_dir + dst[2, :] = _get_3rd_point(dst[0, :], dst[1, :]) + + if inv: + warp_mat = cv2.getAffineTransform(np.float32(dst), np.float32(src)) + else: + warp_mat = cv2.getAffineTransform(np.float32(src), np.float32(dst)) + return warp_mat + + +def _rotate_point(pt: np.ndarray, angle_rad: float) -> np.ndarray: + """Rotate a point by an angle. + + Args: + pt (np.ndarray): 2D point coordinates (x, y) in shape (2, ) + angle_rad (float): rotation angle in radian + + Returns: + np.ndarray: Rotated point in shape (2, ) + """ + + sn, cs = np.sin(angle_rad), np.cos(angle_rad) + rot_mat = np.array([[cs, -sn], [sn, cs]]) + return rot_mat @ pt + + +def _get_3rd_point(a: np.ndarray, b: np.ndarray): + """To calculate the affine matrix, three pairs of points are required. This + function is used to get the 3rd point, given 2D points a & b. + + The 3rd point is defined by rotating vector `a - b` by 90 degrees + anticlockwise, using b as the rotation center. + + Args: + a (np.ndarray): The 1st point (x,y) in shape (2, ) + b (np.ndarray): The 2nd point (x,y) in shape (2, ) + + Returns: + np.ndarray: The 3rd point. + """ + direction = a - b + c = b + np.r_[-direction[1], direction[0]] + return c diff --git a/src/sam3d_body/data/transforms/common.py b/src/sam3d_body/data/transforms/common.py new file mode 100644 index 0000000000000000000000000000000000000000..76e04a4da0757cf4ea285bf830f548823f1842b8 --- /dev/null +++ b/src/sam3d_body/data/transforms/common.py @@ -0,0 +1,345 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from collections.abc import Callable, Sequence + +import cv2 +import numpy as np +import torch.nn as nn +import torchvision.transforms.functional as F +from PIL import Image + +from sam3d_body.models.modules import to_2tuple + +from .bbox_utils import ( + bbox_xywh2cs, + bbox_xyxy2cs, + fix_aspect_ratio, + get_udp_warp_matrix, + get_warp_matrix, +) + + +class Compose: + """Compose multiple transforms sequentially. + + Args: + transforms (Sequence[dict, callable], optional): Sequence of transform + object or config dict to be composed. + """ + + def __init__(self, transforms: list[Callable] | None = None): + if transforms is None: + transforms = [] + else: + self.transforms = transforms + + def __call__(self, data: dict) -> dict | None: + """Call function to apply transforms sequentially. + + Args: + data (dict): A result dict contains the data to transform. + + Returns: + dict: Transformed data. + """ + for t in self.transforms: + data = t(data) + # The transform will return None when it failed to load images or + # cannot find suitable augmentation parameters to augment the data. + # Here we simply return None if the transform returns None and the + # dataset will handle it by randomly selecting another data sample. + if data is None: + return None + return data + + def __repr__(self): + """Print ``self.transforms`` in sequence. + + Returns: + str: Formatted string. + """ + format_string = self.__class__.__name__ + "(" + for t in self.transforms: + format_string += "\n" + format_string += f" {t}" + format_string += "\n)" + return format_string + + +class VisionTransformWrapper: + """A wrapper to use torchvision transform functions in this codebase.""" + + def __init__(self, transform: Callable): + self.transform = transform + + def __call__(self, results: dict) -> dict | None: + results["img"] = self.transform(results["img"]) + return results + + def __repr__(self) -> str: + """print the basic information of the transform. + + Returns: + str: Formatted string. + """ + repr_str = self.transform.__class__.__name__ + return repr_str + + +class GetBBoxCenterScale(nn.Module): + """Convert bboxes to center and scale. + + The center is the coordinates of the bbox center, and the scale is the + bbox width and height normalized by a scale factor. + + Required Keys: + + - bbox + - bbox_format + + Added Keys: + + - bbox_center + - bbox_scale + + Args: + padding (float): The bbox padding scale that will be multilied to + `bbox_scale`. Defaults to 1.25 + """ + + def __init__(self, padding: float = 1.25) -> None: + super().__init__() + + self.padding = padding + + def forward(self, results: dict) -> dict | None: + """The transform function of :class:`GetBBoxCenterScale`. + + Args: + results (dict): The result dict + + Returns: + dict: The result dict. + """ + if "bbox_center" in results and "bbox_scale" in results: + results["bbox_scale"] *= self.padding + else: + bbox = results["bbox"] + bbox_format = results.get("bbox_format", "none") + if bbox_format == "xywh": + center, scale = bbox_xywh2cs(bbox, padding=self.padding) + elif bbox_format == "xyxy": + center, scale = bbox_xyxy2cs(bbox, padding=self.padding) + else: + raise ValueError("Invalid bbox format: {}".format(results["bbox_format"])) + + results["bbox_center"] = center + results["bbox_scale"] = scale + return results + + def __repr__(self) -> str: + """print the basic information of the transform. + + Returns: + str: Formatted string. + """ + repr_str = self.__class__.__name__ + f"(padding={self.padding})" + return repr_str + + +class SquarePad: + def __call__(self, results: dict) -> dict | None: + assert isinstance(results["img"], Image.Image) + w, h = results["img"].size + + max_wh = np.max([w, h]) + hp = int((max_wh - w) / 2) + vp = int((max_wh - h) / 2) + padding = (hp, vp, max_wh - w - hp, max_wh - h - vp) + + results["img"] = F.pad(results["img"], padding, 0, "constant") + return results + + def __repr__(self) -> str: + """print the basic information of the transform. + + Returns: + str: Formatted string. + """ + repr_str = self.__class__.__name__ + return repr_str + + +class ToPIL: + def __call__(self, results: dict) -> dict | None: + if isinstance(results["img"], list): + if isinstance(results["img"][0], np.ndarray): + results["img"] = [Image.fromarray(img) for img in results["img"]] + elif isinstance(results["img"], np.ndarray): + results["img"] = Image.fromarray(results["img"]) + + +class ToCv2: + def __call__(self, results: dict) -> dict | None: + if isinstance(results["img"], list): + if isinstance(results["img"][0], Image.Image): + results["img"] = [np.array(img) for img in results["img"]] + elif isinstance(results["img"], Image.Image): + results["img"] = np.array(results["img"]) + + +class TopdownAffine(nn.Module): + """Get the bbox image as the model input by affine transform. + + Required Keys: + - img + - bbox_center + - bbox_scale + - bbox_rotation (optional) + - keypoints_2d (optional) + - mask (optional) + + Modified Keys: + - img + - bbox_scale + + Added Keys: + - input_size + - transformed_keypoints + + Args: + input_size (Tuple[int, int]): The input image size of the model in + [w, h]. The bbox region will be cropped and resize to `input_size` + use_udp (bool): Whether use unbiased data processing. See + `UDP (CVPR 2020)`_ for details. Defaults to ``False`` + aspect_ratio (float): both HMR2.0 and Sapiens will expand input bbox to + a fixed ratio (width/height = 192/256), then expand to the ratio of + the model input size. E.g., HMR2.0 will eventually expand to 1:1, while + Sapiens will be 768:1024. + + .. _`UDP (CVPR 2020)`: https://arxiv.org/abs/1911.07524 + """ + + def __init__( + self, + input_size: int | tuple[int, int] | Sequence[int], + use_udp: bool = False, + aspect_ratio: float = 0.75, + fix_square: bool = False, + ) -> None: + super().__init__() + + self.input_size = to_2tuple(input_size) + self.use_udp = use_udp + self.aspect_ratio = aspect_ratio + self.fix_square = fix_square + + def forward(self, results: dict) -> dict | None: + """The transform function of :class:`TopdownAffine`. + + See ``transform()`` method of :class:`BaseTransform` for details. + + Args: + results (dict): The result dict + + Returns: + dict: The result dict. + """ + # # Debug only + # import copy + # results['ori_img'] = np.zeros((2000, 2000, 3), dtype=np.uint8) + # results['ori_img'][:results['img'].shape[0], :results['img'].shape[1]] = copy.deepcopy(results['img']) + + w, h = self.input_size + warp_size = (int(w), int(h)) + + # expand bbox to fixed aspect ratio + results["orig_bbox_scale"] = results["bbox_scale"].copy() + if self.fix_square and results["bbox_scale"][0] == results["bbox_scale"][1]: + # In HMR2.0 etc, no fexpand_aspect_ratio for square bbox + bbox_scale = fix_aspect_ratio(results["bbox_scale"], aspect_ratio=w / h) + else: + # first to a prior aspect ratio, then reshape to model input size + bbox_scale = fix_aspect_ratio(results["bbox_scale"], aspect_ratio=self.aspect_ratio) + results["bbox_scale"] = fix_aspect_ratio(bbox_scale, aspect_ratio=w / h) + results["bbox_expand_factor"] = results["bbox_scale"].max() / results["orig_bbox_scale"].max() + rot = 0.0 + if results["bbox_center"].ndim == 2: + assert results["bbox_center"].shape[0] == 1, ( + "Only support cropping one instance at a time. Got invalid " + f"shape of bbox_center {results['bbox_center'].shape}." + ) + center = results["bbox_center"][0] + scale = results["bbox_scale"][0] + if "bbox_rotation" in results: + rot = results["bbox_rotation"][0] + else: + center = results["bbox_center"] + scale = results["bbox_scale"] + if "bbox_rotation" in results: + rot = results["bbox_rotation"] + + if self.use_udp: + warp_mat = get_udp_warp_matrix(center, scale, rot, output_size=(w, h)) + else: + warp_mat = get_warp_matrix(center, scale, rot, output_size=(w, h)) + + if "img" not in results: + pass + elif isinstance(results["img"], list): + results["img"] = [ + cv2.warpAffine(img, warp_mat, warp_size, flags=cv2.INTER_LINEAR) for img in results["img"] + ] + height, width = results["img"][0].shape[:2] + results["ori_img_size"] = np.array([width, height]) + else: + height, width = results["img"].shape[:2] + results["ori_img_size"] = np.array([width, height]) + results["img"] = cv2.warpAffine(results["img"], warp_mat, warp_size, flags=cv2.INTER_LINEAR) + + if results.get("keypoints_2d") is not None: + results["orig_keypoints_2d"] = results["keypoints_2d"].copy() + transformed_keypoints = results["keypoints_2d"].copy() + # Only transform (x, y) coordinates + # cv2 expect the input to be [[[x1, y1], [x2, y2]]] + transformed_keypoints[:, :2] = cv2.transform(results["keypoints_2d"][None, :, :2], warp_mat)[0] + results["keypoints_2d"] = transformed_keypoints + + if results.get("mask") is not None: + results["mask"] = cv2.warpAffine(results["mask"], warp_mat, warp_size, flags=cv2.INTER_LINEAR) + + results["img_size"] = np.array([w, h]) + results["input_size"] = np.array([w, h]) + results["affine_trans"] = warp_mat + return results + + def __repr__(self) -> str: + """print the basic information of the transform. + + Returns: + str: Formatted string. + """ + repr_str = self.__class__.__name__ + repr_str += f"(input_size={self.input_size}, " + repr_str += f"use_udp={self.use_udp})" + return repr_str + + +class NormalizeKeypoint(nn.Module): + """ + Normalize 2D keypoints to range [-0.5, 0.5]. + + Required Keys: + - keypoints_2d + - img_size + + Modified Keys: + - keypoints_2d + """ + + def forward(self, results: dict) -> dict | None: + if "keypoints_2d" in results: + img_size = results.get("img_size", results["input_size"]) + + results["keypoints_2d"][:, :2] = results["keypoints_2d"][:, :2] / np.array(img_size).reshape(1, 2) - 0.5 + return results diff --git a/src/sam3d_body/data/utils/io.py b/src/sam3d_body/data/utils/io.py new file mode 100644 index 0000000000000000000000000000000000000000..24950411e794c1cb56478aeb7c83694f2b1c77f4 --- /dev/null +++ b/src/sam3d_body/data/utils/io.py @@ -0,0 +1,114 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import os +import time +from typing import Any, List + +import braceexpand +import cv2 +import numpy as np + +from PIL import Image + + +def expand(s): + return os.path.expanduser(os.path.expandvars(s)) + + +def expand_urls(urls: str | List[str]): + if isinstance(urls, str): + urls = [urls] + urls = [u for url in urls for u in braceexpand.braceexpand(expand(url))] + return urls + + +def load_image_from_file( + data_info: dict, + backend: str = "cv2", + image_format: str = "rgb", + retry: int = 10, +) -> dict: + img = load_image(data_info["img_path"], backend, image_format, retry) + data_info["img"] = img + data_info["img_shape"] = img.shape[:2] + data_info["ori_shape"] = img.shape[:2] + return data_info + + +def _pil_load(path: str, image_format: str) -> Image.Image: + with Image.open(path) as img: + if img is not None and image_format.lower() == "rgb": + img = img.convert("RGB") + return img + + +def _cv2_load(path: str, image_format: str) -> np.ndarray: + img = cv2.imread(path) + if img is not None and image_format.lower() == "rgb": + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + return img + + +def load_image( + path: str, + backend: str = "pil", + image_format: str = "rgb", + retry: int = 10, +) -> Any: + for i_try in range(retry): + if backend == "pil": + img = _pil_load(path, image_format) + elif backend == "cv2": + img = _cv2_load(path, image_format) + else: + raise ValueError("Invalid backend {} for loading image.".format(backend)) + + if img is not None: + return img + else: + print("Reading {} failed. Will retry.".format(path)) + time.sleep(1.0) + if i_try == retry - 1: + raise Exception("Failed to load image {}".format(path)) + + +def resize_image(img, target_size, center=None, scale=None): + height, width = img.shape[:2] + aspect_ratio = width / height + + # Calculate the new size while maintaining the aspect ratio + if aspect_ratio > 1: + new_width = target_size + new_height = int(target_size / aspect_ratio) + else: + new_width = int(target_size * aspect_ratio) + new_height = target_size + + # Resize the image using OpenCV + resized_img = cv2.resize(img, (new_width, new_height), interpolation=cv2.INTER_AREA) + + # Create a new blank image with the target size + final_img = np.ones((target_size, target_size, 3), dtype=np.uint8) * 255 + + # Paste the resized image onto the blank image, centering it + start_x = (target_size - new_width) // 2 + start_y = (target_size - new_height) // 2 + final_img[start_y : start_y + new_height, start_x : start_x + new_width] = ( + resized_img + ) + + if center is not None and scale is not None: + ratio_width = new_width / width + ratio_height = new_height / height + + new_scale = np.stack( + [scale[:, 0] * ratio_width, scale[:, 1] * ratio_height], axis=1 + ) + new_center = np.stack( + [center[:, 0] * ratio_width, center[:, 1] * ratio_height], axis=1 + ) + new_center[:, 0] += start_x + new_center[:, 1] += start_y + else: + new_center, new_scale = None, None + return aspect_ratio, final_img, new_center, new_scale diff --git a/src/sam3d_body/data/utils/prepare_batch.py b/src/sam3d_body/data/utils/prepare_batch.py new file mode 100644 index 0000000000000000000000000000000000000000..87327e31dbc1b57e458660efec03fae4274277b4 --- /dev/null +++ b/src/sam3d_body/data/utils/prepare_batch.py @@ -0,0 +1,99 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from collections.abc import Callable +from typing import Any, TypedDict, cast + +import numpy as np +import torch +from jaxtyping import Float, UInt8 +from numpy import ndarray +from torch import Tensor +from torch.utils.data import default_collate + + +class PreparedBatchDict(TypedDict, total=False): + img: Float[Tensor, "B N 3 H W"] + img_size: Float[Tensor, "B N 2"] + ori_img_size: Float[Tensor, "B N 2"] + bbox_center: Float[Tensor, "B N 2"] + bbox_scale: Float[Tensor, "B N 2"] + bbox: Float[Tensor, "B N 4"] + affine_trans: Float[Tensor, "B N 2 3"] + mask: Float[Tensor, "B N 1 H W"] + mask_score: Float[Tensor, "B N"] + cam_int: Float[Tensor, "B 3 3"] + person_valid: Float[Tensor, "B N"] + img_ori: list["NoCollate"] + + +class NoCollate: + def __init__(self, data: Any) -> None: + self.data: Any = data + + +def prepare_batch( + img: UInt8[ndarray, "h w 3"], + transform: Callable[[dict[str, Any]], dict[str, Any]], + boxes: Float[ndarray, "n 4"], + masks: Float[ndarray, "n h w"] | None = None, + masks_score: Float[ndarray, "n"] | None = None, + cam_int: Float[Tensor, "B 3 3"] | None = None, +) -> PreparedBatchDict: + """A helper function to prepare data batch for SAM 3D Body model inference.""" + height, width = img.shape[:2] + + # construct batch data samples + data_list: list[dict[str, Any]] = [] + for idx in range(boxes.shape[0]): + data_info: dict[str, Any] = dict(img=img) + data_info["bbox"] = boxes[idx] # shape (4,) + data_info["bbox_format"] = "xyxy" + + if masks is not None: + data_info["mask"] = masks[idx].astype(np.float32, copy=False) + if masks_score is not None: + data_info["mask_score"] = masks_score[idx] + else: + data_info["mask_score"] = np.array(1.0, dtype=np.float32) + else: + data_info["mask"] = np.zeros((height, width, 1), dtype=np.uint8) + data_info["mask_score"] = np.array(0.0, dtype=np.float32) + + data_list.append(transform(data_info)) + + batch = default_collate(data_list) + + max_num_person = batch["img"].shape[0] + for key in [ + "img", + "img_size", + "ori_img_size", + "bbox_center", + "bbox_scale", + "bbox", + "affine_trans", + "mask", + "mask_score", + ]: + if key in batch: + batch[key] = batch[key].unsqueeze(0).float() + if "mask" in batch: + batch["mask"] = batch["mask"].unsqueeze(2) + batch["person_valid"] = torch.ones((1, max_num_person)) + + if cam_int is not None: + batch["cam_int"] = cam_int.to(batch["img"]) + else: + # Default camera intrinsics according image size + batch["cam_int"] = torch.tensor( + [ + [ + [(height**2 + width**2) ** 0.5, 0, width / 2.0], + [0, (height**2 + width**2) ** 0.5, height / 2.0], + [0, 0, 1], + ] + ], + ).to(batch["img"]) + + batch["img_ori"] = [NoCollate(img)] + return cast(PreparedBatchDict, batch) diff --git a/src/sam3d_body/gradio_ui/sam3d_body_ui.py b/src/sam3d_body/gradio_ui/sam3d_body_ui.py new file mode 100644 index 0000000000000000000000000000000000000000..24a8d65ba940e24b048633e22215b0e4c27dba94 --- /dev/null +++ b/src/sam3d_body/gradio_ui/sam3d_body_ui.py @@ -0,0 +1,164 @@ +""" +Demonstrates integrating Rerun visualization with Gradio. + +Provides example implementations of data streaming, keypoint annotation, and dynamic +visualization across multiple Gradio tabs using Rerun's recording and visualization capabilities. +""" + +import os +import shutil +import tempfile +from pathlib import Path +from typing import Final + +import cv2 +import gradio as gr +import rerun as rr +import rerun.blueprint as rrb +import spaces +from gradio_rerun import Rerun +from jaxtyping import Int, UInt8 +from monopriors.relative_depth_models import RelativeDepthPrediction +from numpy import ndarray + +from sam3d_body.api.demo import SAM3Config, SAM3DBodyE2E, SAM3DBodyE2EConfig, create_view, set_annotation_context +from sam3d_body.api.visualization import export_meshes_to_glb, visualize_sample +from sam3d_body.sam_3d_body_estimator import FinalPosePrediction + +CFG: SAM3DBodyE2EConfig = SAM3DBodyE2EConfig(sam3_config=SAM3Config()) +MODEL_E2E: SAM3DBodyE2E = SAM3DBodyE2E(config=CFG) +mesh_faces: Int[ndarray, "n_faces=36874 3"] = MODEL_E2E.sam3d_body_estimator.faces +STATE: Final[str] = "โœ… Ready" +# Absolute path to bundled example data used by Gradio examples. +TEST_INPUT_DIR: Final[Path] = Path(__file__).resolve().parents[3] / "data" / "example-data" + +# Allow Gradio to serve and cache files from the bundled test data directory. +gr.set_static_paths([str(TEST_INPUT_DIR)]) + + +@spaces.GPU() +@rr.thread_local_stream("sam3d_body_gradio_ui") +def sam3d_prediction_fn( + rgb_hw3, + log_relative_depth, + export_glb, + center_glb, + pending_cleanup=None, +) -> tuple[str, str, list[str]]: + # resize rgb so that its largest dimension is 1024 + rgb_hw3: UInt8[ndarray, "h w 3"] = cv2.resize( + rgb_hw3, # type: ignore[arg-type] + dsize=(0, 0), + fx=1024 / max(rgb_hw3.shape[0], rgb_hw3.shape[1]), + fy=1024 / max(rgb_hw3.shape[0], rgb_hw3.shape[1]), + interpolation=cv2.INTER_AREA, + ) + # We eventually want to clean up the RRD file after it's sent to the viewer, so tracking + # any pending files to be cleaned up when the state is deleted. + temp = tempfile.NamedTemporaryFile(prefix="cube_", suffix=".rrd", delete=False) + + if pending_cleanup is not None: + pending_cleanup.append(temp.name) + + view: rrb.ContainerLike = create_view() + blueprint = rrb.Blueprint(view, collapse_panels=True) + rr.save(path=temp.name, default_blueprint=blueprint) + set_annotation_context() + parent_log_path = Path("/world") + rr.log("/", rr.ViewCoordinates.RDF, static=True) + + outputs: tuple[list[FinalPosePrediction], RelativeDepthPrediction] = MODEL_E2E.predict_single_image(rgb_hw3=rgb_hw3) + pred_list: list[FinalPosePrediction] = outputs[0] + relative_pred: RelativeDepthPrediction = outputs[1] + rr.set_time(timeline="image_sequence", sequence=0) + visualize_sample( + pred_list=pred_list, + rgb_hw3=rgb_hw3, + parent_log_path=parent_log_path, + faces=mesh_faces, + relative_depth_pred=relative_pred if log_relative_depth else None, + ) + + glb_files: list[str] = [] + if export_glb and len(pred_list) > 0: + glb_dir: Path = Path(tempfile.mkdtemp(prefix="sam3d_glb_")) + glb_paths = export_meshes_to_glb( + pred_list=pred_list, + faces=mesh_faces, + output_dir=glb_dir, + center_mesh=center_glb, + ) + glb_files = [str(p) for p in glb_paths] + if pending_cleanup is not None: + pending_cleanup.extend(glb_files) + pending_cleanup.append(str(glb_dir)) + + return temp.name, STATE, glb_files + + +def cleanup_rrds(pending_cleanup: list[str]) -> None: + for f in pending_cleanup: + if os.path.isdir(f): + shutil.rmtree(f, ignore_errors=True) + elif os.path.isfile(f): + os.unlink(f) + + +def _switch_to_outputs() -> gr.Tabs: + return gr.update(selected="outputs") + + +def main(): + viewer = Rerun( + streaming=True, + panel_states={ + "time": "collapsed", + "blueprint": "hidden", + "selection": "hidden", + }, + height=800, + ) + + with gr.Blocks() as demo, gr.Tab("SAM3D Body Estimation"): + pending_cleanup = gr.State([], time_to_live=10, delete_callback=cleanup_rrds) + with gr.Row(): + with gr.Column(scale=1): + tabs = gr.Tabs(selected="inputs") + with tabs: + with gr.TabItem("Inputs", id="inputs"): + img = gr.Image(interactive=True, label="Image", type="numpy", image_mode="RGB") + depth_checkbox = gr.Checkbox(label="Log relative depth", value=False) + with gr.Row(): + export_checkbox = gr.Checkbox(label="Export GLB meshes", value=False) + center_checkbox = gr.Checkbox(label="Center GLB at origin", value=True) + create_rrd = gr.Button("Predict Pose") + with gr.TabItem("Outputs", id="outputs"): + status = gr.Text(STATE, label="Status") + mesh_files = gr.Files(label="GLB meshes", file_count="multiple") + gr.Examples( + examples=[ + [str(TEST_INPUT_DIR / "Planche.jpg"), True, False, True], + [str(TEST_INPUT_DIR / "Amir-Khan-Lamont-Peterson_2689582.jpg"), False, False, True], + [str(TEST_INPUT_DIR / "BNAAHPYGMYSE26U6C6T7VA6544.jpg"), False, True, True], + [str(TEST_INPUT_DIR / "yoga-example.jpg"), True, True, False], + ], + inputs=[img, depth_checkbox, export_checkbox, center_checkbox], + outputs=[viewer, status, mesh_files], + fn=sam3d_prediction_fn, + run_on_click=True, + cache_examples=False, + examples_per_page=2, + ) + with gr.Column(scale=5): + viewer.render() + + create_rrd.click( + fn=_switch_to_outputs, + inputs=None, + outputs=[tabs], + ).then( + sam3d_prediction_fn, + inputs=[img, depth_checkbox, export_checkbox, center_checkbox, pending_cleanup], + outputs=[viewer, status, mesh_files], + ) + return demo diff --git a/src/sam3d_body/metadata/__init__.py b/src/sam3d_body/metadata/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d1a796c407caa2b4304b1ae26c5a484d0025b8c3 --- /dev/null +++ b/src/sam3d_body/metadata/__init__.py @@ -0,0 +1,79 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +OPENPOSE_TO_COCO = [0, 16, 15, 18, 17, 5, 2, 6, 3, 7, 4, 12, 9, 13, 10, 14, 11] + +# Mapping the J19 used in HMR2.0 to the 14 common points for evaluation +# J19 is defined as the first 19 keypoints in https://github.com/nkolot/SPIN/blob/master/constants.py#L42 +# The first 14 keypoints in J19 are LSP keypoints +J19_TO_J14 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 18] + +# Mapping from 14 LSP keypoints to 17 COCO keypoints +# Key: coco_idx, value: lsp_idx +LSP_TO_COCO = { + 5: 9, + 6: 8, + 7: 10, + 8: 7, + 9: 11, + 10: 6, + 11: 3, + 12: 2, + 13: 4, + 14: 1, + 15: 5, + 16: 0, +} + +# fmt: off +OPENPOSE_PERMUTATION = [0, 1, 5, 6, 7, 2, 3, 4, 8, 12, 13, 14, 9, 10, 11, 16, 15, 18, 17, 22, 23, 24, 19, 20, 21] +J19_PERMUTATION = [5, 4, 3, 2, 1, 0, 11, 10, 9, 8, 7, 6, 12, 13, 14, 15, 16, 17, 18] +COCO_PERMUTATION = [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15] +# fmt: on + +# Mapping the 70 MHR keypoints to OpenPose (COCO included) +# key: OpenPose, value: mhr_idx +MHR70_TO_OPENPOSE = { + 0: 0, + 1: 69, + 2: 6, + 3: 8, + 4: 41, + 5: 5, + 6: 7, + 7: 62, + 9: 10, + 10: 12, + 11: 14, + 12: 9, + 13: 11, + 14: 13, + 15: 2, + 16: 1, + 17: 4, + 18: 3, + 19: 15, + 20: 16, + 21: 17, + 22: 18, + 23: 19, + 24: 20, +} + +# fmt: off +MHR70_PERMUTATION = [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 18, 19, 20, 15, 16, 17, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 64, 63, 66, 65, 68, 67, 69] +# fmt: on +MHR70_TO_LSP = { + 0: 14, + 1: 12, + 2: 10, + 3: 9, + 4: 11, + 5: 13, + 6: 41, + 7: 8, + 8: 6, + 9: 5, + 10: 7, + 11: 62, + 12: 69, +} diff --git a/src/sam3d_body/metadata/mhr70.py b/src/sam3d_body/metadata/mhr70.py new file mode 100644 index 0000000000000000000000000000000000000000..74d8b2f929c43ab9d3271cd3bf28f270e8824670 --- /dev/null +++ b/src/sam3d_body/metadata/mhr70.py @@ -0,0 +1,915 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +"""The first 70 of 308 MHR keypoints, ignoring the rest for face keypoints""" + +from typing import Final + +mhr_names = [ + "nose", + "left-eye", + "right-eye", + "left-ear", + "right-ear", + "left-shoulder", + "right-shoulder", + "left-elbow", + "right-elbow", + "left-hip", + "right-hip", + "left-knee", + "right-knee", + "left-ankle", + "right-ankle", + "left-big-toe-tip", + "left-small-toe-tip", + "left-heel", + "right-big-toe-tip", + "right-small-toe-tip", + "right-heel", + "right-thumb-tip", + "right-thumb-first-joint", + "right-thumb-second-joint", + "right-thumb-third-joint", + "right-index-tip", + "right-index-first-joint", + "right-index-second-joint", + "right-index-third-joint", + "right-middle-tip", + "right-middle-first-joint", + "right-middle-second-joint", + "right-middle-third-joint", + "right-ring-tip", + "right-ring-first-joint", + "right-ring-second-joint", + "right-ring-third-joint", + "right-pinky-tip", + "right-pinky-first-joint", + "right-pinky-second-joint", + "right-pinky-third-joint", + "right-wrist", + "left-thumb-tip", + "left-thumb-first-joint", + "left-thumb-second-joint", + "left-thumb-third-joint", + "left-index-tip", + "left-index-first-joint", + "left-index-second-joint", + "left-index-third-joint", + "left-middle-tip", + "left-middle-first-joint", + "left-middle-second-joint", + "left-middle-third-joint", + "left-ring-tip", + "left-ring-first-joint", + "left-ring-second-joint", + "left-ring-third-joint", + "left-pinky-tip", + "left-pinky-first-joint", + "left-pinky-second-joint", + "left-pinky-third-joint", + "left-wrist", + "left-olecranon", + "right-olecranon", + "left-cubital-fossa", + "right-cubital-fossa", + "left-acromion", + "right-acromion", + "neck", +] + +pose_info = dict( + pose_format="mhr70", + paper_info=dict( + author="", + year="", + homepage="", + ), + min_visible_keypoints=8, + image_height=4096, + image_width=2668, + original_keypoint_info={ + 0: "nose", + 1: "left_eye", + 2: "right_eye", + 3: "left_ear", + 4: "right_ear", + 5: "left_shoulder", + 6: "right_shoulder", + 7: "left_elbow", + 8: "right_elbow", + 9: "left_hip", + 10: "right_hip", + 11: "left_knee", + 12: "right_knee", + 13: "left_ankle", + 14: "right_ankle", + 15: "left_big_toe_tip", + 16: "left_small_toe_tip", + 17: "left_heel", + 18: "right_big_toe_tip", + 19: "right_small_toe_tip", + 20: "right_heel", + 21: "right_thumb_tip", + 22: "right_thumb_first_joint", + 23: "right_thumb_second_joint", + 24: "right_thumb_third_joint", + 25: "right_index_tip", + 26: "right_index_first_joint", + 27: "right_index_second_joint", + 28: "right_index_third_joint", + 29: "right_middle_tip", + 30: "right_middle_first_joint", + 31: "right_middle_second_joint", + 32: "right_middle_third_joint", + 33: "right_ring_tip", + 34: "right_ring_first_joint", + 35: "right_ring_second_joint", + 36: "right_ring_third_joint", + 37: "right_pinky_tip", + 38: "right_pinky_first_joint", + 39: "right_pinky_second_joint", + 40: "right_pinky_third_joint", + 41: "right_wrist", + 42: "left_thumb_tip", + 43: "left_thumb_first_joint", + 44: "left_thumb_second_joint", + 45: "left_thumb_third_joint", + 46: "left_index_tip", + 47: "left_index_first_joint", + 48: "left_index_second_joint", + 49: "left_index_third_joint", + 50: "left_middle_tip", + 51: "left_middle_first_joint", + 52: "left_middle_second_joint", + 53: "left_middle_third_joint", + 54: "left_ring_tip", + 55: "left_ring_first_joint", + 56: "left_ring_second_joint", + 57: "left_ring_third_joint", + 58: "left_pinky_tip", + 59: "left_pinky_first_joint", + 60: "left_pinky_second_joint", + 61: "left_pinky_third_joint", + 62: "left_wrist", + 63: "left_olecranon", + 64: "right_olecranon", + 65: "left_cubital_fossa", + 66: "right_cubital_fossa", + 67: "left_acromion", + 68: "right_acromion", + 69: "neck", + }, + keypoint_info={ + 0: dict(name="nose", id=0, color=[51, 153, 255], type="upper", swap=""), + 1: dict( + name="left_eye", id=1, color=[51, 153, 255], type="upper", swap="right_eye" + ), + 2: dict( + name="right_eye", id=2, color=[51, 153, 255], type="upper", swap="left_eye" + ), + 3: dict( + name="left_ear", id=3, color=[51, 153, 255], type="upper", swap="right_ear" + ), + 4: dict( + name="right_ear", id=4, color=[51, 153, 255], type="upper", swap="left_ear" + ), + 5: dict( + name="left_shoulder", + id=5, + color=[51, 153, 255], + type="upper", + swap="right_shoulder", + ), + 6: dict( + name="right_shoulder", + id=6, + color=[51, 153, 255], + type="upper", + swap="left_shoulder", + ), + 7: dict( + name="left_elbow", + id=7, + color=[51, 153, 255], + type="upper", + swap="right_elbow", + ), + 8: dict( + name="right_elbow", + id=8, + color=[51, 153, 255], + type="upper", + swap="left_elbow", + ), + 9: dict( + name="left_hip", id=9, color=[51, 153, 255], type="lower", swap="right_hip" + ), + 10: dict( + name="right_hip", id=10, color=[51, 153, 255], type="lower", swap="left_hip" + ), + 11: dict( + name="left_knee", + id=11, + color=[51, 153, 255], + type="lower", + swap="right_knee", + ), + 12: dict( + name="right_knee", + id=12, + color=[51, 153, 255], + type="lower", + swap="left_knee", + ), + 13: dict( + name="left_ankle", + id=13, + color=[51, 153, 255], + type="lower", + swap="right_ankle", + ), + 14: dict( + name="right_ankle", + id=14, + color=[51, 153, 255], + type="lower", + swap="left_ankle", + ), + 15: dict( + name="left_big_toe", + id=15, + color=[51, 153, 255], + type="lower", + swap="right_big_toe", + ), + 16: dict( + name="left_small_toe", + id=16, + color=[51, 153, 255], + type="lower", + swap="right_small_toe", + ), + 17: dict( + name="left_heel", + id=17, + color=[51, 153, 255], + type="lower", + swap="right_heel", + ), + 18: dict( + name="right_big_toe", + id=18, + color=[51, 153, 255], + type="lower", + swap="left_big_toe", + ), + 19: dict( + name="right_small_toe", + id=19, + color=[51, 153, 255], + type="lower", + swap="left_small_toe", + ), + 20: dict( + name="right_heel", + id=20, + color=[51, 153, 255], + type="lower", + swap="left_heel", + ), + 21: dict( + name="right_thumb4", + id=21, + color=[51, 153, 255], + type="upper", + swap="left_thumb4", + ), + 22: dict( + name="right_thumb3", + id=22, + color=[51, 153, 255], + type="upper", + swap="left_thumb3", + ), + 23: dict( + name="right_thumb2", + id=23, + color=[51, 153, 255], + type="upper", + swap="left_thumb2", + ), + 24: dict( + name="right_thumb_third_joint", + id=24, + color=[51, 153, 255], + type="upper", + swap="left_thumb_third_joint", + ), + 25: dict( + name="right_forefinger4", + id=25, + color=[51, 153, 255], + type="upper", + swap="left_forefinger4", + ), + 26: dict( + name="right_forefinger3", + id=26, + color=[51, 153, 255], + type="upper", + swap="left_forefinger3", + ), + 27: dict( + name="right_forefinger2", + id=27, + color=[51, 153, 255], + type="upper", + swap="left_forefinger2", + ), + 28: dict( + name="right_forefinger_third_joint", + id=28, + color=[51, 153, 255], + type="upper", + swap="left_forefinger_third_joint", + ), + 29: dict( + name="right_middle_finger4", + id=29, + color=[51, 153, 255], + type="upper", + swap="left_middle_finger4", + ), + 30: dict( + name="right_middle_finger3", + id=30, + color=[51, 153, 255], + type="upper", + swap="left_middle_finger3", + ), + 31: dict( + name="right_middle_finger2", + id=31, + color=[51, 153, 255], + type="upper", + swap="left_middle_finger2", + ), + 32: dict( + name="right_middle_finger_third_joint", + id=32, + color=[51, 153, 255], + type="upper", + swap="left_middle_finger_third_joint", + ), + 33: dict( + name="right_ring_finger4", + id=33, + color=[51, 153, 255], + type="upper", + swap="left_ring_finger4", + ), + 34: dict( + name="right_ring_finger3", + id=34, + color=[51, 153, 255], + type="upper", + swap="left_ring_finger3", + ), + 35: dict( + name="right_ring_finger2", + id=35, + color=[51, 153, 255], + type="upper", + swap="left_ring_finger2", + ), + 36: dict( + name="right_ring_finger_third_joint", + id=36, + color=[51, 153, 255], + type="upper", + swap="left_ring_finger_third_joint", + ), + 37: dict( + name="right_pinky_finger4", + id=37, + color=[51, 153, 255], + type="upper", + swap="left_pinky_finger4", + ), + 38: dict( + name="right_pinky_finger3", + id=38, + color=[51, 153, 255], + type="upper", + swap="left_pinky_finger3", + ), + 39: dict( + name="right_pinky_finger2", + id=39, + color=[51, 153, 255], + type="upper", + swap="left_pinky_finger2", + ), + 40: dict( + name="right_pinky_finger_third_joint", + id=40, + color=[51, 153, 255], + type="upper", + swap="left_pinky_finger_third_joint", + ), + 41: dict( + name="right_wrist", + id=41, + color=[51, 153, 255], + type="upper", + swap="left_wrist", + ), + 42: dict( + name="left_thumb4", + id=42, + color=[51, 153, 255], + type="upper", + swap="right_thumb4", + ), + 43: dict( + name="left_thumb3", + id=43, + color=[51, 153, 255], + type="upper", + swap="right_thumb3", + ), + 44: dict( + name="left_thumb2", + id=44, + color=[51, 153, 255], + type="upper", + swap="right_thumb2", + ), + 45: dict( + name="left_thumb_third_joint", + id=45, + color=[51, 153, 255], + type="upper", + swap="right_thumb_third_joint", + ), ## doesnt match with wholebody + 46: dict( + name="left_forefinger4", + id=46, + color=[51, 153, 255], + type="upper", + swap="right_forefinger4", + ), + 47: dict( + name="left_forefinger3", + id=47, + color=[51, 153, 255], + type="upper", + swap="right_forefinger3", + ), + 48: dict( + name="left_forefinger2", + id=48, + color=[51, 153, 255], + type="upper", + swap="right_forefinger2", + ), + 49: dict( + name="left_forefinger_third_joint", + id=49, + color=[51, 153, 255], + type="upper", + swap="right_forefinger_third_joint", + ), + 50: dict( + name="left_middle_finger4", + id=50, + color=[51, 153, 255], + type="upper", + swap="right_middle_finger4", + ), + 51: dict( + name="left_middle_finger3", + id=51, + color=[51, 153, 255], + type="upper", + swap="right_middle_finger3", + ), + 52: dict( + name="left_middle_finger2", + id=52, + color=[51, 153, 255], + type="upper", + swap="right_middle_finger2", + ), + 53: dict( + name="left_middle_finger_third_joint", + id=53, + color=[51, 153, 255], + type="upper", + swap="right_middle_finger_third_joint", + ), + 54: dict( + name="left_ring_finger4", + id=54, + color=[51, 153, 255], + type="upper", + swap="right_ring_finger4", + ), + 55: dict( + name="left_ring_finger3", + id=55, + color=[51, 153, 255], + type="upper", + swap="right_ring_finger3", + ), + 56: dict( + name="left_ring_finger2", + id=56, + color=[51, 153, 255], + type="upper", + swap="right_ring_finger2", + ), + 57: dict( + name="left_ring_finger_third_joint", + id=57, + color=[51, 153, 255], + type="upper", + swap="right_ring_finger_third_joint", + ), + 58: dict( + name="left_pinky_finger4", + id=58, + color=[51, 153, 255], + type="upper", + swap="right_pinky_finger4", + ), + 59: dict( + name="left_pinky_finger3", + id=59, + color=[51, 153, 255], + type="upper", + swap="right_pinky_finger3", + ), + 60: dict( + name="left_pinky_finger2", + id=60, + color=[51, 153, 255], + type="upper", + swap="right_pinky_finger2", + ), + 61: dict( + name="left_pinky_finger_third_joint", + id=61, + color=[51, 153, 255], + type="upper", + swap="right_pinky_finger_third_joint", + ), + 62: dict( + name="left_wrist", + id=62, + color=[51, 153, 255], + type="upper", + swap="right_wrist", + ), + 63: dict( + name="left_olecranon", + id=63, + color=[51, 153, 255], + type="", + swap="right_olecranon", + ), + 64: dict( + name="right_olecranon", + id=64, + color=[51, 153, 255], + type="", + swap="left_olecranon", + ), + 65: dict( + name="left_cubital_fossa", + id=65, + color=[51, 153, 255], + type="", + swap="right_cubital_fossa", + ), + 66: dict( + name="right_cubital_fossa", + id=66, + color=[51, 153, 255], + type="", + swap="left_cubital_fossa", + ), + 67: dict( + name="left_acromion", + id=67, + color=[51, 153, 255], + type="", + swap="right_acromion", + ), + 68: dict( + name="right_acromion", + id=68, + color=[51, 153, 255], + type="", + swap="left_acromion", + ), + 69: dict(name="neck", id=69, color=[51, 153, 255], type="", swap=""), + }, + skeleton_info={ + 0: dict(link=("left_ankle", "left_knee"), id=0, color=[0, 255, 0]), + 1: dict(link=("left_knee", "left_hip"), id=1, color=[0, 255, 0]), + 2: dict(link=("right_ankle", "right_knee"), id=2, color=[255, 128, 0]), + 3: dict(link=("right_knee", "right_hip"), id=3, color=[255, 128, 0]), + 4: dict(link=("left_hip", "right_hip"), id=4, color=[51, 153, 255]), + 5: dict(link=("left_shoulder", "left_hip"), id=5, color=[51, 153, 255]), + 6: dict(link=("right_shoulder", "right_hip"), id=6, color=[51, 153, 255]), + 7: dict(link=("left_shoulder", "right_shoulder"), id=7, color=[51, 153, 255]), + 8: dict(link=("left_shoulder", "left_elbow"), id=8, color=[0, 255, 0]), + 9: dict(link=("right_shoulder", "right_elbow"), id=9, color=[255, 128, 0]), + 10: dict(link=("left_elbow", "left_wrist"), id=10, color=[0, 255, 0]), + 11: dict(link=("right_elbow", "right_wrist"), id=11, color=[255, 128, 0]), + 12: dict(link=("left_eye", "right_eye"), id=12, color=[51, 153, 255]), + 13: dict(link=("nose", "left_eye"), id=13, color=[51, 153, 255]), + 14: dict(link=("nose", "right_eye"), id=14, color=[51, 153, 255]), + 15: dict(link=("left_eye", "left_ear"), id=15, color=[51, 153, 255]), + 16: dict(link=("right_eye", "right_ear"), id=16, color=[51, 153, 255]), + 17: dict(link=("left_ear", "left_shoulder"), id=17, color=[51, 153, 255]), + 18: dict(link=("right_ear", "right_shoulder"), id=18, color=[51, 153, 255]), + 19: dict(link=("left_ankle", "left_big_toe"), id=19, color=[0, 255, 0]), + 20: dict(link=("left_ankle", "left_small_toe"), id=20, color=[0, 255, 0]), + 21: dict(link=("left_ankle", "left_heel"), id=21, color=[0, 255, 0]), + 22: dict(link=("right_ankle", "right_big_toe"), id=22, color=[255, 128, 0]), + 23: dict(link=("right_ankle", "right_small_toe"), id=23, color=[255, 128, 0]), + 24: dict(link=("right_ankle", "right_heel"), id=24, color=[255, 128, 0]), + 25: dict( + link=("left_wrist", "left_thumb_third_joint"), id=25, color=[255, 128, 0] + ), + 26: dict( + link=("left_thumb_third_joint", "left_thumb2"), id=26, color=[255, 128, 0] + ), + 27: dict(link=("left_thumb2", "left_thumb3"), id=27, color=[255, 128, 0]), + 28: dict(link=("left_thumb3", "left_thumb4"), id=28, color=[255, 128, 0]), + 29: dict( + link=("left_wrist", "left_forefinger_third_joint"), + id=29, + color=[255, 153, 255], + ), + 30: dict( + link=("left_forefinger_third_joint", "left_forefinger2"), + id=30, + color=[255, 153, 255], + ), + 31: dict( + link=("left_forefinger2", "left_forefinger3"), id=31, color=[255, 153, 255] + ), + 32: dict( + link=("left_forefinger3", "left_forefinger4"), id=32, color=[255, 153, 255] + ), + 33: dict( + link=("left_wrist", "left_middle_finger_third_joint"), + id=33, + color=[102, 178, 255], + ), + 34: dict( + link=("left_middle_finger_third_joint", "left_middle_finger2"), + id=34, + color=[102, 178, 255], + ), + 35: dict( + link=("left_middle_finger2", "left_middle_finger3"), + id=35, + color=[102, 178, 255], + ), + 36: dict( + link=("left_middle_finger3", "left_middle_finger4"), + id=36, + color=[102, 178, 255], + ), + 37: dict( + link=("left_wrist", "left_ring_finger_third_joint"), + id=37, + color=[255, 51, 51], + ), + 38: dict( + link=("left_ring_finger_third_joint", "left_ring_finger2"), + id=38, + color=[255, 51, 51], + ), + 39: dict( + link=("left_ring_finger2", "left_ring_finger3"), id=39, color=[255, 51, 51] + ), + 40: dict( + link=("left_ring_finger3", "left_ring_finger4"), id=40, color=[255, 51, 51] + ), + 41: dict( + link=("left_wrist", "left_pinky_finger_third_joint"), + id=41, + color=[0, 255, 0], + ), + 42: dict( + link=("left_pinky_finger_third_joint", "left_pinky_finger2"), + id=42, + color=[0, 255, 0], + ), + 43: dict( + link=("left_pinky_finger2", "left_pinky_finger3"), id=43, color=[0, 255, 0] + ), + 44: dict( + link=("left_pinky_finger3", "left_pinky_finger4"), id=44, color=[0, 255, 0] + ), + 45: dict( + link=("right_wrist", "right_thumb_third_joint"), id=45, color=[255, 128, 0] + ), + 46: dict( + link=("right_thumb_third_joint", "right_thumb2"), id=46, color=[255, 128, 0] + ), + 47: dict(link=("right_thumb2", "right_thumb3"), id=47, color=[255, 128, 0]), + 48: dict(link=("right_thumb3", "right_thumb4"), id=48, color=[255, 128, 0]), + 49: dict( + link=("right_wrist", "right_forefinger_third_joint"), + id=49, + color=[255, 153, 255], + ), + 50: dict( + link=("right_forefinger_third_joint", "right_forefinger2"), + id=50, + color=[255, 153, 255], + ), + 51: dict( + link=("right_forefinger2", "right_forefinger3"), + id=51, + color=[255, 153, 255], + ), + 52: dict( + link=("right_forefinger3", "right_forefinger4"), + id=52, + color=[255, 153, 255], + ), + 53: dict( + link=("right_wrist", "right_middle_finger_third_joint"), + id=53, + color=[102, 178, 255], + ), + 54: dict( + link=("right_middle_finger_third_joint", "right_middle_finger2"), + id=54, + color=[102, 178, 255], + ), + 55: dict( + link=("right_middle_finger2", "right_middle_finger3"), + id=55, + color=[102, 178, 255], + ), + 56: dict( + link=("right_middle_finger3", "right_middle_finger4"), + id=56, + color=[102, 178, 255], + ), + 57: dict( + link=("right_wrist", "right_ring_finger_third_joint"), + id=57, + color=[255, 51, 51], + ), + 58: dict( + link=("right_ring_finger_third_joint", "right_ring_finger2"), + id=58, + color=[255, 51, 51], + ), + 59: dict( + link=("right_ring_finger2", "right_ring_finger3"), + id=59, + color=[255, 51, 51], + ), + 60: dict( + link=("right_ring_finger3", "right_ring_finger4"), + id=60, + color=[255, 51, 51], + ), + 61: dict( + link=("right_wrist", "right_pinky_finger_third_joint"), + id=61, + color=[0, 255, 0], + ), + 62: dict( + link=("right_pinky_finger_third_joint", "right_pinky_finger2"), + id=62, + color=[0, 255, 0], + ), + 63: dict( + link=("right_pinky_finger2", "right_pinky_finger3"), + id=63, + color=[0, 255, 0], + ), + 64: dict( + link=("right_pinky_finger3", "right_pinky_finger4"), + id=64, + color=[0, 255, 0], + ), + }, + joint_weights=[1.0] * 70, + body_keypoint_names=[ + "nose", + "left_eye", + "right_eye", + "left_ear", + "right_ear", + "left_shoulder", + "right_shoulder", + "left_elbow", + "right_elbow", + "left_wrist", + "right_wrist", + "left_hip", + "right_hip", + "left_knee", + "right_knee", + "left_ankle", + "right_ankle", + ], + foot_keypoint_names=[ + "left_big_toe", + "left_small_toe", + "left_heel", + "right_big_toe", + "right_small_toe", + "right_heel", + ], + left_hand_keypoint_names=[ + "left_thumb4", + "left_thumb3", + "left_thumb2", + "left_thumb_third_joint", + "left_forefinger4", + "left_forefinger3", + "left_forefinger2", + "left_forefinger_third_joint", + "left_middle_finger4", + "left_middle_finger3", + "left_middle_finger2", + "left_middle_finger_third_joint", + "left_ring_finger4", + "left_ring_finger3", + "left_ring_finger2", + "left_ring_finger_third_joint", + "left_pinky_finger4", + "left_pinky_finger3", + "left_pinky_finger2", + "left_pinky_finger_third_joint", + ], + right_hand_keypoint_names=[ + "right_thumb4", + "right_thumb3", + "right_thumb2", + "right_thumb_third_joint", + "right_forefinger4", + "right_forefinger3", + "right_forefinger2", + "right_forefinger_third_joint", + "right_middle_finger4", + "right_middle_finger3", + "right_middle_finger2", + "right_middle_finger_third_joint", + "right_ring_finger4", + "right_ring_finger3", + "right_ring_finger2", + "right_ring_finger_third_joint", + "right_pinky_finger4", + "right_pinky_finger3", + "right_pinky_finger2", + "right_pinky_finger_third_joint", + ], + ## 7 of them + extra_keypoint_names=[ + "neck", + "left_olecranon", + "right_olecranon", + "left_cubital_fossa", + "right_cubital_fossa", + "left_acromion", + "right_acromion", + ], + sigmas=[], +) + +# Rerunโ€‘friendly helpers ---------------------------------------------------- +# These mirror the COCOโ€‘133 helpers exposed by ``simplecv.data.skeleton.coco_133`` +# so downstream code can build annotation contexts without reโ€‘deriving names/links. + +MHR70_ID2NAME: Final[dict[int, str]] = { + idx: info["name"] for idx, info in pose_info["keypoint_info"].items() +} + +MHR70_IDS: Final[list[int]] = sorted(MHR70_ID2NAME.keys()) + +_NAME_TO_ID = {name: idx for idx, name in MHR70_ID2NAME.items()} +MHR70_LINKS: Final[list[tuple[int, int]]] = [ + (_NAME_TO_ID[link_info["link"][0]], _NAME_TO_ID[link_info["link"][1]]) + for link_info in pose_info["skeleton_info"].values() +] + +__all__ = [ + "pose_info", + "MHR70_ID2NAME", + "MHR70_IDS", + "MHR70_LINKS", +] diff --git a/src/sam3d_body/models/__init__.py b/src/sam3d_body/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..71ca4b12c770afea62d06f97064cdf0c97d40ed7 --- /dev/null +++ b/src/sam3d_body/models/__init__.py @@ -0,0 +1 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. diff --git a/src/sam3d_body/models/backbones/__init__.py b/src/sam3d_body/models/backbones/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bc5b304ca7aff7692f8a3f3944539525a87256f9 --- /dev/null +++ b/src/sam3d_body/models/backbones/__init__.py @@ -0,0 +1,35 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + + +def create_backbone(name, cfg=None): + if name in ["vit_hmr"]: + from .vit import vit + + backbone = vit(cfg) + elif name in ["vit_hmr_512_384"]: + from .vit import vit512_384 + + backbone = vit512_384(cfg) + elif name in ["vit_l"]: + from .vit import vit_l + + backbone = vit_l(cfg) + elif name in ["vit_b"]: + from .vit import vit_b + + backbone = vit_b(cfg) + elif name in [ + "dinov3_vit7b", + "dinov3_vith16plus", + "dinov3_vits16", + "dinov3_vits16plus", + "dinov3_vitb16", + "dinov3_vitl16", + ]: + from .dinov3 import Dinov3Backbone + + backbone = Dinov3Backbone(name, cfg=cfg) + else: + raise NotImplementedError("Backbone type is not implemented") + + return backbone diff --git a/src/sam3d_body/models/backbones/dinov3.py b/src/sam3d_body/models/backbones/dinov3.py new file mode 100644 index 0000000000000000000000000000000000000000..bd0103a0050904d6b0d7c2e990aea7017094f3d1 --- /dev/null +++ b/src/sam3d_body/models/backbones/dinov3.py @@ -0,0 +1,69 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import torch +from torch import nn + + +class Dinov3Backbone(nn.Module): + def __init__( + self, name="dinov2_vitb14", pretrained_weight=None, cfg=None, *args, **kwargs + ): + super().__init__() + self.name = name + self.cfg = cfg + + self.encoder = torch.hub.load( + "facebookresearch/dinov3", + self.name, + source="github", + pretrained=False, + drop_path=self.cfg.MODEL.BACKBONE.DROP_PATH_RATE, + ) + self.patch_size = self.encoder.patch_size + self.embed_dim = self.embed_dims = self.encoder.embed_dim + + def forward(self, x, extra_embed=None): + """ + Encode a RGB image using a ViT-backbone + Args: + - x: torch.Tensor of shape [bs,3,w,h] + Return: + - y: torch.Tensor of shape [bs,k,d] - image in patchified mode + """ + assert extra_embed is None, "Not Implemented Yet" + + y = self.encoder.get_intermediate_layers(x, n=1, reshape=True, norm=True)[-1] + + return y + + def get_layer_depth(self, param_name: str, prefix: str = "encoder."): + """Get the layer-wise depth of a parameter. + Args: + param_name (str): The name of the parameter. + prefix (str): The prefix for the parameter. + Defaults to an empty string. + Returns: + Tuple[int, int]: The layer-wise depth and the num of layers. + Note: + The first depth is the stem module (``layer_depth=0``), and the + last depth is the subsequent module (``layer_depth=num_layers-1``) + """ + num_layers = self.encoder.n_blocks + 2 + + if not param_name.startswith(prefix): + # For subsequent module like head + return num_layers - 1, num_layers + + param_name = param_name[len(prefix) :] + + if param_name in ("cls_token", "pos_embed", "storage_tokens"): + layer_depth = 0 + elif param_name.startswith("patch_embed"): + layer_depth = 0 + elif param_name.startswith("blocks"): + layer_id = int(param_name.split(".")[1]) + layer_depth = layer_id + 1 + else: + layer_depth = num_layers - 1 + + return layer_depth, num_layers diff --git a/src/sam3d_body/models/backbones/vit.py b/src/sam3d_body/models/backbones/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..f22081625d0749df6a677ef238c8f34315b00c25 --- /dev/null +++ b/src/sam3d_body/models/backbones/vit.py @@ -0,0 +1,658 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from functools import partial + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.utils.checkpoint as checkpoint + +try: + from flash_attn.flash_attn_interface import flash_attn_func +except: + print("No Flash Attention!") + +from timm.models.layers import drop_path, to_2tuple, trunc_normal_ + +from ..modules.transformer import LayerNorm32 + + +def vit(cfg): + return ViT( + img_size=(256, 192), + patch_size=16, + embed_dim=1280, + depth=32, + num_heads=16, + ratio=1, + norm_layer=LayerNorm32, + use_checkpoint=False, + mlp_ratio=4, + qkv_bias=True, + drop_path_rate=0.55, + frozen_stages=cfg.MODEL.BACKBONE.get("FROZEN_STAGES", -1), + flash_attn=cfg.MODEL.BACKBONE.get("FLASH_ATTN", False), + ) + + +def vit_l(cfg): + return ViT( + img_size=(256, 192), + patch_size=16, + embed_dim=1024, + depth=24, + num_heads=16, + ratio=1, + norm_layer=LayerNorm32, + use_checkpoint=False, + mlp_ratio=4, + qkv_bias=True, + drop_path_rate=0.55, + frozen_stages=cfg.MODEL.BACKBONE.get("FROZEN_STAGES", -1), + flash_attn=cfg.MODEL.BACKBONE.get("FLASH_ATTN", False), + ) + + +def vit_b(cfg): + return ViT( + img_size=(256, 192), + patch_size=16, + embed_dim=768, + depth=12, + num_heads=12, + ratio=1, + norm_layer=LayerNorm32, + use_checkpoint=False, + mlp_ratio=4, + qkv_bias=True, + drop_path_rate=0.3, + frozen_stages=cfg.MODEL.BACKBONE.get("FROZEN_STAGES", -1), + flash_attn=cfg.MODEL.BACKBONE.get("FLASH_ATTN", False), + ) + + +def vit256(cfg): + return ViT( + img_size=(256, 256), + patch_size=16, + embed_dim=1280, + depth=32, + num_heads=16, + ratio=1, + norm_layer=LayerNorm32, + use_checkpoint=False, + mlp_ratio=4, + qkv_bias=True, + drop_path_rate=0.55, + frozen_stages=cfg.MODEL.BACKBONE.get("FROZEN_STAGES", -1), + flash_attn=cfg.MODEL.BACKBONE.get("FLASH_ATTN", False), + ) + + +def vit512_384(cfg): + return ViT( + img_size=(512, 384), + patch_size=16, + embed_dim=1280, + depth=32, + num_heads=16, + ratio=1, + norm_layer=LayerNorm32, + use_checkpoint=False, + mlp_ratio=4, + qkv_bias=True, + drop_path_rate=0.55, + frozen_stages=cfg.MODEL.BACKBONE.get("FROZEN_STAGES", -1), + flash_attn=cfg.MODEL.BACKBONE.get("FLASH_ATTN", False), + ) + + +def get_abs_pos(abs_pos, h, w, ori_h, ori_w, has_cls_token=True): + """ + Calculate absolute positional embeddings. If needed, resize embeddings and remove cls_token + dimension for the original embeddings. + Args: + abs_pos (Tensor): absolute positional embeddings with (1, num_position, C). + has_cls_token (bool): If true, has 1 embedding in abs_pos for cls token. + hw (Tuple): size of input image tokens. + + Returns: + Absolute positional embeddings after processing with shape (1, H, W, C) + """ + cls_token = None + B, L, C = abs_pos.shape + if has_cls_token: + cls_token = abs_pos[:, 0:1] + abs_pos = abs_pos[:, 1:] + + if ori_h != h or ori_w != w: + new_abs_pos = ( + F.interpolate( + abs_pos.reshape(1, ori_h, ori_w, -1).permute(0, 3, 1, 2), + size=(h, w), + mode="bicubic", + align_corners=False, + ) + .permute(0, 2, 3, 1) + .reshape(B, -1, C) + ) + + else: + new_abs_pos = abs_pos + + if cls_token is not None: + new_abs_pos = torch.cat([cls_token, new_abs_pos], dim=1) + return new_abs_pos + + +class DropPath(nn.Module): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).""" + + def __init__(self, drop_prob=None): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + + def forward(self, x): + return drop_path(x, self.drop_prob, self.training) + + def extra_repr(self): + return "p={}".format(self.drop_prob) + + +class Mlp(nn.Module): + def __init__( + self, + in_features, + hidden_features=None, + out_features=None, + act_layer=nn.GELU, + drop=0.0, + ): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class Attention(nn.Module): + def __init__( + self, + dim, + num_heads=8, + qkv_bias=False, + qk_scale=None, + attn_drop=0.0, + proj_drop=0.0, + attn_head_dim=None, + ): + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + self.dim = dim + + if attn_head_dim is not None: + head_dim = attn_head_dim + all_head_dim = head_dim * self.num_heads + + self.scale = qk_scale or head_dim**-0.5 + + self.qkv = nn.Linear(dim, all_head_dim * 3, bias=qkv_bias) + + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(all_head_dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + + def forward(self, x): + B, N, C = x.shape + qkv = self.qkv(x) + qkv = qkv.reshape(B, N, 3, self.num_heads, -1).permute(2, 0, 3, 1, 4) + q, k, v = ( + qkv[0], + qkv[1], + qkv[2], + ) # make torchscript happy (cannot use tensor as tuple) + + q = q * self.scale + attn = q @ k.transpose(-2, -1) + + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + + x = (attn @ v).transpose(1, 2).reshape(B, N, -1) + x = self.proj(x) + x = self.proj_drop(x) + + return x + + +class FlashAttention(nn.Module): + def __init__( + self, + dim, + num_heads=8, + qkv_bias=False, + qk_scale=None, + attn_drop=0.0, + proj_drop=0.0, + attn_head_dim=None, + ): + super().__init__() + self.num_heads = num_heads + head_dim = attn_head_dim or (dim // num_heads) + self.head_dim = head_dim + self.dim = dim + self.qkv = nn.Linear(dim, head_dim * num_heads * 3, bias=qkv_bias) + self.proj = nn.Linear(head_dim * num_heads, dim) + self.proj_drop = nn.Dropout(proj_drop) + self.attn_drop = attn_drop + + def forward(self, x): + B, N, C = x.shape # (batch, sequence_length, embedding_dim) + + qkv = self.qkv(x) # (B, N, 3 * num_heads * head_dim) + qkv = qkv.view(B, N, 3, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4) + q, k, v = qkv[0], qkv[1], qkv[2] # each: (B, num_heads, N, head_dim) + + # FlashAttention expects (B, N, num_heads, head_dim) + q = q.transpose(1, 2).contiguous() + k = k.transpose(1, 2).contiguous() + v = v.transpose(1, 2).contiguous() + + # Optional: FlashAttention requires fp16 or bf16 + if q.dtype == torch.float32: + q = q.half() + k = k.half() + v = v.half() + + out = flash_attn_func( + q, k, v, dropout_p=self.attn_drop, causal=False + ) # (B, N, num_heads * head_dim) + + # If needed, cast back to float32 + out = out.reshape(B, N, -1) + out = out.to(x.dtype) + # breakpoint() + out = self.proj(out) + out = self.proj_drop(out) + return out + + +class Block(nn.Module): + + def __init__( + self, + dim, + num_heads, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop=0.0, + attn_drop=0.0, + drop_path=0.0, + act_layer=nn.GELU, + norm_layer=nn.LayerNorm, + attn_head_dim=None, + flash_attn=False, + ): + super().__init__() + + self.norm1 = norm_layer(dim) + if flash_attn: + self.attn = FlashAttention( + dim, + num_heads=num_heads, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + attn_drop=attn_drop, + proj_drop=drop, + attn_head_dim=attn_head_dim, + ) + else: + self.attn = Attention( + dim, + num_heads=num_heads, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + attn_drop=attn_drop, + proj_drop=drop, + attn_head_dim=attn_head_dim, + ) + + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop, + ) + + def forward(self, x): + x = x + self.drop_path(self.attn(self.norm1(x))) + x = x + self.drop_path(self.mlp(self.norm2(x))) + return x + + +class PatchEmbed(nn.Module): + """Image to Patch Embedding""" + + def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768, ratio=1): + super().__init__() + img_size = to_2tuple(img_size) + patch_size = to_2tuple(patch_size) + num_patches = ( + (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0]) * (ratio**2) + ) + self.patch_shape = ( + int(img_size[0] // patch_size[0] * ratio), + int(img_size[1] // patch_size[1] * ratio), + ) + self.origin_patch_shape = ( + int(img_size[0] // patch_size[0]), + int(img_size[1] // patch_size[1]), + ) + self.img_size = img_size + self.patch_size = patch_size + self.num_patches = num_patches + + self.proj = nn.Conv2d( + in_chans, + embed_dim, + kernel_size=patch_size, + stride=(patch_size[0] // ratio), + padding=4 + 2 * (ratio // 2 - 1), + ) + + def forward(self, x, **kwargs): + B, C, H, W = x.shape + x = self.proj(x) + Hp, Wp = x.shape[2], x.shape[3] + + x = x.flatten(2).transpose(1, 2) + return x, (Hp, Wp) + + +class PatchEmbedNoPadding(nn.Module): + """Image to Patch Embedding""" + + def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768, ratio=1): + super().__init__() + img_size = to_2tuple(img_size) + patch_size = to_2tuple(patch_size) + num_patches = ( + (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0]) * (ratio**2) + ) + self.patch_shape = ( + int(img_size[0] // patch_size[0] * ratio), + int(img_size[1] // patch_size[1] * ratio), + ) + self.origin_patch_shape = ( + int(img_size[0] // patch_size[0]), + int(img_size[1] // patch_size[1]), + ) + self.img_size = img_size + self.patch_size = patch_size + self.num_patches = num_patches + + self.proj = nn.Conv2d( + in_chans, + embed_dim, + kernel_size=patch_size, + stride=(patch_size[0] // ratio), + padding=0, + ) + + def forward(self, x, **kwargs): + B, C, H, W = x.shape + x = self.proj(x) + Hp, Wp = x.shape[2], x.shape[3] + + x = x.flatten(2).transpose(1, 2) + return x, (Hp, Wp) + + +class HybridEmbed(nn.Module): + """CNN Feature Map Embedding + Extract feature map from CNN, flatten, project to embedding dim. + """ + + def __init__( + self, backbone, img_size=224, feature_size=None, in_chans=3, embed_dim=768 + ): + super().__init__() + assert isinstance(backbone, nn.Module) + img_size = to_2tuple(img_size) + self.img_size = img_size + self.backbone = backbone + if feature_size is None: + with torch.no_grad(): + training = backbone.training + if training: + backbone.eval() + o = self.backbone(torch.zeros(1, in_chans, img_size[0], img_size[1]))[ + -1 + ] + feature_size = o.shape[-2:] + feature_dim = o.shape[1] + backbone.train(training) + else: + feature_size = to_2tuple(feature_size) + feature_dim = self.backbone.feature_info.channels()[-1] + self.num_patches = feature_size[0] * feature_size[1] + self.proj = nn.Linear(feature_dim, embed_dim) + + def forward(self, x): + x = self.backbone(x)[-1] + x = x.flatten(2).transpose(1, 2) + x = self.proj(x) + return x + + +class ViT(nn.Module): + + def __init__( + self, + img_size=224, + patch_size=16, + in_chans=3, + num_classes=80, + embed_dim=768, + depth=12, + num_heads=12, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop_rate=0.0, + attn_drop_rate=0.0, + drop_path_rate=0.0, + hybrid_backbone=None, + norm_layer=None, + use_checkpoint=False, + frozen_stages=-1, + ratio=1, + last_norm=True, + patch_padding="pad", + freeze_attn=False, + freeze_ffn=False, + flash_attn=False, + no_patch_padding=False, + ): + # Protect mutable default arguments + super(ViT, self).__init__() + norm_layer = norm_layer or partial(nn.LayerNorm, eps=1e-6) + self.num_classes = num_classes + self.num_features = self.embed_dim = self.embed_dims = ( + embed_dim # num_features for consistency with other models + ) + self.frozen_stages = frozen_stages + self.use_checkpoint = use_checkpoint + self.patch_padding = patch_padding + self.freeze_attn = freeze_attn + self.freeze_ffn = freeze_ffn + self.depth = depth + + if hybrid_backbone is not None: + self.patch_embed = HybridEmbed( + hybrid_backbone, + img_size=img_size, + in_chans=in_chans, + embed_dim=embed_dim, + ) + else: + if no_patch_padding: + self.patch_embed = PatchEmbedNoPadding( + img_size=img_size, + patch_size=patch_size, + in_chans=in_chans, + embed_dim=embed_dim, + ratio=ratio, + ) + else: + self.patch_embed = PatchEmbed( + img_size=img_size, + patch_size=patch_size, + in_chans=in_chans, + embed_dim=embed_dim, + ratio=ratio, + ) + num_patches = self.patch_embed.num_patches + self.patch_size = patch_size + + # since the pretraining model has class token + self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim)) + + dpr = [ + x.item() for x in torch.linspace(0, drop_path_rate, depth) + ] # stochastic depth decay rule + + self.blocks = nn.ModuleList( + [ + Block( + dim=embed_dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + attn_drop=attn_drop_rate, + drop_path=dpr[i], + norm_layer=norm_layer, + flash_attn=flash_attn, + ) + for i in range(depth) + ] + ) + + self.last_norm = norm_layer(embed_dim) if last_norm else nn.Identity() + + if self.pos_embed is not None: + trunc_normal_(self.pos_embed, std=0.02) + + self._freeze_stages() + + def _freeze_stages(self): + """Freeze parameters.""" + if self.frozen_stages >= 0: + self.patch_embed.eval() + for param in self.patch_embed.parameters(): + param.requires_grad = False + + for i in range(1, self.frozen_stages + 1): + m = self.blocks[i - 1] + m.eval() + for param in m.parameters(): + param.requires_grad = False + + if self.freeze_attn: + for i in range(0, self.depth): + m = self.blocks[i] + m.attn.eval() + m.norm1.eval() + for param in m.attn.parameters(): + param.requires_grad = False + for param in m.norm1.parameters(): + param.requires_grad = False + + if self.freeze_ffn: + self.pos_embed.requires_grad = False + self.patch_embed.eval() + for param in self.patch_embed.parameters(): + param.requires_grad = False + for i in range(0, self.depth): + m = self.blocks[i] + m.mlp.eval() + m.norm2.eval() + for param in m.mlp.parameters(): + param.requires_grad = False + for param in m.norm2.parameters(): + param.requires_grad = False + + def init_weights(self): + """Initialize the weights in backbone. + Args: + pretrained (str, optional): Path to pre-trained weights. + Defaults to None. + """ + + def _init_weights(m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight, std=0.02) + if isinstance(m, nn.Linear) and m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.LayerNorm): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) + + self.apply(_init_weights) + + def get_num_layers(self): + return len(self.blocks) + + @torch.jit.ignore + def no_weight_decay(self): + return {"pos_embed", "cls_token"} + + def forward_features(self, x, extra_embed=None): + B, C, H, W = x.shape + x, (Hp, Wp) = self.patch_embed(x) + + if self.pos_embed is not None: + # fit for multiple GPU training + # since the first element for pos embed (sin-cos manner) is zero, it will cause no difference + x = x + self.pos_embed[:, 1:] + self.pos_embed[:, :1] + + if extra_embed is not None: + x = x + extra_embed.flatten(2).transpose(1, 2).to(x) + + for blk in self.blocks: + if self.use_checkpoint: + x = checkpoint.checkpoint(blk, x) + else: + x = blk(x) + + x = self.last_norm(x) + + xp = x.permute(0, 2, 1).reshape(B, -1, Hp, Wp).contiguous() + + return xp + + def forward(self, x, *args, **kwargs): + x = self.forward_features(x, *args, **kwargs) + return x + + def train(self, mode=True): + """Convert the model into training mode.""" + super().train(mode) + self._freeze_stages() diff --git a/src/sam3d_body/models/decoders/__init__.py b/src/sam3d_body/models/decoders/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f5e1e7dd6565e6984bf8b3026e8faca72b70037c --- /dev/null +++ b/src/sam3d_body/models/decoders/__init__.py @@ -0,0 +1,32 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from .keypoint_prompt_sampler import build_keypoint_sampler +from .prompt_encoder import PromptEncoder +from .promptable_decoder import PromptableDecoder + + +def build_decoder(cfg, context_dim=None): + from .promptable_decoder import PromptableDecoder + + if cfg.TYPE == "sam": + return PromptableDecoder( + dims=cfg.DIM, + context_dims=context_dim, + depth=cfg.DEPTH, + num_heads=cfg.HEADS, + head_dims=cfg.DIM_HEAD, + mlp_dims=cfg.MLP_DIM, + layer_scale_init_value=cfg.LAYER_SCALE_INIT, + drop_rate=cfg.DROP_RATE, + attn_drop_rate=cfg.ATTN_DROP_RATE, + drop_path_rate=cfg.DROP_PATH_RATE, + ffn_type=cfg.FFN_TYPE, + enable_twoway=cfg.ENABLE_TWOWAY, + repeat_pe=cfg.REPEAT_PE, + frozen=cfg.get("FROZEN", False), + do_interm_preds=cfg.get("DO_INTERM_PREDS", False), + do_keypoint_tokens=cfg.get("DO_KEYPOINT_TOKENS", False), + keypoint_token_update=cfg.get("KEYPOINT_TOKEN_UPDATE", None), + ) + else: + raise ValueError("Invalid decoder type: ", cfg.TYPE) diff --git a/src/sam3d_body/models/decoders/keypoint_prompt_sampler.py b/src/sam3d_body/models/decoders/keypoint_prompt_sampler.py new file mode 100644 index 0000000000000000000000000000000000000000..5ec597543256cc8f68ea687893dc835a44df25cc --- /dev/null +++ b/src/sam3d_body/models/decoders/keypoint_prompt_sampler.py @@ -0,0 +1,183 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import random +from abc import ABC, abstractmethod +from typing import Dict, List + +import torch + +from omegaconf import DictConfig +from yacs.config import CfgNode + + +def build_keypoint_sampler(sampler_cfg, prompt_keypoints, keybody_idx): + sampler_type = sampler_cfg.get("TYPE", "v1") + if sampler_type == "v1": + sampler_cls = KeypointSamplerV1 + else: + raise ValueError("Invalid sampler type: ", sampler_type) + + return sampler_cls(sampler_cfg, prompt_keypoints, keybody_idx) + + +class BaseKeypointSampler(ABC): + @abstractmethod + def sample( + self, gt_keypoints: torch.Tensor, pred_keypoints: torch.Tensor, is_train: bool + ) -> torch.Tensor: + pass + + def _get_worst_keypoint(self, distances, keypoint_list): + # Set distance to -1 for non-promptable keypoints + cur_dist = torch.ones_like(distances) * -1 + cur_dist[keypoint_list] = distances[keypoint_list] + keypoint_idx = int(cur_dist.argmax()) + if cur_dist[keypoint_idx] > self.distance_thresh: + valid_keypoint = True + else: + valid_keypoint = False + return keypoint_idx, valid_keypoint + + def _get_random_keypoint(self, distances, keypoint_list): + candidates = [idx for idx in keypoint_list if distances[idx] > 0] + if len(candidates): + keypoint_idx = random.choice(candidates) + valid_keypoint = True + else: + keypoint_idx = None + valid_keypoint = False + return keypoint_idx, valid_keypoint + + def _masked_distance(self, x, y, mask=None): + """ + Args: + x, y: [B, K, D] + mask: [B, K] + Return: + distances: [K, B] + """ + distances = (x - y).pow(2).sum(dim=-1) + if mask is not None: + distances[mask] = -1 + return distances.T + + +class KeypointSamplerV1(BaseKeypointSampler): + def __init__( + self, + sampler_cfg: DictConfig | CfgNode, + prompt_keypoints: Dict, + keybody_idx: List, + ): + self.prompt_keypoints = prompt_keypoints + self._keybody_idx = keybody_idx + self._non_keybody_idx = [ + idx for idx in self.prompt_keypoints if idx not in self._keybody_idx + ] + + self.keybody_ratio = sampler_cfg.get("KEYBODY_RATIO", 0.8) + self.worst_ratio = sampler_cfg.get("WORST_RATIO", 0.8) + self.negative_ratio = sampler_cfg.get("NEGATIVE_RATIO", 0.0) + self.dummy_ratio = sampler_cfg.get("DUMMY_RATIO", 0.1) + self.distance_thresh = sampler_cfg.get("DISTANCE_THRESH", 0.0) + + def sample( + self, + gt_keypoints_2d: torch.Tensor, + pred_keypoints_2d: torch.Tensor, + is_train: bool = True, + force_dummy: bool = False, + ) -> torch.Tensor: + # Get the distance between each predicted and gt keypoint + # Elements will be ignored if (1) the gt has low confidence or + # (2) both the gt and pred are outside of the image + mask_1 = gt_keypoints_2d[:, :, -1] < 0.5 + mask_2 = ( + (gt_keypoints_2d[:, :, :2] > 0.5) | (gt_keypoints_2d[:, :, :2] < -0.5) + ).any(dim=-1) + + # Elements to be ignored + if not is_train or torch.rand(1).item() > self.negative_ratio: + mask = mask_1 | mask_2 + # print_base = "positive" + else: + mask_3 = ( + (pred_keypoints_2d[:, :, :2] > 0.5) + | (pred_keypoints_2d[:, :, :2] < -0.5) + ).any(dim=-1) + # To include negative prompts + mask = mask_1 | (mask_2 & mask_3) + # print_base = "negative" + + # Get pairwise distances with shape [K, B] + distances = self._masked_distance( + pred_keypoints_2d, gt_keypoints_2d[..., :2], mask + ) + + batch_size = distances.shape[1] + keypoints_prompt = [] + for b in range(batch_size): + # print_str = print_base + + # Decide to get the worst keypoint or a random keypoint + if not is_train or torch.rand(1).item() < self.worst_ratio: + sampler = self._get_worst_keypoint + # print_str += "_worst" + else: + sampler = self._get_random_keypoint + # print_str += "_random" + + # Decide to prompt keybody kepoints or non-keybody ones + if not is_train or torch.rand(1).item() < self.keybody_ratio: + cur_idx = self._keybody_idx + alt_idx = self._non_keybody_idx + # print_str += "_keybody" + else: + cur_idx = self._non_keybody_idx + alt_idx = self._keybody_idx + # print_str += "_nonkey" + + # Get a valid or dummy prompt + if not is_train or torch.rand(1).item() > self.dummy_ratio: + keypoint_idx, valid_keypoint = sampler(distances[:, b], cur_idx) + + if not valid_keypoint: + # Try the alternative keypoints + keypoint_idx, valid_keypoint = self._get_worst_keypoint( + distances[:, b], alt_idx + ) + else: + valid_keypoint = False + + if valid_keypoint: + cur_point = gt_keypoints_2d[b, keypoint_idx].clone() + if torch.any(cur_point[:2] > 0.5) or torch.any(cur_point[:2] < -0.5): + # Negative prompt --> indicating the predicted keypoint is incorrect + cur_point[:2] = pred_keypoints_2d[b, keypoint_idx][:2] + cur_point = torch.clamp( + cur_point + 0.5, min=0.0, max=1.0 + ) # shift from [-0.5, 0.5] to [0, 1] + cur_point[-1] = -1 + # print_str += "_negative" + else: + cur_point = torch.clamp( + cur_point + 0.5, min=0.0, max=1.0 + ) # shift from [-0.5, 0.5] to [0, 1] + cur_point[-1] = self.prompt_keypoints[ + keypoint_idx + ] # map to prompt_idx + # print_str += "_positive" + else: + cur_point = torch.zeros(3).to(gt_keypoints_2d) + cur_point[-1] = -2 + # print_str += "_dummy" + + if force_dummy: + cur_point = torch.zeros(3).to(gt_keypoints_2d) + cur_point[-1] = -2 + + keypoints_prompt.append(cur_point) + # print(print_str) + + keypoints_prompt = torch.stack(keypoints_prompt, dim=0).view(batch_size, 1, 3) + return keypoints_prompt diff --git a/src/sam3d_body/models/decoders/prompt_encoder.py b/src/sam3d_body/models/decoders/prompt_encoder.py new file mode 100644 index 0000000000000000000000000000000000000000..81d0fab4205d973f42edab197a5822231b7f5ee8 --- /dev/null +++ b/src/sam3d_body/models/decoders/prompt_encoder.py @@ -0,0 +1,256 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from typing import Any, Optional, Tuple + +import numpy as np + +import torch +import torch.nn as nn + +from sam3d_body.models.modules.transformer import LayerNorm2d + + +class PromptEncoder(nn.Module): + def __init__( + self, + embed_dim: int, + num_body_joints: int, + # img_size: Tuple[int, int], + # patch_resolution: Tuple[int, int], + frozen: bool = False, + mask_embed_type: Optional[str] = None, + ) -> None: + """ + Encodes prompts for input to SAM's mask decoder. + + Arguments: + embed_dim (int): The prompts' embedding dimension + num_body_joints (int): The number of body joints + img_size (Tuple): The padded size of the image as input + to the image encoder, as (H, W). + patch_resolution (Tuple): image path size, as (H, W) + """ + super().__init__() + self.embed_dim = embed_dim + self.num_body_joints = num_body_joints + # self.img_size = img_size + # self.patch_resolution = patch_resolution + + # Keypoint prompts + self.pe_layer = PositionEmbeddingRandom(embed_dim // 2) + self.point_embeddings = nn.ModuleList( + [nn.Embedding(1, embed_dim) for _ in range(self.num_body_joints)] + ) + self.not_a_point_embed = nn.Embedding(1, embed_dim) + self.invalid_point_embed = nn.Embedding(1, embed_dim) + + # Mask prompt + if mask_embed_type in ["v1"]: + mask_in_chans = 16 # SAM2 + self.mask_downscaling = nn.Sequential( + nn.Conv2d(1, mask_in_chans // 4, kernel_size=4, stride=4), + LayerNorm2d(mask_in_chans // 4), + nn.GELU(), + nn.Conv2d(mask_in_chans // 4, mask_in_chans, kernel_size=4, stride=4), + LayerNorm2d(mask_in_chans), + nn.GELU(), + nn.Conv2d(mask_in_chans, embed_dim, kernel_size=1), + ) + elif mask_embed_type in ["v2"]: + mask_in_chans = 256 + self.mask_downscaling = nn.Sequential( + nn.Conv2d(1, mask_in_chans // 64, kernel_size=2, stride=2), + LayerNorm2d(mask_in_chans // 64), + nn.GELU(), + nn.Conv2d( + mask_in_chans // 64, + mask_in_chans // 16, + kernel_size=2, + stride=2, + ), + LayerNorm2d(mask_in_chans // 16), + nn.GELU(), + nn.Conv2d( + mask_in_chans // 16, mask_in_chans // 4, kernel_size=2, stride=2 + ), + LayerNorm2d(mask_in_chans // 4), + nn.GELU(), + nn.Conv2d(mask_in_chans // 4, mask_in_chans, kernel_size=2, stride=2), + LayerNorm2d(mask_in_chans), + nn.GELU(), + nn.Conv2d(mask_in_chans, embed_dim, kernel_size=1), + ) + else: + assert mask_embed_type is None + + if mask_embed_type is not None: + # Zero-initialize the last conv layer as gating + nn.init.zeros_(self.mask_downscaling[-1].weight) + nn.init.zeros_(self.mask_downscaling[-1].bias) + + self.no_mask_embed = nn.Embedding(1, embed_dim) + nn.init.zeros_(self.no_mask_embed.weight) + + self.frozen = frozen + self._freeze_stages() + + def get_dense_pe(self, size: Tuple[int, int]) -> torch.Tensor: + """ + Returns the positional encoding used to encode point prompts, + applied to a dense set of points the shape of the image encoding. + + Returns: + torch.Tensor: Positional encoding with shape + 1x(embed_dim)x(embedding_h)x(embedding_w) + """ + return self.pe_layer(size).unsqueeze(0) + + def _embed_keypoints( + self, + points: torch.Tensor, + labels: torch.Tensor, + ) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Embeds point prompts. + Assuming points have been normalized to [0, 1]. + + Output shape [B, N, C], mask shape [B, N] + """ + assert points.min() >= 0 and points.max() <= 1 + point_embedding = self.pe_layer._pe_encoding(points.to(torch.float)) + point_embedding[labels == -2] = 0.0 # invalid points + point_embedding[labels == -2] += self.invalid_point_embed.weight + point_embedding[labels == -1] = 0.0 + point_embedding[labels == -1] += self.not_a_point_embed.weight + for i in range(self.num_body_joints): + point_embedding[labels == i] += self.point_embeddings[i].weight + + point_mask = labels > -2 + return point_embedding, point_mask + + def _get_batch_size( + self, + keypoints: Optional[torch.Tensor], + boxes: Optional[torch.Tensor], + masks: Optional[torch.Tensor], + ) -> int: + """ + Gets the batch size of the output given the batch size of the input prompts. + """ + if keypoints is not None: + return keypoints.shape[0] + elif boxes is not None: + return boxes.shape[0] + elif masks is not None: + return masks.shape[0] + else: + return 1 + + def _get_device(self) -> torch.device: + return self.point_embeddings[0].weight.device + + def forward( + self, + keypoints: Optional[torch.Tensor], + boxes: Optional[torch.Tensor] = None, + masks: Optional[torch.Tensor] = None, + ) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Embeds different types of prompts, returning both sparse and dense + embeddings. + + Arguments: + keypoints (torchTensor or none): point coordinates and labels to embed. + boxes (torch.Tensor or none): boxes to embed + masks (torch.Tensor or none): masks to embed + + Returns: + torch.Tensor: sparse embeddings for the points and boxes, with shape + BxNx(embed_dim), where N is determined by the number of input points + and boxes. + torch.Tensor: dense embeddings for the masks, in the shape + Bx(embed_dim)x(embed_H)x(embed_W) + """ + bs = self._get_batch_size(keypoints, boxes, masks) + sparse_embeddings = torch.empty( + (bs, 0, self.embed_dim), device=self._get_device() + ) + sparse_masks = torch.empty((bs, 0), device=self._get_device()) + if keypoints is not None: + coords = keypoints[:, :, :2] + labels = keypoints[:, :, -1] + point_embeddings, point_mask = self._embed_keypoints( + coords, labels + ) # pad=(boxes is None)) + sparse_embeddings = torch.cat([sparse_embeddings, point_embeddings], dim=1) + sparse_masks = torch.cat([sparse_masks, point_mask], dim=1) + + return sparse_embeddings, sparse_masks + + def get_mask_embeddings( + self, + masks: Optional[torch.Tensor] = None, + bs: int = 1, + size: Tuple[int, int] = (16, 16), # [H, W] + ) -> Tuple[torch.Tensor, torch.Tensor]: + """Embeds mask inputs.""" + no_mask_embeddings = self.no_mask_embed.weight.reshape(1, -1, 1, 1).expand( + bs, -1, size[0], size[1] + ) + if masks is not None: + mask_embeddings = self.mask_downscaling(masks) + else: + mask_embeddings = no_mask_embeddings + return mask_embeddings, no_mask_embeddings + + def _freeze_stages(self): + """Freeze parameters.""" + if self.frozen: + for param in self.parameters(): + param.requires_grad = False + + +class PositionEmbeddingRandom(nn.Module): + """ + Positional encoding using random spatial frequencies. + """ + + def __init__(self, num_pos_feats: int = 64, scale: Optional[float] = None) -> None: + super().__init__() + if scale is None or scale <= 0.0: + scale = 1.0 + self.register_buffer( + "positional_encoding_gaussian_matrix", + scale * torch.randn((2, num_pos_feats)), + ) + + def _pe_encoding(self, coords: torch.Tensor) -> torch.Tensor: + """Positionally encode points that are normalized to [0,1].""" + # assuming coords are in [0, 1]^2 square and have d_1 x ... x d_n x 2 shape + coords = 2 * coords - 1 + coords = coords @ self.positional_encoding_gaussian_matrix + coords = 2 * np.pi * coords + # outputs d_1 x ... x d_n x C shape + return torch.cat([torch.sin(coords), torch.cos(coords)], dim=-1) + + def forward(self, size: Tuple[int, int]) -> torch.Tensor: + """Generate positional encoding for a grid of the specified size.""" + h, w = size + device: Any = self.positional_encoding_gaussian_matrix.device + grid = torch.ones((h, w), device=device, dtype=torch.float32) + y_embed = grid.cumsum(dim=0) - 0.5 + x_embed = grid.cumsum(dim=1) - 0.5 + y_embed = y_embed / h + x_embed = x_embed / w + + pe = self._pe_encoding(torch.stack([x_embed, y_embed], dim=-1)) + return pe.permute(2, 0, 1) # C x H x W + + def forward_with_coords( + self, coords_input: torch.Tensor, image_size: Tuple[int, int] + ) -> torch.Tensor: + """Positionally encode points that are not normalized to [0,1].""" + coords = coords_input.clone() + coords[:, :, 0] = coords[:, :, 0] / image_size[1] + coords[:, :, 1] = coords[:, :, 1] / image_size[0] + return self._pe_encoding(coords.to(torch.float)) # B x N x C diff --git a/src/sam3d_body/models/decoders/promptable_decoder.py b/src/sam3d_body/models/decoders/promptable_decoder.py new file mode 100644 index 0000000000000000000000000000000000000000..a5c8b343ef67e68331941542a1e01890c1f52da8 --- /dev/null +++ b/src/sam3d_body/models/decoders/promptable_decoder.py @@ -0,0 +1,194 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import pickle +from typing import Dict, Optional + +import torch +import torch.nn as nn + +from ..modules.transformer import build_norm_layer, TransformerDecoderLayer + + +class PromptableDecoder(nn.Module): + """Cross-attention based Transformer decoder with prompts input. + + Args: + token_dims (int): The dimension of input pose tokens. + prompt_dims (int): The dimension of input prompt tokens. + context_dims (int): The dimension of image context features. + dims (int): The projected dimension of all tokens in the decoder. + depth (int): The number of layers for Transformer decoder. + num_heads (int): The number of heads for multi-head attention. + head_dims (int): The dimension of each head. + mlp_dims (int): The dimension of hidden layers in MLP. + layer_scale_init_value (float or torch.Tensor): Init value of layer + scale. Defaults to 0. + drop_rate (float): Probability of an element to be zeroed + after the feed forward layer. Defaults to 0. + attn_drop_rate (float): The drop out rate for attention output weights. + Defaults to 0. + drop_path_rate (float): Stochastic depth rate. Defaults to 0. + ffn_type (str): Select the type of ffn layers. Defaults to 'origin'. + act_layer (nn.Module, optional): The activation layer for FFNs. + Default: nn.GELU + norm_cfg (dict): Config dict for normalization layer. + Defaults to ``dict(type='LN')``. + enable_twoway (bool): Whether to enable two-way Transformer (used in SAM). + repeat_pe (bool): Whether to re-add PE at each layer (used in SAM) + """ + + def __init__( + self, + dims: int, + context_dims: int, + depth: int, + num_heads: int = 8, + head_dims: int = 64, + mlp_dims: int = 1024, + layer_scale_init_value: float = 0.0, + drop_rate: float = 0.0, + attn_drop_rate: float = 0.0, + drop_path_rate: float = 0.0, + ffn_type: str = "origin", + act_layer: nn.Module = nn.GELU, + norm_cfg: Dict = dict(type="LN", eps=1e-6), + enable_twoway: bool = False, + repeat_pe: bool = False, + frozen: bool = False, + do_interm_preds: bool = False, + do_keypoint_tokens: bool = False, + keypoint_token_update: bool | str = False, + ): + super().__init__() + + self.layers = nn.ModuleList() + for i in range(depth): + self.layers.append( + TransformerDecoderLayer( + token_dims=dims, + context_dims=context_dims, + num_heads=num_heads, + head_dims=head_dims, + mlp_dims=mlp_dims, + layer_scale_init_value=layer_scale_init_value, + drop_rate=drop_rate, + attn_drop_rate=attn_drop_rate, + drop_path_rate=drop_path_rate, + ffn_type=ffn_type, + act_layer=act_layer, + norm_cfg=norm_cfg, + enable_twoway=enable_twoway, + repeat_pe=repeat_pe, + skip_first_pe=(i == 0), + ) + ) + + self.norm_final = build_norm_layer(norm_cfg, dims) + self.do_interm_preds = do_interm_preds + self.do_keypoint_tokens = do_keypoint_tokens + self.keypoint_token_update = keypoint_token_update + + self.frozen = frozen + self._freeze_stages() + + def forward( + self, + token_embedding: torch.Tensor, + image_embedding: torch.Tensor, + token_augment: Optional[torch.Tensor] = None, + image_augment: Optional[torch.Tensor] = None, + token_mask: Optional[torch.Tensor] = None, + channel_first: bool = True, + token_to_pose_output_fn=None, + keypoint_token_update_fn=None, + hand_embeddings=None, + hand_augment=None, + ): + """ + Args: + token_embedding: [B, N, C] + image_embedding: [B, C, H, W] + """ + if channel_first: + image_embedding = image_embedding.flatten(2).permute(0, 2, 1) + if image_augment is not None: + image_augment = image_augment.flatten(2).permute(0, 2, 1) + if hand_embeddings is not None: + hand_embeddings = hand_embeddings.flatten(2).permute(0, 2, 1) + hand_augment = hand_augment.flatten(2).permute(0, 2, 1) + if len(hand_augment) == 1: + # inflate batch dimension + assert len(hand_augment.shape) == 3 + hand_augment = hand_augment.repeat(len(hand_embeddings), 1, 1) + + if self.do_interm_preds: + assert token_to_pose_output_fn is not None + all_pose_outputs = [] + + for layer_idx, layer in enumerate(self.layers): + if hand_embeddings is None: + token_embedding, image_embedding = layer( + token_embedding, + image_embedding, + token_augment, + image_augment, + token_mask, + ) + else: + token_embedding, image_embedding = layer( + token_embedding, + torch.cat([image_embedding, hand_embeddings], dim=1), + token_augment, + torch.cat([image_augment, hand_augment], dim=1), + token_mask, + ) + image_embedding = image_embedding[:, : image_augment.shape[1]] + + if self.do_interm_preds and layer_idx < len(self.layers) - 1: + curr_pose_output = token_to_pose_output_fn( + self.norm_final(token_embedding), + prev_pose_output=( + all_pose_outputs[-1] if len(all_pose_outputs) > 0 else None + ), + layer_idx=layer_idx, + ) + all_pose_outputs.append(curr_pose_output) + + if self.keypoint_token_update: + assert keypoint_token_update_fn is not None + token_embedding, token_augment, _, _ = keypoint_token_update_fn( + token_embedding, token_augment, curr_pose_output, layer_idx + ) + + out = self.norm_final(token_embedding) + + if self.do_interm_preds: + curr_pose_output = token_to_pose_output_fn( + out, + prev_pose_output=( + all_pose_outputs[-1] if len(all_pose_outputs) > 0 else None + ), + layer_idx=layer_idx, + ) + all_pose_outputs.append(curr_pose_output) + + return out, all_pose_outputs + else: + return out + + def _freeze_stages(self): + """Freeze parameters.""" + if self.frozen: + for layer in self.layers: + layer.eval() + self.norm_final.eval() + for param in self.parameters(): + param.requires_grad = False + + def train(self, mode=True): + """ + Convert the model into training mode. + (not called by lightning in trainer.fit() actually) + """ + super().train(mode) + self._freeze_stages() diff --git a/src/sam3d_body/models/heads/__init__.py b/src/sam3d_body/models/heads/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bf2ecff30392026e6ed698bfba26a68dc9f03b45 --- /dev/null +++ b/src/sam3d_body/models/heads/__init__.py @@ -0,0 +1,28 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from ..modules import to_2tuple +from .camera_head import PerspectiveHead +from .mhr_head import MHRHead + + +def build_head(cfg, head_type="mhr", enable_hand_model=False, default_scale_factor=1.0): + if head_type == "mhr": + return MHRHead( + input_dim=cfg.MODEL.DECODER.DIM, + mlp_depth=cfg.MODEL.MHR_HEAD.get("MLP_DEPTH", 1), + mhr_model_path=cfg.MODEL.MHR_HEAD.MHR_MODEL_PATH, + mlp_channel_div_factor=cfg.MODEL.MHR_HEAD.get("MLP_CHANNEL_DIV_FACTOR", 1), + enable_hand_model=enable_hand_model, + ) + elif head_type == "perspective": + return PerspectiveHead( + input_dim=cfg.MODEL.DECODER.DIM, + img_size=to_2tuple(cfg.MODEL.IMAGE_SIZE), + mlp_depth=cfg.MODEL.get("CAMERA_HEAD", dict()).get("MLP_DEPTH", 1), + mlp_channel_div_factor=cfg.MODEL.get("CAMERA_HEAD", dict()).get( + "MLP_CHANNEL_DIV_FACTOR", 1 + ), + default_scale_factor=default_scale_factor, + ) + else: + raise ValueError("Invalid head type: ", head_type) diff --git a/src/sam3d_body/models/heads/camera_head.py b/src/sam3d_body/models/heads/camera_head.py new file mode 100644 index 0000000000000000000000000000000000000000..fdf6be60aab1327ca06cf3a6343272833b38f44d --- /dev/null +++ b/src/sam3d_body/models/heads/camera_head.py @@ -0,0 +1,110 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from typing import Optional, Sequence, Tuple + +import torch +import torch.nn as nn + +from sam3d_body.models.modules.geometry_utils import perspective_projection + +from ..modules import get_intrinsic_matrix, to_2tuple +from ..modules.transformer import FFN + + +class PerspectiveHead(nn.Module): + """ + Predict camera translation (s, tx, ty) and perform full-perspective + 2D reprojection (CLIFF/CameraHMR setup). + """ + + def __init__( + self, + input_dim: int, + img_size: Tuple[int, int] | Sequence[int], # model input size (W, H) + mlp_depth: int = 1, + drop_ratio: float = 0.0, + mlp_channel_div_factor: int = 8, + default_scale_factor: float | int = 1, + ): + super().__init__() + + # Metadata to compute 3D skeleton and 2D reprojection + self.img_size = to_2tuple(img_size) + self.ncam = 3 # (s, tx, ty) + self.default_scale_factor = default_scale_factor + + self.proj = FFN( + embed_dims=input_dim, + feedforward_channels=input_dim // mlp_channel_div_factor, + output_dims=self.ncam, + num_fcs=mlp_depth, + ffn_drop=drop_ratio, + add_identity=False, + ) + + def forward( + self, + x: torch.Tensor, + init_estimate: Optional[torch.Tensor] = None, + ): + """ + Args: + x: pose token with shape [B, C], usually C=DECODER.DIM + init_estimate: [B, self.ncam] + """ + pred_cam = self.proj(x) + if init_estimate is not None: + pred_cam = pred_cam + init_estimate + + return pred_cam + + def perspective_projection( + self, + points_3d: torch.Tensor, + pred_cam: torch.Tensor, + bbox_center: torch.Tensor, + bbox_size: torch.Tensor, + img_size: torch.Tensor, + cam_int: torch.Tensor, + use_intrin_center: bool = False, + ): + """ + Args: + bbox_center / img_size: shape [N, 2], in original image space (w, h) + bbox_size: shape [N,], in original image space + cam_int: shape [N, 3, 3] + """ + batch_size = points_3d.shape[0] + pred_cam = pred_cam.clone() + pred_cam[..., [0, 2]] *= -1 # Camera system difference + + # Compute camera translation: (scale, x, y) --> (x, y, depth) + # depth ~= f / s + # Note that f is in the NDC space (see Zolly section 3.1) + s, tx, ty = pred_cam[:, 0], pred_cam[:, 1], pred_cam[:, 2] + bs = bbox_size * s * self.default_scale_factor + 1e-8 + focal_length = cam_int[:, 0, 0] + tz = 2 * focal_length / bs + + if not use_intrin_center: + cx = 2 * (bbox_center[:, 0] - (img_size[:, 0] / 2)) / bs + cy = 2 * (bbox_center[:, 1] - (img_size[:, 1] / 2)) / bs + else: + cx = 2 * (bbox_center[:, 0] - (cam_int[:, 0, 2])) / bs + cy = 2 * (bbox_center[:, 1] - (cam_int[:, 1, 2])) / bs + + pred_cam_t = torch.stack([tx + cx, ty + cy, tz], dim=-1) + + # Compute camera translation + j3d_cam = points_3d + pred_cam_t.unsqueeze(1) + + # Projection to the image plane. + # Note that the projection output is in *original* image space now. + j2d = perspective_projection(j3d_cam, cam_int) + + return { + "pred_keypoints_2d": j2d.reshape(batch_size, -1, 2), + "pred_cam_t": pred_cam_t, + "focal_length": focal_length, + "pred_keypoints_2d_depth": j3d_cam.reshape(batch_size, -1, 3)[:, :, 2], + } diff --git a/src/sam3d_body/models/heads/mhr_head.py b/src/sam3d_body/models/heads/mhr_head.py new file mode 100644 index 0000000000000000000000000000000000000000..013fca9b94665f65d3d3a7a754aac30c4d695933 --- /dev/null +++ b/src/sam3d_body/models/heads/mhr_head.py @@ -0,0 +1,369 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import os +import warnings +from typing import Optional + +import roma +import torch +import torch.nn as nn + +from ..modules import rot6d_to_rotmat +from ..modules.mhr_utils import ( + compact_cont_to_model_params_body, + compact_cont_to_model_params_hand, + compact_model_params_to_cont_body, + mhr_param_hand_mask, +) + +from ..modules.transformer import FFN + +MOMENTUM_ENABLED = os.environ.get("MOMENTUM_ENABLED") is None +try: + if MOMENTUM_ENABLED: + from mhr.mhr import MHR + + MOMENTUM_ENABLED = True + warnings.warn("Momentum is enabled") + else: + warnings.warn("Momentum is not enabled") + raise ImportError +except: + MOMENTUM_ENABLED = False + warnings.warn("Momentum is not enabled") + + +class MHRHead(nn.Module): + + def __init__( + self, + input_dim: int, + mlp_depth: int = 1, + mhr_model_path: str = "", + extra_joint_regressor: str = "", + ffn_zero_bias: bool = True, + mlp_channel_div_factor: int = 8, + enable_hand_model=False, + ): + super().__init__() + + self.num_shape_comps = 45 + self.num_scale_comps = 28 + self.num_hand_comps = 54 + self.num_face_comps = 72 + self.enable_hand_model = enable_hand_model + + self.body_cont_dim = 260 + self.npose = ( + 6 # Global Rotation + + self.body_cont_dim # then body + + self.num_shape_comps + + self.num_scale_comps + + self.num_hand_comps * 2 + + self.num_face_comps + ) + + self.proj = FFN( + embed_dims=input_dim, + feedforward_channels=input_dim // mlp_channel_div_factor, + output_dims=self.npose, + num_fcs=mlp_depth, + ffn_drop=0.0, + add_identity=False, + ) + + if ffn_zero_bias: + torch.nn.init.zeros_(self.proj.layers[-2].bias) + + # MHR Parameters + self.model_data_dir = mhr_model_path + self.num_hand_scale_comps = self.num_scale_comps - 18 + self.num_hand_pose_comps = self.num_hand_comps + + # Buffers to be filled in by model state dict + self.joint_rotation = nn.Parameter(torch.zeros(127, 3, 3), requires_grad=False) + self.scale_mean = nn.Parameter(torch.zeros(68), requires_grad=False) + self.scale_comps = nn.Parameter(torch.zeros(28, 68), requires_grad=False) + self.faces = nn.Parameter(torch.zeros(36874, 3).long(), requires_grad=False) + self.hand_pose_mean = nn.Parameter(torch.zeros(54), requires_grad=False) + self.hand_pose_comps = nn.Parameter(torch.eye(54), requires_grad=False) + self.hand_joint_idxs_left = nn.Parameter( + torch.zeros(27).long(), requires_grad=False + ) + self.hand_joint_idxs_right = nn.Parameter( + torch.zeros(27).long(), requires_grad=False + ) + self.keypoint_mapping = nn.Parameter( + torch.zeros(308, 18439 + 127), requires_grad=False + ) + # Some special buffers for the hand-version + self.right_wrist_coords = nn.Parameter(torch.zeros(3), requires_grad=False) + self.root_coords = nn.Parameter(torch.zeros(3), requires_grad=False) + self.local_to_world_wrist = nn.Parameter(torch.zeros(3, 3), requires_grad=False) + self.nonhand_param_idxs = nn.Parameter( + torch.zeros(145).long(), requires_grad=False + ) + + # Load MHR itself + if MOMENTUM_ENABLED: + self.mhr = MHR.from_files( + device=torch.device("cuda" if torch.cuda.is_available() else "cpu"), + lod=1, + ) + else: + self.mhr = torch.jit.load( + mhr_model_path, + map_location=("cuda" if torch.cuda.is_available() else "cpu"), + ) + + for param in self.mhr.parameters(): + param.requires_grad = False + + def get_zero_pose_init(self, factor=1.0): + # Initialize pose token with zero-initialized learnable params + # Note: bias/initial value should be zero-pose in cont, not all-zeros + weights = torch.zeros(1, self.npose) + weights[:, : 6 + self.body_cont_dim] = torch.cat( + [ + torch.FloatTensor([1, 0, 0, 0, 1, 0]), + compact_model_params_to_cont_body(torch.zeros(1, 133)).squeeze() + * factor, + ], + dim=0, + ) + return weights + + def replace_hands_in_pose(self, full_pose_params, hand_pose_params): + assert full_pose_params.shape[1] == 136 + + # This drops in the hand poses from hand_pose_params (PCA 6D) into full_pose_params. + # Split into left and right hands + left_hand_params, right_hand_params = torch.split( + hand_pose_params, + [self.num_hand_pose_comps, self.num_hand_pose_comps], + dim=1, + ) + + # Change from cont to model params + left_hand_params_model_params = compact_cont_to_model_params_hand( + self.hand_pose_mean + + torch.einsum("da,ab->db", left_hand_params, self.hand_pose_comps) + ) + right_hand_params_model_params = compact_cont_to_model_params_hand( + self.hand_pose_mean + + torch.einsum("da,ab->db", right_hand_params, self.hand_pose_comps) + ) + + # Drop it in + full_pose_params[:, self.hand_joint_idxs_left] = left_hand_params_model_params + full_pose_params[:, self.hand_joint_idxs_right] = right_hand_params_model_params + + return full_pose_params # B x 207 + + def mhr_forward( + self, + global_trans, + global_rot, + body_pose_params, + hand_pose_params, + scale_params, + shape_params, + expr_params=None, + return_keypoints=False, + do_pcblend=True, + return_joint_coords=False, + return_model_params=False, + return_joint_rotations=False, + scale_offsets=None, + vertex_offsets=None, + ): + + if self.enable_hand_model: + # Transfer wrist-centric predictions to the body. + global_rot_ori = global_rot.clone() + global_trans_ori = global_trans.clone() + global_rot = roma.rotmat_to_euler( + "xyz", + roma.euler_to_rotmat("xyz", global_rot_ori) @ self.local_to_world_wrist, + ) + global_trans = ( + -( + roma.euler_to_rotmat("xyz", global_rot) + @ (self.right_wrist_coords - self.root_coords) + + self.root_coords + ) + + global_trans_ori + ) + + body_pose_params = body_pose_params[..., :130] + + # Convert from scale and shape params to actual scales and vertices + ## Add singleton batches in case... + if len(scale_params.shape) == 1: + scale_params = scale_params[None] + if len(shape_params.shape) == 1: + shape_params = shape_params[None] + ## Convert scale... + scales = self.scale_mean[None, :] + scale_params @ self.scale_comps + if scale_offsets is not None: + scales = scales + scale_offsets + + # Now, figure out the pose. + ## 10 here is because it's more stable to optimize global translation in meters. + full_pose_params = torch.cat( + [global_trans * 10, global_rot, body_pose_params], dim=1 + ) # B x 127 + ## Put in hands + if hand_pose_params is not None: + full_pose_params = self.replace_hands_in_pose( + full_pose_params, hand_pose_params + ) + model_params = torch.cat([full_pose_params, scales], dim=1) + + if self.enable_hand_model: + # Zero out non-hand parameters + model_params[:, self.nonhand_param_idxs] = 0 + + curr_skinned_verts, curr_skel_state = self.mhr( + shape_params, model_params, expr_params + ) + curr_joint_coords, curr_joint_quats, _ = torch.split( + curr_skel_state, [3, 4, 1], dim=2 + ) + curr_skinned_verts = curr_skinned_verts / 100 + curr_joint_coords = curr_joint_coords / 100 + curr_joint_rots = roma.unitquat_to_rotmat(curr_joint_quats) + + # Prepare returns + to_return = [curr_skinned_verts] + if return_keypoints: + # Get sapiens 308 keypoints + model_vert_joints = torch.cat( + [curr_skinned_verts, curr_joint_coords], dim=1 + ) # B x (num_verts + 127) x 3 + model_keypoints_pred = ( + ( + self.keypoint_mapping + @ model_vert_joints.permute(1, 0, 2).flatten(1, 2) + ) + .reshape(-1, model_vert_joints.shape[0], 3) + .permute(1, 0, 2) + ) + + if self.enable_hand_model: + # Zero out everything except for the right hand + model_keypoints_pred[:, :21] = 0 + model_keypoints_pred[:, 42:] = 0 + + to_return = to_return + [model_keypoints_pred] + if return_joint_coords: + to_return = to_return + [curr_joint_coords] + if return_model_params: + to_return = to_return + [model_params] + if return_joint_rotations: + to_return = to_return + [curr_joint_rots] + + if isinstance(to_return, list) and len(to_return) == 1: + return to_return[0] + else: + return tuple(to_return) + + def forward( + self, + x: torch.Tensor, + init_estimate: Optional[torch.Tensor] = None, + do_pcblend=True, + slim_keypoints=False, + ): + """ + Args: + x: pose token with shape [B, C], usually C=DECODER.DIM + init_estimate: [B, self.npose] + """ + batch_size = x.shape[0] + pred = self.proj(x) + if init_estimate is not None: + pred = pred + init_estimate + + # From pred, we want to pull out individual predictions. + + ## First, get globals + ### Global rotation is first 6. + count = 6 + global_rot_6d = pred[:, :count] + global_rot_rotmat = rot6d_to_rotmat(global_rot_6d) # B x 3 x 3 + global_rot_euler = roma.rotmat_to_euler("ZYX", global_rot_rotmat) # B x 3 + global_trans = torch.zeros_like(global_rot_euler) + + ## Next, get body pose. + ### Hold onto raw, continuous version for iterative correction. + pred_pose_cont = pred[:, count : count + self.body_cont_dim] + count += self.body_cont_dim + ### Convert to eulers (and trans) + pred_pose_euler = compact_cont_to_model_params_body(pred_pose_cont) + ### Zero-out hands + pred_pose_euler[:, mhr_param_hand_mask] = 0 + ### Zero-out jaw + pred_pose_euler[:, -3:] = 0 + + ## Get remaining parameters + pred_shape = pred[:, count : count + self.num_shape_comps] + count += self.num_shape_comps + pred_scale = pred[:, count : count + self.num_scale_comps] + count += self.num_scale_comps + pred_hand = pred[:, count : count + self.num_hand_comps * 2] + count += self.num_hand_comps * 2 + pred_face = pred[:, count : count + self.num_face_comps] * 0 + count += self.num_face_comps + + # Run everything through mhr + output = self.mhr_forward( + global_trans=global_trans, + global_rot=global_rot_euler, + body_pose_params=pred_pose_euler, + hand_pose_params=pred_hand, + scale_params=pred_scale, + shape_params=pred_shape, + expr_params=pred_face, + do_pcblend=do_pcblend, + return_keypoints=True, + return_joint_coords=True, + return_model_params=True, + return_joint_rotations=True, + ) + + # Some existing code to get joints and fix camera system + verts, j3d, jcoords, mhr_model_params, joint_global_rots = output + j3d = j3d[:, :70] # 308 --> 70 keypoints + + if verts is not None: + verts[..., [1, 2]] *= -1 # Camera system difference + j3d[..., [1, 2]] *= -1 # Camera system difference + if jcoords is not None: + jcoords[..., [1, 2]] *= -1 + + # Prep outputs + output = { + "pred_pose_raw": torch.cat( + [global_rot_6d, pred_pose_cont], dim=1 + ), # Both global rot and continuous pose + "pred_pose_rotmat": None, # This normally used for mhr pose param rotmat supervision. + "global_rot": global_rot_euler, + "body_pose": pred_pose_euler, # Unused during training + "shape": pred_shape, + "scale": pred_scale, + "hand": pred_hand, + "face": pred_face, + "pred_keypoints_3d": j3d.reshape(batch_size, -1, 3), + "pred_vertices": ( + verts.reshape(batch_size, -1, 3) if verts is not None else None + ), + "pred_joint_coords": ( + jcoords.reshape(batch_size, -1, 3) if jcoords is not None else None + ), + "faces": self.faces.cpu().numpy(), + "joint_global_rots": joint_global_rots, + "mhr_model_params": mhr_model_params, + } + + return output diff --git a/src/sam3d_body/models/meta_arch/__init__.py b/src/sam3d_body/models/meta_arch/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f2f3c6b9805253831e1774f9c1edfca0a7b304dc --- /dev/null +++ b/src/sam3d_body/models/meta_arch/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from .sam3d_body import SAM3DBody diff --git a/src/sam3d_body/models/meta_arch/base_lightning_module.py b/src/sam3d_body/models/meta_arch/base_lightning_module.py new file mode 100644 index 0000000000000000000000000000000000000000..5ba7edcfa26e60c16d7ad381d06248555d92dc42 --- /dev/null +++ b/src/sam3d_body/models/meta_arch/base_lightning_module.py @@ -0,0 +1,48 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import numpy as np +import pytorch_lightning as pl +from pytorch_lightning.loggers import TensorBoardLogger, WandbLogger + + +class BaseLightningModule(pl.LightningModule): + def _log_metric(self, name, value, step=None): + for logger in self.trainer.loggers: + if isinstance(logger, WandbLogger): + if step is not None: + logger.experiment.log({name: value, "step": step}) + else: + logger.experiment.log({name: value}) + elif isinstance(logger, TensorBoardLogger): + logger.experiment.add_scalar(name, value, step) + else: + raise ValueError(f"Unsupported logger: {logger}") + + def _log_image(self, name, img_tensor, dataformats="CHW", step_count=None): + """Log image tensor to both W&B and TensorBoard.""" + step = step_count if step_count is not None else self.global_step + for logger in self.trainer.loggers: + if isinstance(logger, WandbLogger): + import wandb + + img = img_tensor + if dataformats.upper() == "CHW": + # If in PyTorch format (C,H,W), convert to (H,W,C) for wandb + img = img_tensor.permute(1, 2, 0).cpu().numpy() + logger.experiment.log({name: wandb.Image(img), "step": step}) + elif isinstance(logger, TensorBoardLogger): + logger.experiment.add_image( + name, img_tensor, step, dataformats=dataformats + ) + else: + raise ValueError(f"Unsupported logger: {logger}") + + def _log_hist(self, name, array, step_count=None): + for logger in self.trainer.loggers: + if isinstance(logger, WandbLogger): + import wandb + + value = wandb.Histogram( + np_histogram=(array, np.arange(array.shape[0] + 1)), + ) + logger.experiment.log({name: value, "step": step_count}) diff --git a/src/sam3d_body/models/meta_arch/base_model.py b/src/sam3d_body/models/meta_arch/base_model.py new file mode 100644 index 0000000000000000000000000000000000000000..9f2ead2087da07937192fe2e25c96f6525e29883 --- /dev/null +++ b/src/sam3d_body/models/meta_arch/base_model.py @@ -0,0 +1,162 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +"""Define an abstract base model for consistent format input / processing / output.""" + +from abc import abstractmethod +from functools import partial + +import torch +from yacs.config import CfgNode + +from ..optim.fp16_utils import convert_module_to_f16, convert_to_fp16_safe +from .base_lightning_module import BaseLightningModule + + +class BaseModel(BaseLightningModule): + def __init__(self, cfg: CfgNode | None, **kwargs): + super().__init__() + + # Save hyperparameters + self.save_hyperparameters(logger=False) + self.cfg = cfg + + self._initialze_model(**kwargs) + + # Initialize attributes for image-based batch format + self._max_num_person = None + self._person_valid = None + + @abstractmethod + def _initialze_model(self, **kwargs) -> None: + pass + + def data_preprocess( + self, + inputs: torch.Tensor, + crop_width: bool = False, + is_full: bool = False, # whether for full_branch + crop_hand: int = 0, + ) -> torch.Tensor: + image_mean = self.image_mean if not is_full else self.full_image_mean + image_std = self.image_std if not is_full else self.full_image_std + + if inputs.max() > 1 and image_mean.max() <= 1.0: + inputs = inputs / 255.0 + elif inputs.max() <= 1.0 and image_mean.max() > 1: + inputs = inputs * 255.0 + batch_inputs = (inputs - image_mean) / image_std + + if crop_width: + if crop_hand > 0: + batch_inputs = batch_inputs[:, :, :, crop_hand:-crop_hand] + elif self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr", + "vit", + ]: + # ViT backbone assumes a different aspect ratio as input size + batch_inputs = batch_inputs[:, :, :, 32:-32] + elif self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr_512_384", + ]: + batch_inputs = batch_inputs[:, :, :, 64:-64] + else: + raise Exception + + return batch_inputs + + def _initialize_batch(self, batch: dict) -> None: + # Check whether the input batch is with format + # [batch_size, num_person, ...] + if batch["img"].dim() == 5: + self._batch_size, self._max_num_person = batch["img"].shape[:2] + self._person_valid = self._flatten_person(batch["person_valid"]) > 0 + else: + self._batch_size = batch["img"].shape[0] + self._max_num_person = 0 + self._person_valid = None + + def _flatten_person(self, x: torch.Tensor) -> torch.Tensor: + assert self._max_num_person is not None, "No max_num_person initialized" + + if self._max_num_person: + # Merge person crops to batch dimension + shape = x.shape + x = x.view(self._batch_size * self._max_num_person, *shape[2:]) + return x + + def _unflatten_person(self, x: torch.Tensor) -> torch.Tensor: + shape = x.shape + if self._max_num_person: + x = x.view(self._batch_size, self._max_num_person, *shape[1:]) + return x + + def _get_valid(self, x: torch.Tensor) -> torch.Tensor: + assert self._max_num_person is not None, "No max_num_person initialized" + + if self._person_valid is not None: + x = x[self._person_valid] + return x + + def _full_to_crop(self, batch: dict, pred_keypoints_2d: torch.Tensor) -> torch.Tensor: + """Convert full-image keypoints coordinates to crop and normalize to [-0.5. 0.5]""" + pred_keypoints_2d_cropped = torch.cat( + [pred_keypoints_2d, torch.ones_like(pred_keypoints_2d[:, :, [-1]])], dim=-1 + ) + affine_trans = self._flatten_person(batch["affine_trans"]).to(pred_keypoints_2d_cropped) + img_size = self._flatten_person(batch["img_size"]).unsqueeze(1) + pred_keypoints_2d_cropped = pred_keypoints_2d_cropped @ affine_trans.mT + pred_keypoints_2d_cropped = pred_keypoints_2d_cropped[..., :2] / img_size - 0.5 + + return pred_keypoints_2d_cropped + + def _cam_full_to_crop( + self, batch: dict, pred_cam_t: torch.Tensor, focal_length: torch.Tensor = None + ) -> torch.Tensor: + """Revert the camera translation from full to crop image space""" + num_person = batch["img"].shape[1] + cam_int = self._flatten_person(batch["cam_int"].unsqueeze(1).expand(-1, num_person, -1, -1).contiguous()) + bbox_center = self._flatten_person(batch["bbox_center"]) + bbox_size = self._flatten_person(batch["bbox_scale"])[:, 0] + input_size = self._flatten_person(batch["img_size"])[:, 0] + + tx, ty, tz = pred_cam_t[:, 0], pred_cam_t[:, 1], pred_cam_t[:, 2] + if focal_length is None: + focal_length = cam_int[:, 0, 0] + bs = 2 * focal_length / (tz + 1e-8) + + cx = 2 * (bbox_center[:, 0] - (cam_int[:, 0, 2])) / bs + cy = 2 * (bbox_center[:, 1] - (cam_int[:, 1, 2])) / bs + + crop_cam_t = torch.stack([tx - cx, ty - cy, tz * bbox_size / input_size], dim=-1) + return crop_cam_t + + def convert_to_fp16(self) -> torch.dtype: + """ + Convert the torso of the model to float16. + """ + fp16_type = torch.float16 if self.cfg.TRAIN.get("FP16_TYPE", "float16") == "float16" else torch.bfloat16 + + if hasattr(self, "backbone"): + self._set_fp16(self.backbone, fp16_type) + if hasattr(self, "full_encoder"): + self._set_fp16(self.full_encoder, fp16_type) + + if hasattr(self.backbone, "lhand_pos_embed"): + self.backbone.lhand_pos_embed.data = self.backbone.lhand_pos_embed.data.to(fp16_type) + + if hasattr(self.backbone, "rhand_pos_embed"): + self.backbone.rhand_pos_embed.data = self.backbone.rhand_pos_embed.data.to(fp16_type) + + return fp16_type + + def _set_fp16(self, module, fp16_type): + if hasattr(module, "pos_embed"): + module.apply(partial(convert_module_to_f16, dtype=fp16_type)) + module.pos_embed.data = module.pos_embed.data.to(fp16_type) + elif hasattr(module.encoder, "rope_embed"): + # DINOv3 + module.encoder.apply(partial(convert_to_fp16_safe, dtype=fp16_type)) + module.encoder.rope_embed = module.encoder.rope_embed.to(fp16_type) + else: + # DINOv2 + module.encoder.pos_embed.data = module.encoder.pos_embed.data.to(fp16_type) diff --git a/src/sam3d_body/models/meta_arch/sam3d_body.py b/src/sam3d_body/models/meta_arch/sam3d_body.py new file mode 100644 index 0000000000000000000000000000000000000000..d97269c694c2a3a78ddabaebb77ac3d627844d1a --- /dev/null +++ b/src/sam3d_body/models/meta_arch/sam3d_body.py @@ -0,0 +1,1728 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from collections.abc import Sequence +from dataclasses import dataclass +from typing import Any + +import numpy as np +import roma +import torch +import torch.nn as nn +import torch.nn.functional as F + +from sam3d_body.data.utils.prepare_batch import prepare_batch +from sam3d_body.models.decoders.prompt_encoder import PositionEmbeddingRandom +from sam3d_body.models.modules.mhr_utils import ( + fix_wrist_euler, + rotation_angle_difference, +) +from sam3d_body.utils import recursive_to +from sam3d_body.utils.logging import get_pylogger + +from ..backbones import create_backbone +from ..decoders import PromptEncoder, build_decoder, build_keypoint_sampler +from ..heads import build_head +from ..modules.camera_embed import CameraEncoder +from ..modules.transformer import FFN, MLP +from .base_model import BaseModel + +logger = get_pylogger(__name__) + + +# fmt: off +PROMPT_KEYPOINTS = { # keypoint_idx: prompt_idx + "mhr70": { + i: i for i in range(70) + }, # all 70 keypoints are supported for prompting +} +KEY_BODY = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 41, 62] # key body joints for prompting +KEY_RIGHT_HAND = list(range(21, 42)) +# fmt: on + + +@dataclass +class BodyPredContainer: + """Structured container for main body + optional hand inference outputs.""" + + pose_output: dict[str, Any] + batch_lhand: dict[str, Any] | None = None + batch_rhand: dict[str, Any] | None = None + lhand_output: dict[str, Any] | None = None + rhand_output: dict[str, Any] | None = None + + +class SAM3DBody(BaseModel): + pelvis_idx = [9, 10] # left_hip, right_hip + + def _initialze_model(self): + self.register_buffer("image_mean", torch.tensor(self.cfg.MODEL.IMAGE_MEAN).view(-1, 1, 1), False) + self.register_buffer("image_std", torch.tensor(self.cfg.MODEL.IMAGE_STD).view(-1, 1, 1), False) + + # Create backbone feature extractor for human crops + self.backbone = create_backbone(self.cfg.MODEL.BACKBONE.TYPE, self.cfg) + + # Create header for pose estimation output + self.head_pose = build_head(self.cfg, self.cfg.MODEL.PERSON_HEAD.POSE_TYPE) + self.head_pose.hand_pose_comps_ori = nn.Parameter(self.head_pose.hand_pose_comps.clone(), requires_grad=False) + self.head_pose.hand_pose_comps.data = torch.eye(54).to(self.head_pose.hand_pose_comps.data).float() + + # Initialize pose token with learnable params + # Note: bias/initial value should be zero-pose in cont, not all-zeros + self.init_pose = nn.Embedding(1, self.head_pose.npose) + + # Define header for hand pose estimation + self.head_pose_hand = build_head(self.cfg, self.cfg.MODEL.PERSON_HEAD.POSE_TYPE, enable_hand_model=True) + self.head_pose_hand.hand_pose_comps_ori = nn.Parameter( + self.head_pose_hand.hand_pose_comps.clone(), requires_grad=False + ) + self.head_pose_hand.hand_pose_comps.data = torch.eye(54).to(self.head_pose_hand.hand_pose_comps.data).float() + self.init_pose_hand = nn.Embedding(1, self.head_pose_hand.npose) + + self.head_camera = build_head(self.cfg, self.cfg.MODEL.PERSON_HEAD.CAMERA_TYPE) + self.init_camera = nn.Embedding(1, self.head_camera.ncam) + nn.init.zeros_(self.init_camera.weight) + + self.head_camera_hand = build_head( + self.cfg, + self.cfg.MODEL.PERSON_HEAD.CAMERA_TYPE, + default_scale_factor=self.cfg.MODEL.CAMERA_HEAD.get("DEFAULT_SCALE_FACTOR_HAND", 1.0), + ) + self.init_camera_hand = nn.Embedding(1, self.head_camera_hand.ncam) + nn.init.zeros_(self.init_camera_hand.weight) + + self.camera_type = "perspective" + + # Support conditioned information for decoder + cond_dim = 3 + init_dim = self.head_pose.npose + self.head_camera.ncam + cond_dim + self.init_to_token_mhr = nn.Linear(init_dim, self.cfg.MODEL.DECODER.DIM) + self.prev_to_token_mhr = nn.Linear(init_dim - cond_dim, self.cfg.MODEL.DECODER.DIM) + self.init_to_token_mhr_hand = nn.Linear(init_dim, self.cfg.MODEL.DECODER.DIM) + self.prev_to_token_mhr_hand = nn.Linear(init_dim - cond_dim, self.cfg.MODEL.DECODER.DIM) + + # Create prompt encoder + self.max_num_clicks = 0 + if self.cfg.MODEL.PROMPT_ENCODER.ENABLE: + self.max_num_clicks = self.cfg.MODEL.PROMPT_ENCODER.MAX_NUM_CLICKS + self.prompt_keypoints = PROMPT_KEYPOINTS[self.cfg.MODEL.PROMPT_ENCODER.PROMPT_KEYPOINTS] + + self.prompt_encoder = PromptEncoder( + embed_dim=self.backbone.embed_dims, # need to match backbone dims for PE + num_body_joints=len(set(self.prompt_keypoints.values())), + frozen=self.cfg.MODEL.PROMPT_ENCODER.get("frozen", False), + mask_embed_type=self.cfg.MODEL.PROMPT_ENCODER.get("MASK_EMBED_TYPE", None), + ) + self.prompt_to_token = nn.Linear(self.backbone.embed_dims, self.cfg.MODEL.DECODER.DIM) + + self.keypoint_prompt_sampler = build_keypoint_sampler( + self.cfg.MODEL.PROMPT_ENCODER.get("KEYPOINT_SAMPLER", {}), + prompt_keypoints=self.prompt_keypoints, + keybody_idx=( + KEY_BODY if not self.cfg.MODEL.PROMPT_ENCODER.get("SAMPLE_HAND", False) else KEY_RIGHT_HAND + ), + ) + # To keep track of prompting history + self.prompt_hist = np.zeros( + (len(set(self.prompt_keypoints.values())) + 2, self.max_num_clicks), + dtype=np.float32, + ) + + if self.cfg.MODEL.DECODER.FROZEN: + for param in self.prompt_to_token.parameters(): + param.requires_grad = False + + # Create promptable decoder + self.decoder = build_decoder(self.cfg.MODEL.DECODER, context_dim=self.backbone.embed_dims) + # shared config for the two decoders + self.decoder_hand = build_decoder(self.cfg.MODEL.DECODER, context_dim=self.backbone.embed_dims) + self.hand_pe_layer = PositionEmbeddingRandom(self.backbone.embed_dims // 2) + + # Manually convert the torso of the model to fp16. + if self.cfg.TRAIN.USE_FP16: + self.convert_to_fp16() + if self.cfg.TRAIN.get("FP16_TYPE", "float16") == "float16": + self.backbone_dtype = torch.float16 + else: + self.backbone_dtype = torch.bfloat16 + else: + self.backbone_dtype = torch.float32 + + self.ray_cond_emb = CameraEncoder( + self.backbone.embed_dim, + self.backbone.patch_size, + ) + self.ray_cond_emb_hand = CameraEncoder( + self.backbone.embed_dim, + self.backbone.patch_size, + ) + + self.keypoint_embedding_idxs = list(range(70)) + self.keypoint_embedding = nn.Embedding(len(self.keypoint_embedding_idxs), self.cfg.MODEL.DECODER.DIM) + self.keypoint_embedding_idxs_hand = list(range(70)) + self.keypoint_embedding_hand = nn.Embedding(len(self.keypoint_embedding_idxs_hand), self.cfg.MODEL.DECODER.DIM) + + if self.cfg.MODEL.DECODER.get("DO_HAND_DETECT_TOKENS", False): + self.hand_box_embedding = nn.Embedding(2, self.cfg.MODEL.DECODER.DIM) # for two hands + # decice if there is left or right hand inside the image + self.hand_cls_embed = nn.Linear(self.cfg.MODEL.DECODER.DIM, 2) + self.bbox_embed = MLP(self.cfg.MODEL.DECODER.DIM, self.cfg.MODEL.DECODER.DIM, 4, 3) + + self.keypoint_posemb_linear = FFN( + embed_dims=2, + feedforward_channels=self.cfg.MODEL.DECODER.DIM, + output_dims=self.cfg.MODEL.DECODER.DIM, + num_fcs=2, + add_identity=False, + ) + self.keypoint_posemb_linear_hand = FFN( + embed_dims=2, + feedforward_channels=self.cfg.MODEL.DECODER.DIM, + output_dims=self.cfg.MODEL.DECODER.DIM, + num_fcs=2, + add_identity=False, + ) + self.keypoint_feat_linear = nn.Linear(self.backbone.embed_dims, self.cfg.MODEL.DECODER.DIM) + self.keypoint_feat_linear_hand = nn.Linear(self.backbone.embed_dims, self.cfg.MODEL.DECODER.DIM) + + # Do all KPS + self.keypoint3d_embedding_idxs = list(range(70)) + self.keypoint3d_embedding = nn.Embedding(len(self.keypoint3d_embedding_idxs), self.cfg.MODEL.DECODER.DIM) + + # Assume always do full body for the hand decoder + self.keypoint3d_embedding_idxs_hand = list(range(70)) + self.keypoint3d_embedding_hand = nn.Embedding( + len(self.keypoint3d_embedding_idxs_hand), self.cfg.MODEL.DECODER.DIM + ) + + self.keypoint3d_posemb_linear = FFN( + embed_dims=3, + feedforward_channels=self.cfg.MODEL.DECODER.DIM, + output_dims=self.cfg.MODEL.DECODER.DIM, + num_fcs=2, + add_identity=False, + ) + self.keypoint3d_posemb_linear_hand = FFN( + embed_dims=3, + feedforward_channels=self.cfg.MODEL.DECODER.DIM, + output_dims=self.cfg.MODEL.DECODER.DIM, + num_fcs=2, + add_identity=False, + ) + + def _get_decoder_condition(self, batch: dict) -> torch.Tensor | None: + num_person = batch["img"].shape[1] + + if self.cfg.MODEL.DECODER.CONDITION_TYPE == "cliff": + # CLIFF-style condition info (cx/f, cy/f, b/f) + cx, cy = torch.chunk(self._flatten_person(batch["bbox_center"]), chunks=2, dim=-1) + img_w, img_h = torch.chunk(self._flatten_person(batch["ori_img_size"]), chunks=2, dim=-1) + b = self._flatten_person(batch["bbox_scale"])[:, [0]] + + focal_length = self._flatten_person( + batch["cam_int"].unsqueeze(1).expand(-1, num_person, -1, -1).contiguous() + )[:, 0, 0] + if not self.cfg.MODEL.DECODER.get("USE_INTRIN_CENTER", False): + condition_info = torch.cat([cx - img_w / 2.0, cy - img_h / 2.0, b], dim=-1) + else: + full_img_cxy = self._flatten_person( + batch["cam_int"].unsqueeze(1).expand(-1, num_person, -1, -1).contiguous() + )[:, [0, 1], [2, 2]] + condition_info = torch.cat([cx - full_img_cxy[:, [0]], cy - full_img_cxy[:, [1]], b], dim=-1) + condition_info[:, :2] = condition_info[:, :2] / focal_length.unsqueeze(-1) # [-1, 1] + condition_info[:, 2] = condition_info[:, 2] / focal_length # [-1, 1] + elif self.cfg.MODEL.DECODER.CONDITION_TYPE == "none": + return None + else: + raise NotImplementedError + + return condition_info.type(batch["img"].dtype) + + def forward_decoder( + self, + image_embeddings: torch.Tensor, + init_estimate: torch.Tensor | None = None, + keypoints: torch.Tensor | None = None, + prev_estimate: torch.Tensor | None = None, + condition_info: torch.Tensor | None = None, + batch=None, + ): + """ + Args: + image_embeddings: image features from the backbone, shape (B, C, H, W) + init_estimate: initial estimate to be refined on, shape (B, 1, C) + keypoints: optional prompt input, shape (B, N, 3), + 3 for coordinates (x,y) + label. + (x, y) should be normalized to range [0, 1]. + label==-1 indicates incorrect points, + label==-2 indicates invalid points + prev_estimate: optional prompt input, shape (B, 1, C), + previous estimate for pose refinement. + condition_info: optional condition information that is concatenated with + the input tokens, shape (B, c) + """ + batch_size = image_embeddings.shape[0] + + # Initial estimation for residual prediction. + if init_estimate is None: + init_pose = self.init_pose.weight.expand(batch_size, -1).unsqueeze(dim=1) + if hasattr(self, "init_camera"): + init_camera = self.init_camera.weight.expand(batch_size, -1).unsqueeze(dim=1) + + init_estimate = ( + init_pose if not hasattr(self, "init_camera") else torch.cat([init_pose, init_camera], dim=-1) + ) # This is basically pose & camera translation at the end. B x 1 x (404 + 3) + + init_input = ( + torch.cat([condition_info.view(batch_size, 1, -1), init_estimate], dim=-1) + if condition_info is not None + else init_estimate + ) # B x 1 x 410 (this is with the CLIFF condition) + token_embeddings = self.init_to_token_mhr(init_input).view(batch_size, 1, -1) # B x 1 x 1024 (linear layered) + + num_pose_token = token_embeddings.shape[1] + assert num_pose_token == 1 + + image_augment, token_augment, token_mask = None, None, None + if hasattr(self, "prompt_encoder") and keypoints is not None: + if prev_estimate is None: + # Use initial embedding if no previous embedding + prev_estimate = init_estimate + # Previous estimate w/o the CLIFF condition. + prev_embeddings = self.prev_to_token_mhr(prev_estimate).view( + batch_size, 1, -1 + ) # 407 -> B x 1 x 1024; linear layer-ed + + if self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr", + "vit", + "vit_b", + "vit_l", + ]: + # ViT backbone assumes a different aspect ratio as input size + image_augment = self.prompt_encoder.get_dense_pe((16, 16))[:, :, :, 2:-2] + elif self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr_512_384", + ]: + # ViT backbone assumes a different aspect ratio as input size + image_augment = self.prompt_encoder.get_dense_pe((32, 32))[:, :, :, 4:-4] + else: + image_augment = self.prompt_encoder.get_dense_pe(image_embeddings.shape[-2:]) # (1, C, H, W) + + image_embeddings = self.ray_cond_emb(image_embeddings, batch["ray_cond"]) + + # To start, keypoints is all [0, 0, -2]. The points get sent into self.pe_layer._pe_encoding, + # the labels determine the embedding weight (special one for -2, -1, then each of joint.) + prompt_embeddings, prompt_mask = self.prompt_encoder(keypoints=keypoints) # B x 1 x 1280 + prompt_embeddings = self.prompt_to_token(prompt_embeddings) # Linear layered: B x 1 x 1024 + + # Concatenate pose tokens and prompt embeddings as decoder input + token_embeddings = torch.cat( + [ + token_embeddings, + prev_embeddings, + prompt_embeddings, + ], + dim=1, + ) + + token_augment = torch.zeros_like(token_embeddings) + token_augment[:, [num_pose_token]] = prev_embeddings + token_augment[:, (num_pose_token + 1) :] = prompt_embeddings + token_mask = None + + if self.cfg.MODEL.DECODER.get("DO_HAND_DETECT_TOKENS", False): + # Put in a token for each hand + hand_det_emb_start_idx = token_embeddings.shape[1] + token_embeddings = torch.cat( + [ + token_embeddings, + self.hand_box_embedding.weight[None, :, :].repeat(batch_size, 1, 1), + ], + dim=1, + ) # B x 5 + 70 x 1024 + # No positional embeddings + token_augment = torch.cat( + [ + token_augment, + torch.zeros_like(token_embeddings[:, token_augment.shape[1] :, :]), + ], + dim=1, + ) # B x 5 + 70 x 1024 + + assert self.cfg.MODEL.DECODER.get("DO_KEYPOINT_TOKENS", False) + # Put in a token for each keypoint + kps_emb_start_idx = token_embeddings.shape[1] + token_embeddings = torch.cat( + [ + token_embeddings, + self.keypoint_embedding.weight[None, :, :].repeat(batch_size, 1, 1), + ], + dim=1, + ) # B x 3 + 70 x 1024 + # No positional embeddings + token_augment = torch.cat( + [ + token_augment, + torch.zeros_like(token_embeddings[:, token_augment.shape[1] :, :]), + ], + dim=1, + ) # B x 3 + 70 x 1024 + if self.cfg.MODEL.DECODER.get("DO_KEYPOINT3D_TOKENS", False): + # Put in a token for each keypoint + kps3d_emb_start_idx = token_embeddings.shape[1] + token_embeddings = torch.cat( + [ + token_embeddings, + self.keypoint3d_embedding.weight[None, :, :].repeat(batch_size, 1, 1), + ], + dim=1, + ) # B x 3 + 70 + 70 x 1024 + # No positional embeddings + token_augment = torch.cat( + [ + token_augment, + torch.zeros_like(token_embeddings[:, token_augment.shape[1] :, :]), + ], + dim=1, + ) # B x 3 + 70 + 70 x 1024 + + # We're doing intermediate model predictions + def token_to_pose_output_fn(tokens, prev_pose_output, layer_idx): + # Get the pose token + pose_token = tokens[:, 0] + + prev_pose = init_pose.view(batch_size, -1) + prev_camera = init_camera.view(batch_size, -1) + + # Get pose outputs + pose_output = self.head_pose(pose_token, prev_pose) + # Get Camera Translation + if hasattr(self, "head_camera"): + pred_cam = self.head_camera(pose_token, prev_camera) + pose_output["pred_cam"] = pred_cam + # Run camera projection + pose_output = self.camera_project(pose_output, batch) + + # Get 2D KPS in crop + pose_output["pred_keypoints_2d_cropped"] = self._full_to_crop( + batch, pose_output["pred_keypoints_2d"], self.body_batch_idx + ) + + return pose_output + + kp_token_update_fn = self.keypoint_token_update_fn + + # Now for 3D + kp3d_token_update_fn = self.keypoint3d_token_update_fn + + # Combine the 2D and 3D functionse + def keypoint_token_update_fn_comb(*args): + if kp_token_update_fn is not None: + args = kp_token_update_fn(kps_emb_start_idx, image_embeddings, *args) + if kp3d_token_update_fn is not None: + args = kp3d_token_update_fn(kps3d_emb_start_idx, *args) + return args + + pose_token, pose_output = self.decoder( + token_embeddings, + image_embeddings, + token_augment, + image_augment, + token_mask, + token_to_pose_output_fn=token_to_pose_output_fn, + keypoint_token_update_fn=keypoint_token_update_fn_comb, + ) + + if self.cfg.MODEL.DECODER.get("DO_HAND_DETECT_TOKENS", False): + return ( + pose_token[:, hand_det_emb_start_idx : hand_det_emb_start_idx + 2], + pose_output, + ) + else: + return pose_token, pose_output + + def forward_decoder_hand( + self, + image_embeddings: torch.Tensor, + init_estimate: torch.Tensor | None = None, + keypoints: torch.Tensor | None = None, + prev_estimate: torch.Tensor | None = None, + condition_info: torch.Tensor | None = None, + batch=None, + ): + """ + Args: + image_embeddings: image features from the backbone, shape (B, C, H, W) + init_estimate: initial estimate to be refined on, shape (B, 1, C) + keypoints: optional prompt input, shape (B, N, 3), + 3 for coordinates (x,y) + label. + (x, y) should be normalized to range [0, 1]. + label==-1 indicates incorrect points, + label==-2 indicates invalid points + prev_estimate: optional prompt input, shape (B, 1, C), + previous estimate for pose refinement. + condition_info: optional condition information that is concatenated with + the input tokens, shape (B, c) + """ + batch_size = image_embeddings.shape[0] + + # Initial estimation for residual prediction. + if init_estimate is None: + init_pose = self.init_pose_hand.weight.expand(batch_size, -1).unsqueeze(dim=1) + if hasattr(self, "init_camera_hand"): + init_camera = self.init_camera_hand.weight.expand(batch_size, -1).unsqueeze(dim=1) + + init_estimate = ( + init_pose if not hasattr(self, "init_camera_hand") else torch.cat([init_pose, init_camera], dim=-1) + ) # This is basically pose & camera translation at the end. B x 1 x (404 + 3) + + init_input = ( + torch.cat([condition_info.view(batch_size, 1, -1), init_estimate], dim=-1) + if condition_info is not None + else init_estimate + ) # B x 1 x 410 (this is with the CLIFF condition) + token_embeddings = self.init_to_token_mhr_hand(init_input).view( + batch_size, 1, -1 + ) # B x 1 x 1024 (linear layered) + num_pose_token = token_embeddings.shape[1] + + image_augment, token_augment, token_mask = None, None, None + if hasattr(self, "prompt_encoder") and keypoints is not None: + if prev_estimate is None: + # Use initial embedding if no previous embedding + prev_estimate = init_estimate + # Previous estimate w/o the CLIFF condition. + prev_embeddings = self.prev_to_token_mhr_hand(prev_estimate).view( + batch_size, 1, -1 + ) # 407 -> B x 1 x 1024; linear layer-ed + + if self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr", + "vit", + "vit_b", + "vit_l", + ]: + # ViT backbone assumes a different aspect ratio as input size + image_augment = self.hand_pe_layer((16, 16)).unsqueeze(0)[:, :, :, 2:-2] + elif self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr_512_384", + ]: + # ViT backbone assumes a different aspect ratio as input size + image_augment = self.hand_pe_layer((32, 32)).unsqueeze(0)[:, :, :, 4:-4] + else: + image_augment = self.hand_pe_layer(image_embeddings.shape[-2:]).unsqueeze(0) # (1, C, H, W) + + image_embeddings = self.ray_cond_emb_hand(image_embeddings, batch["ray_cond_hand"]) + + # To start, keypoints is all [0, 0, -2]. The points get sent into self.pe_layer._pe_encoding, + # the labels determine the embedding weight (special one for -2, -1, then each of joint.) + prompt_embeddings, prompt_mask = self.prompt_encoder(keypoints=keypoints) # B x 1 x 1280 + prompt_embeddings = self.prompt_to_token(prompt_embeddings) # Linear layered: B x 1 x 1024 + + # Concatenate pose tokens and prompt embeddings as decoder input + token_embeddings = torch.cat( + [ + token_embeddings, + prev_embeddings, + prompt_embeddings, + ], + dim=1, + ) + + token_augment = torch.zeros_like(token_embeddings) + token_augment[:, [num_pose_token]] = prev_embeddings + token_augment[:, (num_pose_token + 1) :] = prompt_embeddings + token_mask = None + + if self.cfg.MODEL.DECODER.get("DO_HAND_DETECT_TOKENS", False): + # Put in a token for each hand + hand_det_emb_start_idx = token_embeddings.shape[1] + token_embeddings = torch.cat( + [ + token_embeddings, + self.hand_box_embedding.weight[None, :, :].repeat(batch_size, 1, 1), + ], + dim=1, + ) # B x 5 + 70 x 1024 + # No positional embeddings + token_augment = torch.cat( + [ + token_augment, + torch.zeros_like(token_embeddings[:, token_augment.shape[1] :, :]), + ], + dim=1, + ) # B x 5 + 70 x 1024 + + assert self.cfg.MODEL.DECODER.get("DO_KEYPOINT_TOKENS", False) + # Put in a token for each keypoint + kps_emb_start_idx = token_embeddings.shape[1] + token_embeddings = torch.cat( + [ + token_embeddings, + self.keypoint_embedding_hand.weight[None, :, :].repeat(batch_size, 1, 1), + ], + dim=1, + ) # B x 3 + 70 x 1024 + # No positional embeddings + token_augment = torch.cat( + [ + token_augment, + torch.zeros_like(token_embeddings[:, token_augment.shape[1] :, :]), + ], + dim=1, + ) # B x 3 + 70 x 1024 + + if self.cfg.MODEL.DECODER.get("DO_KEYPOINT3D_TOKENS", False): + # Put in a token for each keypoint + kps3d_emb_start_idx = token_embeddings.shape[1] + token_embeddings = torch.cat( + [ + token_embeddings, + self.keypoint3d_embedding_hand.weight[None, :, :].repeat(batch_size, 1, 1), + ], + dim=1, + ) # B x 3 + 70 + 70 x 1024 + # No positional embeddings + token_augment = torch.cat( + [ + token_augment, + torch.zeros_like(token_embeddings[:, token_augment.shape[1] :, :]), + ], + dim=1, + ) # B x 3 + 70 + 70 x 1024 + + # We're doing intermediate model predictions + def token_to_pose_output_fn(tokens, prev_pose_output, layer_idx): + # Get the pose token + pose_token = tokens[:, 0] + + prev_pose = init_pose.view(batch_size, -1) + prev_camera = init_camera.view(batch_size, -1) + + # Get pose outputs + pose_output = self.head_pose_hand(pose_token, prev_pose) + + # Get Camera Translation + if hasattr(self, "head_camera_hand"): + pred_cam = self.head_camera_hand(pose_token, prev_camera) + pose_output["pred_cam"] = pred_cam + # Run camera projection + pose_output = self.camera_project_hand(pose_output, batch) + + # Get 2D KPS in crop + pose_output["pred_keypoints_2d_cropped"] = self._full_to_crop( + batch, pose_output["pred_keypoints_2d"], self.hand_batch_idx + ) + + return pose_output + + kp_token_update_fn = self.keypoint_token_update_fn_hand + + # Now for 3D + kp3d_token_update_fn = self.keypoint3d_token_update_fn_hand + + # Combine the 2D and 3D functionse + def keypoint_token_update_fn_comb(*args): + if kp_token_update_fn is not None: + args = kp_token_update_fn(kps_emb_start_idx, image_embeddings, *args) + if kp3d_token_update_fn is not None: + args = kp3d_token_update_fn(kps3d_emb_start_idx, *args) + return args + + pose_token, pose_output = self.decoder_hand( + token_embeddings, + image_embeddings, + token_augment, + image_augment, + token_mask, + token_to_pose_output_fn=token_to_pose_output_fn, + keypoint_token_update_fn=keypoint_token_update_fn_comb, + ) + + if self.cfg.MODEL.DECODER.get("DO_HAND_DETECT_TOKENS", False): + return ( + pose_token[:, hand_det_emb_start_idx : hand_det_emb_start_idx + 2], + pose_output, + ) + else: + return pose_token, pose_output + + @torch.no_grad() + def _get_keypoint_prompt(self, batch, pred_keypoints_2d, force_dummy=False): + if self.camera_type == "perspective": + pred_keypoints_2d = self._full_to_crop(batch, pred_keypoints_2d) + + gt_keypoints_2d = self._flatten_person(batch["keypoints_2d"]).clone() + + keypoint_prompt = self.keypoint_prompt_sampler.sample( + gt_keypoints_2d, + pred_keypoints_2d, + is_train=self.training, + force_dummy=force_dummy, + ) + return keypoint_prompt + + def _get_mask_prompt(self, batch, image_embeddings): + x_mask = self._flatten_person(batch["mask"]) + mask_embeddings, no_mask_embeddings = self.prompt_encoder.get_mask_embeddings( + x_mask, image_embeddings.shape[0], image_embeddings.shape[2:] + ) + if self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr", + "vit", + ]: + # ViT backbone assumes a different aspect ratio as input size + mask_embeddings = mask_embeddings[:, :, :, 2:-2] + elif self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr_512_384", + ]: + # for x2 resolution + mask_embeddings = mask_embeddings[:, :, :, 4:-4] + + mask_score = self._flatten_person(batch["mask_score"]).view(-1, 1, 1, 1) + mask_embeddings = torch.where( + mask_score > 0, + mask_score * mask_embeddings.to(image_embeddings), + no_mask_embeddings.to(image_embeddings), + ) + return mask_embeddings + + def _one_prompt_iter(self, batch, output, prev_prompt, full_output): + image_embeddings = output["image_embeddings"] + condition_info = output["condition_info"] + + if "mhr" in output and output["mhr"] is not None: + pose_output = output["mhr"] # body-only output + # Use previous estimate as initialization + prev_estimate = torch.cat( + [ + pose_output["pred_pose_raw"].detach(), # (B, 6) + pose_output["shape"].detach(), + pose_output["scale"].detach(), + pose_output["hand"].detach(), + pose_output["face"].detach(), + ], + dim=1, + ).unsqueeze(dim=1) + if hasattr(self, "init_camera"): + prev_estimate = torch.cat( + [prev_estimate, pose_output["pred_cam"].detach().unsqueeze(1)], + dim=-1, + ) + prev_shape = prev_estimate.shape[1:] + + pred_keypoints_2d = output["mhr"]["pred_keypoints_2d"].detach().clone() + kpt_shape = pred_keypoints_2d.shape[1:] + + if "mhr_hand" in output and output["mhr_hand"] is not None: + pose_output_hand = output["mhr_hand"] + # Use previous estimate as initialization + prev_estimate_hand = torch.cat( + [ + pose_output_hand["pred_pose_raw"].detach(), # (B, 6) + pose_output_hand["shape"].detach(), + pose_output_hand["scale"].detach(), + pose_output_hand["hand"].detach(), + pose_output_hand["face"].detach(), + ], + dim=1, + ).unsqueeze(dim=1) + if hasattr(self, "init_camera_hand"): + prev_estimate_hand = torch.cat( + [ + prev_estimate_hand, + pose_output_hand["pred_cam"].detach().unsqueeze(1), + ], + dim=-1, + ) + prev_shape = prev_estimate_hand.shape[1:] + + pred_keypoints_2d_hand = output["mhr_hand"]["pred_keypoints_2d"].detach().clone() + kpt_shape = pred_keypoints_2d_hand.shape[1:] + + all_prev_estimate = torch.zeros((image_embeddings.shape[0], *prev_shape), device=image_embeddings.device) + if "mhr" in output and output["mhr"] is not None: + all_prev_estimate[self.body_batch_idx] = prev_estimate + if "mhr_hand" in output and output["mhr_hand"] is not None: + all_prev_estimate[self.hand_batch_idx] = prev_estimate_hand + + # Get keypoint prompts + all_pred_keypoints_2d = torch.zeros((image_embeddings.shape[0], *kpt_shape), device=image_embeddings.device) + if "mhr" in output and output["mhr"] is not None: + all_pred_keypoints_2d[self.body_batch_idx] = pred_keypoints_2d + if "mhr_hand" in output and output["mhr_hand"] is not None: + all_pred_keypoints_2d[self.hand_batch_idx] = pred_keypoints_2d_hand + + keypoint_prompt = self._get_keypoint_prompt(batch, all_pred_keypoints_2d) + cur_keypoint_prompt = ( + torch.cat(prev_prompt + [keypoint_prompt], dim=1) if len(prev_prompt) else keypoint_prompt + ) # [B, 1, 3] + + pose_output, pose_output_hand = None, None + if len(self.body_batch_idx): + tokens_output, pose_output = self.forward_decoder( + image_embeddings[self.body_batch_idx], + init_estimate=None, # not recurring previous estimate + keypoints=cur_keypoint_prompt[self.body_batch_idx], + prev_estimate=all_prev_estimate[self.body_batch_idx], + condition_info=condition_info[self.body_batch_idx], + batch=batch, + full_output=None, + ) + pose_output = pose_output[-1] + + # Update prediction output + output.update( + { + "mhr": pose_output, + "mhr_hand": pose_output_hand, + } + ) + + return output, keypoint_prompt + + def _full_to_crop( + self, + batch: dict, + pred_keypoints_2d: torch.Tensor, + batch_idx: torch.Tensor | Sequence[int] | None = None, + ) -> torch.Tensor: + """Convert full-image keypoints coordinates to crop and normalize to [-0.5. 0.5]""" + pred_keypoints_2d_cropped = torch.cat( + [pred_keypoints_2d, torch.ones_like(pred_keypoints_2d[:, :, [-1]])], dim=-1 + ) + if batch_idx is not None: + affine_trans = self._flatten_person(batch["affine_trans"])[batch_idx].to(pred_keypoints_2d_cropped) + img_size = self._flatten_person(batch["img_size"])[batch_idx].unsqueeze(1) + else: + affine_trans = self._flatten_person(batch["affine_trans"]).to(pred_keypoints_2d_cropped) + img_size = self._flatten_person(batch["img_size"]).unsqueeze(1) + pred_keypoints_2d_cropped = pred_keypoints_2d_cropped @ affine_trans.mT + pred_keypoints_2d_cropped = pred_keypoints_2d_cropped[..., :2] / img_size - 0.5 + + return pred_keypoints_2d_cropped + + def camera_project(self, pose_output: dict, batch: dict) -> dict: + """ + Project 3D keypoints to 2D using the camera parameters. + Args: + pose_output (Dict): Dictionary containing the pose output. + batch (Dict): Dictionary containing the batch data. + Returns: + Dict: Dictionary containing the projected 2D keypoints. + """ + if hasattr(self, "head_camera"): + head_camera = self.head_camera + pred_cam = pose_output["pred_cam"] + else: + raise AssertionError("head_camera is not defined") + + cam_out = head_camera.perspective_projection( + pose_output["pred_keypoints_3d"], + pred_cam, + self._flatten_person(batch["bbox_center"])[self.body_batch_idx], + self._flatten_person(batch["bbox_scale"])[self.body_batch_idx, 0], + self._flatten_person(batch["ori_img_size"])[self.body_batch_idx], + self._flatten_person(batch["cam_int"].unsqueeze(1).expand(-1, batch["img"].shape[1], -1, -1).contiguous())[ + self.body_batch_idx + ], + use_intrin_center=self.cfg.MODEL.DECODER.get("USE_INTRIN_CENTER", False), + ) + + if pose_output.get("pred_vertices") is not None: + cam_out_vertices = head_camera.perspective_projection( + pose_output["pred_vertices"], + pred_cam, + self._flatten_person(batch["bbox_center"])[self.body_batch_idx], + self._flatten_person(batch["bbox_scale"])[self.body_batch_idx, 0], + self._flatten_person(batch["ori_img_size"])[self.body_batch_idx], + self._flatten_person( + batch["cam_int"].unsqueeze(1).expand(-1, batch["img"].shape[1], -1, -1).contiguous() + )[self.body_batch_idx], + use_intrin_center=self.cfg.MODEL.DECODER.get("USE_INTRIN_CENTER", False), + ) + pose_output["pred_keypoints_2d_verts"] = cam_out_vertices["pred_keypoints_2d"] + + pose_output.update(cam_out) + + return pose_output + + def camera_project_hand(self, pose_output: dict, batch: dict) -> dict: + """ + Project 3D keypoints to 2D using the camera parameters. + Args: + pose_output (Dict): Dictionary containing the pose output. + batch (Dict): Dictionary containing the batch data. + Returns: + Dict: Dictionary containing the projected 2D keypoints. + """ + if hasattr(self, "head_camera_hand"): + head_camera = self.head_camera_hand + pred_cam = pose_output["pred_cam"] + else: + raise AssertionError("head_camera_hand is not defined") + + cam_out = head_camera.perspective_projection( + pose_output["pred_keypoints_3d"], + pred_cam, + self._flatten_person(batch["bbox_center"])[self.hand_batch_idx], + self._flatten_person(batch["bbox_scale"])[self.hand_batch_idx, 0], + self._flatten_person(batch["ori_img_size"])[self.hand_batch_idx], + self._flatten_person(batch["cam_int"].unsqueeze(1).expand(-1, batch["img"].shape[1], -1, -1).contiguous())[ + self.hand_batch_idx + ], + use_intrin_center=self.cfg.MODEL.DECODER.get("USE_INTRIN_CENTER", False), + ) + + if pose_output.get("pred_vertices") is not None: + cam_out_vertices = head_camera.perspective_projection( + pose_output["pred_vertices"], + pred_cam, + self._flatten_person(batch["bbox_center"])[self.hand_batch_idx], + self._flatten_person(batch["bbox_scale"])[self.hand_batch_idx, 0], + self._flatten_person(batch["ori_img_size"])[self.hand_batch_idx], + self._flatten_person( + batch["cam_int"].unsqueeze(1).expand(-1, batch["img"].shape[1], -1, -1).contiguous() + )[self.hand_batch_idx], + use_intrin_center=self.cfg.MODEL.DECODER.get("USE_INTRIN_CENTER", False), + ) + pose_output["pred_keypoints_2d_verts"] = cam_out_vertices["pred_keypoints_2d"] + + pose_output.update(cam_out) + + return pose_output + + def get_ray_condition(self, batch): + B, N, _, H, W = batch["img"].shape + meshgrid_xy = ( + torch.stack(torch.meshgrid(torch.arange(H), torch.arange(W), indexing="xy"), dim=2)[None, None, :, :, :] + .repeat(B, N, 1, 1, 1) + .cuda() + ) # B x N x H x W x 2 + meshgrid_xy = meshgrid_xy / batch["affine_trans"][:, :, None, None, [0, 1], [0, 1]] + meshgrid_xy = ( + meshgrid_xy + - batch["affine_trans"][:, :, None, None, [0, 1], [2, 2]] + / batch["affine_trans"][:, :, None, None, [0, 1], [0, 1]] + ) + + # Subtract out center & normalize to be rays + meshgrid_xy = meshgrid_xy - batch["cam_int"][:, None, None, None, [0, 1], [2, 2]] + meshgrid_xy = meshgrid_xy / batch["cam_int"][:, None, None, None, [0, 1], [0, 1]] + + return meshgrid_xy.permute(0, 1, 4, 2, 3).to(batch["img"].dtype) # This is B x num_person x 2 x H x W + + def forward_pose_branch(self, batch: dict) -> dict: + """Run a forward pass for the crop-image (pose) branch.""" + batch_size, num_person = batch["img"].shape[:2] + + # Forward backbone encoder + x = self.data_preprocess( + self._flatten_person(batch["img"]), + crop_width=( + self.cfg.MODEL.BACKBONE.TYPE + in [ + "vit_hmr", + "vit", + "vit_b", + "vit_l", + "vit_hmr_512_384", + ] + ), + ) + + # Optionally get ray conditioining + ray_cond = self.get_ray_condition(batch) # This is B x num_person x 2 x H x W + ray_cond = self._flatten_person(ray_cond) + if self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr", + "vit", + "vit_b", + "vit_l", + ]: + ray_cond = ray_cond[:, :, :, 32:-32] + elif self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr_512_384", + ]: + ray_cond = ray_cond[:, :, :, 64:-64] + + if len(self.body_batch_idx): + batch["ray_cond"] = ray_cond[self.body_batch_idx].clone() + if len(self.hand_batch_idx): + batch["ray_cond_hand"] = ray_cond[self.hand_batch_idx].clone() + ray_cond = None + + image_embeddings = self.backbone(x.type(self.backbone_dtype), extra_embed=ray_cond) # (B, C, H, W) + + if isinstance(image_embeddings, tuple): + image_embeddings = image_embeddings[-1] + image_embeddings = image_embeddings.type(x.dtype) + + # Mask condition if available + if self.cfg.MODEL.PROMPT_ENCODER.get("MASK_EMBED_TYPE", None) is not None: + # v1: non-iterative mask conditioning + if self.cfg.MODEL.PROMPT_ENCODER.get("MASK_PROMPT", "v1") == "v1": + mask_embeddings = self._get_mask_prompt(batch, image_embeddings) + image_embeddings = image_embeddings + mask_embeddings + else: + raise NotImplementedError + + # Prepare input for promptable decoder + condition_info = self._get_decoder_condition(batch) + + # Initial estimate with a dummy prompt + keypoints_prompt = torch.zeros((batch_size * num_person, 1, 3)).to(batch["img"]) + keypoints_prompt[:, :, -1] = -2 + + # Forward promptable decoder to get updated pose tokens and regression output + pose_output, pose_output_hand = None, None + if len(self.body_batch_idx): + tokens_output, pose_output = self.forward_decoder( + image_embeddings[self.body_batch_idx], + init_estimate=None, + keypoints=keypoints_prompt[self.body_batch_idx], + prev_estimate=None, + condition_info=condition_info[self.body_batch_idx], + batch=batch, + ) + pose_output = pose_output[-1] + if len(self.hand_batch_idx): + tokens_output_hand, pose_output_hand = self.forward_decoder_hand( + image_embeddings[self.hand_batch_idx], + init_estimate=None, + keypoints=keypoints_prompt[self.hand_batch_idx], + prev_estimate=None, + condition_info=condition_info[self.hand_batch_idx], + batch=batch, + ) + pose_output_hand = pose_output_hand[-1] + + output = { + # "pose_token": pose_token, + "mhr": pose_output, # mhr prediction output + "mhr_hand": pose_output_hand, # mhr prediction output + "condition_info": condition_info, + "image_embeddings": image_embeddings, + } + + if self.cfg.MODEL.DECODER.get("DO_HAND_DETECT_TOKENS", False): + if len(self.body_batch_idx): + output_hand_box_tokens = tokens_output + hand_coords = self.bbox_embed(output_hand_box_tokens).sigmoid() # x1, y1, w, h for body samples, 0 ~ 1 + hand_logits = self.hand_cls_embed(output_hand_box_tokens) + + output["mhr"]["hand_box"] = hand_coords + output["mhr"]["hand_logits"] = hand_logits + + if len(self.hand_batch_idx): + output_hand_box_tokens_hand_batch = tokens_output_hand + + hand_coords_hand_batch = self.bbox_embed( + output_hand_box_tokens_hand_batch + ).sigmoid() # x1, y1, w, h for hand samples + hand_logits_hand_batch = self.hand_cls_embed(output_hand_box_tokens_hand_batch) + + output["mhr_hand"]["hand_box"] = hand_coords_hand_batch + output["mhr_hand"]["hand_logits"] = hand_logits_hand_batch + + return output + + def forward_step(self, batch: dict, decoder_type: str = "body") -> dict: + batch_size, num_person = batch["img"].shape[:2] + + if decoder_type == "body": + self.hand_batch_idx = [] + self.body_batch_idx = list(range(batch_size * num_person)) + elif decoder_type == "hand": + self.hand_batch_idx = list(range(batch_size * num_person)) + self.body_batch_idx = [] + else: + ValueError("Invalid decoder type: ", decoder_type) + + # Crop-image (pose) branch + pose_output = self.forward_pose_branch(batch) + + return pose_output + + def run_inference( + self, + img, + batch: dict, + inference_type: str = "full", + transform_hand: Any = None, + thresh_wrist_angle=1.4, + ): + """ + Run 3DB inference (optionally with hand detector). + + inference_type: + - full: full-body inference with both body and hand decoders + - body: inference with body decoder only (still full-body output) + - hand: inference with hand decoder only (only hand output) + """ + + height, width = img.shape[:2] + cam_int = batch["cam_int"].clone() + + if inference_type == "body": + pose_output = self.forward_step(batch, decoder_type="body") + return BodyPredContainer(pose_output=pose_output) + elif inference_type == "hand": + pose_output = self.forward_step(batch, decoder_type="hand") + return BodyPredContainer(pose_output=pose_output) + elif inference_type != "full": + raise ValueError("Invalid inference type: ", inference_type) + + # Step 1. For full-body inference, we first inference with the body decoder. + pose_output = self.forward_step(batch, decoder_type="body") + left_xyxy, right_xyxy = self._get_hand_box(pose_output, batch) + ori_local_wrist_rotmat = roma.euler_to_rotmat( + "XZY", + pose_output["mhr"]["body_pose"][:, [41, 43, 42, 31, 33, 32]].unflatten(1, (2, 3)), + ) + + # Step 2. Re-run with each hand + ## Left... Flip image & box + flipped_img = img[:, ::-1] + tmp = left_xyxy.copy() + left_xyxy[:, 0] = width - tmp[:, 2] - 1 + left_xyxy[:, 2] = width - tmp[:, 0] - 1 + + batch_lhand = prepare_batch(flipped_img, transform_hand, left_xyxy, cam_int=cam_int.clone()) + batch_lhand = recursive_to(batch_lhand, "cuda") + lhand_output = self.forward_step(batch_lhand, decoder_type="hand") + + # Unflip output + ## Flip scale + ### Get MHR values + scale_r_hands_mean = self.head_pose.scale_mean[8].item() + scale_l_hands_mean = self.head_pose.scale_mean[9].item() + scale_r_hands_std = self.head_pose.scale_comps[8, 8].item() + scale_l_hands_std = self.head_pose.scale_comps[9, 9].item() + ### Apply + lhand_output["mhr_hand"]["scale"][:, 9] = ( + (scale_r_hands_mean + scale_r_hands_std * lhand_output["mhr_hand"]["scale"][:, 8]) - scale_l_hands_mean + ) / scale_l_hands_std + ## Get the right hand global rotation, flip it, put it in as left. + lhand_output["mhr_hand"]["joint_global_rots"][:, 78] = lhand_output["mhr_hand"]["joint_global_rots"][ + :, 42 + ].clone() + lhand_output["mhr_hand"]["joint_global_rots"][:, 78, [1, 2], :] *= -1 + ### Flip hand pose + lhand_output["mhr_hand"]["hand"][:, :54] = lhand_output["mhr_hand"]["hand"][:, 54:] + ### Unflip box + batch_lhand["bbox_center"][:, :, 0] = width - batch_lhand["bbox_center"][:, :, 0] - 1 + + ## Right... + batch_rhand = prepare_batch(img, transform_hand, right_xyxy, cam_int=cam_int.clone()) + batch_rhand = recursive_to(batch_rhand, "cuda") + rhand_output = self.forward_step(batch_rhand, decoder_type="hand") + + # Step 3. replace hand pose estimation from the body decoder. + ## CRITERIA 1: LOCAL WRIST POSE DIFFERENCE + joint_rotations = pose_output["mhr"]["joint_global_rots"] + ### Get lowarm + lowarm_joint_idxs = torch.LongTensor([76, 40]).cuda() # left, right + lowarm_joint_rotations = joint_rotations[:, lowarm_joint_idxs] # B x 2 x 3 x 3 + ### Get zero-wrist pose + wrist_twist_joint_idxs = torch.LongTensor([77, 41]).cuda() # left, right + wrist_zero_rot_pose = lowarm_joint_rotations @ self.head_pose.joint_rotation[wrist_twist_joint_idxs] + ### Get globals from left & right + left_joint_global_rots = lhand_output["mhr_hand"]["joint_global_rots"] + right_joint_global_rots = rhand_output["mhr_hand"]["joint_global_rots"] + pred_global_wrist_rotmat = torch.stack( + [ + left_joint_global_rots[:, 78], + right_joint_global_rots[:, 42], + ], + dim=1, + ) + ### Get the local poses that lead to the wrist being pred_global_wrist_rotmat + fused_local_wrist_rotmat = torch.einsum("kabc,kabd->kadc", pred_global_wrist_rotmat, wrist_zero_rot_pose) + angle_difference = rotation_angle_difference(ori_local_wrist_rotmat, fused_local_wrist_rotmat) # B x 2 x 3 x3 + angle_difference_valid_mask = angle_difference < thresh_wrist_angle + + ## CRITERIA 2: hand box size + hand_box_size_thresh = 64 + hand_box_size_valid_mask = torch.stack( + [ + (batch_lhand["bbox_scale"].flatten(0, 1) > hand_box_size_thresh).all(dim=1), + (batch_rhand["bbox_scale"].flatten(0, 1) > hand_box_size_thresh).all(dim=1), + ], + dim=1, + ) + + ## CRITERIA 3: all hand 2D KPS (including wrist) inside of box. + hand_kps2d_thresh = 0.5 + hand_kps2d_valid_mask = torch.stack( + [ + lhand_output["mhr_hand"]["pred_keypoints_2d_cropped"].abs().amax(dim=(1, 2)) < hand_kps2d_thresh, + rhand_output["mhr_hand"]["pred_keypoints_2d_cropped"].abs().amax(dim=(1, 2)) < hand_kps2d_thresh, + ], + dim=1, + ) + + ## CRITERIA 4: 2D wrist distance. + hand_wrist_kps2d_thresh = 0.25 + kps_right_wrist_idx = 41 + kps_left_wrist_idx = 62 + right_kps_full = rhand_output["mhr_hand"]["pred_keypoints_2d"][:, [kps_right_wrist_idx]].clone() + left_kps_full = lhand_output["mhr_hand"]["pred_keypoints_2d"][:, [kps_right_wrist_idx]].clone() + left_kps_full[:, :, 0] = width - left_kps_full[:, :, 0] - 1 # Flip left hand + body_right_kps_full = pose_output["mhr"]["pred_keypoints_2d"][:, [kps_right_wrist_idx]].clone() + body_left_kps_full = pose_output["mhr"]["pred_keypoints_2d"][:, [kps_left_wrist_idx]].clone() + right_kps_dist = (right_kps_full - body_right_kps_full).flatten(0, 1).norm(dim=-1) / batch_lhand[ + "bbox_scale" + ].flatten(0, 1)[:, 0] + left_kps_dist = (left_kps_full - body_left_kps_full).flatten(0, 1).norm(dim=-1) / batch_rhand[ + "bbox_scale" + ].flatten(0, 1)[:, 0] + hand_wrist_kps2d_valid_mask = torch.stack( + [ + left_kps_dist < hand_wrist_kps2d_thresh, + right_kps_dist < hand_wrist_kps2d_thresh, + ], + dim=1, + ) + ## Left-right + hand_valid_mask = ( + angle_difference_valid_mask & hand_box_size_valid_mask & hand_kps2d_valid_mask & hand_wrist_kps2d_valid_mask + ) + + # Keypoint prompting with the body decoder. + # We use the wrist location from the hand decoder and the elbow location + # from the body decoder as prompts to get an updated body pose estimation. + batch_size, num_person = batch["img"].shape[:2] + self.hand_batch_idx = [] + self.body_batch_idx = list(range(batch_size * num_person)) + + ## Get right & left wrist keypoints from crops; full image. Each are B x 1 x 2 + kps_right_wrist_idx = 41 + kps_left_wrist_idx = 62 + right_kps_full = rhand_output["mhr_hand"]["pred_keypoints_2d"][:, [kps_right_wrist_idx]].clone() + left_kps_full = lhand_output["mhr_hand"]["pred_keypoints_2d"][:, [kps_right_wrist_idx]].clone() + left_kps_full[:, :, 0] = width - left_kps_full[:, :, 0] - 1 # Flip left hand + + # Next, get them to crop-normalized space. + right_kps_crop = self._full_to_crop(batch, right_kps_full) + left_kps_crop = self._full_to_crop(batch, left_kps_full) + + # Get right & left elbow keypoints from crops; full image. Each are B x 1 x 2 + kps_right_elbow_idx = 8 + kps_left_elbow_idx = 7 + right_kps_elbow_full = pose_output["mhr"]["pred_keypoints_2d"][:, [kps_right_elbow_idx]].clone() + left_kps_elbow_full = pose_output["mhr"]["pred_keypoints_2d"][:, [kps_left_elbow_idx]].clone() + + # Next, get them to crop-normalized space. + right_kps_elbow_crop = self._full_to_crop(batch, right_kps_elbow_full) + left_kps_elbow_crop = self._full_to_crop(batch, left_kps_elbow_full) + + # Assemble them into keypoint prompts + keypoint_prompt = torch.cat( + [right_kps_crop, left_kps_crop, right_kps_elbow_crop, left_kps_elbow_crop], + dim=1, + ) + keypoint_prompt = torch.cat([keypoint_prompt, keypoint_prompt[..., [-1]]], dim=-1) + keypoint_prompt[:, 0, -1] = kps_right_wrist_idx + keypoint_prompt[:, 1, -1] = kps_left_wrist_idx + keypoint_prompt[:, 2, -1] = kps_right_elbow_idx + keypoint_prompt[:, 3, -1] = kps_left_elbow_idx + + if keypoint_prompt.shape[0] > 1: + # Replace invalid keypoints to dummy prompts + invalid_prompt = ( + (keypoint_prompt[..., 0] < -0.5) + | (keypoint_prompt[..., 0] > 0.5) + | (keypoint_prompt[..., 1] < -0.5) + | (keypoint_prompt[..., 1] > 0.5) + | (~hand_valid_mask[..., [1, 0, 1, 0]]) + ).unsqueeze(-1) + dummy_prompt = torch.zeros((1, 1, 3)).to(keypoint_prompt) + dummy_prompt[:, :, -1] = -2 + keypoint_prompt[:, :, :2] = torch.clamp( + keypoint_prompt[:, :, :2] + 0.5, min=0.0, max=1.0 + ) # [-0.5, 0.5] --> [0, 1] + keypoint_prompt = torch.where(invalid_prompt, dummy_prompt, keypoint_prompt) + else: + # Only keep valid keypoints + valid_keypoint = ( + torch.all( + (keypoint_prompt[:, :, :2] > -0.5) & (keypoint_prompt[:, :, :2] < 0.5), + dim=2, + ) + & hand_valid_mask[..., [1, 0, 1, 0]] + ).squeeze() + keypoint_prompt = keypoint_prompt[:, valid_keypoint] + keypoint_prompt[:, :, :2] = torch.clamp( + keypoint_prompt[:, :, :2] + 0.5, min=0.0, max=1.0 + ) # [-0.5, 0.5] --> [0, 1] + + if keypoint_prompt.numel() != 0: + pose_output, _ = self.run_keypoint_prompt(batch, pose_output, keypoint_prompt) + + ############################################################################## + + # Drop in hand pose + left_hand_pose_params = lhand_output["mhr_hand"]["hand"][:, :54] + right_hand_pose_params = rhand_output["mhr_hand"]["hand"][:, 54:] + updated_hand_pose = torch.cat([left_hand_pose_params, right_hand_pose_params], dim=1) + + # Drop in hand scales + updated_scale = pose_output["mhr"]["scale"].clone() + updated_scale[:, 9] = lhand_output["mhr_hand"]["scale"][:, 9] + updated_scale[:, 8] = rhand_output["mhr_hand"]["scale"][:, 8] + updated_scale[:, 18:] = ( + lhand_output["mhr_hand"]["scale"][:, 18:] + rhand_output["mhr_hand"]["scale"][:, 18:] + ) / 2 + + # Update hand shape + updated_shape = pose_output["mhr"]["shape"].clone() + updated_shape[:, 40:] = ( + lhand_output["mhr_hand"]["shape"][:, 40:] + rhand_output["mhr_hand"]["shape"][:, 40:] + ) / 2 + + ############################ Doing IK ############################ + + # First, forward just FK + joint_rotations = self.head_pose.mhr_forward( + global_trans=pose_output["mhr"]["global_rot"] * 0, + global_rot=pose_output["mhr"]["global_rot"], + body_pose_params=pose_output["mhr"]["body_pose"], + hand_pose_params=updated_hand_pose, + scale_params=updated_scale, + shape_params=updated_shape, + expr_params=pose_output["mhr"]["face"], + return_joint_rotations=True, + )[1] + + # Get lowarm + lowarm_joint_idxs = torch.LongTensor([76, 40]).cuda() # left, right + lowarm_joint_rotations = joint_rotations[:, lowarm_joint_idxs] # B x 2 x 3 x 3 + + # Get zero-wrist pose + wrist_twist_joint_idxs = torch.LongTensor([77, 41]).cuda() # left, right + wrist_zero_rot_pose = lowarm_joint_rotations @ self.head_pose.joint_rotation[wrist_twist_joint_idxs] + + # Get globals from left & right + left_joint_global_rots = lhand_output["mhr_hand"]["joint_global_rots"] + right_joint_global_rots = rhand_output["mhr_hand"]["joint_global_rots"] + pred_global_wrist_rotmat = torch.stack( + [ + left_joint_global_rots[:, 78], + right_joint_global_rots[:, 42], + ], + dim=1, + ) + + # Now we want to get the local poses that lead to the wrist being pred_global_wrist_rotmat + fused_local_wrist_rotmat = torch.einsum("kabc,kabd->kadc", pred_global_wrist_rotmat, wrist_zero_rot_pose) + wrist_xzy = fix_wrist_euler(roma.rotmat_to_euler("XZY", fused_local_wrist_rotmat)) + + # Put it in. + angle_difference = rotation_angle_difference(ori_local_wrist_rotmat, fused_local_wrist_rotmat) # B x 2 x 3 x3 + valid_angle = angle_difference < thresh_wrist_angle + valid_angle = valid_angle & hand_valid_mask + valid_angle = valid_angle.unsqueeze(-1) + + body_pose = pose_output["mhr"]["body_pose"][:, [41, 43, 42, 31, 33, 32]].unflatten(1, (2, 3)) + updated_body_pose = torch.where(valid_angle, wrist_xzy, body_pose) + pose_output["mhr"]["body_pose"][:, [41, 43, 42, 31, 33, 32]] = updated_body_pose.flatten(1, 2) + + hand_pose = pose_output["mhr"]["hand"].unflatten(1, (2, 54)) + pose_output["mhr"]["hand"] = torch.where( + valid_angle, updated_hand_pose.unflatten(1, (2, 54)), hand_pose + ).flatten(1, 2) + + hand_scale = torch.stack( + [pose_output["mhr"]["scale"][:, 9], pose_output["mhr"]["scale"][:, 8]], + dim=1, + ) + updated_hand_scale = torch.stack([updated_scale[:, 9], updated_scale[:, 8]], dim=1) + masked_hand_scale = torch.where(valid_angle.squeeze(-1), updated_hand_scale, hand_scale) + pose_output["mhr"]["scale"][:, 9] = masked_hand_scale[:, 0] + pose_output["mhr"]["scale"][:, 8] = masked_hand_scale[:, 1] + + # Replace shared shape and scale + pose_output["mhr"]["scale"][:, 18:] = torch.where( + valid_angle.squeeze(-1).sum(dim=1, keepdim=True) > 0, + ( + lhand_output["mhr_hand"]["scale"][:, 18:] * valid_angle.squeeze(-1)[:, [0]] + + rhand_output["mhr_hand"]["scale"][:, 18:] * valid_angle.squeeze(-1)[:, [1]] + ) + / (valid_angle.squeeze(-1).sum(dim=1, keepdim=True) + 1e-8), + pose_output["mhr"]["scale"][:, 18:], + ) + pose_output["mhr"]["shape"][:, 40:] = torch.where( + valid_angle.squeeze(-1).sum(dim=1, keepdim=True) > 0, + ( + lhand_output["mhr_hand"]["shape"][:, 40:] * valid_angle.squeeze(-1)[:, [0]] + + rhand_output["mhr_hand"]["shape"][:, 40:] * valid_angle.squeeze(-1)[:, [1]] + ) + / (valid_angle.squeeze(-1).sum(dim=1, keepdim=True) + 1e-8), + pose_output["mhr"]["shape"][:, 40:], + ) + + ######################################################## + + # Re-run forward + with torch.no_grad(): + verts, j3d, jcoords, mhr_model_params, joint_global_rots = self.head_pose.mhr_forward( + global_trans=pose_output["mhr"]["global_rot"] * 0, + global_rot=pose_output["mhr"]["global_rot"], + body_pose_params=pose_output["mhr"]["body_pose"], + hand_pose_params=pose_output["mhr"]["hand"], + scale_params=pose_output["mhr"]["scale"], + shape_params=pose_output["mhr"]["shape"], + expr_params=pose_output["mhr"]["face"], + return_keypoints=True, + return_joint_coords=True, + return_model_params=True, + return_joint_rotations=True, + ) + j3d = j3d[:, :70] # 308 --> 70 keypoints + verts[..., [1, 2]] *= -1 # Camera system difference + j3d[..., [1, 2]] *= -1 # Camera system difference + jcoords[..., [1, 2]] *= -1 + pose_output["mhr"]["pred_keypoints_3d"] = j3d + pose_output["mhr"]["pred_vertices"] = verts + pose_output["mhr"]["pred_joint_coords"] = jcoords + pose_output["mhr"]["pred_pose_raw"][...] = 0 # pred_pose_raw is not valid anymore + pose_output["mhr"]["mhr_model_params"] = mhr_model_params + + ######################################################## + # Project to 2D + pred_keypoints_3d_proj = pose_output["mhr"]["pred_keypoints_3d"] + pose_output["mhr"]["pred_cam_t"][:, None, :] + pred_keypoints_3d_proj[:, :, [0, 1]] *= pose_output["mhr"]["focal_length"][:, None, None] + pred_keypoints_3d_proj[:, :, [0, 1]] = ( + pred_keypoints_3d_proj[:, :, [0, 1]] + + torch.FloatTensor([width / 2, height / 2]).to(pred_keypoints_3d_proj)[None, None, :] + * pred_keypoints_3d_proj[:, :, [2]] + ) + pred_keypoints_3d_proj[:, :, :2] = pred_keypoints_3d_proj[:, :, :2] / pred_keypoints_3d_proj[:, :, [2]] + pose_output["mhr"]["pred_keypoints_2d"] = pred_keypoints_3d_proj[:, :, :2] + + return BodyPredContainer( + pose_output=pose_output, + batch_lhand=batch_lhand, + batch_rhand=batch_rhand, + lhand_output=lhand_output, + rhand_output=rhand_output, + ) + + def run_keypoint_prompt(self, batch, output, keypoint_prompt): + image_embeddings = output["image_embeddings"] + condition_info = output["condition_info"] + pose_output = output["mhr"] # body-only output + # Use previous estimate as initialization + prev_estimate = torch.cat( + [ + pose_output["pred_pose_raw"].detach(), # (B, 6) + pose_output["shape"].detach(), + pose_output["scale"].detach(), + pose_output["hand"].detach(), + pose_output["face"].detach(), + ], + dim=1, + ).unsqueeze(dim=1) + if hasattr(self, "init_camera"): + prev_estimate = torch.cat( + [prev_estimate, pose_output["pred_cam"].detach().unsqueeze(1)], + dim=-1, + ) + + tokens_output, pose_output = self.forward_decoder( + image_embeddings, + init_estimate=None, # not recurring previous estimate + keypoints=keypoint_prompt, + prev_estimate=prev_estimate, + condition_info=condition_info, + batch=batch, + ) + pose_output = pose_output[-1] + + output.update({"mhr": pose_output}) + return output, keypoint_prompt + + def _get_hand_box(self, pose_output, batch): + """Get hand bbox from the hand detector""" + pred_left_hand_box = pose_output["mhr"]["hand_box"][:, 0].detach().cpu().numpy() * self.cfg.MODEL.IMAGE_SIZE[0] + pred_right_hand_box = pose_output["mhr"]["hand_box"][:, 1].detach().cpu().numpy() * self.cfg.MODEL.IMAGE_SIZE[0] + + # Change boxes into squares + batch["left_center"] = pred_left_hand_box[:, :2] + batch["left_scale"] = pred_left_hand_box[:, 2:].max(axis=1, keepdims=True).repeat(2, axis=1) + batch["right_center"] = pred_right_hand_box[:, :2] + batch["right_scale"] = pred_right_hand_box[:, 2:].max(axis=1, keepdims=True).repeat(2, axis=1) + + # Crop to full. batch["affine_trans"] is full-to-crop, right application + batch["left_scale"] = batch["left_scale"] / batch["affine_trans"][0, :, 0, 0].cpu().numpy()[:, None] + batch["right_scale"] = batch["right_scale"] / batch["affine_trans"][0, :, 0, 0].cpu().numpy()[:, None] + batch["left_center"] = ( + batch["left_center"] - batch["affine_trans"][0, :, [0, 1], [2, 2]].cpu().numpy() + ) / batch["affine_trans"][0, :, 0, 0].cpu().numpy()[:, None] + batch["right_center"] = ( + batch["right_center"] - batch["affine_trans"][0, :, [0, 1], [2, 2]].cpu().numpy() + ) / batch["affine_trans"][0, :, 0, 0].cpu().numpy()[:, None] + + left_xyxy = np.concatenate( + [ + (batch["left_center"][:, 0] - batch["left_scale"][:, 0] * 1 / 2).reshape(-1, 1), + (batch["left_center"][:, 1] - batch["left_scale"][:, 1] * 1 / 2).reshape(-1, 1), + (batch["left_center"][:, 0] + batch["left_scale"][:, 0] * 1 / 2).reshape(-1, 1), + (batch["left_center"][:, 1] + batch["left_scale"][:, 1] * 1 / 2).reshape(-1, 1), + ], + axis=1, + ) + right_xyxy = np.concatenate( + [ + (batch["right_center"][:, 0] - batch["right_scale"][:, 0] * 1 / 2).reshape(-1, 1), + (batch["right_center"][:, 1] - batch["right_scale"][:, 1] * 1 / 2).reshape(-1, 1), + (batch["right_center"][:, 0] + batch["right_scale"][:, 0] * 1 / 2).reshape(-1, 1), + (batch["right_center"][:, 1] + batch["right_scale"][:, 1] * 1 / 2).reshape(-1, 1), + ], + axis=1, + ) + + return left_xyxy, right_xyxy + + def keypoint_token_update_fn( + self, + kps_emb_start_idx, + image_embeddings, + token_embeddings, + token_augment, + pose_output, + layer_idx, + ): + # It's already after the last layer, we're done. + if layer_idx == len(self.decoder.layers) - 1: + return token_embeddings, token_augment, pose_output, layer_idx + + # Clone + token_embeddings = token_embeddings.clone() + token_augment = token_augment.clone() + + num_keypoints = self.keypoint_embedding.weight.shape[0] + + # Get current 2D KPS predictions + pred_keypoints_2d_cropped = pose_output["pred_keypoints_2d_cropped"].clone() # These are -0.5 ~ 0.5 + pred_keypoints_2d_depth = pose_output["pred_keypoints_2d_depth"].clone() + + pred_keypoints_2d_cropped = pred_keypoints_2d_cropped[:, self.keypoint_embedding_idxs] + pred_keypoints_2d_depth = pred_keypoints_2d_depth[:, self.keypoint_embedding_idxs] + + # Get 2D KPS to be 0 ~ 1 + pred_keypoints_2d_cropped_01 = pred_keypoints_2d_cropped + 0.5 + + # Get a mask of those that are 1) beyond image boundaries or 2) behind the camera + invalid_mask = ( + (pred_keypoints_2d_cropped_01[:, :, 0] < 0) + | (pred_keypoints_2d_cropped_01[:, :, 0] > 1) + | (pred_keypoints_2d_cropped_01[:, :, 1] < 0) + | (pred_keypoints_2d_cropped_01[:, :, 1] > 1) + | (pred_keypoints_2d_depth[:, :] < 1e-5) + ) + + # Run them through the prompt encoder's pos emb function + token_augment[:, kps_emb_start_idx : kps_emb_start_idx + num_keypoints, :] = self.keypoint_posemb_linear( + pred_keypoints_2d_cropped + ) * (~invalid_mask[:, :, None]) + + # Also maybe update token_embeddings with the grid sampled 2D feature. + # Remember that pred_keypoints_2d_cropped are -0.5 ~ 0.5. We want -1 ~ 1 + # Sample points... + ## Get sampling points + pred_keypoints_2d_cropped_sample_points = pred_keypoints_2d_cropped * 2 + if self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr", + "vit", + "vit_b", + "vit_l", + "vit_hmr_512_384", + ]: + # Need to go from 256 x 256 coords to 256 x 192 (HW) because image_embeddings is 16x12 + # Aka, for x, what was normally -1 ~ 1 for 256 should be -16/12 ~ 16/12 (since to sample at original 256, need to overflow) + pred_keypoints_2d_cropped_sample_points[:, :, 0] = ( + pred_keypoints_2d_cropped_sample_points[:, :, 0] / 12 * 16 + ) + + # Version 2 is projecting & bilinear sampling + pred_keypoints_2d_cropped_feats = ( + F.grid_sample( + image_embeddings, + pred_keypoints_2d_cropped_sample_points[:, :, None, :], # -1 ~ 1, xy + mode="bilinear", + padding_mode="zeros", + align_corners=False, + ) + .squeeze(3) + .permute(0, 2, 1) + ) # B x kps x C + # Zero out invalid locations... + pred_keypoints_2d_cropped_feats = pred_keypoints_2d_cropped_feats * (~invalid_mask[:, :, None]) + # This is ADDING + token_embeddings = token_embeddings.clone() + token_embeddings[ + :, + kps_emb_start_idx : kps_emb_start_idx + num_keypoints, + :, + ] += self.keypoint_feat_linear(pred_keypoints_2d_cropped_feats) + + return token_embeddings, token_augment, pose_output, layer_idx + + def keypoint3d_token_update_fn( + self, + kps3d_emb_start_idx, + token_embeddings, + token_augment, + pose_output, + layer_idx, + ): + # It's already after the last layer, we're done. + if layer_idx == len(self.decoder.layers) - 1: + return token_embeddings, token_augment, pose_output, layer_idx + + num_keypoints3d = self.keypoint3d_embedding.weight.shape[0] + + # Get current 3D kps predictions + pred_keypoints_3d = pose_output["pred_keypoints_3d"].clone() + + # Now, pelvis normalize + pred_keypoints_3d = ( + pred_keypoints_3d + - (pred_keypoints_3d[:, [self.pelvis_idx[0]], :] + pred_keypoints_3d[:, [self.pelvis_idx[1]], :]) / 2 + ) + + # Get the kps we care about, _after_ pelvis norm (just in case idxs shift) + pred_keypoints_3d = pred_keypoints_3d[:, self.keypoint3d_embedding_idxs] + + # Run through embedding MLP & put in + token_augment = token_augment.clone() + token_augment[ + :, + kps3d_emb_start_idx : kps3d_emb_start_idx + num_keypoints3d, + :, + ] = self.keypoint3d_posemb_linear(pred_keypoints_3d) + + return token_embeddings, token_augment, pose_output, layer_idx + + def keypoint_token_update_fn_hand( + self, + kps_emb_start_idx, + image_embeddings, + token_embeddings, + token_augment, + pose_output, + layer_idx, + ): + # It's already after the last layer, we're done. + if layer_idx == len(self.decoder_hand.layers) - 1: + return token_embeddings, token_augment, pose_output, layer_idx + + # Clone + token_embeddings = token_embeddings.clone() + token_augment = token_augment.clone() + + num_keypoints = self.keypoint_embedding_hand.weight.shape[0] + + # Get current 2D KPS predictions + pred_keypoints_2d_cropped = pose_output["pred_keypoints_2d_cropped"].clone() # These are -0.5 ~ 0.5 + pred_keypoints_2d_depth = pose_output["pred_keypoints_2d_depth"].clone() + + pred_keypoints_2d_cropped = pred_keypoints_2d_cropped[:, self.keypoint_embedding_idxs_hand] + pred_keypoints_2d_depth = pred_keypoints_2d_depth[:, self.keypoint_embedding_idxs_hand] + + # Get 2D KPS to be 0 ~ 1 + pred_keypoints_2d_cropped_01 = pred_keypoints_2d_cropped + 0.5 + + # Get a mask of those that are 1) beyond image boundaries or 2) behind the camera + invalid_mask = ( + (pred_keypoints_2d_cropped_01[:, :, 0] < 0) + | (pred_keypoints_2d_cropped_01[:, :, 0] > 1) + | (pred_keypoints_2d_cropped_01[:, :, 1] < 0) + | (pred_keypoints_2d_cropped_01[:, :, 1] > 1) + | (pred_keypoints_2d_depth[:, :] < 1e-5) + ) + + # Run them through the prompt encoder's pos emb function + token_augment[:, kps_emb_start_idx : kps_emb_start_idx + num_keypoints, :] = self.keypoint_posemb_linear_hand( + pred_keypoints_2d_cropped + ) * (~invalid_mask[:, :, None]) + + # Also maybe update token_embeddings with the grid sampled 2D feature. + # Remember that pred_keypoints_2d_cropped are -0.5 ~ 0.5. We want -1 ~ 1 + # Sample points... + ## Get sampling points + pred_keypoints_2d_cropped_sample_points = pred_keypoints_2d_cropped * 2 + if self.cfg.MODEL.BACKBONE.TYPE in [ + "vit_hmr", + "vit", + "vit_b", + "vit_l", + "vit_hmr_512_384", + ]: + # Need to go from 256 x 256 coords to 256 x 192 (HW) because image_embeddings is 16x12 + # Aka, for x, what was normally -1 ~ 1 for 256 should be -16/12 ~ 16/12 (since to sample at original 256, need to overflow) + pred_keypoints_2d_cropped_sample_points[:, :, 0] = ( + pred_keypoints_2d_cropped_sample_points[:, :, 0] / 12 * 16 + ) + + # Version 2 is projecting & bilinear sampling + pred_keypoints_2d_cropped_feats = ( + F.grid_sample( + image_embeddings, + pred_keypoints_2d_cropped_sample_points[:, :, None, :], # -1 ~ 1, xy + mode="bilinear", + padding_mode="zeros", + align_corners=False, + ) + .squeeze(3) + .permute(0, 2, 1) + ) # B x kps x C + # Zero out invalid locations... + pred_keypoints_2d_cropped_feats = pred_keypoints_2d_cropped_feats * (~invalid_mask[:, :, None]) + # This is ADDING + token_embeddings = token_embeddings.clone() + token_embeddings[ + :, + kps_emb_start_idx : kps_emb_start_idx + num_keypoints, + :, + ] += self.keypoint_feat_linear_hand(pred_keypoints_2d_cropped_feats) + + return token_embeddings, token_augment, pose_output, layer_idx + + def keypoint3d_token_update_fn_hand( + self, + kps3d_emb_start_idx, + token_embeddings, + token_augment, + pose_output, + layer_idx, + ): + # It's already after the last layer, we're done. + if layer_idx == len(self.decoder_hand.layers) - 1: + return token_embeddings, token_augment, pose_output, layer_idx + + num_keypoints3d = self.keypoint3d_embedding_hand.weight.shape[0] + + # Get current 3D kps predictions + pred_keypoints_3d = pose_output["pred_keypoints_3d"].clone() + + # Now, pelvis normalize + pred_keypoints_3d = ( + pred_keypoints_3d + - (pred_keypoints_3d[:, [self.pelvis_idx[0]], :] + pred_keypoints_3d[:, [self.pelvis_idx[1]], :]) / 2 + ) + + # Get the kps we care about, _after_ pelvis norm (just in case idxs shift) + pred_keypoints_3d = pred_keypoints_3d[:, self.keypoint3d_embedding_idxs_hand] + + # Run through embedding MLP & put in + token_augment = token_augment.clone() + token_augment[ + :, + kps3d_emb_start_idx : kps3d_emb_start_idx + num_keypoints3d, + :, + ] = self.keypoint3d_posemb_linear_hand(pred_keypoints_3d) + + return token_embeddings, token_augment, pose_output, layer_idx diff --git a/src/sam3d_body/models/modules/__init__.py b/src/sam3d_body/models/modules/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..110ca32a2fb7f04bb95c19fa66b86c945f436547 --- /dev/null +++ b/src/sam3d_body/models/modules/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from .geometry_utils import ( + aa_to_rotmat, + cam_crop_to_full, + focal_length_normalization, + get_focalLength_from_fieldOfView, + get_intrinsic_matrix, + inverse_perspective_projection, + log_depth, + perspective_projection, + rot6d_to_rotmat, + transform_points, + undo_focal_length_normalization, + undo_log_depth, +) + +from .misc import to_2tuple, to_3tuple, to_4tuple, to_ntuple diff --git a/src/sam3d_body/models/modules/camera_embed.py b/src/sam3d_body/models/modules/camera_embed.py new file mode 100644 index 0000000000000000000000000000000000000000..ffee1fa6a66daca40763067f4798abcb62d73700 --- /dev/null +++ b/src/sam3d_body/models/modules/camera_embed.py @@ -0,0 +1,111 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import einops +import numpy as np +import torch +import torch.nn.functional as F + +from sam3d_body.models.modules.transformer import LayerNorm2d +from torch import nn + + +class CameraEncoder(nn.Module): + def __init__(self, embed_dim, patch_size=14): + super().__init__() + self.patch_size = patch_size + self.embed_dim = embed_dim + self.camera = FourierPositionEncoding(n=3, num_bands=16, max_resolution=64) + + self.conv = nn.Conv2d(embed_dim + 99, embed_dim, kernel_size=1, bias=False) + self.norm = LayerNorm2d(embed_dim) + + def forward(self, img_embeddings, rays): + B, D, _h, _w = img_embeddings.shape + + with torch.no_grad(): + scale = 1 / self.patch_size + rays = F.interpolate( + rays, + scale_factor=(scale, scale), + mode="bilinear", + align_corners=False, + antialias=True, + ) + rays = rays.permute(0, 2, 3, 1).contiguous() # [b, h, w, 2] + rays = torch.cat([rays, torch.ones_like(rays[..., :1])], dim=-1) + rays_embeddings = self.camera( + pos=rays.reshape(B, -1, 3) + ) # (bs, N, 99): rays fourier embedding + rays_embeddings = einops.rearrange( + rays_embeddings, "b (h w) c -> b c h w", h=_h, w=_w + ).contiguous() + + z = torch.concat([img_embeddings, rays_embeddings], dim=1) + z = self.norm(self.conv(z)) + + return z + + +class FourierPositionEncoding(nn.Module): + def __init__(self, n, num_bands, max_resolution): + """ + Module that generate Fourier encoding - no learning involved + """ + super().__init__() + + self.num_bands = num_bands + self.max_resolution = [max_resolution] * n + + @property + def channels(self): + """ + Return the output dimension + """ + num_dims = len(self.max_resolution) + encoding_size = self.num_bands * num_dims + encoding_size *= 2 # sin-cos + encoding_size += num_dims # concat + + return encoding_size + + def forward(self, pos): + """ + Forward pass that take rays as input and generate Fourier positional encodings + """ + fourier_pos_enc = _generate_fourier_features( + pos, num_bands=self.num_bands, max_resolution=self.max_resolution + ) + return fourier_pos_enc + + +def _generate_fourier_features(pos, num_bands, max_resolution): + """Generate fourier features from a given set of positions and frequencies""" + b, n = pos.shape[:2] + device = pos.device + + # Linear frequency sampling + min_freq = 1.0 + freq_bands = torch.stack( + [ + torch.linspace(start=min_freq, end=res / 2, steps=num_bands, device=device) + for res in max_resolution + ], + dim=0, + ) + + # Stacking + per_pos_features = torch.stack( + [pos[i, :, :][:, :, None] * freq_bands[None, :, :] for i in range(b)], 0 + ) + per_pos_features = per_pos_features.reshape(b, n, -1) + + # Sin-Cos + per_pos_features = torch.cat( + [torch.sin(np.pi * per_pos_features), torch.cos(np.pi * per_pos_features)], + dim=-1, + ) + + # Concat with initial pos + per_pos_features = torch.cat([pos, per_pos_features], dim=-1) + + return per_pos_features diff --git a/src/sam3d_body/models/modules/drop_path.py b/src/sam3d_body/models/modules/drop_path.py new file mode 100644 index 0000000000000000000000000000000000000000..3c08a6937c3f44a711d6bae1d17d0eef661a2db3 --- /dev/null +++ b/src/sam3d_body/models/modules/drop_path.py @@ -0,0 +1,42 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import torch +import torch.nn as nn + + +def drop_path( + x: torch.Tensor, drop_prob: float = 0.0, training: bool = False +) -> torch.Tensor: + """Drop paths (Stochastic Depth) per sample (when applied in main path of + residual blocks). + + We follow the implementation + https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 + """ + if not training: + return x + keep_prob = 1 - drop_prob + # handle tensors with different dimensions, not just 4D tensors. + shape = (x.shape[0],) + (1,) * (x.ndim - 1) + random_tensor = keep_prob + torch.rand(shape, dtype=x.dtype, device=x.device) + output = x.div(keep_prob) * random_tensor.floor() + return output + + +class DropPath(nn.Module): + """Drop paths (Stochastic Depth) per sample (when applied in main path of + residual blocks). + + We follow the implementation + https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 + + Args: + drop_prob (float): Probability of the path to be zeroed. Default: 0.1 + """ + + def __init__(self, drop_prob: float = 0.1): + super().__init__() + self.drop_prob = drop_prob + + def forward(self, x: torch.Tensor) -> torch.Tensor: + return drop_path(x, self.drop_prob, self.training) diff --git a/src/sam3d_body/models/modules/geometry_utils.py b/src/sam3d_body/models/modules/geometry_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..0ba35bf359e06822f9f2bf5a52a8b00513172eac --- /dev/null +++ b/src/sam3d_body/models/modules/geometry_utils.py @@ -0,0 +1,304 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from typing import Optional + +import cv2 + +import numpy as np +import torch +from torch.nn import functional as F +from jaxtyping import Float + + +def cam_crop_to_full(cam_bbox, box_center, box_size, img_size, focal_length=5000.0): + # Convert cam_bbox to full image + img_w, img_h = img_size[:, 0], img_size[:, 1] + cx, cy, b = box_center[:, 0], box_center[:, 1], box_size + w_2, h_2 = img_w / 2.0, img_h / 2.0 + bs = b * cam_bbox[:, 0] + 1e-9 + if type(focal_length) is float: + focal_length = torch.ones_like(cam_bbox[:, 0]) * focal_length + tz = 2 * focal_length / bs + tx = (2 * (cx - w_2) / bs) + cam_bbox[:, 1] + ty = (2 * (cy - h_2) / bs) + cam_bbox[:, 2] + full_cam = torch.stack([tx, ty, tz], dim=-1) + return full_cam + + +def aa_to_rotmat(theta: torch.Tensor): + """ + Convert axis-angle representation to rotation matrix. + Works by first converting it to a quaternion. + Args: + theta (torch.Tensor): Tensor of shape (B, 3) containing axis-angle representations. + Returns: + torch.Tensor: Corresponding rotation matrices with shape (B, 3, 3). + + Alternatives: + import roma + y = roma.rotvec_to_rotmat(x) + """ + norm = torch.norm(theta + 1e-8, p=2, dim=1) + angle = torch.unsqueeze(norm, -1) + normalized = torch.div(theta, angle) + angle = angle * 0.5 + v_cos = torch.cos(angle) + v_sin = torch.sin(angle) + quat = torch.cat([v_cos, v_sin * normalized], dim=1) + return _quat_to_rotmat(quat) + + +def _quat_to_rotmat(quat: torch.Tensor) -> torch.Tensor: + """ + Convert quaternion representation to rotation matrix. + Args: + quat (torch.Tensor) of shape (B, 4); 4 <===> (w, x, y, z). + Returns: + torch.Tensor: Corresponding rotation matrices with shape (B, 3, 3). + """ + norm_quat = quat + norm_quat = norm_quat / norm_quat.norm(p=2, dim=1, keepdim=True) + w, x, y, z = norm_quat[:, 0], norm_quat[:, 1], norm_quat[:, 2], norm_quat[:, 3] + + B = quat.size(0) + + w2, x2, y2, z2 = w.pow(2), x.pow(2), y.pow(2), z.pow(2) + wx, wy, wz = w * x, w * y, w * z + xy, xz, yz = x * y, x * z, y * z + + rotMat = torch.stack( + [ + w2 + x2 - y2 - z2, + 2 * xy - 2 * wz, + 2 * wy + 2 * xz, + 2 * wz + 2 * xy, + w2 - x2 + y2 - z2, + 2 * yz - 2 * wx, + 2 * xz - 2 * wy, + 2 * wx + 2 * yz, + w2 - x2 - y2 + z2, + ], + dim=1, + ).view(B, 3, 3) + return rotMat + + +def rot6d_to_rotmat(x: torch.Tensor) -> torch.Tensor: + """ + Convert 6D rotation representation to 3x3 rotation matrix. + Based on Zhou et al., "On the Continuity of Rotation Representations in Neural Networks", CVPR 2019 + Args: + x (torch.Tensor): (B,6) Batch of 6-D rotation representations. + Returns: + torch.Tensor: Batch of corresponding rotation matrices with shape (B,3,3). + + Alternatives: + import roma + x = x.reshape(-1,2,3).permute(0, 2, 1).contiguous() + y = roma.special_gramschmidt(x) + """ + x = x.reshape(-1, 2, 3).permute(0, 2, 1).contiguous() + a1 = x[:, :, 0] + a2 = x[:, :, 1] + b1 = F.normalize(a1) + b2 = F.normalize(a2 - torch.einsum("bi,bi->b", b1, a2).unsqueeze(-1) * b1) + b3 = torch.linalg.cross(b1, b2) + return torch.stack((b1, b2, b3), dim=-1) + + +def rotmat_to_rot6d(x: torch.Tensor) -> torch.Tensor: + """ + Converts rotation matrices to 6D rotation representation by Zhou et al. [1] + by dropping the last row. Note that 6D representation is not unique. + Args: + x: batch of rotation matrices of size (B, 3, 3) + + Returns: + 6D rotation representation, of size (B, 6) + + [1] Zhou, Y., Barnes, C., Lu, J., Yang, J., & Li, H. + On the Continuity of Rotation Representations in Neural Networks. + IEEE Conference on Computer Vision and Pattern Recognition, 2019. + Retrieved from http://arxiv.org/abs/1812.07035 + """ + batch_dim = x.size()[:-2] + return x[..., :2, :].clone().reshape(batch_dim + (6,)) + + +def rot_aa(aa: Float[np.ndarray, "3"], rot: float) -> Float[np.ndarray, "3"]: + """ + Rotate axis angle parameters. + Args: + aa (np.array): Axis-angle vector of shape (3,). + rot (np.array): Rotation angle in degrees. + Returns: + np.array: Rotated axis-angle vector. + """ + # pose parameters + R: Float[np.ndarray, "3 3"] = np.array( + [ + [np.cos(np.deg2rad(-rot)), -np.sin(np.deg2rad(-rot)), 0], + [np.sin(np.deg2rad(-rot)), np.cos(np.deg2rad(-rot)), 0], + [0, 0, 1], + ], + dtype=np.float64, + ) + # find the rotation of the body in camera frame + per_rdg: Float[np.ndarray, "3 3"] + per_rdg, _ = cv2.Rodrigues(aa) + # apply the global rotation to the global orientation + resrot: Float[np.ndarray, "3 3"] + resrot, _ = cv2.Rodrigues(np.dot(R, per_rdg)) + aa_vec: Float[np.ndarray, "3"] = (resrot.T)[0] + return aa_vec.astype(np.float32) + + +def transform_points( + points: torch.Tensor, + translation: Optional[torch.Tensor] = None, + rotation: Optional[torch.Tensor] = None, +) -> torch.Tensor: + """ + Transform a set of 3D points given translation and rotation. + Args: + points (torch.Tensor): Tensor of shape (B, N, 3) containing the input 3D points. + translation (torch.Tensor): Tensor of shape (B, 3) containing the 3D camera translation. + rotation (torch.Tensor): Tensor of shape (B, 3, 3) containing the camera rotation. + Returns: + torch.Tensor: Tensor of shape (B, N, 3) containing the transformed points. + """ + if rotation is not None: + points = torch.einsum("bij,bkj->bki", rotation, points) + + if translation is not None: + points = points + translation.unsqueeze(1) + + return points + + +def get_intrinsic_matrix( + focal_length: torch.Tensor, principle: torch.Tensor +) -> torch.Tensor: + """ + Populate intrinsic camera matrix K given focal length and principle point. + Args: + focal_length: Tensor of shape (2,) + principle: Tensor of shape (2,) + Returns: + Tensor of shape (3, 3) + """ + if isinstance(focal_length, float): + fl_x = fl_y = focal_length + elif len(focal_length) == 1: + fl_x = fl_y = focal_length[0] + else: + fl_x, fl_y = focal_length[0], focal_length[1] + K = torch.eye(3) + K[0, 0] = fl_x + K[1, 1] = fl_y + K[0, -1] = principle[0] + K[1, -1] = principle[1] + + return K + + +def perspective_projection(x, K): + """ + Computes the perspective projection of a set of points assuming the extrinsinc params have already been applied + Args: + - x [bs,N,3]: 3D points + - K [bs,3,3]: Camera instrincs params + """ + # Apply perspective distortion + y = x / x[:, :, -1].unsqueeze(-1) # (bs, N, 3) + + # Apply camera intrinsics + y = torch.einsum("bij,bkj->bki", K, y) # (bs, N, 3) + + return y[:, :, :2] + + +def inverse_perspective_projection(points, K, distance): + """ + Computes the inverse perspective projection of a set of points given an estimated distance. + Input: + points (bs, N, 2): 2D points + K (bs,3,3): camera intrinsics params + distance (bs, N, 1): distance in the 3D world + Similar to: + - pts_l_norm = cv2.undistortPoints(np.expand_dims(pts_l, axis=1), cameraMatrix=K_l, distCoeffs=None) + """ + # Apply camera intrinsics + points = torch.cat([points, torch.ones_like(points[..., :1])], -1) + points = torch.einsum("bij,bkj->bki", torch.inverse(K), points) + + # Apply perspective distortion + if distance == None: + return points + points = points * distance + return points + + +def get_cam_intrinsics(img_size, fov=55, p_x=None, p_y=None): + """Given image size, fov and principal point coordinates, return K the camera parameter matrix""" + K = np.eye(3) + # Get focal length. + focal = get_focalLength_from_fieldOfView(fov=fov, img_size=img_size) + K[0, 0], K[1, 1] = focal, focal + + # Set principal point + if p_x is not None and p_y is not None: + K[0, -1], K[1, -1] = p_x * img_size, p_y * img_size + else: + K[0, -1], K[1, -1] = img_size // 2, img_size // 2 + + return K + + +def get_focalLength_from_fieldOfView(fov=60, img_size=512): + """ + Compute the focal length of the camera lens by assuming a certain FOV for the entire image + Args: + - fov: float, expressed in degree + - img_size: int + Return: + focal: float + """ + focal = img_size / (2 * np.tan(np.radians(fov) / 2)) + return focal + + +def focal_length_normalization(x, f, fovn=60, img_size=448): + """ + Section 3.1 of https://arxiv.org/pdf/1904.02028.pdf + E = (fn/f) * E' where E is 1/d + """ + fn = get_focalLength_from_fieldOfView(fov=fovn, img_size=img_size) + y = x * (fn / f) + return y + + +def undo_focal_length_normalization(y, f, fovn=60, img_size=448): + """ + Undo focal_length_normalization() + """ + fn = get_focalLength_from_fieldOfView(fov=fovn, img_size=img_size) + x = y * (f / fn) + return x + + +EPS_LOG = 1e-10 + + +def log_depth(x, eps=EPS_LOG): + """ + Move depth to log space + """ + return torch.log(x + eps) + + +def undo_log_depth(y, eps=EPS_LOG): + """ + Undo log_depth() + """ + return torch.exp(y) - eps diff --git a/src/sam3d_body/models/modules/layer_scale.py b/src/sam3d_body/models/modules/layer_scale.py new file mode 100644 index 0000000000000000000000000000000000000000..9d602fe9e69f5a11baa1d9e913d7989839cbbb57 --- /dev/null +++ b/src/sam3d_body/models/modules/layer_scale.py @@ -0,0 +1,45 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from typing import Union + +import torch +import torch.nn as nn + + +class LayerScale(nn.Module): + """LayerScale layer. + + Args: + dim (int): Dimension of input features. + layer_scale_init_value (float or torch.Tensor): Init value of layer + scale. Defaults to 1e-5. + inplace (bool): inplace: can optionally do the + operation in-place. Defaults to False. + data_format (str): The input data format, could be 'channels_last' + or 'channels_first', representing (B, C, H, W) and + (B, N, C) format data respectively. Defaults to 'channels_last'. + """ + + def __init__( + self, + dim: int, + layer_scale_init_value: Union[float, torch.Tensor] = 1e-5, + inplace: bool = False, + data_format: str = "channels_last", + ): + super().__init__() + assert data_format in ( + "channels_last", + "channels_first", + ), "'data_format' could only be channels_last or channels_first." + self.inplace = inplace + self.data_format = data_format + self.weight = nn.Parameter(torch.ones(dim) * layer_scale_init_value) + + def forward(self, x): + if self.data_format == "channels_first": + if self.inplace: + return x.mul_(self.weight.view(-1, 1, 1)) + else: + return x * self.weight.view(-1, 1, 1) + return x.mul_(self.weight) if self.inplace else x * self.weight diff --git a/src/sam3d_body/models/modules/mhr_utils.py b/src/sam3d_body/models/modules/mhr_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..c2b690669a44d7fba5654b70400199877d5f597a --- /dev/null +++ b/src/sam3d_body/models/modules/mhr_utils.py @@ -0,0 +1,392 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import json +import math +import os.path as osp +import pickle + +import cv2 + +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F + + +def rotation_angle_difference(A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: + """ + Compute the angle difference (magnitude) between two batches of SO(3) rotation matrices. + Args: + A: Tensor of shape (*, 3, 3), batch of rotation matrices. + B: Tensor of shape (*, 3, 3), batch of rotation matrices. + Returns: + Tensor of shape (*,), angle differences in radians. + """ + # Compute relative rotation matrix + R_rel = torch.matmul(A, B.transpose(-2, -1)) # (B, 3, 3) + # Compute trace of relative rotation + trace = R_rel[..., 0, 0] + R_rel[..., 1, 1] + R_rel[..., 2, 2] # (B,) + # Compute angle using the trace formula + cos_theta = (trace - 1) / 2 + # Clamp for numerical stability + cos_theta_clamped = torch.clamp(cos_theta, -1.0, 1.0) + # Compute angle difference + angle = torch.acos(cos_theta_clamped) + return angle + + +def fix_wrist_euler( + wrist_xzy, limits_x=(-2.2, 1.0), limits_z=(-2.2, 1.5), limits_y=(-1.2, 1.5) +): + """ + wrist_xzy: B x 2 x 3 (X, Z, Y angles) + Returns: Fixed angles within joint limits + """ + x, z, y = wrist_xzy[..., 0], wrist_xzy[..., 1], wrist_xzy[..., 2] + + x_alt = torch.atan2(torch.sin(x + torch.pi), torch.cos(x + torch.pi)) + z_alt = torch.atan2(torch.sin(-(z + torch.pi)), torch.cos(-(z + torch.pi))) + y_alt = torch.atan2(torch.sin(y + torch.pi), torch.cos(y + torch.pi)) + + # Calculate L2 violation distance + def calc_violation(val, limits): + below = torch.clamp(limits[0] - val, min=0.0) + above = torch.clamp(val - limits[1], min=0.0) + return below**2 + above**2 + + violation_orig = ( + calc_violation(x, limits_x) + + calc_violation(z, limits_z) + + calc_violation(y, limits_y) + ) + + violation_alt = ( + calc_violation(x_alt, limits_x) + + calc_violation(z_alt, limits_z) + + calc_violation(y_alt, limits_y) + ) + + # Use alternative where it has lower L2 violation + use_alt = violation_alt < violation_orig + + # Stack alternative and apply mask + wrist_xzy_alt = torch.stack([x_alt, z_alt, y_alt], dim=-1) + result = torch.where(use_alt.unsqueeze(-1), wrist_xzy_alt, wrist_xzy) + + return result + + +def batch6DFromXYZ(r, return_9D=False): + """ + Generate a matrix representing a rotation defined by a XYZ-Euler + rotation. + + Args: + r: ... x 3 rotation vectors + + Returns: + ... x 6 + """ + rc = torch.cos(r) + rs = torch.sin(r) + cx = rc[..., 0] + cy = rc[..., 1] + cz = rc[..., 2] + sx = rs[..., 0] + sy = rs[..., 1] + sz = rs[..., 2] + + result = torch.empty(list(r.shape[:-1]) + [3, 3], dtype=r.dtype).to(r.device) + + result[..., 0, 0] = cy * cz + result[..., 0, 1] = -cx * sz + sx * sy * cz + result[..., 0, 2] = sx * sz + cx * sy * cz + result[..., 1, 0] = cy * sz + result[..., 1, 1] = cx * cz + sx * sy * sz + result[..., 1, 2] = -sx * cz + cx * sy * sz + result[..., 2, 0] = -sy + result[..., 2, 1] = sx * cy + result[..., 2, 2] = cx * cy + + if not return_9D: + return torch.cat([result[..., :, 0], result[..., :, 1]], dim=-1) + else: + return result + + +# https://github.com/papagina/RotationContinuity/blob/758b0ce551c06372cab7022d4c0bdf331c89c696/shapenet/code/tools.py#L82 +def batchXYZfrom6D(poses): + # Args: poses: ... x 6, where "6" is the combined first and second columns + # First, get the rotaiton matrix + x_raw = poses[..., :3] + y_raw = poses[..., 3:] + + x = F.normalize(x_raw, dim=-1) + z = torch.cross(x, y_raw, dim=-1) + z = F.normalize(z, dim=-1) + y = torch.cross(z, x, dim=-1) + + matrix = torch.stack([x, y, z], dim=-1) # ... x 3 x 3 + + # Now get it into euler + # https://github.com/papagina/RotationContinuity/blob/758b0ce551c06372cab7022d4c0bdf331c89c696/shapenet/code/tools.py#L412 + sy = torch.sqrt( + matrix[..., 0, 0] * matrix[..., 0, 0] + matrix[..., 1, 0] * matrix[..., 1, 0] + ) + singular = sy < 1e-6 + singular = singular.float() + + x = torch.atan2(matrix[..., 2, 1], matrix[..., 2, 2]) + y = torch.atan2(-matrix[..., 2, 0], sy) + z = torch.atan2(matrix[..., 1, 0], matrix[..., 0, 0]) + + xs = torch.atan2(-matrix[..., 1, 2], matrix[..., 1, 1]) + ys = torch.atan2(-matrix[..., 2, 0], sy) + zs = matrix[..., 1, 0] * 0 + + out_euler = torch.zeros_like(matrix[..., 0]) + out_euler[..., 0] = x * (1 - singular) + xs * singular + out_euler[..., 1] = y * (1 - singular) + ys * singular + out_euler[..., 2] = z * (1 - singular) + zs * singular + + return out_euler + + +def resize_image(image_array, scale_factor, interpolation=cv2.INTER_LINEAR): + new_height = int(image_array.shape[0] // scale_factor) + new_width = int(image_array.shape[1] // scale_factor) + resized_image = cv2.resize( + image_array, (new_width, new_height), interpolation=interpolation + ) + + return resized_image + + +def compact_cont_to_model_params_hand(hand_cont): + # These are ordered by joint, not model params ^^ + assert hand_cont.shape[-1] == 54 + hand_dofs_in_order = torch.tensor([3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 2, 3, 1, 1]) + assert sum(hand_dofs_in_order) == 27 + # Mask of 3DoFs into hand_cont + mask_cont_threedofs = torch.cat( + [torch.ones(2 * k).bool() * (k in [3]) for k in hand_dofs_in_order] + ) + # Mask of 1DoFs (including 2DoF) into hand_cont + mask_cont_onedofs = torch.cat( + [torch.ones(2 * k).bool() * (k in [1, 2]) for k in hand_dofs_in_order] + ) + # Mask of 3DoFs into hand_model_params + mask_model_params_threedofs = torch.cat( + [torch.ones(k).bool() * (k in [3]) for k in hand_dofs_in_order] + ) + # Mask of 1DoFs (including 2DoF) into hand_model_params + mask_model_params_onedofs = torch.cat( + [torch.ones(k).bool() * (k in [1, 2]) for k in hand_dofs_in_order] + ) + + # Convert hand_cont to eulers + ## First for 3DoFs + hand_cont_threedofs = hand_cont[..., mask_cont_threedofs].unflatten(-1, (-1, 6)) + hand_model_params_threedofs = batchXYZfrom6D(hand_cont_threedofs).flatten(-2, -1) + ## Next for 1DoFs + hand_cont_onedofs = hand_cont[..., mask_cont_onedofs].unflatten( + -1, (-1, 2) + ) # (sincos) + hand_model_params_onedofs = torch.atan2( + hand_cont_onedofs[..., -2], hand_cont_onedofs[..., -1] + ) + + # Finally, assemble into a 27-dim vector, ordered by joint, then XYZ. + hand_model_params = torch.zeros(*hand_cont.shape[:-1], 27).to(hand_cont) + hand_model_params[..., mask_model_params_threedofs] = hand_model_params_threedofs + hand_model_params[..., mask_model_params_onedofs] = hand_model_params_onedofs + + return hand_model_params + + +def compact_model_params_to_cont_hand(hand_model_params): + # These are ordered by joint, not model params ^^ + assert hand_model_params.shape[-1] == 27 + hand_dofs_in_order = torch.tensor([3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 2, 3, 1, 1]) + assert sum(hand_dofs_in_order) == 27 + # Mask of 3DoFs into hand_cont + mask_cont_threedofs = torch.cat( + [torch.ones(2 * k).bool() * (k in [3]) for k in hand_dofs_in_order] + ) + # Mask of 1DoFs (including 2DoF) into hand_cont + mask_cont_onedofs = torch.cat( + [torch.ones(2 * k).bool() * (k in [1, 2]) for k in hand_dofs_in_order] + ) + # Mask of 3DoFs into hand_model_params + mask_model_params_threedofs = torch.cat( + [torch.ones(k).bool() * (k in [3]) for k in hand_dofs_in_order] + ) + # Mask of 1DoFs (including 2DoF) into hand_model_params + mask_model_params_onedofs = torch.cat( + [torch.ones(k).bool() * (k in [1, 2]) for k in hand_dofs_in_order] + ) + + # Convert eulers to hand_cont hand_cont + ## First for 3DoFs + hand_model_params_threedofs = hand_model_params[ + ..., mask_model_params_threedofs + ].unflatten(-1, (-1, 3)) + hand_cont_threedofs = batch6DFromXYZ(hand_model_params_threedofs).flatten(-2, -1) + ## Next for 1DoFs + hand_model_params_onedofs = hand_model_params[..., mask_model_params_onedofs] + hand_cont_onedofs = torch.stack( + [hand_model_params_onedofs.sin(), hand_model_params_onedofs.cos()], dim=-1 + ).flatten(-2, -1) + + # Finally, assemble into a 27-dim vector, ordered by joint, then XYZ. + hand_cont = torch.zeros(*hand_model_params.shape[:-1], 54).to(hand_model_params) + hand_cont[..., mask_cont_threedofs] = hand_cont_threedofs + hand_cont[..., mask_cont_onedofs] = hand_cont_onedofs + + return hand_cont + + +def batch9Dfrom6D(poses): + # Args: poses: ... x 6, where "6" is the combined first and second columns + # First, get the rotaiton matrix + x_raw = poses[..., :3] + y_raw = poses[..., 3:] + + x = F.normalize(x_raw, dim=-1) + z = torch.cross(x, y_raw, dim=-1) + z = F.normalize(z, dim=-1) + y = torch.cross(z, x, dim=-1) + + matrix = torch.stack([x, y, z], dim=-1).flatten(-2, -1) # ... x 3 x 3 -> x9 + + return matrix + + +def batch4Dfrom2D(poses): + # Args: poses: ... x 2, where "2" is sincos + poses_norm = F.normalize(poses, dim=-1) + + poses_4d = torch.stack( + [ + poses_norm[..., 1], + poses_norm[..., 0], + -poses_norm[..., 0], + poses_norm[..., 1], + ], + dim=-1, + ) # Flattened SO2. + + return poses_4d # .... x 4 + + +def compact_cont_to_rotmat_body(body_pose_cont, inflate_trans=False): + # fmt: off + all_param_3dof_rot_idxs = torch.LongTensor([(0, 2, 4), (6, 8, 10), (12, 13, 14), (15, 16, 17), (18, 19, 20), (21, 22, 23), (24, 25, 26), (27, 28, 29), (34, 35, 36), (37, 38, 39), (44, 45, 46), (53, 54, 55), (64, 65, 66), (85, 69, 73), (86, 70, 79), (87, 71, 82), (88, 72, 76), (91, 92, 93), (112, 96, 100), (113, 97, 106), (114, 98, 109), (115, 99, 103), (130, 131, 132)]) + all_param_1dof_rot_idxs = torch.LongTensor([1, 3, 5, 7, 9, 11, 30, 31, 32, 33, 40, 41, 42, 43, 47, 48, 49, 50, 51, 52, 56, 57, 58, 59, 60, 61, 62, 63, 67, 68, 74, 75, 77, 78, 80, 81, 83, 84, 89, 90, 94, 95, 101, 102, 104, 105, 107, 108, 110, 111, 116, 117, 118, 119, 120, 121, 122, 123]) + all_param_1dof_trans_idxs = torch.LongTensor([124, 125, 126, 127, 128, 129]) + # fmt: on + num_3dof_angles = len(all_param_3dof_rot_idxs) * 3 + num_1dof_angles = len(all_param_1dof_rot_idxs) + num_1dof_trans = len(all_param_1dof_trans_idxs) + assert body_pose_cont.shape[-1] == ( + 2 * num_3dof_angles + 2 * num_1dof_angles + num_1dof_trans + ) + # Get subsets + body_cont_3dofs = body_pose_cont[..., : 2 * num_3dof_angles] + body_cont_1dofs = body_pose_cont[ + ..., 2 * num_3dof_angles : 2 * num_3dof_angles + 2 * num_1dof_angles + ] + body_cont_trans = body_pose_cont[..., 2 * num_3dof_angles + 2 * num_1dof_angles :] + # Convert conts to model params + ## First for 3dofs + body_cont_3dofs = body_cont_3dofs.unflatten(-1, (-1, 6)) + body_rotmat_3dofs = batch9Dfrom6D(body_cont_3dofs).flatten(-2, -1) + ## Next for 1dofs + body_cont_1dofs = body_cont_1dofs.unflatten(-1, (-1, 2)) # (sincos) + body_rotmat_1dofs = batch4Dfrom2D(body_cont_1dofs).flatten(-2, -1) + if inflate_trans: + assert ( + False + ), "This is left as a possibility to increase the space/contribution/supervision trans params gets compared to rots" + else: + ## Nothing to do for trans + body_rotmat_trans = body_cont_trans + # Put them together + body_rotmat_params = torch.cat( + [body_rotmat_3dofs, body_rotmat_1dofs, body_rotmat_trans], dim=-1 + ) + return body_rotmat_params + + +def compact_cont_to_model_params_body(body_pose_cont): + # fmt: off + all_param_3dof_rot_idxs = torch.LongTensor([(0, 2, 4), (6, 8, 10), (12, 13, 14), (15, 16, 17), (18, 19, 20), (21, 22, 23), (24, 25, 26), (27, 28, 29), (34, 35, 36), (37, 38, 39), (44, 45, 46), (53, 54, 55), (64, 65, 66), (85, 69, 73), (86, 70, 79), (87, 71, 82), (88, 72, 76), (91, 92, 93), (112, 96, 100), (113, 97, 106), (114, 98, 109), (115, 99, 103), (130, 131, 132)]) + all_param_1dof_rot_idxs = torch.LongTensor([1, 3, 5, 7, 9, 11, 30, 31, 32, 33, 40, 41, 42, 43, 47, 48, 49, 50, 51, 52, 56, 57, 58, 59, 60, 61, 62, 63, 67, 68, 74, 75, 77, 78, 80, 81, 83, 84, 89, 90, 94, 95, 101, 102, 104, 105, 107, 108, 110, 111, 116, 117, 118, 119, 120, 121, 122, 123]) + all_param_1dof_trans_idxs = torch.LongTensor([124, 125, 126, 127, 128, 129]) + # fmt: on + num_3dof_angles = len(all_param_3dof_rot_idxs) * 3 + num_1dof_angles = len(all_param_1dof_rot_idxs) + num_1dof_trans = len(all_param_1dof_trans_idxs) + assert body_pose_cont.shape[-1] == ( + 2 * num_3dof_angles + 2 * num_1dof_angles + num_1dof_trans + ) + # Get subsets + body_cont_3dofs = body_pose_cont[..., : 2 * num_3dof_angles] + body_cont_1dofs = body_pose_cont[ + ..., 2 * num_3dof_angles : 2 * num_3dof_angles + 2 * num_1dof_angles + ] + body_cont_trans = body_pose_cont[..., 2 * num_3dof_angles + 2 * num_1dof_angles :] + # Convert conts to model params + ## First for 3dofs + body_cont_3dofs = body_cont_3dofs.unflatten(-1, (-1, 6)) + body_params_3dofs = batchXYZfrom6D(body_cont_3dofs).flatten(-2, -1) + ## Next for 1dofs + body_cont_1dofs = body_cont_1dofs.unflatten(-1, (-1, 2)) # (sincos) + body_params_1dofs = torch.atan2(body_cont_1dofs[..., -2], body_cont_1dofs[..., -1]) + ## Nothing to do for trans + body_params_trans = body_cont_trans + # Put them together + body_pose_params = torch.zeros(*body_pose_cont.shape[:-1], 133).to(body_pose_cont) + body_pose_params[..., all_param_3dof_rot_idxs.flatten()] = body_params_3dofs + body_pose_params[..., all_param_1dof_rot_idxs] = body_params_1dofs + body_pose_params[..., all_param_1dof_trans_idxs] = body_params_trans + return body_pose_params + + +def compact_model_params_to_cont_body(body_pose_params): + # fmt: off + all_param_3dof_rot_idxs = torch.LongTensor([(0, 2, 4), (6, 8, 10), (12, 13, 14), (15, 16, 17), (18, 19, 20), (21, 22, 23), (24, 25, 26), (27, 28, 29), (34, 35, 36), (37, 38, 39), (44, 45, 46), (53, 54, 55), (64, 65, 66), (85, 69, 73), (86, 70, 79), (87, 71, 82), (88, 72, 76), (91, 92, 93), (112, 96, 100), (113, 97, 106), (114, 98, 109), (115, 99, 103), (130, 131, 132)]) + all_param_1dof_rot_idxs = torch.LongTensor([1, 3, 5, 7, 9, 11, 30, 31, 32, 33, 40, 41, 42, 43, 47, 48, 49, 50, 51, 52, 56, 57, 58, 59, 60, 61, 62, 63, 67, 68, 74, 75, 77, 78, 80, 81, 83, 84, 89, 90, 94, 95, 101, 102, 104, 105, 107, 108, 110, 111, 116, 117, 118, 119, 120, 121, 122, 123]) + all_param_1dof_trans_idxs = torch.LongTensor([124, 125, 126, 127, 128, 129]) + # fmt: on + num_3dof_angles = len(all_param_3dof_rot_idxs) * 3 + num_1dof_angles = len(all_param_1dof_rot_idxs) + num_1dof_trans = len(all_param_1dof_trans_idxs) + assert body_pose_params.shape[-1] == ( + num_3dof_angles + num_1dof_angles + num_1dof_trans + ) + # Take out params + body_params_3dofs = body_pose_params[..., all_param_3dof_rot_idxs.flatten()] + body_params_1dofs = body_pose_params[..., all_param_1dof_rot_idxs] + body_params_trans = body_pose_params[..., all_param_1dof_trans_idxs] + # params to cont + body_cont_3dofs = batch6DFromXYZ(body_params_3dofs.unflatten(-1, (-1, 3))).flatten( + -2, -1 + ) + body_cont_1dofs = torch.stack( + [body_params_1dofs.sin(), body_params_1dofs.cos()], dim=-1 + ).flatten(-2, -1) + body_cont_trans = body_params_trans + # Put them together + body_pose_cont = torch.cat( + [body_cont_3dofs, body_cont_1dofs, body_cont_trans], dim=-1 + ) + return body_pose_cont + + +# fmt: off +mhr_param_hand_idxs = [62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115] +mhr_cont_hand_idxs = [72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237] +mhr_param_hand_mask = torch.zeros(133).bool(); mhr_param_hand_mask[mhr_param_hand_idxs] = True +mhr_cont_hand_mask = torch.zeros(260).bool(); mhr_cont_hand_mask[mhr_cont_hand_idxs] = True +# fmt: on diff --git a/src/sam3d_body/models/modules/misc.py b/src/sam3d_body/models/modules/misc.py new file mode 100644 index 0000000000000000000000000000000000000000..b84620e216ee175c94deb98d8d55840e885ddf70 --- /dev/null +++ b/src/sam3d_body/models/modules/misc.py @@ -0,0 +1,31 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import collections.abc +from itertools import repeat + + +# From PyTorch internals +def _ntuple(n): + """A `to_tuple` function generator. + + It returns a function, this function will repeat the input to a tuple of + length ``n`` if the input is not an Iterable object, otherwise, return the + input directly. + + Args: + n (int): The number of the target length. + """ + + def parse(x): + if isinstance(x, collections.abc.Iterable): + return x + return tuple(repeat(x, n)) + + return parse + + +to_1tuple = _ntuple(1) +to_2tuple = _ntuple(2) +to_3tuple = _ntuple(3) +to_4tuple = _ntuple(4) +to_ntuple = _ntuple diff --git a/src/sam3d_body/models/modules/swiglu_ffn.py b/src/sam3d_body/models/modules/swiglu_ffn.py new file mode 100644 index 0000000000000000000000000000000000000000..f42f723dc19353ed54933017be9c21cf14b4576e --- /dev/null +++ b/src/sam3d_body/models/modules/swiglu_ffn.py @@ -0,0 +1,96 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from typing import Optional + +import torch +import torch.nn as nn +import torch.nn.functional as F + +from .drop_path import DropPath + +from .layer_scale import LayerScale + + +class SwiGLUFFN(nn.Module): + """SwiGLU FFN layer. + + Modified from https://github.com/facebookresearch/dinov2/blob/main/dinov2/layers/swiglu_ffn.py + """ # noqa + + def __init__( + self, + embed_dims: int, + feedforward_channels: Optional[int] = None, + out_dims: Optional[int] = None, + layer_scale_init_value: float = 0.0, + bias: bool = True, + drop_path_rate: float = 0.0, + norm_layer: nn.Module = nn.LayerNorm, + add_identity: bool = True, + ) -> None: + super().__init__() + self.embed_dims = embed_dims + self.out_dims = out_dims or embed_dims + hidden_dims = feedforward_channels or embed_dims + + self.w12 = nn.Linear(self.embed_dims, 2 * hidden_dims, bias=bias) + + self.norm = norm_layer + + self.w3 = nn.Linear(hidden_dims, self.out_dims, bias=bias) + + if layer_scale_init_value > 0: + self.gamma2 = LayerScale( + dim=embed_dims, layer_scale_init_value=layer_scale_init_value + ) + else: + self.gamma2 = nn.Identity() + + self.dropout_layer = DropPath(drop_path_rate) + self.add_identity = add_identity + + def forward( + self, x: torch.Tensor, identity: Optional[torch.Tensor] = None + ) -> torch.Tensor: + x12 = self.w12(x) + x1, x2 = x12.chunk(2, dim=-1) + hidden = F.silu(x1) * x2 + hidden = self.norm(hidden) + out = self.w3(hidden) + out = self.gamma2(out) + out = self.dropout_layer(out) + + if self.out_dims != self.embed_dims or not self.add_identity: + # due to the dimension inconsistence or user setting + # not to apply residual operation + return out + + if identity is None: + identity = x + return identity + out + + +class SwiGLUFFNFused(SwiGLUFFN): + """SwiGLU FFN layer with fusing. + + Modified from https://github.com/facebookresearch/dinov2/blob/main/dinov2/layers/swiglu_ffn.py + """ # noqa + + def __init__( + self, + embed_dims: int, + feedforward_channels: Optional[int] = None, + out_dims: Optional[int] = None, + layer_scale_init_value: float = 0.0, + bias: bool = True, + ) -> None: + out_dims = out_dims or embed_dims + feedforward_channels = feedforward_channels or embed_dims + feedforward_channels = (int(feedforward_channels * 2 / 3) + 7) // 8 * 8 + super().__init__( + embed_dims=embed_dims, + feedforward_channels=feedforward_channels, + out_dims=out_dims, + layer_scale_init_value=layer_scale_init_value, + bias=bias, + ) diff --git a/src/sam3d_body/models/modules/transformer.py b/src/sam3d_body/models/modules/transformer.py new file mode 100644 index 0000000000000000000000000000000000000000..7f9d776268e8e68dabd6ab07bfb813e143e577aa --- /dev/null +++ b/src/sam3d_body/models/modules/transformer.py @@ -0,0 +1,651 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from typing import Dict, Optional + +import torch +import torch.nn as nn +import torch.nn.functional as F + +from .drop_path import DropPath + +from .layer_scale import LayerScale +from .swiglu_ffn import SwiGLUFFNFused + + +class MLP(nn.Module): + # borrowed from DET R + """Very simple multi-layer perceptron (also called FFN)""" + + def __init__(self, input_dim, hidden_dim, output_dim, num_layers): + super().__init__() + self.num_layers = num_layers + h = [hidden_dim] * (num_layers - 1) + self.layers = nn.ModuleList( + nn.Linear(n, k) for n, k in zip([input_dim] + h, h + [output_dim]) + ) + + def forward(self, x): + for i, layer in enumerate(self.layers): + x = F.relu(layer(x)) if i < self.num_layers - 1 else layer(x) + return x + + +class LayerNorm32(nn.LayerNorm): + def forward(self, x: torch.Tensor) -> torch.Tensor: + return super().forward(x.float()).type(x.dtype) + + +def build_norm_layer(cfg: Dict, num_features: int): + """Build normalization layer. + + Args: + cfg (dict): The norm layer config, which should contain: + + - type (str): Layer type. + - layer args: Args needed to instantiate a norm layer. + - requires_grad (bool, optional): Whether stop gradient updates. + num_features (int): Number of input channels. + postfix (int | str): The postfix to be appended into norm abbreviation + to create named layer. + + Returns: + tuple[str, nn.Module]: The first element is the layer name consisting + of abbreviation and postfix, e.g., bn1, gn. The second element is the + created norm layer. + """ + if not isinstance(cfg, dict): + raise TypeError("cfg must be a dict") + if "type" not in cfg: + raise KeyError('the cfg dict must contain the key "type"') + cfg_ = cfg.copy() + + layer_type = cfg_.pop("type") + if layer_type == "LN": + norm_layer = LayerNorm32 + else: + raise ValueError("Unsupported norm layer: ", layer_type) + + requires_grad = cfg_.pop("requires_grad", True) + cfg_.setdefault("eps", 1e-5) + if norm_layer is not nn.GroupNorm: + layer = norm_layer(num_features, **cfg_) + if layer_type == "SyncBN" and hasattr(layer, "_specify_ddp_gpu_num"): + layer._specify_ddp_gpu_num(1) + else: + assert "num_groups" in cfg_ + layer = norm_layer(num_channels=num_features, **cfg_) + + for param in layer.parameters(): + param.requires_grad = requires_grad + + return layer + + +class LayerNorm2d(nn.Module): + def __init__(self, num_channels: int, eps: float = 1e-6) -> None: + super().__init__() + self.weight = nn.Parameter(torch.ones(num_channels)) + self.bias = nn.Parameter(torch.zeros(num_channels)) + self.eps = eps + + def forward(self, x: torch.Tensor) -> torch.Tensor: + u = x.mean(1, keepdim=True) + s = (x - u).pow(2).mean(1, keepdim=True) + x = (x - u) / torch.sqrt(s + self.eps) + x = self.weight[:, None, None] * x + self.bias[:, None, None] + return x + + +class FFN(nn.Module): + """Implements feed-forward networks (FFNs) with identity connection. + + Args: + embed_dims (int): The feature dimension. Same as + `MultiheadAttention`. Defaults: 256. + feedforward_channels (int): The hidden dimension of FFNs. + Defaults: 1024. + num_fcs (int, optional): The number of fully-connected layers in + FFNs. Default: 2. + act_layer (nn.Module, optional): The activation layer for FFNs. + Default: nn.ReLU + ffn_drop (float, optional): Probability of an element to be + zeroed in FFN. Default 0.0. + add_identity (bool, optional): Whether to add the + identity connection. Default: `True`. + drop_path_rate (float): Stochastic depth rate. Defaults to 0. + layer_scale_init_value (float): Initial value of scale factor in + LayerScale. Default: 1.0 + """ + + # @deprecated_api_warning( + # { + # 'dropout': 'ffn_drop', + # 'add_residual': 'add_identity' + # }, + # cls_name='FFN') + def __init__( + self, + embed_dims=256, + feedforward_channels=1024, + output_dims=None, + num_fcs=2, + act_layer=nn.ReLU, + ffn_drop=0.0, + drop_path_rate=0.0, + add_identity=True, + layer_scale_init_value=0.0, + ): + super().__init__() + self.embed_dims = embed_dims + self.feedforward_channels = feedforward_channels + self.output_dims = output_dims or embed_dims + self.num_fcs = num_fcs + + layers = [] + in_channels = embed_dims + for _ in range(num_fcs - 1): + layers.append( + nn.Sequential( + nn.Linear(in_channels, feedforward_channels), + act_layer(), + nn.Dropout(ffn_drop), + ) + ) + in_channels = feedforward_channels + layers.append(nn.Linear(in_channels, self.output_dims)) + layers.append(nn.Dropout(ffn_drop)) + self.layers = nn.Sequential(*layers) + self.dropout_layer = ( + DropPath(drop_path_rate) if drop_path_rate > 0.0 else torch.nn.Identity() + ) + self.add_identity = add_identity + + if layer_scale_init_value > 0: + self.gamma2 = LayerScale(embed_dims, scale=layer_scale_init_value) + else: + self.gamma2 = nn.Identity() + + # @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') + def forward(self, x, identity=None): + """Forward function for `FFN`. + + The function would add x to the output tensor if residue is None. + """ + out = self.layers(x) + out = self.gamma2(out) + if not self.add_identity: + return self.dropout_layer(out) + if identity is None: + identity = x + return identity + self.dropout_layer(out) + + +class MultiheadAttention(nn.Module): + """Multi-head Attention Module. + + This module implements multi-head attention that supports different input + dims and embed dims. And it also supports a shortcut from ``value``, which + is useful if input dims is not the same with embed dims. + + Args: + embed_dims (int): The embedding dimension. + num_heads (int): Parallel attention heads. + input_dims (int, optional): The input dimension, and if None, + use ``embed_dims``. Defaults to None. + attn_drop (float): Dropout rate of the dropout layer after the + attention calculation of query and key. Defaults to 0. + proj_drop (float): Dropout rate of the dropout layer after the + output projection. Defaults to 0. + drop_path_rate (float): Stochastic depth rate. Defaults to 0. + qkv_bias (bool): If True, add a learnable bias to q, k, v. + Defaults to True. + qk_scale (float, optional): Override default qk scale of + ``head_dim ** -0.5`` if set. Defaults to None. + proj_bias (bool) If True, add a learnable bias to output projection. + Defaults to True. + v_shortcut (bool): Add a shortcut from value to output. It's usually + used if ``input_dims`` is different from ``embed_dims``. + Defaults to False. + use_layer_scale (bool): Whether to use layer scale. Defaults to False. + layer_scale_init_value (float or torch.Tensor): Init value of layer + scale. Defaults to 0. + """ + + def __init__( + self, + embed_dims, + num_heads, + input_dims=None, + attn_drop=0.0, + proj_drop=0.0, + drop_path_rate=0.0, + qkv_bias=True, + proj_bias=True, + v_shortcut=False, + layer_scale_init_value=0.0, + ): + super().__init__() + + self.input_dims = input_dims or embed_dims + self.embed_dims = embed_dims + self.num_heads = num_heads + self.v_shortcut = v_shortcut + + self.head_dims = embed_dims // num_heads + + self.qkv = nn.Linear(self.input_dims, embed_dims * 3, bias=qkv_bias) + self.attn_drop = attn_drop + self.proj = nn.Linear(embed_dims, embed_dims, bias=proj_bias) + self.proj_drop = nn.Dropout(proj_drop) + + self.out_drop = DropPath(drop_path_rate) + + if layer_scale_init_value > 0: + layer_scale_init_value = layer_scale_init_value or 1e-5 + self.gamma1 = LayerScale( + embed_dims, layer_scale_init_value=layer_scale_init_value + ) + else: + self.gamma1 = nn.Identity() + + def forward(self, x): + B, N, _ = x.shape + qkv = ( + self.qkv(x) + .reshape(B, N, 3, self.num_heads, self.head_dims) + .permute(2, 0, 3, 1, 4) + ) + q, k, v = qkv[0], qkv[1], qkv[2] + + attn_drop = self.attn_drop if self.training else 0.0 + x = F.scaled_dot_product_attention(q, k, v, dropout_p=attn_drop) + x = x.transpose(1, 2).reshape(B, N, self.embed_dims) + + x = self.proj(x) + x = self.out_drop(self.gamma1(self.proj_drop(x))) + + if self.v_shortcut: + x = v.squeeze(1) + x + return x + + +class Attention(nn.Module): + """Multi-head Attention Module for both self and cross attention. + + Support masking invalid elements for attention. + + Args: + embed_dims (int): The embedding dimension. + num_heads (int): Parallel attention heads. + input_dims (int, optional): The input dimension, and if None, + use ``embed_dims``. Defaults to None. + attn_drop (float): Dropout rate of the dropout layer after the + attention calculation of query and key. Defaults to 0. + proj_drop (float): Dropout rate of the dropout layer after the + output projection. Defaults to 0. + drop_path_rate (float): Stochastic depth rate. Defaults to 0. + qkv_bias (bool): If True, add a learnable bias to q, k, v. + Defaults to True. + qk_scale (float, optional): Override default qk scale of + ``head_dim ** -0.5`` if set. Defaults to None. + proj_bias (bool) If True, add a learnable bias to output projection. + Defaults to True. + v_shortcut (bool): Add a shortcut from value to output. It's usually + used if ``input_dims`` is different from ``embed_dims``. + Defaults to False. + use_layer_scale (bool): Whether to use layer scale. Defaults to False. + layer_scale_init_value (float or torch.Tensor): Init value of layer + scale. Defaults to 0. + """ + + def __init__( + self, + embed_dims, + num_heads, + query_dims=None, + key_dims=None, + value_dims=None, + attn_drop=0.0, + proj_drop=0.0, + drop_path_rate=0.0, + qkv_bias=True, + proj_bias=True, + v_shortcut=False, + layer_scale_init_value=0.0, + ): + super().__init__() + + self.query_dims = query_dims or embed_dims + self.key_dims = key_dims or embed_dims + self.value_dims = value_dims or embed_dims + self.embed_dims = embed_dims + self.num_heads = num_heads + self.v_shortcut = v_shortcut + + self.head_dims = embed_dims // num_heads + + self.q_proj = nn.Linear(self.query_dims, embed_dims, bias=qkv_bias) + self.k_proj = nn.Linear(self.key_dims, embed_dims, bias=qkv_bias) + self.v_proj = nn.Linear(self.value_dims, embed_dims, bias=qkv_bias) + self.attn_drop = attn_drop + self.proj = nn.Linear(embed_dims, self.query_dims, bias=proj_bias) + self.proj_drop = nn.Dropout(proj_drop) + + self.out_drop = DropPath(drop_path_rate) + + if layer_scale_init_value > 0: + layer_scale_init_value = layer_scale_init_value or 1e-5 + self.gamma1 = LayerScale( + embed_dims, layer_scale_init_value=layer_scale_init_value + ) + else: + self.gamma1 = nn.Identity() + + def _separate_heads(self, x: torch.Tensor) -> torch.Tensor: + b, n, _ = x.shape + x = x.reshape(b, n, self.num_heads, self.head_dims) + return x.transpose(1, 2) # B x N_heads x N_tokens x C_per_head + + def forward( + self, + q: torch.Tensor, + k: torch.Tensor, + v: torch.Tensor, + attn_mask: Optional[torch.Tensor] = None, + ): + B, N, _ = q.shape + q = self._separate_heads(self.q_proj(q)) + k = self._separate_heads(self.k_proj(k)) + v = self._separate_heads(self.v_proj(v)) + + attn_drop = self.attn_drop if self.training else 0.0 + if attn_mask is not None: + attn_mask = attn_mask.unsqueeze(1).expand(-1, self.num_heads, -1, -1) + + x = F.scaled_dot_product_attention( + q, k, v, attn_mask=attn_mask, dropout_p=attn_drop + ) + x = x.transpose(1, 2).reshape(B, N, self.embed_dims) + + x = self.proj(x) + x = self.out_drop(self.gamma1(self.proj_drop(x))) + + if self.v_shortcut: + x = v.squeeze(1) + x + return x + + +class TransformerEncoderLayer(nn.Module): + """Implements one encoder layer in Vision Transformer. + + Args: + embed_dims (int): The feature dimension + num_heads (int): Parallel attention heads + feedforward_channels (int): The hidden dimension for FFNs + layer_scale_init_value (float or torch.Tensor): Init value of layer + scale. Defaults to 0. + drop_rate (float): Probability of an element to be zeroed + after the feed forward layer. Defaults to 0. + attn_drop_rate (float): The drop out rate for attention output weights. + Defaults to 0. + drop_path_rate (float): Stochastic depth rate. Defaults to 0. + num_fcs (int): The number of fully-connected layers for FFNs. + Defaults to 2. + qkv_bias (bool): enable bias for qkv if True. Defaults to True. + ffn_type (str): Select the type of ffn layers. Defaults to 'origin'. + act_layer (nn.Module, optional): The activation layer for FFNs. + Default: nn.GELU + norm_cfg (dict): Config dict for normalization layer. + Defaults to ``dict(type='LN')``. + """ + + def __init__( + self, + embed_dims, + num_heads, + feedforward_channels, + layer_scale_init_value=0.0, + drop_rate=0.0, + attn_drop_rate=0.0, + drop_path_rate=0.0, + num_fcs=2, + qkv_bias=True, + ffn_type="origin", + act_layer=nn.GELU, + norm_cfg=dict(type="LN", eps=1e-6), + ): + super().__init__() + + self.embed_dims = embed_dims + + self.ln1 = build_norm_layer(norm_cfg, self.embed_dims) + + self.attn = MultiheadAttention( + embed_dims=embed_dims, + num_heads=num_heads, + attn_drop=attn_drop_rate, + proj_drop=drop_rate, + drop_path_rate=drop_path_rate, + qkv_bias=qkv_bias, + layer_scale_init_value=layer_scale_init_value, + ) + + self.ln2 = build_norm_layer(norm_cfg, self.embed_dims) + + if ffn_type == "origin": + self.ffn = FFN( + embed_dims=embed_dims, + feedforward_channels=feedforward_channels, + num_fcs=num_fcs, + ffn_drop=drop_rate, + drop_path_rate=drop_path_rate, + act_layer=act_layer, + layer_scale_init_value=layer_scale_init_value, + ) + elif ffn_type == "swiglu_fused": + self.ffn = SwiGLUFFNFused( + embed_dims=embed_dims, + feedforward_channels=feedforward_channels, + layer_scale_init_value=layer_scale_init_value, + ) + else: + raise NotImplementedError + + @property + def norm1(self): + return self.ln1 + + @property + def norm2(self): + return self.ln2 + + def forward(self, x): + x = x + self.attn(self.ln1(x)) + x = self.ffn(self.ln2(x), identity=x) + return x + + +class TransformerDecoderLayer(nn.Module): + """Implements one decoder layer in cross-attention Transformer. + + Adapted from Segment Anything Model (SAM) implementation. + + Args: + embed_dims (int): The feature dimension + num_heads (int): Parallel attention heads + feedforward_channels (int): The hidden dimension for FFNs + layer_scale_init_value (float or torch.Tensor): Init value of layer + scale. Defaults to 0. + drop_rate (float): Probability of an element to be zeroed + after the feed forward layer. Defaults to 0. + attn_drop_rate (float): The drop out rate for attention output weights. + Defaults to 0. + drop_path_rate (float): Stochastic depth rate. Defaults to 0. + num_fcs (int): The number of fully-connected layers for FFNs. + Defaults to 2. + qkv_bias (bool): enable bias for qkv if True. Defaults to True. + ffn_type (str): Select the type of ffn layers. Defaults to 'origin'. + act_layer (nn.Module, optional): The activation layer for FFNs. + Default: nn.GELU + norm_cfg (dict): Config dict for normalization layer. + Defaults to ``dict(type='LN')``. + enable_twoway (bool): Whether to enable two-way Transformer (used in SAM). + repeat_pe (bool): Whether to re-add PE at each layer (used in SAM) + skip_first_pe (bool) + """ + + def __init__( + self, + token_dims: int, + context_dims: int, + num_heads: int = 8, + head_dims: int = 64, + mlp_dims: int = 1024, + layer_scale_init_value: float = 0.0, + drop_rate: float = 0.0, + attn_drop_rate: float = 0.0, + drop_path_rate: float = 0.0, + ffn_type: str = "origin", + act_layer: type[nn.Module] | nn.Module = nn.GELU, + norm_cfg: Dict = dict(type="LN", eps=1e-6), + enable_twoway: bool = False, + repeat_pe: bool = False, + skip_first_pe: bool = False, + ): + super().__init__() + self.repeat_pe = repeat_pe + self.skip_first_pe = skip_first_pe + if self.repeat_pe: + self.ln_pe_1 = build_norm_layer(norm_cfg, token_dims) + self.ln_pe_2 = build_norm_layer(norm_cfg, context_dims) + + self.ln1 = build_norm_layer(norm_cfg, token_dims) + + self.self_attn = Attention( + embed_dims=num_heads * head_dims, + num_heads=num_heads, + query_dims=token_dims, + key_dims=token_dims, + value_dims=token_dims, + attn_drop=attn_drop_rate, + proj_drop=drop_rate, + drop_path_rate=drop_path_rate, + layer_scale_init_value=layer_scale_init_value, + ) + + self.ln2_1 = build_norm_layer(norm_cfg, token_dims) + self.ln2_2 = build_norm_layer(norm_cfg, context_dims) + + self.cross_attn = Attention( + embed_dims=num_heads * head_dims, + num_heads=num_heads, + query_dims=token_dims, + key_dims=context_dims, + value_dims=context_dims, + attn_drop=attn_drop_rate, + proj_drop=drop_rate, + drop_path_rate=drop_path_rate, + layer_scale_init_value=layer_scale_init_value, + ) + + self.ln3 = build_norm_layer(norm_cfg, token_dims) + + if ffn_type == "origin": + self.ffn = FFN( + embed_dims=token_dims, + feedforward_channels=mlp_dims, + ffn_drop=drop_rate, + drop_path_rate=drop_path_rate, + act_layer=act_layer, + layer_scale_init_value=layer_scale_init_value, + ) + elif ffn_type == "swiglu_fused": + self.ffn = SwiGLUFFNFused( + embed_dims=token_dims, + feedforward_channels=mlp_dims, + layer_scale_init_value=layer_scale_init_value, + ) + else: + raise NotImplementedError + + self.enable_twoway = enable_twoway + if self.enable_twoway: + self.ln4_1 = build_norm_layer(norm_cfg, context_dims) + self.ln4_2 = build_norm_layer(norm_cfg, token_dims) + + self.cross_attn_2 = Attention( + embed_dims=num_heads * head_dims, + num_heads=num_heads, + query_dims=context_dims, + key_dims=token_dims, + value_dims=token_dims, + attn_drop=attn_drop_rate, + proj_drop=drop_rate, + drop_path_rate=drop_path_rate, + layer_scale_init_value=layer_scale_init_value, + ) + + def forward( + self, + x: torch.Tensor, + context: torch.Tensor, + x_pe: Optional[torch.Tensor] = None, + context_pe: Optional[torch.Tensor] = None, + x_mask: Optional[torch.Tensor] = None, + ): + """ + Args: + x: shape [B, N, C] + context: shape [B, N, C] + x_mask: shape [B, N] + """ + if self.repeat_pe and context_pe is not None: + # LaPE: https://openaccess.thecvf.com/content/ICCV2023/papers/Yu_LaPE_Layer-adaptive_Position_Embedding_for_Vision_Transformers_with_Independent_Layer_ICCV_2023_paper.pdf + x_pe = self.ln_pe_1(x_pe) + context_pe = self.ln_pe_2(context_pe) + + # Self attention block for tokens + if self.repeat_pe and not self.skip_first_pe and x_pe is not None: + q = k = self.ln1(x) + x_pe + v = self.ln1(x) + else: + q = k = v = self.ln1(x) + + attn_mask = None + if x_mask is not None: + attn_mask = x_mask[:, :, None] @ x_mask[:, None, :] + # Set diagonal to 1 to prevent nan output + attn_mask.diagonal(dim1=1, dim2=2).fill_(1) + attn_mask = attn_mask > 0 + x = x + self.self_attn(q=q, k=k, v=v, attn_mask=attn_mask) + + # Cross attention block, tokens attending to image embedding + if self.repeat_pe and context_pe is not None: + q = self.ln2_1(x) + x_pe + k = self.ln2_2(context) + context_pe + v = self.ln2_2(context) + else: + q = self.ln2_1(x) + k = v = self.ln2_2(context) + x = x + self.cross_attn(q=q, k=k, v=v) + + # MLP block + x = self.ffn(self.ln3(x), identity=x) + + # (Optional) Cross attention block, image embeddings attending to tokens + if self.enable_twoway: + if self.repeat_pe and context_pe is not None: + q = self.ln4_1(context) + context_pe + k = self.ln4_2(x) + x_pe + v = self.ln4_2(x) + else: + q = self.ln4_1(context) + k = v = self.ln4_2(x) + attn_mask = ( + (x_mask[:, None, :].repeat(1, context.shape[1], 1)) > 0 + if x_mask is not None + else None + ) + context = context + self.cross_attn_2(q=q, k=k, v=v, attn_mask=attn_mask) + + return x, context diff --git a/src/sam3d_body/models/optim/__init__.py b/src/sam3d_body/models/optim/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..71ca4b12c770afea62d06f97064cdf0c97d40ed7 --- /dev/null +++ b/src/sam3d_body/models/optim/__init__.py @@ -0,0 +1 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. diff --git a/src/sam3d_body/models/optim/fp16_utils.py b/src/sam3d_body/models/optim/fp16_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..76ebfc6f719ac62136237e4a8add7ba71ae79d76 --- /dev/null +++ b/src/sam3d_body/models/optim/fp16_utils.py @@ -0,0 +1,66 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import torch +import torch.nn as nn + +# FP16_TYPE = torch.float16 + +FP16_MODULES = [ + nn.Conv1d, + nn.Conv2d, + nn.Conv3d, + nn.ConvTranspose1d, + nn.ConvTranspose2d, + nn.ConvTranspose3d, + nn.Linear, +] + +FP16_MODULES = tuple(FP16_MODULES) + + +def convert_to_fp16_safe(module, dtype=torch.float16): + for child in module.children(): + convert_to_fp16_safe(child, dtype) + if not isinstance(module, (nn.LayerNorm, nn.BatchNorm1d, nn.BatchNorm2d)): + module.to(dtype) + + +def convert_module_to_f16(l, dtype=torch.float16): + """ + Convert primitive modules to float16. + """ + if isinstance(l, FP16_MODULES): + for p in l.parameters(): + # p.data = p.data.half() + p.data = p.data.to(dtype) + + +def convert_module_to_f32(l): + """ + Convert primitive modules to float32, undoing convert_module_to_f16(). + """ + if isinstance(l, FP16_MODULES): + for p in l.parameters(): + p.data = p.data.float() + + +def zero_module(module): + """ + Zero out the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().zero_() + return module + + +def scale_module(module, scale): + """ + Scale the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().mul_(scale) + return module + + +def modulate(x, shift, scale): + return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1) diff --git a/src/sam3d_body/sam_3d_body_estimator.py b/src/sam3d_body/sam_3d_body_estimator.py new file mode 100644 index 0000000000000000000000000000000000000000..931e723be78461965e38585920eb24f603dbaaeb --- /dev/null +++ b/src/sam3d_body/sam_3d_body_estimator.py @@ -0,0 +1,281 @@ +"""Core inference utilities for SAM 3D Body model (hands + body fusion).""" + +from collections.abc import Callable +from typing import Any, Literal, cast + +import numpy as np +import torch +from jaxtyping import Float, Int, UInt8 +from numpy import ndarray +from serde import from_dict, serde +from torch import Tensor +from torchvision.transforms import ToTensor + +from sam3d_body.data.transforms import ( + Compose, + GetBBoxCenterScale, + TopdownAffine, + VisionTransformWrapper, +) +from sam3d_body.data.utils.prepare_batch import PreparedBatchDict, prepare_batch +from sam3d_body.models.meta_arch import SAM3DBody +from sam3d_body.models.meta_arch.sam3d_body import BodyPredContainer +from sam3d_body.utils import recursive_to + + +@serde +class PoseOutputsNP: + """Batch pose outputs in numpy form straight from the model forward pass.""" + + pred_pose_raw: Float[ndarray, "n pose_raw=266"] + """Raw 266D pose vector per item (SMPL-X ordering).""" + pred_pose_rotmat: Float[ndarray, ""] | None + """Optional rotation matrices derived from ``pred_pose_raw``.""" + global_rot: Float[ndarray, "n 3"] + """Root/global rotation in radians for each item (XYZ Euler).""" + body_pose: Float[ndarray, "n body_pose_params=133"] + """Body pose parameters (133D continuous) per item.""" + shape: Float[ndarray, "n shape_params=45"] + """Body shape PCA coefficients (45D) per item.""" + scale: Float[ndarray, "n scale_params=28"] + """Body scale PCA coefficients (28D) per item.""" + hand: Float[ndarray, "n hand_pose_params=108"] + """Hand pose parameters in PCA space (108D) per item.""" + face: Float[ndarray, "n expr_params=72"] + """Facial expression PCA coefficients (72D) per item.""" + pred_keypoints_3d: Float[ndarray, "n joints3d 3"] + """3D keypoints in camera coordinates for each item.""" + pred_vertices: Float[ndarray, "n verts=18439 3"] + """Full mesh vertices in camera coordinates per item.""" + pred_joint_coords: Float[ndarray, "n joints3d 3"] + """Internal skeleton joint centers (camera coordinates) per item.""" + faces: Int[ndarray, "faces 3"] + """Mesh face indices shared across items.""" + joint_global_rots: Float[ndarray, "n joints_rot 3 3"] + """Global rotation matrices per joint for each item.""" + mhr_model_params: Float[ndarray, "n mhr_params"] + """Model hyper-regularization parameters per item.""" + pred_cam: Float[ndarray, "n 3"] + """Weak-perspective camera parameters (sx, sy, tx) per item.""" + pred_keypoints_2d_verts: Float[ndarray, "n verts 2"] + """2D projected vertices per item (pixels).""" + pred_keypoints_2d: Float[ndarray, "n joints2d 2"] + """2D projected keypoints per item (pixels).""" + pred_cam_t: Float[ndarray, "n 3"] + """Camera-space translation vectors applied to each mesh.""" + focal_length: Float[ndarray, "n"] + """Focal lengths per item (pixels).""" + pred_keypoints_2d_depth: Float[ndarray, "n joints2d"] + """Depth values for 2D keypoints per item.""" + pred_keypoints_2d_cropped: Float[ndarray, "n joints2d 2"] + """2D keypoints in the cropped input frame per item (pixels).""" + + +@serde +class FinalPosePrediction: + """Per-person prediction bundle returned by SAM 3D Body.""" + + bbox: Float[ndarray, "4"] + """Axis-aligned XYXY box in the original image (pixels).""" + focal_length: Float[ndarray, ""] + """Scalar focal length for the frame (pixels).""" + pred_keypoints_3d: Float[ndarray, "joints 3"] + """3D keypoints in camera coordinates (OpenCV: x right, y down, z forward).""" + pred_keypoints_2d: Float[ndarray, "joints 2"] + """2D keypoints in image pixel coordinates.""" + pred_vertices: Float[ndarray, "verts 3"] + """Full body mesh vertices in camera coordinates.""" + pred_cam_t: Float[ndarray, "3"] + """Camera-space translation (x, y, z) applied to the mesh.""" + pred_pose_raw: Float[ndarray, "pose_params=266"] + """Raw 266D pose vector (SMPL-X style ordering).""" + global_rot: Float[ndarray, "3"] + """Root/global rotation in radians (XYZ Euler).""" + body_pose_params: Float[ndarray, "body_pose_params=133"] + """Body pose parameters (133D continuous).""" + hand_pose_params: Float[ndarray, "hand_pose_params=108"] + """Hand pose parameters (108D PCA space).""" + scale_params: Float[ndarray, "scale_params=28"] + """Body scale PCA coefficients (28D).""" + shape_params: Float[ndarray, "shape_params=45"] + """Body shape PCA coefficients (45D).""" + expr_params: Float[ndarray, "expr_params=72"] + """Facial expression PCA coefficients (72D).""" + mask: UInt8[ndarray, "h w 1"] | None = None + """Optional instance segmentation mask (Hร—Wร—1, uint8).""" + pred_joint_coords: Float[ndarray, "joints 3"] | None = None + """Full internal skeleton joint centers (camera coordinates).""" + pred_global_rots: Float[ndarray, "joints 3 3"] | None = None + """Global rotation matrices per joint aligned with ``pred_joint_coords``.""" + lhand_bbox: Float[ndarray, "4"] | None = None + """Optional left-hand XYXY box in the original image (pixels).""" + rhand_bbox: Float[ndarray, "4"] | None = None + """Optional right-hand XYXY box in the original image (pixels).""" + + +Transform = Callable[[dict], dict | None] + + +class SAM3DBodyEstimator: + """Wraps the SAM 3D Body meta-architecture for single-frame inference.""" + + def __init__( + self, + sam_3d_body_model: SAM3DBody, + ) -> None: + """Initialize preprocessing pipelines and cache reusable assets. + + Args: + sam_3d_body_model: Loaded ``SAM3DBody`` instance (checkpoints already restored). + """ + self.model: SAM3DBody = sam_3d_body_model + self.thresh_wrist_angle: float = 1.4 + + # For mesh visualization + self.faces: Int[ndarray, "n_faces=36874 3"] = self.model.head_pose.faces.cpu().numpy() # type: ignore + + # Define transforms + body_transforms: list[Transform] = [ + cast(Transform, GetBBoxCenterScale()), + cast(Transform, TopdownAffine(input_size=512, use_udp=False)), + cast(Transform, VisionTransformWrapper(ToTensor())), + ] + hand_transforms: list[Transform] = [ + cast(Transform, GetBBoxCenterScale(padding=0.9)), + cast(Transform, TopdownAffine(input_size=512, use_udp=False)), + cast(Transform, VisionTransformWrapper(ToTensor())), + ] + + self.transform: Compose = Compose(body_transforms) + self.transform_hand: Compose = Compose(hand_transforms) + + @torch.no_grad() + def process_one_image( + self, + rgb_hw3: UInt8[ndarray, "h w 3"], + xyxy: Float[ndarray, "n 4"] | None = None, + masks: Float[ndarray, "n h w"] | None = None, + masks_score: Float[ndarray, "n"] | None = None, + K_33: Float[ndarray, "3 3"] | None = None, + inference_type: Literal["full", "body", "hand"] = "full", + ) -> list[FinalPosePrediction]: + """Run full SAM 3D Body inference for one RGB frame. + + Args: + rgb_hw3: Input image in RGB order with dtype ``uint8`` and shape ``[H, W, 3]``. + xyxy: Optional person boxes (XYXY, pixels) to bypass detector; defaults to the + full-frame box when ``None``. + masks: Optional binary instance masks aligned with ``xyxy`` (shape ``[N, H, W]``); + when provided, segmentation is skipped. + masks_score: Optional confidence scores for ``masks``. + K_33: Optional camera intrinsic matrix ``[3, 3]``; if ``None``, the model will rely on + its default relative-FOV heuristic. Intrinsics follow the project convention + of mapping world points into the camera frame via ``cam_T_world`` style matrices. + inference_type: Controls which decoders run: ``"full"`` (body + hands), ``"body"`` + (body-only), or ``"hand"`` (hand-only output paths). + + Returns: + A list of ``FinalPosePrediction`` structures, one per detected person. + """ + + height: int = rgb_hw3.shape[0] + width: int = rgb_hw3.shape[1] + + if xyxy is None: + xyxy = np.array([0, 0, width, height], dtype=np.float32).reshape(1, 4) + + # If there are no detected humans, don't run prediction + if len(xyxy) == 0: + return [] + + # number of people detected + n_dets: int = xyxy.shape[0] + + #################### Construct batch data samples #################### + batch: PreparedBatchDict = prepare_batch(rgb_hw3, self.transform, xyxy, masks, masks_score) + + #################### Run model inference on an image #################### + batch: PreparedBatchDict = recursive_to(batch, "cuda") + self.model._initialize_batch(batch) + batch_img: Float[Tensor, "B=1 N 3 H W"] = batch["img"] + + # Handle camera intrinsics + # - either provided externally or generated via default FOV estimator + if K_33 is None: + print("") + else: + K_b33: Float[Tensor, "b=1 3 3"] = torch.as_tensor( + K_33[np.newaxis, ...], device=batch_img.device, dtype=batch_img.dtype + ) + batch["cam_int"] = K_b33.clone() + + outputs: BodyPredContainer = self.model.run_inference( + rgb_hw3, + batch, + inference_type=inference_type, + transform_hand=self.transform_hand, + thresh_wrist_angle=self.thresh_wrist_angle, + ) + pose_output: dict[str, Any] = outputs.pose_output + batch_lhand: dict[str, Any] | None = outputs.batch_lhand + batch_rhand: dict[str, Any] | None = outputs.batch_rhand + + mhr_dict: dict[str, Any] = pose_output["mhr"] + out_np_dict: dict[str, ndarray] = cast(dict[str, ndarray], recursive_to(recursive_to(mhr_dict, "cpu"), "numpy")) + out_np: PoseOutputsNP = from_dict(PoseOutputsNP, out_np_dict) + + all_out: list[FinalPosePrediction] = [] + bbox_tensor: Float[Tensor, "B=1 N 4"] = batch["bbox"] + + for idx in range(n_dets): + mask_arr: UInt8[ndarray, "h w 1"] | None = None + if masks is not None: + mask_arr = masks[idx] + if mask_arr.ndim == 2: + mask_arr = mask_arr[..., np.newaxis] + mask_arr = (mask_arr > 0.5).astype(np.uint8, copy=False) + pred = FinalPosePrediction( + bbox=bbox_tensor[0, idx].cpu().numpy(), + focal_length=np.asarray(out_np.focal_length[idx]), + pred_keypoints_3d=out_np.pred_keypoints_3d[idx], + pred_keypoints_2d=out_np.pred_keypoints_2d[idx], + pred_vertices=out_np.pred_vertices[idx], + pred_cam_t=out_np.pred_cam_t[idx], + pred_pose_raw=out_np.pred_pose_raw[idx], + global_rot=out_np.global_rot[idx], + body_pose_params=out_np.body_pose[idx], + hand_pose_params=out_np.hand[idx], + scale_params=out_np.scale[idx], + shape_params=out_np.shape[idx], + expr_params=out_np.face[idx], + mask=mask_arr, + pred_joint_coords=out_np.pred_joint_coords[idx], + pred_global_rots=out_np.joint_global_rots[idx], + ) + + if inference_type == "full" and batch_lhand is not None and batch_rhand is not None: + lhand_center = batch_lhand["bbox_center"].flatten(0, 1)[idx] + lhand_scale = batch_lhand["bbox_scale"].flatten(0, 1)[idx] + pred.lhand_bbox = np.array( + [ + (lhand_center[0] - lhand_scale[0] / 2).item(), + (lhand_center[1] - lhand_scale[1] / 2).item(), + (lhand_center[0] + lhand_scale[0] / 2).item(), + (lhand_center[1] + lhand_scale[1] / 2).item(), + ] + ) + + rhand_center = batch_rhand["bbox_center"].flatten(0, 1)[idx] + rhand_scale = batch_rhand["bbox_scale"].flatten(0, 1)[idx] + pred.rhand_bbox = np.array( + [ + (rhand_center[0] - rhand_scale[0] / 2).item(), + (rhand_center[1] - rhand_scale[1] / 2).item(), + (rhand_center[0] + rhand_scale[0] / 2).item(), + (rhand_center[1] + rhand_scale[1] / 2).item(), + ] + ) + + all_out.append(pred) + + return all_out diff --git a/src/sam3d_body/transforms/__init__.py b/src/sam3d_body/transforms/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a4830beaed1e5c2ac655dd6f5f9a0ad3042478cb --- /dev/null +++ b/src/sam3d_body/transforms/__init__.py @@ -0,0 +1,21 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from .bbox_utils import ( + bbox_cs2xywh, + bbox_cs2xyxy, + bbox_xywh2cs, + bbox_xywh2xyxy, + bbox_xyxy2cs, + bbox_xyxy2xywh, + flip_bbox, + get_udp_warp_matrix, + get_warp_matrix, +) +from .common import ( + Compose, + GetBBoxCenterScale, + NormalizeKeypoint, + SquarePad, + TopdownAffine, + VisionTransformWrapper, +) diff --git a/src/sam3d_body/transforms/bbox_utils.py b/src/sam3d_body/transforms/bbox_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..a10a3821e2a66e41b926de48d79d57ef4efcf88b --- /dev/null +++ b/src/sam3d_body/transforms/bbox_utils.py @@ -0,0 +1,397 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import math +from typing import Tuple + +import cv2 +import numpy as np + + +def bbox_xyxy2xywh(bbox_xyxy: np.ndarray) -> np.ndarray: + """Transform the bbox format from x1y1x2y2 to xywh. + + Args: + bbox_xyxy (np.ndarray): Bounding boxes (with scores), shaped (n, 4) or + (n, 5). (left, top, right, bottom, [score]) + + Returns: + np.ndarray: Bounding boxes (with scores), + shaped (n, 4) or (n, 5). (left, top, width, height, [score]) + """ + bbox_xywh = bbox_xyxy.copy() + bbox_xywh[:, 2] = bbox_xywh[:, 2] - bbox_xywh[:, 0] + bbox_xywh[:, 3] = bbox_xywh[:, 3] - bbox_xywh[:, 1] + + return bbox_xywh + + +def bbox_xywh2xyxy(bbox_xywh: np.ndarray) -> np.ndarray: + """Transform the bbox format from xywh to x1y1x2y2. + + Args: + bbox_xywh (ndarray): Bounding boxes (with scores), + shaped (n, 4) or (n, 5). (left, top, width, height, [score]) + Returns: + np.ndarray: Bounding boxes (with scores), shaped (n, 4) or + (n, 5). (left, top, right, bottom, [score]) + """ + bbox_xyxy = bbox_xywh.copy() + bbox_xyxy[:, 2] = bbox_xyxy[:, 2] + bbox_xyxy[:, 0] + bbox_xyxy[:, 3] = bbox_xyxy[:, 3] + bbox_xyxy[:, 1] + + return bbox_xyxy + + +def bbox_xyxy2cs( + bbox: np.ndarray, padding: float = 1.0 +) -> Tuple[np.ndarray, np.ndarray]: + """Transform the bbox format from (x,y,w,h) into (center, scale) + + Args: + bbox (ndarray): Bounding box(es) in shape (4,) or (n, 4), formatted + as (left, top, right, bottom) + padding (float): BBox padding factor that will be multilied to scale. + Default: 1.0 + + Returns: + tuple: A tuple containing center and scale. + - np.ndarray[float32]: Center (x, y) of the bbox in shape (2,) or + (n, 2) + - np.ndarray[float32]: Scale (w, h) of the bbox in shape (2,) or + (n, 2) + """ + # convert single bbox from (4, ) to (1, 4) + dim = bbox.ndim + if dim == 1: + bbox = bbox[None, :] + + x1, y1, x2, y2 = np.hsplit(bbox, [1, 2, 3]) + center = np.hstack([x1 + x2, y1 + y2]) * 0.5 + scale = np.hstack([x2 - x1, y2 - y1]) * padding + + if dim == 1: + center = center[0] + scale = scale[0] + + return center, scale + + +def bbox_xywh2cs( + bbox: np.ndarray, padding: float = 1.0 +) -> Tuple[np.ndarray, np.ndarray]: + """Transform the bbox format from (x,y,w,h) into (center, scale) + + Args: + bbox (ndarray): Bounding box(es) in shape (4,) or (n, 4), formatted + as (x, y, h, w) + padding (float): BBox padding factor that will be multilied to scale. + Default: 1.0 + + Returns: + tuple: A tuple containing center and scale. + - np.ndarray[float32]: Center (x, y) of the bbox in shape (2,) or + (n, 2) + - np.ndarray[float32]: Scale (w, h) of the bbox in shape (2,) or + (n, 2) + """ + + # convert single bbox from (4, ) to (1, 4) + dim = bbox.ndim + if dim == 1: + bbox = bbox[None, :] + + x, y, w, h = np.hsplit(bbox, [1, 2, 3]) + center = np.hstack([x + w * 0.5, y + h * 0.5]) + scale = np.hstack([w, h]) * padding + + if dim == 1: + center = center[0] + scale = scale[0] + + return center, scale + + +def bbox_cs2xyxy( + center: np.ndarray, scale: np.ndarray, padding: float = 1.0 +) -> np.ndarray: + """Transform the bbox format from (center, scale) to (x1,y1,x2,y2). + + Args: + center (ndarray): BBox center (x, y) in shape (2,) or (n, 2) + scale (ndarray): BBox scale (w, h) in shape (2,) or (n, 2) + padding (float): BBox padding factor that will be multilied to scale. + Default: 1.0 + + Returns: + ndarray[float32]: BBox (x1, y1, x2, y2) in shape (4, ) or (n, 4) + """ + + dim = center.ndim + assert scale.ndim == dim + + if dim == 1: + center = center[None, :] + scale = scale[None, :] + + wh = scale / padding + xy = center - 0.5 * wh + bbox = np.hstack((xy, xy + wh)) + + if dim == 1: + bbox = bbox[0] + + return bbox + + +def bbox_cs2xywh( + center: np.ndarray, scale: np.ndarray, padding: float = 1.0 +) -> np.ndarray: + """Transform the bbox format from (center, scale) to (x,y,w,h). + + Args: + center (ndarray): BBox center (x, y) in shape (2,) or (n, 2) + scale (ndarray): BBox scale (w, h) in shape (2,) or (n, 2) + padding (float): BBox padding factor that will be multilied to scale. + Default: 1.0 + + Returns: + ndarray[float32]: BBox (x, y, w, h) in shape (4, ) or (n, 4) + """ + + dim = center.ndim + assert scale.ndim == dim + + if dim == 1: + center = center[None, :] + scale = scale[None, :] + + wh = scale / padding + xy = center - 0.5 * wh + bbox = np.hstack((xy, wh)) + + if dim == 1: + bbox = bbox[0] + + return bbox + + +def flip_bbox( + bbox: np.ndarray, + image_size: Tuple[int, int], + bbox_format: str = "xywh", + direction: str = "horizontal", +) -> np.ndarray: + """Flip the bbox in the given direction. + + Args: + bbox (np.ndarray): The bounding boxes. The shape should be (..., 4) + if ``bbox_format`` is ``'xyxy'`` or ``'xywh'``, and (..., 2) if + ``bbox_format`` is ``'center'`` + image_size (tuple): The image shape in [w, h] + bbox_format (str): The bbox format. Options are ``'xywh'``, ``'xyxy'`` + and ``'center'``. + direction (str): The flip direction. Options are ``'horizontal'``, + ``'vertical'`` and ``'diagonal'``. Defaults to ``'horizontal'`` + + Returns: + np.ndarray: The flipped bounding boxes. + """ + direction_options = {"horizontal", "vertical", "diagonal"} + assert direction in direction_options, ( + f'Invalid flipping direction "{direction}". ' f"Options are {direction_options}" + ) + + format_options = {"xywh", "xyxy", "center"} + assert bbox_format in format_options, ( + f'Invalid bbox format "{bbox_format}". ' f"Options are {format_options}" + ) + + bbox_flipped = bbox.copy() + w, h = image_size + + if direction == "horizontal": + if bbox_format == "xywh" or bbox_format == "center": + bbox_flipped[..., 0] = w - bbox[..., 0] - 1 + elif bbox_format == "xyxy": + bbox_flipped[..., ::2] = w - bbox[..., ::2] - 1 + elif direction == "vertical": + if bbox_format == "xywh" or bbox_format == "center": + bbox_flipped[..., 1] = h - bbox[..., 1] - 1 + elif bbox_format == "xyxy": + bbox_flipped[..., 1::2] = h - bbox[..., 1::2] - 1 + elif direction == "diagonal": + if bbox_format == "xywh" or bbox_format == "center": + bbox_flipped[..., :2] = [w, h] - bbox[..., :2] - 1 + elif bbox_format == "xyxy": + bbox_flipped[...] = [w, h, w, h] - bbox - 1 + + return bbox_flipped + + +def fix_aspect_ratio(bbox_scale: np.ndarray, aspect_ratio: float): + """Reshape the bbox to a fixed aspect ratio. + + Args: + bbox_scale (np.ndarray): The bbox scales (w, h) in shape (n, 2) + aspect_ratio (float): The ratio of ``w/h`` + + Returns: + np.darray: The reshaped bbox scales in (n, 2) + """ + dim = bbox_scale.ndim + if dim == 1: + bbox_scale = bbox_scale[None, :] + + w, h = np.hsplit(bbox_scale, [1]) + bbox_scale = np.where( + w > h * aspect_ratio, + np.hstack([w, w / aspect_ratio]), + np.hstack([h * aspect_ratio, h]), + ) + if dim == 1: + bbox_scale = bbox_scale[0] + + return bbox_scale + + +def get_udp_warp_matrix( + center: np.ndarray, + scale: np.ndarray, + rot: float, + output_size: Tuple[int, int], +) -> np.ndarray: + """Calculate the affine transformation matrix under the unbiased + constraint. See `UDP (CVPR 2020)`_ for details. + + Note: + + - The bbox number: N + + Args: + center (np.ndarray[2, ]): Center of the bounding box (x, y). + scale (np.ndarray[2, ]): Scale of the bounding box + wrt [width, height]. + rot (float): Rotation angle (degree). + output_size (tuple): Size ([w, h]) of the output image + + Returns: + np.ndarray: A 2x3 transformation matrix + + .. _`UDP (CVPR 2020)`: https://arxiv.org/abs/1911.07524 + """ + assert len(center) == 2 + assert len(scale) == 2 + assert len(output_size) == 2 + + input_size = center * 2 + rot_rad = np.deg2rad(rot) + warp_mat = np.zeros((2, 3), dtype=np.float32) + scale_x = (output_size[0] - 1) / scale[0] + scale_y = (output_size[1] - 1) / scale[1] + warp_mat[0, 0] = math.cos(rot_rad) * scale_x + warp_mat[0, 1] = -math.sin(rot_rad) * scale_x + warp_mat[0, 2] = scale_x * ( + -0.5 * input_size[0] * math.cos(rot_rad) + + 0.5 * input_size[1] * math.sin(rot_rad) + + 0.5 * scale[0] + ) + warp_mat[1, 0] = math.sin(rot_rad) * scale_y + warp_mat[1, 1] = math.cos(rot_rad) * scale_y + warp_mat[1, 2] = scale_y * ( + -0.5 * input_size[0] * math.sin(rot_rad) + - 0.5 * input_size[1] * math.cos(rot_rad) + + 0.5 * scale[1] + ) + return warp_mat + + +def get_warp_matrix( + center: np.ndarray, + scale: np.ndarray, + rot: float, + output_size: Tuple[int, int], + shift: Tuple[float, float] = (0.0, 0.0), + inv: bool = False, +) -> np.ndarray: + """Calculate the affine transformation matrix that can warp the bbox area + in the input image to the output size. + + Args: + center (np.ndarray[2, ]): Center of the bounding box (x, y). + scale (np.ndarray[2, ]): Scale of the bounding box + wrt [width, height]. + rot (float): Rotation angle (degree). + output_size (np.ndarray[2, ] | list(2,)): Size of the + destination heatmaps. + shift (0-100%): Shift translation ratio wrt the width/height. + Default (0., 0.). + inv (bool): Option to inverse the affine transform direction. + (inv=False: src->dst or inv=True: dst->src) + + Returns: + np.ndarray: A 2x3 transformation matrix + """ + assert len(center) == 2 + assert len(scale) == 2 + assert len(output_size) == 2 + assert len(shift) == 2 + + shift = np.array(shift) + src_w = scale[0] + dst_w = output_size[0] + dst_h = output_size[1] + + rot_rad = np.deg2rad(rot) + src_dir = _rotate_point(np.array([0.0, src_w * -0.5]), rot_rad) + dst_dir = np.array([0.0, dst_w * -0.5]) + + src = np.zeros((3, 2), dtype=np.float32) + src[0, :] = center + scale * shift + src[1, :] = center + src_dir + scale * shift + src[2, :] = _get_3rd_point(src[0, :], src[1, :]) + + dst = np.zeros((3, 2), dtype=np.float32) + dst[0, :] = [dst_w * 0.5, dst_h * 0.5] + dst[1, :] = np.array([dst_w * 0.5, dst_h * 0.5]) + dst_dir + dst[2, :] = _get_3rd_point(dst[0, :], dst[1, :]) + + if inv: + warp_mat = cv2.getAffineTransform(np.float32(dst), np.float32(src)) + else: + warp_mat = cv2.getAffineTransform(np.float32(src), np.float32(dst)) + return warp_mat + + +def _rotate_point(pt: np.ndarray, angle_rad: float) -> np.ndarray: + """Rotate a point by an angle. + + Args: + pt (np.ndarray): 2D point coordinates (x, y) in shape (2, ) + angle_rad (float): rotation angle in radian + + Returns: + np.ndarray: Rotated point in shape (2, ) + """ + + sn, cs = np.sin(angle_rad), np.cos(angle_rad) + rot_mat = np.array([[cs, -sn], [sn, cs]]) + return rot_mat @ pt + + +def _get_3rd_point(a: np.ndarray, b: np.ndarray): + """To calculate the affine matrix, three pairs of points are required. This + function is used to get the 3rd point, given 2D points a & b. + + The 3rd point is defined by rotating vector `a - b` by 90 degrees + anticlockwise, using b as the rotation center. + + Args: + a (np.ndarray): The 1st point (x,y) in shape (2, ) + b (np.ndarray): The 2nd point (x,y) in shape (2, ) + + Returns: + np.ndarray: The 3rd point. + """ + direction = a - b + c = b + np.r_[-direction[1], direction[0]] + return c diff --git a/src/sam3d_body/transforms/common.py b/src/sam3d_body/transforms/common.py new file mode 100644 index 0000000000000000000000000000000000000000..28b786236d7a3ef3d53b8cbf085d1fc1d57fa2cf --- /dev/null +++ b/src/sam3d_body/transforms/common.py @@ -0,0 +1,345 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from typing import Callable, Dict, List, Optional, Tuple, Union + +import cv2 +import numpy as np +import torch.nn as nn +import torchvision.transforms.functional as F +from PIL import Image +from sam3d_body.models.modules import to_2tuple + +from .bbox_utils import ( + bbox_cs2xyxy, + bbox_xywh2cs, + bbox_xyxy2cs, + fix_aspect_ratio, + get_udp_warp_matrix, + get_warp_matrix, +) + + +class Compose: + """Compose multiple transforms sequentially. + + Args: + transforms (Sequence[dict, callable], optional): Sequence of transform + object or config dict to be composed. + """ + + def __init__(self, transforms: Optional[List[Callable]] = None): + if transforms is None: + transforms = [] + else: + self.transforms = transforms + + def __call__(self, data: dict) -> Optional[dict]: + """Call function to apply transforms sequentially. + + Args: + data (dict): A result dict contains the data to transform. + + Returns: + dict: Transformed data. + """ + for t in self.transforms: + data = t(data) + # The transform will return None when it failed to load images or + # cannot find suitable augmentation parameters to augment the data. + # Here we simply return None if the transform returns None and the + # dataset will handle it by randomly selecting another data sample. + if data is None: + return None + return data + + def __repr__(self): + """Print ``self.transforms`` in sequence. + + Returns: + str: Formatted string. + """ + format_string = self.__class__.__name__ + "(" + for t in self.transforms: + format_string += "\n" + format_string += f" {t}" + format_string += "\n)" + return format_string + + +class VisionTransformWrapper: + """A wrapper to use torchvision transform functions in this codebase.""" + + def __init__(self, transform: Callable): + self.transform = transform + + def __call__(self, results: Dict) -> Optional[dict]: + results["img"] = self.transform(results["img"]) + return results + + def __repr__(self) -> str: + """print the basic information of the transform. + + Returns: + str: Formatted string. + """ + repr_str = self.transform.__class__.__name__ + return repr_str + + +class GetBBoxCenterScale(nn.Module): + """Convert bboxes to center and scale. + + The center is the coordinates of the bbox center, and the scale is the + bbox width and height normalized by a scale factor. + + Required Keys: + + - bbox + - bbox_format + + Added Keys: + + - bbox_center + - bbox_scale + + Args: + padding (float): The bbox padding scale that will be multilied to + `bbox_scale`. Defaults to 1.25 + """ + + def __init__(self, padding: float = 1.25) -> None: + super().__init__() + + self.padding = padding + + def forward(self, results: Dict) -> Optional[dict]: + """The transform function of :class:`GetBBoxCenterScale`. + + Args: + results (dict): The result dict + + Returns: + dict: The result dict. + """ + if "bbox_center" in results and "bbox_scale" in results: + results["bbox_scale"] *= self.padding + else: + bbox = results["bbox"] + bbox_format = results.get("bbox_format", "none") + if bbox_format == "xywh": + center, scale = bbox_xywh2cs(bbox, padding=self.padding) + elif bbox_format == "xyxy": + center, scale = bbox_xyxy2cs(bbox, padding=self.padding) + else: + raise ValueError("Invalid bbox format: {}".format(results["bbox_format"])) + + results["bbox_center"] = center + results["bbox_scale"] = scale + return results + + def __repr__(self) -> str: + """print the basic information of the transform. + + Returns: + str: Formatted string. + """ + repr_str = self.__class__.__name__ + f"(padding={self.padding})" + return repr_str + + +class SquarePad: + def __call__(self, results: Dict) -> Optional[dict]: + assert isinstance(results["img"], Image.Image) + w, h = results["img"].size + + max_wh = np.max([w, h]) + hp = int((max_wh - w) / 2) + vp = int((max_wh - h) / 2) + padding = (hp, vp, max_wh - w - hp, max_wh - h - vp) + + results["img"] = F.pad(results["img"], padding, 0, "constant") + return results + + def __repr__(self) -> str: + """print the basic information of the transform. + + Returns: + str: Formatted string. + """ + repr_str = self.__class__.__name__ + return repr_str + + +class ToPIL: + def __call__(self, results: Dict) -> Optional[dict]: + if isinstance(results["img"], list): + if isinstance(results["img"][0], np.ndarray): + results["img"] = [Image.fromarray(img) for img in results["img"]] + elif isinstance(results["img"], np.ndarray): + results["img"] = Image.fromarray(results["img"]) + + +class ToCv2: + def __call__(self, results: Dict) -> Optional[dict]: + if isinstance(results["img"], list): + if isinstance(results["img"][0], Image.Image): + results["img"] = [np.array(img) for img in results["img"]] + elif isinstance(results["img"], Image.Image): + results["img"] = np.array(results["img"]) + + +class TopdownAffine(nn.Module): + """Get the bbox image as the model input by affine transform. + + Required Keys: + - img + - bbox_center + - bbox_scale + - bbox_rotation (optional) + - keypoints_2d (optional) + - mask (optional) + + Modified Keys: + - img + - bbox_scale + + Added Keys: + - input_size + - transformed_keypoints + + Args: + input_size (Tuple[int, int]): The input image size of the model in + [w, h]. The bbox region will be cropped and resize to `input_size` + use_udp (bool): Whether use unbiased data processing. See + `UDP (CVPR 2020)`_ for details. Defaults to ``False`` + aspect_ratio (float): both HMR2.0 and Sapiens will expand input bbox to + a fixed ratio (width/height = 192/256), then expand to the ratio of + the model input size. E.g., HMR2.0 will eventually expand to 1:1, while + Sapiens will be 768:1024. + + .. _`UDP (CVPR 2020)`: https://arxiv.org/abs/1911.07524 + """ + + def __init__( + self, + input_size: Union[int, Tuple[int, int]], + use_udp: bool = False, + aspect_ratio: float = 0.75, + fix_square: bool = False, + ) -> None: + super().__init__() + + self.input_size = to_2tuple(input_size) + self.use_udp = use_udp + self.aspect_ratio = aspect_ratio + self.fix_square = fix_square + + def forward(self, results: Dict) -> Optional[dict]: + """The transform function of :class:`TopdownAffine`. + + See ``transform()`` method of :class:`BaseTransform` for details. + + Args: + results (dict): The result dict + + Returns: + dict: The result dict. + """ + # # Debug only + # import copy + # results['ori_img'] = np.zeros((2000, 2000, 3), dtype=np.uint8) + # results['ori_img'][:results['img'].shape[0], :results['img'].shape[1]] = copy.deepcopy(results['img']) + + w, h = self.input_size + warp_size = (int(w), int(h)) + + # expand bbox to fixed aspect ratio + results["orig_bbox_scale"] = results["bbox_scale"].copy() + if self.fix_square and results["bbox_scale"][0] == results["bbox_scale"][1]: + # In HMR2.0 etc, no fexpand_aspect_ratio for square bbox + bbox_scale = fix_aspect_ratio(results["bbox_scale"], aspect_ratio=w / h) + else: + # first to a prior aspect ratio, then reshape to model input size + bbox_scale = fix_aspect_ratio(results["bbox_scale"], aspect_ratio=self.aspect_ratio) + results["bbox_scale"] = fix_aspect_ratio(bbox_scale, aspect_ratio=w / h) + results["bbox_expand_factor"] = results["bbox_scale"].max() / results["orig_bbox_scale"].max() + rot = 0.0 + if results["bbox_center"].ndim == 2: + assert results["bbox_center"].shape[0] == 1, ( + "Only support cropping one instance at a time. Got invalid " + f"shape of bbox_center {results['bbox_center'].shape}." + ) + center = results["bbox_center"][0] + scale = results["bbox_scale"][0] + if "bbox_rotation" in results: + rot = results["bbox_rotation"][0] + else: + center = results["bbox_center"] + scale = results["bbox_scale"] + if "bbox_rotation" in results: + rot = results["bbox_rotation"] + + if self.use_udp: + warp_mat = get_udp_warp_matrix(center, scale, rot, output_size=(w, h)) + else: + warp_mat = get_warp_matrix(center, scale, rot, output_size=(w, h)) + + if "img" not in results: + pass + elif isinstance(results["img"], list): + results["img"] = [ + cv2.warpAffine(img, warp_mat, warp_size, flags=cv2.INTER_LINEAR) for img in results["img"] + ] + height, width = results["img"][0].shape[:2] + results["ori_img_size"] = np.array([width, height]) + else: + height, width = results["img"].shape[:2] + results["ori_img_size"] = np.array([width, height]) + results["img"] = cv2.warpAffine(results["img"], warp_mat, warp_size, flags=cv2.INTER_LINEAR) + + if results.get("keypoints_2d", None) is not None: + results["orig_keypoints_2d"] = results["keypoints_2d"].copy() + transformed_keypoints = results["keypoints_2d"].copy() + # Only transform (x, y) coordinates + # cv2 expect the input to be [[[x1, y1], [x2, y2]]] + transformed_keypoints[:, :2] = cv2.transform(results["keypoints_2d"][None, :, :2], warp_mat)[0] + results["keypoints_2d"] = transformed_keypoints + + if results.get("mask", None) is not None: + results["mask"] = cv2.warpAffine(results["mask"], warp_mat, warp_size, flags=cv2.INTER_LINEAR) + + results["img_size"] = np.array([w, h]) + results["input_size"] = np.array([w, h]) + results["affine_trans"] = warp_mat + return results + + def __repr__(self) -> str: + """print the basic information of the transform. + + Returns: + str: Formatted string. + """ + repr_str = self.__class__.__name__ + repr_str += f"(input_size={self.input_size}, " + repr_str += f"use_udp={self.use_udp})" + return repr_str + + +class NormalizeKeypoint(nn.Module): + """ + Normalize 2D keypoints to range [-0.5, 0.5]. + + Required Keys: + - keypoints_2d + - img_size + + Modified Keys: + - keypoints_2d + """ + + def forward(self, results: Dict) -> Optional[dict]: + if "keypoints_2d" in results: + img_size = results.get("img_size", results["input_size"]) + + results["keypoints_2d"][:, :2] = results["keypoints_2d"][:, :2] / np.array(img_size).reshape(1, 2) - 0.5 + return results diff --git a/src/sam3d_body/utils/__init__.py b/src/sam3d_body/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..125439922f2aff3c1cf1219db2750493bfa5e680 --- /dev/null +++ b/src/sam3d_body/utils/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from .dist import recursive_to diff --git a/src/sam3d_body/utils/checkpoint.py b/src/sam3d_body/utils/checkpoint.py new file mode 100644 index 0000000000000000000000000000000000000000..751720b7482b642395dc7dbf773eec2d22294439 --- /dev/null +++ b/src/sam3d_body/utils/checkpoint.py @@ -0,0 +1,118 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +from collections import namedtuple + +import pytorch_lightning as pl +import torch + +from .logging import get_pylogger + +log = get_pylogger(__name__) + + +class CheckpointCallback(pl.callbacks.ModelCheckpoint): + """Disable model checkpoint after validation to avoid DDP job hanging after resume""" + + def on_validation_end(self, trainer, pl_module): + # Override to do nothing + pass + + +class _IncompatibleKeys( + namedtuple("IncompatibleKeys", ["missing_keys", "unexpected_keys"]) +): + + def __repr__(self): + if not self.missing_keys and not self.unexpected_keys: + return "" + return super().__repr__() + + __str__ = __repr__ + + +def load_state_dict(module, state_dict, strict=False, logger=None): + """Load state_dict to a module. + + This method is modified from :meth:`torch.nn.Module.load_state_dict`. + Default value for ``strict`` is set to ``False`` and the message for + param mismatch will be shown even if strict is False. + + Args: + module (Module): Module that receives the state_dict. + state_dict (OrderedDict): Weights. + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Defaults to False. + logger (:obj:`logging.Logger`, optional): Logger to log the error + message. If not specified, print function will be used. + """ + unexpected_keys = [] + missing_keys = [] + err_msg = [] + + # copy state_dict so _load_from_state_dict can modify it + metadata = getattr(state_dict, "_metadata", None) + state_dict = state_dict.copy() + if metadata is not None: + state_dict._metadata = metadata + + # use _load_from_state_dict to enable checkpoint version control + def load(module, local_state_dict, prefix=""): + # recursively check parallel module in case that the model has a + # complicated structure, e.g., nn.Module(nn.Module(DDP)) + if isinstance(module, torch.nn.parallel.DistributedDataParallel): + module = module.module + local_metadata = {} if metadata is None else metadata.get(prefix[:-1], {}) + module._load_from_state_dict( + local_state_dict, + prefix, + local_metadata, + True, + missing_keys, + unexpected_keys, + err_msg, + ) + for name, child in module._modules.items(): + if child is not None: + child_prefix = prefix + name + "." + child_state_dict = { + k: v + for k, v in local_state_dict.items() + if k.startswith(child_prefix) + } + load(child, child_state_dict, child_prefix) + + # Note that the hook can modify missing_keys and unexpected_keys. + incompatible_keys = _IncompatibleKeys(missing_keys, unexpected_keys) + if hasattr(module, "_load_state_dict_post_hooks"): + for hook in module._load_state_dict_post_hooks.values(): + out = hook(module, incompatible_keys) + assert out is None, ( + "Hooks registered with " + "``register_load_state_dict_post_hook`` are not expected " + "to return new values, if incompatible_keys need to be " + "modified, it should be done inplace." + ) + + load(module, state_dict) + load = None # break load->load reference cycle + + # ignore "num_batches_tracked" of BN layers + missing_keys = [key for key in missing_keys if "num_batches_tracked" not in key] + + if unexpected_keys: + err_msg.append( + "unexpected key in source " f'state_dict: {", ".join(unexpected_keys)}\n' + ) + if missing_keys: + err_msg.append( + f'missing keys in source state_dict: {", ".join(missing_keys)}\n' + ) + + if len(err_msg) > 0: + err_msg.insert(0, "The model and loaded state dict do not match exactly\n") + err_msg = "\n".join(err_msg) + if strict: + raise RuntimeError(err_msg) + else: + log.warning(err_msg) diff --git a/src/sam3d_body/utils/config.py b/src/sam3d_body/utils/config.py new file mode 100644 index 0000000000000000000000000000000000000000..dace3303f81e3bc61412064c582f6c767e3a2d68 --- /dev/null +++ b/src/sam3d_body/utils/config.py @@ -0,0 +1,75 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +from omegaconf import DictConfig, ListConfig, OmegaConf +from omegaconf.errors import InterpolationResolutionError +from yacs.config import CfgNode as CN + + +# OmegaConf support for variable interpolation (e.g. ${paths.MODEL}/weights.ckpt) +# Skips interpolations it can't resolve, which lets us load Hydra .yamls without a Hydra runtime +def resolve_omegaconf_to_dict(conf): + """ + Recursively convert an OmegaConf object to a dictionary, resolving interpolations + where possible and leaving unsupported ones as-is. + """ + if isinstance(conf, DictConfig): + result = {} + for k, v in conf.items(): + try: + result[k] = resolve_omegaconf_to_dict(v) + except InterpolationResolutionError: + # Convert unresolved OmegaConf objects to containers without resolving + result[k] = OmegaConf.to_container(v, resolve=False) + return result + elif isinstance(conf, ListConfig): + result = [] + for item in conf: + try: + result.append(resolve_omegaconf_to_dict(item)) + except InterpolationResolutionError: + # Convert unresolved OmegaConf objects to containers without resolving + result.append(OmegaConf.to_container(item, resolve=False)) + return result + else: + # Base case: conf is a primitive value or an interpolation + if OmegaConf.is_config(conf): + try: + return OmegaConf.to_container(conf, resolve=True) + except InterpolationResolutionError: + # Convert unresolved OmegaConf objects to containers without resolving + return OmegaConf.to_container(conf, resolve=False) + else: + # conf is a primitive value + return conf + + +def to_lower(x: dict) -> dict: + """ + Convert all dictionary keys to lowercase + Args: + x (dict): Input dictionary + Returns: + dict: Output dictionary with all keys converted to lowercase + """ + return {k.lower(): v for k, v in x.items()} + + +def get_config(config_file: str) -> CN: + """ + Read a config file and optionally merge it with the default config file. + Args: + config_file (str): Path to config file. + Returns: + CfgNode: Config as a yacs CfgNode object. + """ + cfg = CN(new_allowed=True) + + # Resolve config with OmegaConf + conf = OmegaConf.load(config_file) + conf_dict = resolve_omegaconf_to_dict(conf) + conf_cfg = CN(conf_dict) + + # Merge resolved config with the default or new one + cfg.merge_from_other_cfg(conf_cfg) + + cfg.freeze() + return cfg diff --git a/src/sam3d_body/utils/dist.py b/src/sam3d_body/utils/dist.py new file mode 100644 index 0000000000000000000000000000000000000000..08f6856a421d7eb9d39b9a3a88cf2837fd80df06 --- /dev/null +++ b/src/sam3d_body/utils/dist.py @@ -0,0 +1,611 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. + +import os +import pickle +import shutil +import tempfile +from collections.abc import Iterable, Mapping +from typing import Any + +import torch +from torch import Tensor +from torch import distributed as torch_dist +from torch.distributed import ProcessGroup + + +def recursive_to(x: Any, target: torch.device | str): + """ + Recursively transfer a batch of data to the target device + Args: + x (Any): Batch of data. + target (torch.device): Target device. + Returns: + Batch of data where all tensors are transfered to the target device. + """ + if isinstance(x, dict): + return {k: recursive_to(v, target) for k, v in x.items()} + elif isinstance(x, torch.Tensor): + if target == "numpy": + return x.numpy() + else: + return x.to(target) + elif isinstance(x, list): + return [recursive_to(i, target) for i in x] + else: + return x + + +def is_distributed() -> bool: + """Return True if distributed environment has been initialized.""" + return torch_dist.is_available() and torch_dist.is_initialized() + + +def get_default_group(): + """Return default process group.""" + return torch_dist.distributed_c10d._get_default_group() + + +def get_world_size(group: ProcessGroup | None = None) -> int: + """Return the number of the given process group. + + Note: + Calling ``get_world_size`` in non-distributed environment will return + 1. + + Args: + group (ProcessGroup, optional): The process group to work on. If None, + the default process group will be used. Defaults to None. + + Returns: + int: Return the number of processes of the given process group if in + distributed environment, otherwise 1. + """ + if is_distributed(): + # handle low versions of torch like 1.5.0 which does not support + # passing in None for group argument + if group is None: + group = get_default_group() + return torch_dist.get_world_size(group) + else: + return 1 + + +def get_rank(group: ProcessGroup | None = None) -> int: + """Return the rank of the given process group. + + Rank is a unique identifier assigned to each process within a distributed + process group. They are always consecutive integers ranging from 0 to + ``world_size``. + + Note: + Calling ``get_rank`` in non-distributed environment will return 0. + + Args: + group (ProcessGroup, optional): The process group to work on. If None, + the default process group will be used. Defaults to None. + + Returns: + int: Return the rank of the process group if in distributed + environment, otherwise 0. + """ + + if is_distributed(): + # handle low versions of torch like 1.5.0 which does not support + # passing in None for group argument + if group is None: + group = get_default_group() + return torch_dist.get_rank(group) + else: + return 0 + + +def get_dist_info(group: ProcessGroup | None = None) -> tuple[int, int]: + """Get distributed information of the given process group. + + Note: + Calling ``get_dist_info`` in non-distributed environment will return + (0, 1). + + Args: + group (ProcessGroup, optional): The process group to work on. If None, + the default process group will be used. Defaults to None. + + Returns: + tuple[int, int]: Return a tuple containing the ``rank`` and + ``world_size``. + """ + world_size = get_world_size(group) + rank = get_rank(group) + return rank, world_size + + +def is_main_process(group: ProcessGroup | None = None) -> bool: + """Whether the current rank of the given process group is equal to 0. + + Args: + group (ProcessGroup, optional): The process group to work on. If None, + the default process group will be used. Defaults to None. + + Returns: + bool: Return True if the current rank of the given process group is + equal to 0, otherwise False. + """ + return get_rank(group) == 0 + + +def barrier(group: ProcessGroup | None = None) -> None: + """Synchronize all processes from the given process group. + + This collective blocks processes until the whole group enters this + function. + + Note: + Calling ``barrier`` in non-distributed environment will do nothing. + + Args: + group (ProcessGroup, optional): The process group to work on. If None, + the default process group will be used. Defaults to None. + """ + if is_distributed(): + # handle low versions of torch like 1.5.0 which does not support + # passing in None for group argument + if group is None: + group = get_default_group() + torch_dist.barrier(group) + + +def get_data_device(data: Tensor | Mapping | Iterable) -> torch.device: + """Return the device of ``data``. + + If ``data`` is a sequence of Tensor, all items in ``data`` should have a + same device type. + + If ``data`` is a dict whose values are Tensor, all values should have a + same device type. + + Args: + data (Tensor or Sequence or dict): Inputs to be inferred the device. + + Returns: + torch.device: The device of ``data``. + + Examples: + >>> import torch + >>> from mmengine.dist import cast_data_device + >>> # data is a Tensor + >>> data = torch.tensor([0, 1]) + >>> get_data_device(data) + device(type='cpu') + >>> # data is a list of Tensor + >>> data = [torch.tensor([0, 1]), torch.tensor([2, 3])] + >>> get_data_device(data) + device(type='cpu') + >>> # data is a dict + >>> data = {'key1': torch.tensor([0, 1]), 'key2': torch.tensor([0, 1])} + >>> get_data_device(data) + device(type='cpu') + """ + if isinstance(data, Tensor): + return data.device + elif isinstance(data, Mapping): + pre = None + for v in data.values(): + cur = get_data_device(v) + if pre is None: + pre = cur + else: + if cur != pre: + raise ValueError(f"device type in data should be consistent, but got {cur} and {pre}") + if pre is None: + raise ValueError("data should not be empty.") + return pre + elif isinstance(data, Iterable) and not isinstance(data, str): + pre = None + for item in data: + cur = get_data_device(item) + if pre is None: + pre = cur + else: + if cur != pre: + raise ValueError(f"device type in data should be consistent, but got {cur} and {pre}") + if pre is None: + raise ValueError("data should not be empty.") + return pre + else: + raise TypeError(f"data should be a Tensor, sequence of tensor or dict, but got {data}") + + +def get_backend(group: ProcessGroup | None = None) -> str | None: + """Return the backend of the given process group. + + Note: + Calling ``get_backend`` in non-distributed environment will return + None. + + Args: + group (ProcessGroup, optional): The process group to work on. The + default is the general main process group. If another specific + group is specified, the calling process must be part of + :attr:`group`. Defaults to None. + + Returns: + str or None: Return the backend of the given process group as a lower + case string if in distributed environment, otherwise None. + """ + if is_distributed(): + # handle low versions of torch like 1.5.0 which does not support + # passing in None for group argument + if group is None: + group = get_default_group() + return torch_dist.get_backend(group) + else: + return None + + +def get_comm_device(group: ProcessGroup | None = None) -> torch.device: + """Return the device for communication among groups. + + Args: + group (ProcessGroup, optional): The process group to work on. + + Returns: + torch.device: The device of backend. + """ + backend = get_backend(group) + if backend == "hccl": + import torch_npu # noqa: F401 + + return torch.device("npu", torch.npu.current_device()) + elif backend == torch_dist.Backend.NCCL: + return torch.device("cuda", torch.cuda.current_device()) + elif backend == "cncl": + import torch_mlu # noqa: F401 + + return torch.device("mlu", torch.mlu.current_device()) + elif backend == "smddp": + return torch.device("cuda", torch.cuda.current_device()) + else: + # GLOO and MPI backends use cpu device by default + return torch.device("cpu") + + +def cast_data_device( + data: Tensor | Mapping | Iterable, + device: torch.device, + out: Tensor | Mapping | Iterable | None = None, +) -> Tensor | Mapping | Iterable: + """Recursively convert Tensor in ``data`` to ``device``. + + If ``data`` has already on the ``device``, it will not be casted again. + + Args: + data (Tensor or list or dict): Inputs to be casted. + device (torch.device): Destination device type. + out (Tensor or list or dict, optional): If ``out`` is specified, its + value will be equal to ``data``. Defaults to None. + + Returns: + Tensor or list or dict: ``data`` was casted to ``device``. + """ + if out is not None: + if type(data) is not type(out): + raise TypeError( + f"out should be the same type with data, but got data is {type(data)} and out is {type(out)}" + ) + + if isinstance(out, set): + raise TypeError("out should not be a set") + + if isinstance(data, Tensor): + data_on_device = data if get_data_device(data) == device else data.to(device) + + if out is not None: + # modify the value of out inplace + out.copy_(data_on_device) # type: ignore + + return data_on_device + elif isinstance(data, Mapping): + data_on_device = {} + if out is not None: + data_len = len(data) + out_len = len(out) # type: ignore + if data_len != out_len: + raise ValueError(f"length of data and out should be same, but got {data_len} and {out_len}") + + for k, v in data.items(): + data_on_device[k] = cast_data_device(v, device, out[k]) # type: ignore + else: + for k, v in data.items(): + data_on_device[k] = cast_data_device(v, device) + + if len(data_on_device) == 0: + raise ValueError("data should not be empty") + + # To ensure the type of output as same as input, we use `type(data)` + # to wrap the output + return type(data)(data_on_device) # type: ignore + elif isinstance(data, Iterable) and not isinstance(data, str) and not isinstance(data, np.ndarray): + data_on_device = [] + if out is not None: + for v1, v2 in zip(data, out, strict=False): + data_on_device.append(cast_data_device(v1, device, v2)) + else: + for v in data: + data_on_device.append(cast_data_device(v, device)) + + if len(data_on_device) == 0: + raise ValueError("data should not be empty") + + return type(data)(data_on_device) # type: ignore + else: + raise TypeError(f"data should be a Tensor, list of tensor or dict, but got {data}") + + +def broadcast(data: Tensor, src: int = 0, group: ProcessGroup | None = None) -> None: + """Broadcast the data from ``src`` process to the whole group. + + ``data`` must have the same number of elements in all processes + participating in the collective. + + Note: + Calling ``broadcast`` in non-distributed environment does nothing. + + Args: + data (Tensor): Data to be sent if ``src`` is the rank of current + process, and data to be used to save received data otherwise. + src (int): Source rank. Defaults to 0. + group (ProcessGroup, optional): The process group to work on. If None, + the default process group will be used. Defaults to None. + + Examples: + >>> import torch + >>> import mmengine.dist as dist + + >>> # non-distributed environment + >>> data = torch.arange(2, dtype=torch.int64) + >>> data + tensor([0, 1]) + >>> dist.broadcast(data) + >>> data + tensor([0, 1]) + + >>> # distributed environment + >>> # We have 2 process groups, 2 ranks. + >>> data = torch.arange(2, dtype=torch.int64) + 1 + 2 * rank + >>> data + tensor([1, 2]) # Rank 0 + tensor([3, 4]) # Rank 1 + >>> dist.broadcast(data) + >>> data + tensor([1, 2]) # Rank 0 + tensor([1, 2]) # Rank 1 + """ + if get_world_size(group) > 1: + if group is None: + group = get_default_group() + + input_device = get_data_device(data) + backend_device = get_comm_device(group) + data_on_device = cast_data_device(data, backend_device) + # broadcast requires tensor is contiguous + data_on_device = data_on_device.contiguous() # type: ignore + torch_dist.broadcast(data_on_device, src, group) + + if get_rank(group) != src: + cast_data_device(data_on_device, input_device, data) + + +def broadcast_object_list(data: list[Any], src: int = 0, group: Any | None = None) -> None: + """Broadcasts picklable objects in ``object_list`` to the whole group. + Similar to :func:`broadcast`, but Python objects can be passed in. Note + that all objects in ``object_list`` must be picklable in order to be + broadcasted. + + Note: + Calling ``broadcast_object_list`` in non-distributed environment does + nothing. + + Args: + data (List[Any]): List of input objects to broadcast. + Each object must be picklable. Only objects on the ``src`` rank + will be broadcast, but each rank must provide lists of equal sizes. + src (int): Source rank from which to broadcast ``object_list``. + group: (ProcessGroup, optional): The process group to work on. If None, + the default process group will be used. Default is ``None``. + device (``torch.device``, optional): If not None, the objects are + serialized and converted to tensors which are moved to the + ``device`` before broadcasting. Default is ``None``. + + Note: + For NCCL-based process groups, internal tensor representations of + objects must be moved to the GPU device before communication starts. + In this case, the used device is given by + ``torch.cuda.current_device()`` and it is the user's responsibility to + ensure that this is correctly set so that each rank has an individual + GPU, via ``torch.cuda.set_device()``. + + Examples: + >>> import torch + >>> import mmengine.dist as dist + + >>> # non-distributed environment + >>> data = ['foo', 12, {1: 2}] + >>> dist.broadcast_object_list(data) + >>> data + ['foo', 12, {1: 2}] + + >>> # distributed environment + >>> # We have 2 process groups, 2 ranks. + >>> if dist.get_rank() == 0: + >>> # Assumes world_size of 3. + >>> data = ["foo", 12, {1: 2}] # any picklable object + >>> else: + >>> data = [None, None, None] + >>> dist.broadcast_object_list(data) + >>> data + ["foo", 12, {1: 2}] # Rank 0 + ["foo", 12, {1: 2}] # Rank 1 + """ + assert isinstance(data, list) + + if get_world_size() > 1: + if group is None: + group = get_default_group() + + torch_dist.broadcast_object_list(data, src, group) + + +def collect_results(results: list, size: int, device: str = "cpu", tmpdir: str | None = None) -> list | None: + """Collected results in distributed environments. + + Args: + results (list[object]): Result list containing result parts to be + collected. Each item of ``result_part`` should be a picklable + object. + size (int): Size of the results, commonly equal to length of + the results. + device (str): Device name. Optional values are 'cpu', 'gpu' or 'npu'. + tmpdir (str | None): Temporal directory for collected results to + store. If set to None, it will create a temporal directory for it. + ``tmpdir`` should be None when device is 'gpu' or 'npu'. + Defaults to None. + + Returns: + list or None: The collected results. + + Examples: + >>> # distributed environment + >>> # We have 2 process groups, 2 ranks. + >>> import mmengine.dist as dist + >>> if dist.get_rank() == 0: + data = ['foo', {1: 2}] + else: + data = [24, {'a': 'b'}] + >>> size = 4 + >>> output = dist.collect_results(data, size, device='cpu') + >>> output + ['foo', 24, {1: 2}, {'a': 'b'}] # rank 0 + None # rank 1 + """ + if device not in ["gpu", "cpu", "npu"]: + raise NotImplementedError(f"device must be 'cpu' , 'gpu' or 'npu', but got {device}") + + if device == "gpu" or device == "npu": + return _collect_results_device(results, size) + else: + return collect_results_cpu(results, size, tmpdir) + + +def _collect_results_device(result_part: list, size: int) -> list | None: + """Collect results under gpu or npu mode.""" + rank, world_size = get_dist_info() + if world_size == 1: + return result_part[:size] + + # gather all result part. Note that NCCL does not support gather so use + # all_gather_object instead. + part_list = [None] * world_size + group = get_default_group() + torch_dist.all_gather_object(part_list, result_part, group) + + if rank == 0: + # sort the results + ordered_results = [] + for res in zip(*part_list, strict=False): + ordered_results.extend(list(res)) + # the dataloader may pad some samples + ordered_results = ordered_results[:size] + return ordered_results + else: + return None + + +def collect_results_cpu(result_part: list, size: int, tmpdir: str | None = None) -> list | None: + """Collect results under cpu mode. + + On cpu mode, this function will save the results on different gpus to + ``tmpdir`` and collect them by the rank 0 worker. + + Args: + result_part (list): Result list containing result parts + to be collected. Each item of ``result_part`` should be a picklable + object. + size (int): Size of the results, commonly equal to length of + the results. + tmpdir (str | None): Temporal directory for collected results to + store. If set to None, it will create a random temporal directory + for it. Defaults to None. + + Returns: + list or None: The collected results. + + Examples: + >>> # distributed environment + >>> # We have 2 process groups, 2 ranks. + >>> import mmengine.dist as dist + >>> if dist.get_rank() == 0: + data = ['foo', {1: 2}] + else: + data = [24, {'a': 'b'}] + >>> size = 4 + >>> output = dist.collect_results_cpu(data, size) + >>> output + ['foo', 24, {1: 2}, {'a': 'b'}] # rank 0 + None # rank 1 + """ + rank, world_size = get_dist_info() + if world_size == 1: + return result_part[:size] + + # create a tmp dir if it is not specified + if tmpdir is None: + MAX_LEN = 512 + # 32 is whitespace + dir_tensor = torch.full((MAX_LEN,), 32, dtype=torch.uint8) + if rank == 0: + os.makedirs(".dist_test", exist_ok=True) + tmpdir = tempfile.mkdtemp(dir=".dist_test") + tmpdir = torch.tensor(bytearray(tmpdir.encode()), dtype=torch.uint8) + dir_tensor[: len(tmpdir)] = tmpdir + broadcast(dir_tensor, 0) + tmpdir = dir_tensor.numpy().tobytes().decode().rstrip() + else: + os.makedirs(tmpdir, exist_ok=True) + + # dump the part result to the dir + with open(os.path.join(tmpdir, f"part_{rank}.pkl"), "wb") as f: # type: ignore + pickle.dump(result_part, f, protocol=2) + + barrier() + # collect all parts + if rank != 0: + return None + else: + # load results of all parts from tmp dir + part_list = [] + for i in range(world_size): + path = os.path.join(tmpdir, f"part_{i}.pkl") # type: ignore + if not os.path.exists(path): + raise FileNotFoundError( + f"{tmpdir} is not an shared directory for " + f"rank {i}, please make sure {tmpdir} is a shared " + "directory for all ranks!" + ) + with open(path, "rb") as f: + part_list.append(pickle.load(f)) + # With webdataset, some gpus don't get any data + # They don't get a shard with nodesplitter. + # Hence, the zip below can fail, since some elements are length 0. + # So, we're only going to keep the non-zero ones. This is hacky, + # as the root cause is not having enough shards, but it shouldn't + # introduce any errors in and of itself. + part_list = [single for single in part_list if len(single) > 0] + # sort the results + ordered_results = [] + for res in zip(*part_list, strict=False): + ordered_results.extend(list(res)) + # the dataloader may pad some samples + ordered_results = ordered_results[:size] + # remove tmp dir + shutil.rmtree(tmpdir) # type: ignore + return ordered_results diff --git a/src/sam3d_body/utils/logging.py b/src/sam3d_body/utils/logging.py new file mode 100644 index 0000000000000000000000000000000000000000..be78297c3045c96bc858476aaab1956e320503c8 --- /dev/null +++ b/src/sam3d_body/utils/logging.py @@ -0,0 +1,26 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +import logging + +from pytorch_lightning.utilities import rank_zero_only + + +def get_pylogger(name=__name__) -> logging.Logger: + """Initializes multi-GPU-friendly python command line logger.""" + + logger = logging.getLogger(name) + + # this ensures all logging levels get marked with the rank zero decorator + # otherwise logs would get multiplied for each GPU process in multi-GPU setup + logging_levels = ( + "debug", + "info", + "warning", + "error", + "exception", + "fatal", + "critical", + ) + for level in logging_levels: + setattr(logger, level, rank_zero_only(getattr(logger, level))) + + return logger diff --git a/tool/demo.py b/tool/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..eb0cee9c01ae8d8ecf771ea5aedcacdab6fe09d6 --- /dev/null +++ b/tool/demo.py @@ -0,0 +1,6 @@ +import tyro + +from sam3d_body.api.demo import Sam3DBodyDemoConfig, main + +if __name__ == "__main__": + main(tyro.cli(Sam3DBodyDemoConfig)) diff --git a/tool/gradio_sam3.py b/tool/gradio_sam3.py new file mode 100644 index 0000000000000000000000000000000000000000..4b71ef4e07264ba7c5a725324eb06e45011f05b3 --- /dev/null +++ b/tool/gradio_sam3.py @@ -0,0 +1,170 @@ +import warnings + +import gradio as gr +import numpy as np +import requests + +# import spaces +import torch +from PIL import Image +from transformers import Sam3Model, Sam3Processor + +warnings.filterwarnings("ignore") + +# Global model and processor +device = "cuda" if torch.cuda.is_available() else "cpu" +model = Sam3Model.from_pretrained( + "facebook/sam3", torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32 +).to(device) +processor = Sam3Processor.from_pretrained("facebook/sam3") + + +# @spaces.GPU() +def segment(image: Image.Image, text: str, threshold: float, mask_threshold: float): + """ + Perform promptable concept segmentation using SAM3. + Returns format compatible with gr.AnnotatedImage: (image, [(mask, label), ...]) + """ + if image is None: + return None, "โŒ Please upload an image." + + if not text.strip(): + return (image, []), "โŒ Please enter a text prompt." + + try: + inputs = processor(images=image, text=text.strip(), return_tensors="pt").to(device) + + for key in inputs: + if inputs[key].dtype == torch.float32: + inputs[key] = inputs[key].to(model.dtype) + + with torch.no_grad(): + outputs = model(**inputs) + + results = processor.post_process_instance_segmentation( + outputs, + threshold=threshold, + mask_threshold=mask_threshold, + target_sizes=inputs.get("original_sizes").tolist(), + )[0] + + n_masks = len(results["masks"]) + if n_masks == 0: + return (image, []), f"โŒ No objects found matching '{text}' (try adjusting thresholds)." + + # Format for AnnotatedImage: list of (mask, label) tuples + # mask should be numpy array with values 0-1 (float) matching image dimensions + annotations = [] + for i, (mask, score) in enumerate(zip(results["masks"], results["scores"])): + # Convert binary mask to float numpy array (0-1 range) + mask_np = mask.cpu().numpy().astype(np.float32) + label = f"{text} #{i + 1} ({score:.2f})" + annotations.append((mask_np, label)) + + scores_text = ", ".join([f"{s:.2f}" for s in results["scores"].cpu().numpy()[:5]]) + info = f"โœ… Found **{n_masks}** objects matching **'{text}'**\nConfidence scores: {scores_text}{'...' if n_masks > 5 else ''}" + + # Return tuple: (base_image, list_of_annotations) + return (image, annotations), info + + except Exception as e: + return (image, []), f"โŒ Error during segmentation: {str(e)}" + + +def clear_all(): + """Clear all inputs and outputs""" + return None, "", None, 0.5, 0.5, "๐Ÿ“ Enter a prompt and click **Segment** to start." + + +def segment_example(image_path: str, prompt: str): + """Handle example clicks""" + if image_path.startswith("http"): + image = Image.open(requests.get(image_path, stream=True).raw).convert("RGB") + else: + image = Image.open(image_path).convert("RGB") + return segment(image, prompt, 0.5, 0.5) + + +# Gradio Interface +with gr.Blocks( + theme=gr.themes.Soft(), + title="SAM3 - Promptable Concept Segmentation", + css=".gradio-container {max-width: 1400px !important;}", +) as demo: + gr.Markdown( + """ + # SAM3 - Promptable Concept Segmentation (PCS) + + **SAM3** performs zero-shot instance segmentation using natural language prompts. + Upload an image, enter a text prompt (e.g., "person", "car", "dog"), and get segmentation masks. + + Built with [anycoder](https://huggingface.co/spaces/akhaliq/anycoder) + """ + ) + + gr.Markdown("### Inputs") + with gr.Row(variant="panel"): + image_input = gr.Image( + label="Input Image", + type="pil", + height=400, + ) + # AnnotatedImage expects: (base_image, [(mask, label), ...]) + image_output = gr.AnnotatedImage( + label="Output (Segmented Image)", + height=400, + show_legend=True, + ) + + with gr.Row(): + text_input = gr.Textbox(label="Text Prompt", placeholder="e.g., person, ear, cat, bicycle...", scale=3) + clear_btn = gr.Button("๐Ÿ” Clear", size="sm", variant="secondary") + + with gr.Row(): + thresh_slider = gr.Slider( + minimum=0.0, + maximum=1.0, + value=0.5, + step=0.01, + label="Detection Threshold", + info="Higher = fewer detections", + ) + mask_thresh_slider = gr.Slider( + minimum=0.0, maximum=1.0, value=0.5, step=0.01, label="Mask Threshold", info="Higher = sharper masks" + ) + + info_output = gr.Markdown(value="๐Ÿ“ Enter a prompt and click **Segment** to start.", label="Info / Results") + + segment_btn = gr.Button("๐ŸŽฏ Segment", variant="primary", size="lg") + + gr.Examples( + examples=[ + ["http://images.cocodataset.org/val2017/000000077595.jpg", "cat"], + ], + inputs=[image_input, text_input], + outputs=[image_output, info_output], + fn=segment_example, + cache_examples=False, + ) + + clear_btn.click( + fn=clear_all, outputs=[image_input, text_input, image_output, thresh_slider, mask_thresh_slider, info_output] + ) + + segment_btn.click( + fn=segment, + inputs=[image_input, text_input, thresh_slider, mask_thresh_slider], + outputs=[image_output, info_output], + ) + + gr.Markdown( + """ + ### Notes + - **Model**: [facebook/sam3](https://huggingface.co/facebook/sam3) + - Click on segments in the output to see labels + - GPU recommended for faster inference + """ + ) + +if __name__ == "__main__": + demo.launch(server_name="0.0.0.0", server_port=7860, share=False, debug=True) diff --git a/tool/gradio_sam3d_body.py b/tool/gradio_sam3d_body.py new file mode 100644 index 0000000000000000000000000000000000000000..c9b84c5afc52d59b28fd068caa29cde1ee3e1c5e --- /dev/null +++ b/tool/gradio_sam3d_body.py @@ -0,0 +1,5 @@ +from sam3d_body.gradio_ui.sam3d_body_ui import TEST_INPUT_DIR, main + +if __name__ == "__main__": + demo = main() + demo.launch(ssr_mode=False, allowed_paths=[str(TEST_INPUT_DIR)])