From 7d678075f6a0e41c07d723161807770b4488849d Mon Sep 17 00:00:00 2001 From: remydubois Date: Sun, 15 Jan 2023 12:50:32 +0100 Subject: [PATCH 01/16] Fixed issue 12, updated changelog --- changelog.md | 7 + lsnms/nms.py | 6 +- poetry.lock | 589 +++++++++++++++++++++++++++------------------- pyproject.toml | 7 +- tests/test_nms.py | 20 ++ 5 files changed, 389 insertions(+), 240 deletions(-) diff --git a/changelog.md b/changelog.md index 0d73285..bef121d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,13 @@ Changelog ========= +Version 0.3.2 +------------ +- Fixed issue https://github.com/remydubois/lsnms/issues/12, by masking scores as well as boxes. +- Added torch and torchvision as proper dev dependencies +- Fixed Pillow version to 9.3.0 in dev dependencies because 9.4.0 does not compile on my mbp (see https://github.com/python-pillow/Pillow/issues/6862) + + Version 0.3.1 ------------ - Edge case where all box scores are zero (or all below threshold) is now handled (threw uggly error before) diff --git a/lsnms/nms.py b/lsnms/nms.py index 3f04dfb..d9f7bde 100644 --- a/lsnms/nms.py +++ b/lsnms/nms.py @@ -19,8 +19,10 @@ def _nms( """ keep = [] - # Discard boxes below score threshold right now to avoid building the tree on useless boxes - boxes = boxes[scores > score_threshold] + # Discard boxes and scores below score threshold right now to avoid building the tree on useless boxes + score_mask = scores > score_threshold + boxes = boxes[score_mask] + scores = scores[score_mask] if len(boxes) == 0: return np.zeros(0, dtype=np.int64) diff --git a/poetry.lock b/poetry.lock index 2b77dd1..cac92ac 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,92 +1,107 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + [[package]] name = "atomicwrites" -version = "1.4.0" +version = "1.4.1" description = "Atomic file writes." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, +] [[package]] name = "attrs" -version = "21.2.0" +version = "22.2.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] - -[[package]] -name = "backports.entry-points-selectable" -version = "1.1.0" -description = "Compatibility shim providing selectable entry points for older implementations" -category = "dev" -optional = false -python-versions = ">=2.7" - -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +python-versions = ">=3.6" +files = [ + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, +] [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-flake8", "pytest-cov", "pytest-black (>=0.3.7)", "pytest-mypy", "pytest-checkdocs (>=2.4)", "pytest-enabler (>=1.0.1)"] +cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] +tests = ["attrs[tests-no-zope]", "zope.interface"] +tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] [[package]] name = "colorama" -version = "0.4.4" +version = "0.4.6" description = "Cross-platform colored terminal text." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] [[package]] name = "distlib" -version = "0.3.3" +version = "0.3.6" description = "Distribution utilities" category = "dev" optional = false python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] [[package]] name = "filelock" -version = "3.3.2" +version = "3.9.0" description = "A platform independent file lock." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"}, + {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"}, +] [package.extras] -docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] -testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] +docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] [[package]] name = "importlib-metadata" -version = "4.8.1" +version = "6.0.0" description = "Read metadata from Python packages" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"}, + {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"}, +] [package.dependencies] typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] [[package]] name = "llvmlite" @@ -95,6 +110,27 @@ description = "lightweight wrapper around basic LLVM functionality" category = "main" optional = false python-versions = ">=3.7,<3.10" +files = [ + {file = "llvmlite-0.37.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf7d623d33d24df51adc4e9e9f5734df752330661793d1662425ad2e926cb2d4"}, + {file = "llvmlite-0.37.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:74b6c3d2eb8cef32a09e8fd7637d0d37628c74f4deedf9361e0c0ebecc239208"}, + {file = "llvmlite-0.37.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:447b01c25d18921c4179f2eccba218d7c82b65cfe3952b0d018d569945427bf9"}, + {file = "llvmlite-0.37.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:f9e84d683943c2f636b08db9b2d182d4b40b83e1a3e31e100af3bb9ed8d94bcd"}, + {file = "llvmlite-0.37.0-cp37-cp37m-win32.whl", hash = "sha256:14030a1c0f9aee0185db069163240c51d4e8a3eec0daf02468e057281dee612b"}, + {file = "llvmlite-0.37.0-cp37-cp37m-win_amd64.whl", hash = "sha256:15b8ac7a489e31b7d5c482193edaa44374e3c18e409bea494224e31eb60e38e5"}, + {file = "llvmlite-0.37.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d31a3bd69894b31bbc68df00e0b37b0980a0cf025f9dbea9cdd37988230c33a3"}, + {file = "llvmlite-0.37.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:ab00b7996e5ef795f59d95d3125850f3af28d19e43bdc08473947cb8045ce098"}, + {file = "llvmlite-0.37.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:57c1dae337863b497c141d40736041d4acb7769226b44fe05959fce3c3570d5d"}, + {file = "llvmlite-0.37.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:df1d1b162a426480b37d6c4adeddff49e2fb9f71b307c7facac67bdce4767746"}, + {file = "llvmlite-0.37.0-cp38-cp38-win32.whl", hash = "sha256:30431fe9a9b7b1c3585b71149cc11dc79b9d62dc86d3db15c3dcca33d274b5be"}, + {file = "llvmlite-0.37.0-cp38-cp38-win_amd64.whl", hash = "sha256:794191922ac6414c55d66058eaba8b88a630c6e9f2cf0db7e8e661e74d71fa14"}, + {file = "llvmlite-0.37.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c92a209439fd0b8a41f6e2aba1d3afa260357028a29ed7db8c602c4d67c21540"}, + {file = "llvmlite-0.37.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:4c1e91fd4ba2764161e9a05b6fff46a52d26170186bad99629777e8c7246f0ef"}, + {file = "llvmlite-0.37.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:b6466d6369051e5c083b15cf285c00595ddb7f828be1ebecb1dfb97f3fab0bff"}, + {file = "llvmlite-0.37.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:4616e17914fcc7c5bfb7d1014acbd4fca478949820e86218a29d9473d0aa221b"}, + {file = "llvmlite-0.37.0-cp39-cp39-win32.whl", hash = "sha256:995c1a2c8b6a11a7f2c66e52576de6a28292d37842d383aae5be7b965b56d10f"}, + {file = "llvmlite-0.37.0-cp39-cp39-win_amd64.whl", hash = "sha256:7449acca596f45e9e12b20c0b72d184f83025341cc2d44d7ccf5fe31356dcd08"}, + {file = "llvmlite-0.37.0.tar.gz", hash = "sha256:6392b870cd018ec0c645d6bbb918d6aa0eeca8c62674baaee30862d6b6865b15"}, +] [[package]] name = "numba" @@ -103,41 +139,172 @@ description = "compiling Python code using LLVM" category = "main" optional = false python-versions = ">=3.7,<3.10" +files = [ + {file = "numba-0.54.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:c36e50271146c3c33f10111488307a6aa75416aa53384709b037599426a967ea"}, + {file = "numba-0.54.1-cp37-cp37m-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b657cece0b069cd4361a6d25aaae2e9e9df9e65abfa63f09345352fbb1069a11"}, + {file = "numba-0.54.1-cp37-cp37m-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:77479e79b6840e3eb5e0613bbdbb4be8f4b9c4130bafdf6ac39b9507ea742f15"}, + {file = "numba-0.54.1-cp37-cp37m-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ef4d27ee039007510c3de9c42fd6bb57051661ceeca4a9a6244b642a742632a0"}, + {file = "numba-0.54.1-cp37-cp37m-win32.whl", hash = "sha256:1380429f4a3f73440aae093a058713c780fdc14930b3070c883bc1737e8711b0"}, + {file = "numba-0.54.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d0799e7e8640a31d9567a032a6e046d797356afb3e812e0a0f97e6e74ded7e35"}, + {file = "numba-0.54.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0f1c2c23c4e05cbed19f7a15710a25e71ab818ba7cd0bf66572bacd221721f22"}, + {file = "numba-0.54.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:64451b4fd2437ebb7bbcff72133b28575cb8464eb3f10ccd88c70a3792e6de0a"}, + {file = "numba-0.54.1-cp38-cp38-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:5239bf413a9d3c7fad839400d5082032635511c3b7058e17835c7c4090f223ed"}, + {file = "numba-0.54.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ec7033409e66158e9f2b83c22d887fda7949bf2ac652bbbdcbc006b590c37339"}, + {file = "numba-0.54.1-cp38-cp38-win32.whl", hash = "sha256:b385451355a9023c9611400c7c6d4088f5781ed11b104b5d690f0ad65b142860"}, + {file = "numba-0.54.1-cp38-cp38-win_amd64.whl", hash = "sha256:c2e877a33f6920365e96ad088023f786a4b1ce44a7e772763cc02c55f49614dd"}, + {file = "numba-0.54.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:7da918aed4790a4ce6682061971e6248e7422dd5618dcac8054d4a47955182dc"}, + {file = "numba-0.54.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0354df1fcfa9d9d8df3b63780fae408c8f23c474d71a4e929f4c5b44f2c9ce5a"}, + {file = "numba-0.54.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:5492ffa42425b7dc783e4376dfc07617c751d7d087d64fe8c2e7944038e35261"}, + {file = "numba-0.54.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:606ebf5b0474d89f96a2e1354f0349e985c3897c2989b78e47b095d67434cf4c"}, + {file = "numba-0.54.1-cp39-cp39-win32.whl", hash = "sha256:fe4f0c881dbaac0c818dafc80e348edf8d8f1022278c368390ca20e92ed381cc"}, + {file = "numba-0.54.1-cp39-cp39-win_amd64.whl", hash = "sha256:884ad2cdebb6f8bcc7b5ec70e56c9acdb8456482c49cea12273d34709dfc2c9c"}, + {file = "numba-0.54.1.tar.gz", hash = "sha256:f9dfc803c864edcc2381219b800abf366793400aea55e26d4d5b7d953e14f43f"}, +] [package.dependencies] llvmlite = ">=0.37.0rc1,<0.38" numpy = ">=1.17,<1.21" +setuptools = "*" [[package]] name = "numpy" -version = "1.19.5" +version = "1.20.0" description = "NumPy is the fundamental package for array computing with Python." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "numpy-1.20.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:89bd70c9ad540febe6c28451ba225eb4e49d27f64728357f512c808002325dfa"}, + {file = "numpy-1.20.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1264c66129f5ef63187649dd43f1ca59532e8c098723643336a85131c0dcce3f"}, + {file = "numpy-1.20.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e9c5fd330d2fedf06051bafb996252de9b032fcb2ec03eefc9a543e56efa66d4"}, + {file = "numpy-1.20.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:db5e69d08756a2fa75a42b4e433880b6187768fe1bc73d21819def893e5128c6"}, + {file = "numpy-1.20.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:1abc02e30e3efd81a4571e00f8e62bf42e343c76698e0a3e11d9c2b3ee0d77a7"}, + {file = "numpy-1.20.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:5ae765dd29c71a555f8102281f6fb15a3f4dbd35f6e7daf36af9df6d9dd716a5"}, + {file = "numpy-1.20.0-cp37-cp37m-win32.whl", hash = "sha256:b51b9ef0624f4b01b846c981034c10d2e30db33f9f8be71e992f3900741f6f77"}, + {file = "numpy-1.20.0-cp37-cp37m-win_amd64.whl", hash = "sha256:afeee581b50df20ef07b736e62ca612858f1fcdba96651d26ab44e3d567a4e6e"}, + {file = "numpy-1.20.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2bf0e68c92ef077fe766e53f8937d8ac341bdbca68ec128ae049b7d5c34e3206"}, + {file = "numpy-1.20.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:2445a96fbae23a4109c61be0f0af0f3bc273905dc5687a710850c1dfde0fc994"}, + {file = "numpy-1.20.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:33edfc0eb229f86f539493917b34035054313a11afbed48404aaf9f86bf4b0f6"}, + {file = "numpy-1.20.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:894aaee60043a98b03f0ad992c810f62e3a15f98a701e1c0f58a4f4a0df13429"}, + {file = "numpy-1.20.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b66a6c15d793eda7cdad986e737775aa31b9306d588c14dd0277d2dda5546150"}, + {file = "numpy-1.20.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:eee454d3aa3955d0c0069a0f265fea47f1e1384c35a110a95efed358eb6e1562"}, + {file = "numpy-1.20.0-cp38-cp38-win32.whl", hash = "sha256:abdfa075e293d73638ece434708aa60b510dc6e70d805f57f481a0f550b25a9e"}, + {file = "numpy-1.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:f1e9424e9aa3834ea27cc12f9c6ea8ace5da18ee60a720bb3a85b2f733f41782"}, + {file = "numpy-1.20.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cb257bb0c0a3176c32782a63cfab2eace7eabfa2a3b2dfd85a13700617ccaf28"}, + {file = "numpy-1.20.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:cf5d9dcbdbe523fa665c5309cce5f144648d94a7fddbf5a40f8e0d5c9f5b596d"}, + {file = "numpy-1.20.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:93c2abea7bb69f47029b84ceac30ab46dfcfdb99b671ad850a333ff794a765e4"}, + {file = "numpy-1.20.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0d28a54afcf46f1f9ebd163e49ad6b49087f22986fefd01a23ca0c1cdda25ca6"}, + {file = "numpy-1.20.0-cp39-cp39-win32.whl", hash = "sha256:d1bc331e1706fd1809a1bc8a31205329e5b30cf5ba50461c624da267e99f6ae6"}, + {file = "numpy-1.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:e3db646af9f6a145f0c57202f4b55d4a33f975e395e78fb7b394644c17c1a3a6"}, + {file = "numpy-1.20.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:4d592264d2a4f368afbb4288b5ceb646d4cbaf559c0249c096fbb0a149806b90"}, + {file = "numpy-1.20.0.zip", hash = "sha256:3d8233c03f116d068d5365fed4477f2947c7229582dad81e5953088989294cec"}, +] [[package]] name = "packaging" -version = "21.2" +version = "23.0" description = "Core utilities for Python packages" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, + {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, +] -[package.dependencies] -pyparsing = ">=2.0.2,<3" +[[package]] +name = "pillow" +version = "9.3.0" +description = "Python Imaging Library (Fork)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pillow-9.3.0-1-cp37-cp37m-win32.whl", hash = "sha256:e6ea6b856a74d560d9326c0f5895ef8050126acfdc7ca08ad703eb0081e82b74"}, + {file = "Pillow-9.3.0-1-cp37-cp37m-win_amd64.whl", hash = "sha256:32a44128c4bdca7f31de5be641187367fe2a450ad83b833ef78910397db491aa"}, + {file = "Pillow-9.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:0b7257127d646ff8676ec8a15520013a698d1fdc48bc2a79ba4e53df792526f2"}, + {file = "Pillow-9.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b90f7616ea170e92820775ed47e136208e04c967271c9ef615b6fbd08d9af0e3"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68943d632f1f9e3dce98908e873b3a090f6cba1cbb1b892a9e8d97c938871fbe"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be55f8457cd1eac957af0c3f5ece7bc3f033f89b114ef30f710882717670b2a8"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d77adcd56a42d00cc1be30843d3426aa4e660cab4a61021dc84467123f7a00c"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:829f97c8e258593b9daa80638aee3789b7df9da5cf1336035016d76f03b8860c"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:801ec82e4188e935c7f5e22e006d01611d6b41661bba9fe45b60e7ac1a8f84de"}, + {file = "Pillow-9.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:871b72c3643e516db4ecf20efe735deb27fe30ca17800e661d769faab45a18d7"}, + {file = "Pillow-9.3.0-cp310-cp310-win32.whl", hash = "sha256:655a83b0058ba47c7c52e4e2df5ecf484c1b0b0349805896dd350cbc416bdd91"}, + {file = "Pillow-9.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:9f47eabcd2ded7698106b05c2c338672d16a6f2a485e74481f524e2a23c2794b"}, + {file = "Pillow-9.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:57751894f6618fd4308ed8e0c36c333e2f5469744c34729a27532b3db106ee20"}, + {file = "Pillow-9.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7db8b751ad307d7cf238f02101e8e36a128a6cb199326e867d1398067381bff4"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3033fbe1feb1b59394615a1cafaee85e49d01b51d54de0cbf6aa8e64182518a1"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22b012ea2d065fd163ca096f4e37e47cd8b59cf4b0fd47bfca6abb93df70b34c"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a65733d103311331875c1dca05cb4606997fd33d6acfed695b1232ba1df193"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:502526a2cbfa431d9fc2a079bdd9061a2397b842bb6bc4239bb176da00993812"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:90fb88843d3902fe7c9586d439d1e8c05258f41da473952aa8b328d8b907498c"}, + {file = "Pillow-9.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:89dca0ce00a2b49024df6325925555d406b14aa3efc2f752dbb5940c52c56b11"}, + {file = "Pillow-9.3.0-cp311-cp311-win32.whl", hash = "sha256:3168434d303babf495d4ba58fc22d6604f6e2afb97adc6a423e917dab828939c"}, + {file = "Pillow-9.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:18498994b29e1cf86d505edcb7edbe814d133d2232d256db8c7a8ceb34d18cef"}, + {file = "Pillow-9.3.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:772a91fc0e03eaf922c63badeca75e91baa80fe2f5f87bdaed4280662aad25c9"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa4107d1b306cdf8953edde0534562607fe8811b6c4d9a486298ad31de733b2"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4012d06c846dc2b80651b120e2cdd787b013deb39c09f407727ba90015c684f"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77ec3e7be99629898c9a6d24a09de089fa5356ee408cdffffe62d67bb75fdd72"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:6c738585d7a9961d8c2821a1eb3dcb978d14e238be3d70f0a706f7fa9316946b"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:828989c45c245518065a110434246c44a56a8b2b2f6347d1409c787e6e4651ee"}, + {file = "Pillow-9.3.0-cp37-cp37m-win32.whl", hash = "sha256:82409ffe29d70fd733ff3c1025a602abb3e67405d41b9403b00b01debc4c9a29"}, + {file = "Pillow-9.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:41e0051336807468be450d52b8edd12ac60bebaa97fe10c8b660f116e50b30e4"}, + {file = "Pillow-9.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:b03ae6f1a1878233ac620c98f3459f79fd77c7e3c2b20d460284e1fb370557d4"}, + {file = "Pillow-9.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4390e9ce199fc1951fcfa65795f239a8a4944117b5935a9317fb320e7767b40f"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40e1ce476a7804b0fb74bcfa80b0a2206ea6a882938eaba917f7a0f004b42502"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a06a052c5f37b4ed81c613a455a81f9a3a69429b4fd7bb913c3fa98abefc20"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03150abd92771742d4a8cd6f2fa6246d847dcd2e332a18d0c15cc75bf6703040"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:15c42fb9dea42465dfd902fb0ecf584b8848ceb28b41ee2b58f866411be33f07"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:51e0e543a33ed92db9f5ef69a0356e0b1a7a6b6a71b80df99f1d181ae5875636"}, + {file = "Pillow-9.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3dd6caf940756101205dffc5367babf288a30043d35f80936f9bfb37f8355b32"}, + {file = "Pillow-9.3.0-cp38-cp38-win32.whl", hash = "sha256:f1ff2ee69f10f13a9596480335f406dd1f70c3650349e2be67ca3139280cade0"}, + {file = "Pillow-9.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:276a5ca930c913f714e372b2591a22c4bd3b81a418c0f6635ba832daec1cbcfc"}, + {file = "Pillow-9.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:73bd195e43f3fadecfc50c682f5055ec32ee2c933243cafbfdec69ab1aa87cad"}, + {file = "Pillow-9.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c7c8ae3864846fc95f4611c78129301e203aaa2af813b703c55d10cc1628535"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0918e03aa0c72ea56edbb00d4d664294815aa11291a11504a377ea018330d3"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0915e734b33a474d76c28e07292f196cdf2a590a0d25bcc06e64e545f2d146c"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0372acb5d3598f36ec0914deed2a63f6bcdb7b606da04dc19a88d31bf0c05b"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ad58d27a5b0262c0c19b47d54c5802db9b34d38bbf886665b626aff83c74bacd"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:97aabc5c50312afa5e0a2b07c17d4ac5e865b250986f8afe2b02d772567a380c"}, + {file = "Pillow-9.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9aaa107275d8527e9d6e7670b64aabaaa36e5b6bd71a1015ddd21da0d4e06448"}, + {file = "Pillow-9.3.0-cp39-cp39-win32.whl", hash = "sha256:bac18ab8d2d1e6b4ce25e3424f709aceef668347db8637c2296bcf41acb7cf48"}, + {file = "Pillow-9.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:b472b5ea442148d1c3e2209f20f1e0bb0eb556538690fa70b5e1f79fa0ba8dc2"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ab388aaa3f6ce52ac1cb8e122c4bd46657c15905904b3120a6248b5b8b0bc228"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb8e7f2abee51cef77673be97760abff1674ed32847ce04b4af90f610144c7b"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca31dd6014cb8b0b2db1e46081b0ca7d936f856da3b39744aef499db5d84d02"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c7025dce65566eb6e89f56c9509d4f628fddcedb131d9465cacd3d8bac337e7e"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ebf2029c1f464c59b8bdbe5143c79fa2045a581ac53679733d3a91d400ff9efb"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b59430236b8e58840a0dfb4099a0e8717ffb779c952426a69ae435ca1f57210c"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12ce4932caf2ddf3e41d17fc9c02d67126935a44b86df6a206cf0d7161548627"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae5331c23ce118c53b172fa64a4c037eb83c9165aba3a7ba9ddd3ec9fa64a699"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0b07fffc13f474264c336298d1b4ce01d9c5a011415b79d4ee5527bb69ae6f65"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:073adb2ae23431d3b9bcbcff3fe698b62ed47211d0716b067385538a1b0f28b8"}, + {file = "Pillow-9.3.0.tar.gz", hash = "sha256:c935a22a557a560108d780f9a0fc426dd7459940dc54faa49d83249c8d3e760f"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] [[package]] name = "platformdirs" -version = "2.4.0" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "2.6.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, + {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} [package.extras] -docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" @@ -146,6 +313,10 @@ description = "plugin and hook calling mechanisms for python" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] [package.dependencies] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} @@ -161,14 +332,10 @@ description = "library with cross-python path, ini-parsing, io, code, log facili category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "pyparsing" -version = "2.4.7" -description = "Python parsing module" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] [[package]] name = "pytest" @@ -177,6 +344,10 @@ description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, +] [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} @@ -192,6 +363,23 @@ toml = "*" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +[[package]] +name = "setuptools" +version = "65.7.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-65.7.0-py3-none-any.whl", hash = "sha256:8ab4f1dbf2b4a65f7eec5ad0c620e84c34111a68d3349833494b9088212214dd"}, + {file = "setuptools-65.7.0.tar.gz", hash = "sha256:4d3c92fac8f1118bb77a22181355e29c239cabfe2b9effdaa665c66b711136d7"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "six" version = "1.16.0" @@ -199,6 +387,10 @@ description = "Python 2 and 3 compatibility utilities" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] [[package]] name = "toml" @@ -207,6 +399,78 @@ description = "Python Library for Tom's Obvious, Minimal Language" category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "torch" +version = "1.10.2" +description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +category = "dev" +optional = false +python-versions = ">=3.6.2" +files = [ + {file = "torch-1.10.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:8f3fd2e3ffc3bb867133fdf7fbcc8a0bb2e62a5c0696396f51856f5abf9045a8"}, + {file = "torch-1.10.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:258a0729fb77a3457d5822d84b536057cd119b08049a8d3c41dc3dcdeb48d56e"}, + {file = "torch-1.10.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:935e5ac804c5093c79f23a7e6ca5b912c166071aa9d8b4a0a3d6a85126d6a47b"}, + {file = "torch-1.10.2-cp36-cp36m-win_amd64.whl", hash = "sha256:65fd02ed889c63fd82bf1a440c5a94c1310c29f3e6f9f62add416d34da355d97"}, + {file = "torch-1.10.2-cp36-none-macosx_10_9_x86_64.whl", hash = "sha256:6a81f886823bbd15edc2dc0908fa214070df61c9f7ab8831f0a03630275cca5a"}, + {file = "torch-1.10.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3eee3cf53c1f8fb3f1fe107a22025a8501fc6440d14e09599ba7153002531f84"}, + {file = "torch-1.10.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:ef99b8cca5f9358119b07956915faf6e7906f433ab4a603c160ae9de88918371"}, + {file = "torch-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d43bc3f3a2d89ae185ef96d903c935c335219231e57685658648396984e2a67a"}, + {file = "torch-1.10.2-cp37-none-macosx_10_9_x86_64.whl", hash = "sha256:6da1b877880435440a5aa9678ef0f01986d4886416844db1d97ebfb7fd1778d0"}, + {file = "torch-1.10.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ab77a9f838874f295ed5410c0686fa22547456e0116efb281c66ef5f9d46fe28"}, + {file = "torch-1.10.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:9ef4c004f9e5168bd1c1930c6aff25fed5b097de81db6271ffbb2e4fb8b89319"}, + {file = "torch-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:376fc18407add20daa6bbaaffc5a5e06d733abe53bcbd60ef2532bfed34bc091"}, + {file = "torch-1.10.2-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:f281438ee99bd72ad65c0bba1026a32e45c3b636bc067fc145ad291e9ea2faab"}, + {file = "torch-1.10.2-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:3592d3dd62b32760c82624e7586222747fe2281240e8653970b35f1d6d4a434c"}, + {file = "torch-1.10.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fbaf18c1b3e0b31af194a9d853e3739464cf982d279df9d34dd18f1c2a471878"}, + {file = "torch-1.10.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:97b7b0c667e8b0dd1fc70137a36e0a4841ec10ef850bda60500ad066bef3e2de"}, + {file = "torch-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:901b52787baeb2e9e1357ca7037da0028bc6ad743f530e0040ae96ef8e27156c"}, + {file = "torch-1.10.2-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:5b68e9108bd7ebd99eee941686046c517cfaac5331f757bcf440fe02f2e3ced1"}, + {file = "torch-1.10.2-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:b07ef01e36b716d0d65ca60c4db0ac9d094a0e797d9b55290da4dcda91463b6c"}, +] + +[package.dependencies] +typing-extensions = "*" + +[[package]] +name = "torchvision" +version = "0.11.3" +description = "image and video datasets and models for torch deep learning" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "torchvision-0.11.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:8bc8a7db80c97ca254be362ba883a202192e361ba2f6dff7ff5bb010d4bfc23a"}, + {file = "torchvision-0.11.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:55c226803d0ab17972e3f61054ba5d931ca005846c34ec0ae219c17289600813"}, + {file = "torchvision-0.11.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:abb8ace93194a1bc93a6fc5609d6e9518451c5c82b559abfdc77b758014276d1"}, + {file = "torchvision-0.11.3-cp36-cp36m-win_amd64.whl", hash = "sha256:870496b022c49f1c200ac26881481e8c3916360fe86185ae86b950a7ecbef7dc"}, + {file = "torchvision-0.11.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6137eb2694688adf863134dc04336159b3326c30a5de66bb773634ea10ef837e"}, + {file = "torchvision-0.11.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:253e389529df26d39a934512b57bd3fafd7a2a79c3bcad70d3dacab64de95d17"}, + {file = "torchvision-0.11.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:a8cafd5fd5e583730532b40587149d39f9e496023019f1cd86b083e3ca219e6d"}, + {file = "torchvision-0.11.3-cp37-cp37m-win_amd64.whl", hash = "sha256:7a2ea29054274aa391f4acb403c55a9bafc0df7c0395c811522a9bbb18044fa8"}, + {file = "torchvision-0.11.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:63647dfb9b0354bc8c5bb120f2dc15a123cee7f5fd6a8e84561da46cc2c89e3a"}, + {file = "torchvision-0.11.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a82d7d383fabb45fb54aa569ab216b87b98f9eb9de75b3cbfedab555a71209fa"}, + {file = "torchvision-0.11.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:0eda00afc5176b35e69eddd018ee633e3e3d74bbcf139eccdc150781c4ae83a7"}, + {file = "torchvision-0.11.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:b237b39e1c558ad5c9043b6e56bc568ee745f7f064f53270a3fceb53b9725c4b"}, + {file = "torchvision-0.11.3-cp38-cp38-win_amd64.whl", hash = "sha256:dc144114d5991a33bf8909277b02ea082d99cee4cdcf3f7a9c6b48f0c6c8ddde"}, + {file = "torchvision-0.11.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3380211bf061d114c380f52fb33f55d2fbe483e2fd297f6aa596803f7cbdb408"}, + {file = "torchvision-0.11.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a3997b63bd8fac985323b6068e689c9617b0b36e1126616f7b380e17c501aefa"}, + {file = "torchvision-0.11.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:eca0b0f7a0e462bdecf7926d89faae6dcd51da418ca0cf70e725981ed775a11b"}, + {file = "torchvision-0.11.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:25e72231be8ce03467a77806d9c3f5fd34b9cd23b9543d3e999bf57622377532"}, + {file = "torchvision-0.11.3-cp39-cp39-win_amd64.whl", hash = "sha256:5263770a9a91011206b3566b33bbba040b92932885c63cfe5ac9c720ed1fdaca"}, +] + +[package.dependencies] +numpy = "*" +pillow = ">=5.3.0,<8.3.0 || >8.3.0" +torch = "1.10.2" + +[package.extras] +scipy = ["scipy"] [[package]] name = "tox" @@ -215,6 +479,10 @@ description = "tox is a generic virtualenv management and test command line tool category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "tox-3.24.4-py2.py3-none-any.whl", hash = "sha256:5e274227a53dc9ef856767c21867377ba395992549f02ce55eb549f9fb9a8d10"}, + {file = "tox-3.24.4.tar.gz", hash = "sha256:c30b57fa2477f1fb7c36aa1d83292d5c2336cd0018119e1b1c17340e2c2708ca"}, +] [package.dependencies] colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} @@ -229,210 +497,59 @@ virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2, [package.extras] docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] -testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)", "pytest-xdist (>=1.22.2)", "pathlib2 (>=2.3.3)"] +testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)", "pytest-xdist (>=1.22.2)"] [[package]] name = "typing-extensions" -version = "3.10.0.2" -description = "Backported and Experimental Type Hints for Python 3.5+" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, +] [[package]] name = "virtualenv" -version = "20.10.0" +version = "20.17.1" description = "Virtual Python Environment builder" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" +files = [ + {file = "virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, + {file = "virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, +] [package.dependencies] -"backports.entry-points-selectable" = ">=1.0.4" -distlib = ">=0.3.1,<1" -filelock = ">=3.2,<4" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -platformdirs = ">=2,<3" -six = ">=1.9.0,<2" +distlib = ">=0.3.6,<1" +filelock = ">=3.4.1,<4" +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} +platformdirs = ">=2.4,<3" [package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] +docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] [[package]] name = "zipp" -version = "3.6.0" +version = "3.11.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, + {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, +] [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] -lock-version = "1.1" +lock-version = "2.0" python-versions = ">=3.7,<3.10" -content-hash = "8904ae3e5399f0400ec337d16bc182a4c6ef87aee69464200fd2e1e69d8b220c" - -[metadata.files] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] -attrs = [ - {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, - {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, -] -"backports.entry-points-selectable" = [ - {file = "backports.entry_points_selectable-1.1.0-py2.py3-none-any.whl", hash = "sha256:a6d9a871cde5e15b4c4a53e3d43ba890cc6861ec1332c9c2428c92f977192acc"}, - {file = "backports.entry_points_selectable-1.1.0.tar.gz", hash = "sha256:988468260ec1c196dab6ae1149260e2f5472c9110334e5d51adcb77867361f6a"}, -] -colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, -] -distlib = [ - {file = "distlib-0.3.3-py2.py3-none-any.whl", hash = "sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31"}, - {file = "distlib-0.3.3.zip", hash = "sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05"}, -] -filelock = [ - {file = "filelock-3.3.2-py3-none-any.whl", hash = "sha256:bb2a1c717df74c48a2d00ed625e5a66f8572a3a30baacb7657add1d7bac4097b"}, - {file = "filelock-3.3.2.tar.gz", hash = "sha256:7afc856f74fa7006a289fd10fa840e1eebd8bbff6bffb69c26c54a0512ea8cf8"}, -] -importlib-metadata = [ - {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"}, - {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -llvmlite = [ - {file = "llvmlite-0.37.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf7d623d33d24df51adc4e9e9f5734df752330661793d1662425ad2e926cb2d4"}, - {file = "llvmlite-0.37.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:74b6c3d2eb8cef32a09e8fd7637d0d37628c74f4deedf9361e0c0ebecc239208"}, - {file = "llvmlite-0.37.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:447b01c25d18921c4179f2eccba218d7c82b65cfe3952b0d018d569945427bf9"}, - {file = "llvmlite-0.37.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:f9e84d683943c2f636b08db9b2d182d4b40b83e1a3e31e100af3bb9ed8d94bcd"}, - {file = "llvmlite-0.37.0-cp37-cp37m-win32.whl", hash = "sha256:14030a1c0f9aee0185db069163240c51d4e8a3eec0daf02468e057281dee612b"}, - {file = "llvmlite-0.37.0-cp37-cp37m-win_amd64.whl", hash = "sha256:15b8ac7a489e31b7d5c482193edaa44374e3c18e409bea494224e31eb60e38e5"}, - {file = "llvmlite-0.37.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d31a3bd69894b31bbc68df00e0b37b0980a0cf025f9dbea9cdd37988230c33a3"}, - {file = "llvmlite-0.37.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:ab00b7996e5ef795f59d95d3125850f3af28d19e43bdc08473947cb8045ce098"}, - {file = "llvmlite-0.37.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:57c1dae337863b497c141d40736041d4acb7769226b44fe05959fce3c3570d5d"}, - {file = "llvmlite-0.37.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:df1d1b162a426480b37d6c4adeddff49e2fb9f71b307c7facac67bdce4767746"}, - {file = "llvmlite-0.37.0-cp38-cp38-win32.whl", hash = "sha256:30431fe9a9b7b1c3585b71149cc11dc79b9d62dc86d3db15c3dcca33d274b5be"}, - {file = "llvmlite-0.37.0-cp38-cp38-win_amd64.whl", hash = "sha256:794191922ac6414c55d66058eaba8b88a630c6e9f2cf0db7e8e661e74d71fa14"}, - {file = "llvmlite-0.37.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c92a209439fd0b8a41f6e2aba1d3afa260357028a29ed7db8c602c4d67c21540"}, - {file = "llvmlite-0.37.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:4c1e91fd4ba2764161e9a05b6fff46a52d26170186bad99629777e8c7246f0ef"}, - {file = "llvmlite-0.37.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:b6466d6369051e5c083b15cf285c00595ddb7f828be1ebecb1dfb97f3fab0bff"}, - {file = "llvmlite-0.37.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:4616e17914fcc7c5bfb7d1014acbd4fca478949820e86218a29d9473d0aa221b"}, - {file = "llvmlite-0.37.0-cp39-cp39-win32.whl", hash = "sha256:995c1a2c8b6a11a7f2c66e52576de6a28292d37842d383aae5be7b965b56d10f"}, - {file = "llvmlite-0.37.0-cp39-cp39-win_amd64.whl", hash = "sha256:7449acca596f45e9e12b20c0b72d184f83025341cc2d44d7ccf5fe31356dcd08"}, - {file = "llvmlite-0.37.0.tar.gz", hash = "sha256:6392b870cd018ec0c645d6bbb918d6aa0eeca8c62674baaee30862d6b6865b15"}, -] -numba = [ - {file = "numba-0.54.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:c36e50271146c3c33f10111488307a6aa75416aa53384709b037599426a967ea"}, - {file = "numba-0.54.1-cp37-cp37m-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b657cece0b069cd4361a6d25aaae2e9e9df9e65abfa63f09345352fbb1069a11"}, - {file = "numba-0.54.1-cp37-cp37m-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:77479e79b6840e3eb5e0613bbdbb4be8f4b9c4130bafdf6ac39b9507ea742f15"}, - {file = "numba-0.54.1-cp37-cp37m-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ef4d27ee039007510c3de9c42fd6bb57051661ceeca4a9a6244b642a742632a0"}, - {file = "numba-0.54.1-cp37-cp37m-win32.whl", hash = "sha256:1380429f4a3f73440aae093a058713c780fdc14930b3070c883bc1737e8711b0"}, - {file = "numba-0.54.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d0799e7e8640a31d9567a032a6e046d797356afb3e812e0a0f97e6e74ded7e35"}, - {file = "numba-0.54.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0f1c2c23c4e05cbed19f7a15710a25e71ab818ba7cd0bf66572bacd221721f22"}, - {file = "numba-0.54.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:64451b4fd2437ebb7bbcff72133b28575cb8464eb3f10ccd88c70a3792e6de0a"}, - {file = "numba-0.54.1-cp38-cp38-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:5239bf413a9d3c7fad839400d5082032635511c3b7058e17835c7c4090f223ed"}, - {file = "numba-0.54.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ec7033409e66158e9f2b83c22d887fda7949bf2ac652bbbdcbc006b590c37339"}, - {file = "numba-0.54.1-cp38-cp38-win32.whl", hash = "sha256:b385451355a9023c9611400c7c6d4088f5781ed11b104b5d690f0ad65b142860"}, - {file = "numba-0.54.1-cp38-cp38-win_amd64.whl", hash = "sha256:c2e877a33f6920365e96ad088023f786a4b1ce44a7e772763cc02c55f49614dd"}, - {file = "numba-0.54.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:7da918aed4790a4ce6682061971e6248e7422dd5618dcac8054d4a47955182dc"}, - {file = "numba-0.54.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0354df1fcfa9d9d8df3b63780fae408c8f23c474d71a4e929f4c5b44f2c9ce5a"}, - {file = "numba-0.54.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:5492ffa42425b7dc783e4376dfc07617c751d7d087d64fe8c2e7944038e35261"}, - {file = "numba-0.54.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:606ebf5b0474d89f96a2e1354f0349e985c3897c2989b78e47b095d67434cf4c"}, - {file = "numba-0.54.1-cp39-cp39-win32.whl", hash = "sha256:fe4f0c881dbaac0c818dafc80e348edf8d8f1022278c368390ca20e92ed381cc"}, - {file = "numba-0.54.1-cp39-cp39-win_amd64.whl", hash = "sha256:884ad2cdebb6f8bcc7b5ec70e56c9acdb8456482c49cea12273d34709dfc2c9c"}, - {file = "numba-0.54.1.tar.gz", hash = "sha256:f9dfc803c864edcc2381219b800abf366793400aea55e26d4d5b7d953e14f43f"}, -] -numpy = [ - {file = "numpy-1.19.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc6bd4fd593cb261332568485e20a0712883cf631f6f5e8e86a52caa8b2b50ff"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aeb9ed923be74e659984e321f609b9ba54a48354bfd168d21a2b072ed1e833ea"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8b5e972b43c8fc27d56550b4120fe6257fdc15f9301914380b27f74856299fea"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:43d4c81d5ffdff6bae58d66a3cd7f54a7acd9a0e7b18d97abb255defc09e3140"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a4646724fba402aa7504cd48b4b50e783296b5e10a524c7a6da62e4a8ac9698d"}, - {file = "numpy-1.19.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:2e55195bc1c6b705bfd8ad6f288b38b11b1af32f3c8289d6c50d47f950c12e76"}, - {file = "numpy-1.19.5-cp36-cp36m-win32.whl", hash = "sha256:39b70c19ec771805081578cc936bbe95336798b7edf4732ed102e7a43ec5c07a"}, - {file = "numpy-1.19.5-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd18bcf4889b720ba13a27ec2f2aac1981bd41203b3a3b27ba7a33f88ae4827"}, - {file = "numpy-1.19.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:603aa0706be710eea8884af807b1b3bc9fb2e49b9f4da439e76000f3b3c6ff0f"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cae865b1cae1ec2663d8ea56ef6ff185bad091a5e33ebbadd98de2cfa3fa668f"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:36674959eed6957e61f11c912f71e78857a8d0604171dfd9ce9ad5cbf41c511c"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:06fab248a088e439402141ea04f0fffb203723148f6ee791e9c75b3e9e82f080"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6149a185cece5ee78d1d196938b2a8f9d09f5a5ebfbba66969302a778d5ddd1d"}, - {file = "numpy-1.19.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:50a4a0ad0111cc1b71fa32dedd05fa239f7fb5a43a40663269bb5dc7877cfd28"}, - {file = "numpy-1.19.5-cp37-cp37m-win32.whl", hash = "sha256:d051ec1c64b85ecc69531e1137bb9751c6830772ee5c1c426dbcfe98ef5788d7"}, - {file = "numpy-1.19.5-cp37-cp37m-win_amd64.whl", hash = "sha256:a12ff4c8ddfee61f90a1633a4c4afd3f7bcb32b11c52026c92a12e1325922d0d"}, - {file = "numpy-1.19.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf2402002d3d9f91c8b01e66fbb436a4ed01c6498fffed0e4c7566da1d40ee1e"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1ded4fce9cfaaf24e7a0ab51b7a87be9038ea1ace7f34b841fe3b6894c721d1c"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:012426a41bc9ab63bb158635aecccc7610e3eff5d31d1eb43bc099debc979d94"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:759e4095edc3c1b3ac031f34d9459fa781777a93ccc633a472a5468587a190ff"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:a9d17f2be3b427fbb2bce61e596cf555d6f8a56c222bd2ca148baeeb5e5c783c"}, - {file = "numpy-1.19.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:99abf4f353c3d1a0c7a5f27699482c987cf663b1eac20db59b8c7b061eabd7fc"}, - {file = "numpy-1.19.5-cp38-cp38-win32.whl", hash = "sha256:384ec0463d1c2671170901994aeb6dce126de0a95ccc3976c43b0038a37329c2"}, - {file = "numpy-1.19.5-cp38-cp38-win_amd64.whl", hash = "sha256:811daee36a58dc79cf3d8bdd4a490e4277d0e4b7d103a001a4e73ddb48e7e6aa"}, - {file = "numpy-1.19.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c843b3f50d1ab7361ca4f0b3639bf691569493a56808a0b0c54a051d260b7dbd"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d6631f2e867676b13026e2846180e2c13c1e11289d67da08d71cacb2cd93d4aa"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7fb43004bce0ca31d8f13a6eb5e943fa73371381e53f7074ed21a4cb786c32f8"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2ea52bd92ab9f768cc64a4c3ef8f4b2580a17af0a5436f6126b08efbd1838371"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:400580cbd3cff6ffa6293df2278c75aef2d58d8d93d3c5614cd67981dae68ceb"}, - {file = "numpy-1.19.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:df609c82f18c5b9f6cb97271f03315ff0dbe481a2a02e56aeb1b1a985ce38e60"}, - {file = "numpy-1.19.5-cp39-cp39-win32.whl", hash = "sha256:ab83f24d5c52d60dbc8cd0528759532736b56db58adaa7b5f1f76ad551416a1e"}, - {file = "numpy-1.19.5-cp39-cp39-win_amd64.whl", hash = "sha256:0eef32ca3132a48e43f6a0f5a82cb508f22ce5a3d6f67a8329c81c8e226d3f6e"}, - {file = "numpy-1.19.5-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:a0d53e51a6cb6f0d9082decb7a4cb6dfb33055308c4c44f53103c073f649af73"}, - {file = "numpy-1.19.5.zip", hash = "sha256:a76f502430dd98d7546e1ea2250a7360c065a5fdea52b2dffe8ae7180909b6f4"}, -] -packaging = [ - {file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"}, - {file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"}, -] -platformdirs = [ - {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, - {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -pyparsing = [ - {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, - {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, -] -pytest = [ - {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, - {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] -tox = [ - {file = "tox-3.24.4-py2.py3-none-any.whl", hash = "sha256:5e274227a53dc9ef856767c21867377ba395992549f02ce55eb549f9fb9a8d10"}, - {file = "tox-3.24.4.tar.gz", hash = "sha256:c30b57fa2477f1fb7c36aa1d83292d5c2336cd0018119e1b1c17340e2c2708ca"}, -] -typing-extensions = [ - {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, - {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, - {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, -] -virtualenv = [ - {file = "virtualenv-20.10.0-py2.py3-none-any.whl", hash = "sha256:4b02e52a624336eece99c96e3ab7111f469c24ba226a53ec474e8e787b365814"}, - {file = "virtualenv-20.10.0.tar.gz", hash = "sha256:576d05b46eace16a9c348085f7d0dc8ef28713a2cabaa1cf0aea41e8f12c9218"}, -] -zipp = [ - {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, - {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, -] +content-hash = "dd6802a5a44681607afe99e703d1e6b3337b1be2d919a0f6696b875f59a3303d" diff --git a/pyproject.toml b/pyproject.toml index 8e41cf4..ff2525e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "lsnms" -version = "0.3.1" +version = "0.3.2" description = "Large Scale Non Maximum Suppression" authors = ["Rémy Dubois "] license = "MIT" @@ -11,12 +11,15 @@ classifiers=["Programming Language :: Python :: 3", "Topic :: Scientific/Enginee [tool.poetry.dependencies] python = ">=3.7,<3.10" -numpy = "1.19.5" +numpy = "1.20.0" numba = "0.54.1" [tool.poetry.dev-dependencies] pytest = "6.2.5" tox = "3.24.4" +torch = "1.10.2" +torchvision = "0.11.3" +pillow = "9.3.0" [tool.black] line-length = 100 diff --git a/tests/test_nms.py b/tests/test_nms.py index 63a6191..01cceab 100644 --- a/tests/test_nms.py +++ b/tests/test_nms.py @@ -98,3 +98,23 @@ def test_warning_tree_arg(): boxes, scores = datagen() with pytest.warns(None): nms(boxes, scores, 0.5, 0.1, tree="faketree") + +def test_issue_12(): + # From https://github.com/remydubois/lsnms/issues/12 + import numpy as np + from lsnms import nms #v0.3.1 + nms_test_dict_fail = { + 'scores': np.array([0.03128776, 0.15489164, 0.05489164]), + 'boxes': np.array([[ 623.47991943, 391.94015503, 675.83850098, 445.0836792 ], + [ 11.48574257, 15.99506855, 1053.84313965, 1074.78381348], + [ 11.48574257, 15.99506855, 1053.84313965, 1074.78381348]]), + 'class_labels': np.array([ 1, 27, 23]), + 'score_thresh': 0.1} + + keep = nms( + boxes=nms_test_dict_fail["boxes"], + scores=nms_test_dict_fail["scores"], + score_threshold=nms_test_dict_fail["score_thresh"], + class_ids=nms_test_dict_fail["class_labels"], + ) + assert len(keep)==1 \ No newline at end of file From 0a63e843b23792a87c74126813404b857a91665c Mon Sep 17 00:00:00 2001 From: remydubois Date: Mon, 16 Jan 2023 20:10:25 +0100 Subject: [PATCH 02/16] Added pre-commit hooks and non-zero threshold tests --- .flake8 | 2 +- .pre-commit-config.yaml | 11 ++++ README.md | 20 +++---- changelog.md | 2 - lsnms/nms.py | 4 +- lsnms/util.py | 2 +- tests/conftest.py | 18 ++++++ tests/test_nms.py | 118 ++++++++++++++++++++++++++-------------- 8 files changed, 119 insertions(+), 58 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 tests/conftest.py diff --git a/.flake8 b/.flake8 index d581ba1..fbb06bf 100644 --- a/.flake8 +++ b/.flake8 @@ -7,4 +7,4 @@ ignore = E741, # Ambiguous variable names W503, # line break before binary operator W504, # line break after binary operator -max-line-length = 100 \ No newline at end of file +max-line-length = 100 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..6271931 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,11 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/psf/black + rev: stable + hooks: + - id: black diff --git a/README.md b/README.md index 6cab8f3..5e5b4b8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # LSNMS -Speeding up Non Maximum Suppression with a multiclass support ran on very large images by a several folds factor, using a sparse implementation of NMS. +Speeding up Non Maximum Suppression with a multiclass support ran on very large images by a several folds factor, using a sparse implementation of NMS. This project becomes useful in the case of very high dimensional images data, when the amount of predicted instances to prune becomes considerable (> 10,000 objects).

@@ -52,23 +52,23 @@ pooled_boxes, pooled_scores, cluster_indices = wbc(boxes, scores, iou_threshold= ``` # Description ## Non Maximum Suppression - -A nice introduction of the non maximum suppression algorithm can be found here: https://www.coursera.org/lecture/convolutional-neural-networks/non-max-suppression-dvrjH. +A nice introduction of the non maximum suppression algorithm can be found here: https://www.coursera.org/lecture/convolutional-neural-networks/non-max-suppression-dvrjH. Basically, NMS discards redundant boxes in a set of predicted instances. It is an essential - and often unavoidable, step of object detection pipelines. ## Scaling up the Non Maximum Suppression process ### Complexity * In the best case scenario, NMS is a **linear-complex** process (`O(n)`): if all boxes are perfectly overlapping, then one pass of the algorithm discards all the boxes except the highest scoring one. -* In worst case scenario, NMS is a **quadratic-complex** operation (one needs to perform `n * (n - 1) / 2 ` iou comparisons): if all boxes are perfectly disconnected, each NMS step will discard only one box (the highest scoring one, by decreasing order of score). Hence, one needs to perform `(n-1) + (n-2) + ... + 1 = n * (n - 1) / 2 ` iou computations. +* In worst case scenario, NMS is a **quadratic-complex** operation (one needs to perform `n * (n - 1) / 2 ` iou comparisons): if all boxes are perfectly disconnected, each NMS step will discard only one box (the highest scoring one, by decreasing order of score). Hence, one needs to perform `(n-1) + (n-2) + ... + 1 = n * (n - 1) / 2 ` iou computations. ### Working with huge images -When working with high-dimensional images (such as satellital or histology images), one often runs object detection inference by patching (with overlap) the input image and applying NMS to independant patches. Because patches do overlap, a final NMS needs to be re-applied afterward. -In that final case, one is close to be in the worst case scenario since each NMS step will discard only a very low amount of candidate instances (actually, pretty much the amount of overlapping passes over each instance, usually <= 10). Hence, depending on the size of the input image, computation time can reach several minutes on CPU. +When working with high-dimensional images (such as satellital or histology images), one often runs object detection inference by patching (with overlap) the input image and applying NMS to independant patches. Because patches do overlap, a final NMS needs to be re-applied afterward. +In that final case, one is close to be in the worst case scenario since each NMS step will discard only a very low amount of candidate instances (actually, pretty much the amount of overlapping passes over each instance, usually <= 10). Hence, depending on the size of the input image, computation time can reach several minutes on CPU. A more natural way to speed up NMS could be through parallelization, like it is done for GPU-based implementations, but: 1. Efficiently parallelizing NMS is not a straightforward process 2. If too many instances are predicted, GPU VRAM will often not be sufficient, retaining one from using GPU accelerators @@ -76,7 +76,7 @@ A more natural way to speed up NMS could be through parallelization, like it is ### LSNMS This project offers a way to overcome the aforementioned issues elegantly: 1. Before the NMS process, a R-Tree is built on bounding boxes (in a `O(n*log(n))` time) -2. At each NMS step, only boxes overlapping with the current highest scoring box are queried in the tree (in a `O(log(n))` complexity time), and only those neighbors are considered in the pruning process: IoU computation + pruning if necessary. Hence, the overall NMS process is turned from a `O(n**2)` into a `O(n * log(n))` process. See a comparison of run times on the graph below (results obtained on sets of instances whose coordinates vary between 0 and 10,000 (x and y)). +2. At each NMS step, only boxes overlapping with the current highest scoring box are queried in the tree (in a `O(log(n))` complexity time), and only those neighbors are considered in the pruning process: IoU computation + pruning if necessary. Hence, the overall NMS process is turned from a `O(n**2)` into a `O(n * log(n))` process. See a comparison of run times on the graph below (results obtained on sets of instances whose coordinates vary between 0 and 10,000 (x and y)). A nice introduction of R-Tree can be found here: https://iq.opengenus.org/r-tree/. Note that the timing reported below are all inclusive: it notably includes the tree building process, otherwise comparison would not be fair. @@ -89,13 +89,13 @@ Note that the timing reported below are all inclusive: it notably includes the t For the sake of speed, this repo is entirely (including the binary tree) built using Numba's just-in-time compilation. ->Concrete example: +>Concrete example: >Some tests were ran considering ~ 40k x 40k pixels images, and detection inference ran on 512 x 512 overlapping patches (256-strided). Aproximately 300,000 bounding boxes (post patchwise NMS) resulted. Naive NMS ran in approximately 5 minutes on modern CPU, while this implementation ran in 5 seconds, hence offering a close to 60 folds speed up. ### Going further: weighted box clustering For the sake of completeness, this repo also implements a variant of the Weighted Box Clustering algorithm (from https://arxiv.org/pdf/1811.08661.pdf). Since NMS can artificially push up confidence scores (by selecting only the highest scoring box per instance), WBC overcomes this by averaging box coordinates and scores of all the overlapping boxes (instead of discarding all the non-maximally scored overlaping boxes). -## Disclaimer: +## Disclaimer: 1. The tree implementation could probably be further optimized, see implementation notes below. 2. Much simpler implementation could rely on existing KD-Tree implementations (such as sklearn's), query the tree before NMS, and tweak the NMS process to accept tree query's result. This repo implements it from scratch in full numba for the sake of completeness and elegance. 3. The main parameter deciding the speed up brought by this method is (along with the amount of instances) the **density** of boxes over the image: in other words, the amount of overlapping boxes trimmed at each step of the NMS process. The lower the density of boxes, the higher the speed up factor. @@ -109,7 +109,7 @@ As said above, the main parameter guiding speed up from naive NMS is instance (o --- # Implementations notes ## Tree implementation -Due to Numba compiler's limitations, tree implementations has some specificities: +Due to Numba compiler's limitations, tree implementations has some specificities: Because jit-class methods can not be recursive, the tree building process (node splitting + children instanciation) can not be entirely done inside the `Node.__init__` method: * Otherwise, the `__init__` method would be recursive (children instanciation) * However, methods can call recursive (instance-external) functions: a `build` function is dedicated to this diff --git a/changelog.md b/changelog.md index bef121d..4c6b813 100644 --- a/changelog.md +++ b/changelog.md @@ -40,5 +40,3 @@ Version 0.1.0 - Both BallTree and KDTree are implemented for the sake of exhaustivity - A cutoff distance needs to be specified to discard boxes to distant one from the other - Compilation time at first use is quite long: 13 seconds and functions can not be precompiled due to recursivity. - - diff --git a/lsnms/nms.py b/lsnms/nms.py index d9f7bde..a5bb574 100644 --- a/lsnms/nms.py +++ b/lsnms/nms.py @@ -94,10 +94,10 @@ def nms( boxes = # array of boxes in format pascal VOC (x0, y0, x1, y1) scores = # one-dimensional array of confidence scores class_ids = # one-dimensional array of class indicators (one per object) - + keep = nms(boxes, scores, iou_threshold=0.5, score_threshold=0., class_ids=class_ids) ``` - + Note that this implementation could be further optimized: - Memory management is quite poor: several back and forth list-to-numpy conversions happen diff --git a/lsnms/util.py b/lsnms/util.py index 641efa2..f755ff2 100644 --- a/lsnms/util.py +++ b/lsnms/util.py @@ -77,7 +77,7 @@ def distance_to_hypersphere(X, centroid, radius): Distance to the sphere. """ centroid_dist = rdist(X, centroid) - return max(0, centroid_dist ** 0.5 - radius ** 0.5) ** 2 + return max(0, centroid_dist**0.5 - radius**0.5) ** 2 @njit(cache=True) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..5338c57 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,18 @@ +import pytest +import numpy as np + + +@pytest.fixture +def instances(): + topleft = np.random.uniform(0.0, high=1_000, size=(10_000, 2)) + wh = np.random.uniform(15, 45, size=topleft.shape) + + boxes = np.concatenate([topleft, topleft + wh], axis=1) + scores = np.random.uniform(0.01, 1.0, size=len(topleft)) + + return boxes, scores + + +@pytest.fixture +def score_threshold(): + return 0.5 diff --git a/tests/test_nms.py b/tests/test_nms.py index 01cceab..17a5c92 100644 --- a/tests/test_nms.py +++ b/tests/test_nms.py @@ -6,40 +6,47 @@ from torchvision.ops import boxes as box_ops -def datagen(n=10_000): - topleft = np.random.uniform(0.0, high=1_000, size=(n, 2)) - wh = np.random.uniform(15, 45, size=topleft.shape) +def test_rtree_nms(instances): - boxes = np.concatenate([topleft, topleft + wh], axis=1) - scores = np.random.uniform(0.1, 1.0, size=len(topleft)) + boxes, scores = instances - return boxes, scores + # Compare against torch + k1 = box_ops.nms(torch.tensor(boxes), torch.tensor(scores), 0.5).numpy() + + # Compare sparse NMS + k2 = nms(boxes, scores, 0.5, 0.0) + + assert np.allclose(k1, k2) -def test_rtree_nms(): +def test_rtree_nms_non_null_threshold(instances, score_threshold): - boxes, scores = datagen() + boxes, scores = instances + + # Manually filter instances based on scores because torch's NMS does not do it + torch_boxes = boxes[scores > score_threshold] + torch_scores = scores[scores > score_threshold] # Compare against torch - k1 = box_ops.nms(torch.tensor(boxes), torch.tensor(scores), 0.5).numpy() + k1 = box_ops.nms(torch.tensor(torch_boxes), torch.tensor(torch_scores), 0.5).numpy() # Compare sparse NMS - k2 = nms(boxes, scores, 0.5, 0.0) + k2 = nms(boxes, scores, 0.5, score_threshold) assert np.allclose(k1, k2) -def test_empty_nms(): - boxes, scores = datagen() +def test_empty_nms(instances): + boxes, scores = instances # Put all the scores to zero artificially - keep = nms(boxes, scores * 0.) + keep = nms(boxes, scores * 0.0) assert keep.size == 0 - -def test_rtree_multiclass_nms(): - boxes, scores = datagen() +def test_rtree_multiclass_nms(instances): + + boxes, scores = instances class_ids = np.random.randint(0, 50, size=len(boxes)) # Compare against torch @@ -53,9 +60,29 @@ def test_rtree_multiclass_nms(): assert np.allclose(k1, k2) -def test_naive_nms(): +def test_rtree_multiclass_nms_non_null_threshold(instances, score_threshold): + + boxes, scores = instances + class_ids = np.random.randint(0, 50, size=len(boxes)) + + # Manually filter instances based on scores because torch's NMS does not do it + torch_boxes = boxes[scores > score_threshold] + torch_scores = scores[scores > score_threshold] + + # Compare against torch + k1 = box_ops.batched_nms( + torch.tensor(torch_boxes), torch.tensor(torch_scores), torch.tensor(class_ids), 0.5 + ).numpy() + + # Compare sparse NMS + k2 = nms(boxes, scores, 0.5, score_threshold, class_ids=class_ids) + + assert np.allclose(k1, k2) + + +def test_naive_nms(instances): - boxes, scores = datagen() + boxes, scores = instances # Compare against torch k1 = box_ops.nms(torch.tensor(boxes), torch.tensor(scores), 0.5).numpy() @@ -66,55 +93,62 @@ def test_naive_nms(): assert np.allclose(k1, k2) -def test_boxes_shape(): - boxes, scores = datagen() +def test_boxes_shape(instances): + boxes, scores = instances boxes = boxes[None] with pytest.raises(ValueError): nms(boxes, scores, 0.5, 0.1) -def test_scores_shape(): - boxes, scores = datagen() +def test_scores_shape(instances): + boxes, scores = instances scores = scores[..., None] with pytest.raises(ValueError): nms(boxes, scores, 0.5, 0.1) -def test_box_encoding(): - boxes, scores = datagen() +def test_box_encoding(instances): + boxes, scores = instances # Make the first box odd: x1 > x2 boxes[0, 0] = boxes[0, 2] + 1 with pytest.raises(ValueError): nms(boxes, scores, 0.5, 0.1) -def test_warning_dist_arg(): - boxes, scores = datagen() +def test_warning_dist_arg(instances): + boxes, scores = instances with pytest.warns(None): nms(boxes, scores, 0.5, 0.1, cutoff_distance=64) -def test_warning_tree_arg(): - boxes, scores = datagen() +def test_warning_tree_arg(instances): + boxes, scores = instances with pytest.warns(None): nms(boxes, scores, 0.5, 0.1, tree="faketree") + def test_issue_12(): # From https://github.com/remydubois/lsnms/issues/12 - import numpy as np - from lsnms import nms #v0.3.1 + import numpy as np + from lsnms import nms # v0.3.1 + nms_test_dict_fail = { - 'scores': np.array([0.03128776, 0.15489164, 0.05489164]), - 'boxes': np.array([[ 623.47991943, 391.94015503, 675.83850098, 445.0836792 ], - [ 11.48574257, 15.99506855, 1053.84313965, 1074.78381348], - [ 11.48574257, 15.99506855, 1053.84313965, 1074.78381348]]), - 'class_labels': np.array([ 1, 27, 23]), - 'score_thresh': 0.1} + "scores": np.array([0.03128776, 0.15489164, 0.05489164]), + "boxes": np.array( + [ + [623.47991943, 391.94015503, 675.83850098, 445.0836792], + [11.48574257, 15.99506855, 1053.84313965, 1074.78381348], + [11.48574257, 15.99506855, 1053.84313965, 1074.78381348], + ] + ), + "class_labels": np.array([1, 27, 23]), + "score_thresh": 0.1, + } keep = nms( - boxes=nms_test_dict_fail["boxes"], - scores=nms_test_dict_fail["scores"], - score_threshold=nms_test_dict_fail["score_thresh"], - class_ids=nms_test_dict_fail["class_labels"], - ) - assert len(keep)==1 \ No newline at end of file + boxes=nms_test_dict_fail["boxes"], + scores=nms_test_dict_fail["scores"], + score_threshold=nms_test_dict_fail["score_thresh"], + class_ids=nms_test_dict_fail["class_labels"], + ) + assert len(keep) == 1 From 226941127ea4b8fd1f2377e303f6376a3ce1501a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Dubois?= Date: Mon, 16 Jan 2023 20:14:46 +0100 Subject: [PATCH 03/16] Create python-package.yml --- .github/workflows/python-package.yml | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/python-package.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..b9cbca9 --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,40 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python package + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest From d436af1afae7f83e9cbc663fb1006953808f2566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Dubois?= Date: Mon, 16 Jan 2023 20:17:02 +0100 Subject: [PATCH 04/16] Update python-package.yml Added package installation --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index b9cbca9..1f31e26 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -28,7 +28,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install flake8 pytest - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + python -m pip install . - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names From 10065f67b8e971f14a1c3f6253ed843ba051af1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Dubois?= Date: Mon, 16 Jan 2023 20:19:02 +0100 Subject: [PATCH 05/16] Update python-package.yml Switched to poetry installation --- .github/workflows/python-package.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 1f31e26..6e3ad14 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -26,9 +26,8 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - python -m pip install --upgrade pip - python -m pip install flake8 pytest - python -m pip install . + python -m pip install --upgrade poetry + python -m poetry install --with test - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names @@ -37,4 +36,4 @@ jobs: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with pytest run: | - pytest + poetry run pytest From 9a38b454a73773fe62cd0528da3c3ceffea0906e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Dubois?= Date: Mon, 16 Jan 2023 20:21:36 +0100 Subject: [PATCH 06/16] Update python-package.yml fixed flake8 install --- .github/workflows/python-package.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 6e3ad14..3f1cf0b 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -27,6 +27,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade poetry + python -m pip install flake8 python -m poetry install --with test - name: Lint with flake8 run: | From 9eeea205f7b40a0a9a36c3368020bbbd6933348a Mon Sep 17 00:00:00 2001 From: remydubois Date: Mon, 16 Jan 2023 20:25:03 +0100 Subject: [PATCH 07/16] updated flake8 config to flake8 6 --- .flake8 | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.flake8 b/.flake8 index fbb06bf..1b3f333 100644 --- a/.flake8 +++ b/.flake8 @@ -1,10 +1,17 @@ [flake8] ignore = - E20, # Extra space in brackets - E231,E241, # Multiple spaces around "," - E26, # Comments - E731, # Assigning lambda expression - E741, # Ambiguous variable names - W503, # line break before binary operator - W504, # line break after binary operator + # Extra space in brackets + E20, + # Multiple spaces around "," + E231,E241, + # Comments + E26, + # Assigning lambda expression + E731, + # Ambiguous variable names + E741, + # line break before binary operator + W503, + # line break after binary operator + W504, max-line-length = 100 From 1897f74545d33a31f5510993a0453c449a010397 Mon Sep 17 00:00:00 2001 From: remydubois Date: Mon, 16 Jan 2023 20:26:39 +0100 Subject: [PATCH 08/16] Updated flake8 cfg to flake8 v6 --- .flake8 | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/.flake8 b/.flake8 index d581ba1..08cc70e 100644 --- a/.flake8 +++ b/.flake8 @@ -1,10 +1,17 @@ [flake8] ignore = - E20, # Extra space in brackets - E231,E241, # Multiple spaces around "," - E26, # Comments - E731, # Assigning lambda expression - E741, # Ambiguous variable names - W503, # line break before binary operator - W504, # line break after binary operator -max-line-length = 100 \ No newline at end of file + # Extra space in brackets + E20, + # Multiple spaces around "," + E231,E241, + # Comments + E26, + # Assigning lambda expression + E731, + # Ambiguous variable names + E741, + # line break before binary operator + W503, + # line break after binary operator + W504, +max-line-length = 100 From 9c364f9b9c5a0bac5137207b0e5844de7e272d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Dubois?= Date: Mon, 16 Jan 2023 20:31:12 +0100 Subject: [PATCH 09/16] Update python-package.yml Fixed versions and pytest command --- .github/workflows/python-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 3f1cf0b..1cebe61 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8"] steps: - uses: actions/checkout@v3 @@ -37,4 +37,4 @@ jobs: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with pytest run: | - poetry run pytest + python -m poetry run pytest From 8f8c3256066a0b95cdd11cf4a59a54a7cdf64ceb Mon Sep 17 00:00:00 2001 From: remydubois Date: Mon, 16 Jan 2023 20:41:09 +0100 Subject: [PATCH 10/16] Added isort pre-commit and fixed tests --- .pre-commit-config.yaml | 5 +++++ tests/test_nms.py | 7 +++++-- tests/test_util.py | 5 ++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6271931..b702cb9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,3 +9,8 @@ repos: rev: stable hooks: - id: black +- repo: https://github.com/pycqa/isort + rev: v5.11.3 + hooks: + - id: isort + name: isort (python) diff --git a/tests/test_nms.py b/tests/test_nms.py index 17a5c92..a53902e 100644 --- a/tests/test_nms.py +++ b/tests/test_nms.py @@ -1,9 +1,10 @@ import numpy as np import pytest import torch +from torchvision.ops import boxes as box_ops + from lsnms import nms from lsnms.nms import naive_nms -from torchvision.ops import boxes as box_ops def test_rtree_nms(instances): @@ -68,10 +69,11 @@ def test_rtree_multiclass_nms_non_null_threshold(instances, score_threshold): # Manually filter instances based on scores because torch's NMS does not do it torch_boxes = boxes[scores > score_threshold] torch_scores = scores[scores > score_threshold] + torch_class_ids = class_ids[scores > score_threshold] # Compare against torch k1 = box_ops.batched_nms( - torch.tensor(torch_boxes), torch.tensor(torch_scores), torch.tensor(class_ids), 0.5 + torch.tensor(torch_boxes), torch.tensor(torch_scores), torch.tensor(torch_class_ids), 0.5 ).numpy() # Compare sparse NMS @@ -130,6 +132,7 @@ def test_warning_tree_arg(instances): def test_issue_12(): # From https://github.com/remydubois/lsnms/issues/12 import numpy as np + from lsnms import nms # v0.3.1 nms_test_dict_fail = { diff --git a/tests/test_util.py b/tests/test_util.py index 6035282..a1862a5 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,7 +1,9 @@ import numpy as np +import pytest from numba import njit -from lsnms.util import offset_bboxes, box_englobing_boxes, intersection + from lsnms.rtree import RTree +from lsnms.util import box_englobing_boxes, intersection, offset_bboxes def datagen(n=10_000): @@ -48,6 +50,7 @@ def test_offset_bboxes(): assert_no_intersect(boxes_i, boxes_j) +@pytest.mark.skip(reason="Visual test") def test_visual_offset_bboxes(): import matplotlib.pyplot as plt From fe79ca3b026e4f4a14a65c0da044ec17bf5309cf Mon Sep 17 00:00:00 2001 From: remydubois Date: Mon, 16 Jan 2023 20:46:48 +0100 Subject: [PATCH 11/16] Added 3.9 and 3.10 in tests and flak8 pre-commit --- .github/workflows/python-package.yml | 2 +- .pre-commit-config.yaml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 1cebe61..59e4dc8 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8"] + python-version: ["3.7", "3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b702cb9..5b47ea5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,3 +14,7 @@ repos: hooks: - id: isort name: isort (python) +- repo: https://github.com/pycqa/flake8 + rev: "5.0.4" + hooks: + - id: flake8 From 7c1f619f967598d0f983ffd30a94d85912e82290 Mon Sep 17 00:00:00 2001 From: remydubois Date: Mon, 16 Jan 2023 20:54:19 +0100 Subject: [PATCH 12/16] Added flake8 pre-commit and isort --- lsnms/__init__.py | 4 ++-- lsnms/util.py | 6 +++--- lsnms/wbc.py | 8 +++++--- tests/conftest.py | 2 +- tests/test_boxtree.py | 1 + tests/test_util.py | 1 - tests/test_wbc.py | 1 + tests/timings_boxtree.py | 17 +++++++---------- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lsnms/__init__.py b/lsnms/__init__.py index 25be5e4..8ee6c55 100644 --- a/lsnms/__init__.py +++ b/lsnms/__init__.py @@ -1,2 +1,2 @@ -from lsnms.nms import nms -from lsnms.wbc import wbc +from lsnms.nms import nms # noqa: F401 +from lsnms.wbc import wbc # noqa: F401 diff --git a/lsnms/util.py b/lsnms/util.py index f755ff2..792b948 100644 --- a/lsnms/util.py +++ b/lsnms/util.py @@ -1,8 +1,8 @@ -from numba import njit, int64, float64 -from typing import Optional -from numba.typed import Dict import math +from typing import Optional + import numpy as np +from numba import njit @njit(cache=True) diff --git a/lsnms/wbc.py b/lsnms/wbc.py index fe002c2..fbf19cd 100644 --- a/lsnms/wbc.py +++ b/lsnms/wbc.py @@ -1,9 +1,11 @@ -from numba import njit +import warnings from typing import Optional + import numpy as np -from lsnms.util import area, check_correct_input +from numba import njit + from lsnms.rtree import RTree -import warnings +from lsnms.util import area, check_correct_input @njit diff --git a/tests/conftest.py b/tests/conftest.py index 5338c57..5c34c0a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,5 @@ -import pytest import numpy as np +import pytest @pytest.fixture diff --git a/tests/test_boxtree.py b/tests/test_boxtree.py index 748bf05..c43f4b6 100644 --- a/tests/test_boxtree.py +++ b/tests/test_boxtree.py @@ -1,4 +1,5 @@ import numpy as np + from lsnms.rtree import RTree diff --git a/tests/test_util.py b/tests/test_util.py index a1862a5..375d363 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -2,7 +2,6 @@ import pytest from numba import njit -from lsnms.rtree import RTree from lsnms.util import box_englobing_boxes, intersection, offset_bboxes diff --git a/tests/test_wbc.py b/tests/test_wbc.py index bd4a562..b42e99f 100644 --- a/tests/test_wbc.py +++ b/tests/test_wbc.py @@ -1,4 +1,5 @@ import numpy as np + from lsnms import wbc diff --git a/tests/timings_boxtree.py b/tests/timings_boxtree.py index 8e1cf93..96773d3 100644 --- a/tests/timings_boxtree.py +++ b/tests/timings_boxtree.py @@ -1,18 +1,16 @@ -from timeit import Timer, default_timer +from collections import defaultdict +from timeit import Timer import matplotlib.pyplot as plt -import torch -from collections import defaultdict import numpy as np -from lsnms import nms -from lsnms.nms import naive_nms - -from lsnms.rtree import RTree +import torch from sklearn.neighbors import KDTree as skKDT from torchvision.ops import boxes as box_ops from tqdm import tqdm -import time -from pathlib import Path + +from lsnms import nms +from lsnms.nms import naive_nms +from lsnms.rtree import RTree def intersection(boxA, boxB): @@ -122,7 +120,6 @@ def test_tree_query_timing(): f, ax = plt.subplots(figsize=(8, 8)) ax.plot(ns, timings["tree"], label="box tree", marker="o") ax.plot(ns, timings["linear"], label="linear", marker="o") - # ax.plot(ns, timings["sktree"], label="sklearn's kdtree indexed\non box corners", marker="o") ax.set_xlabel("Number of boxes to intersect with", c="k") ax.set_ylabel(f"Elapsed time (us) (mean of {repeats} runs)") ax.set_title("LSNMS box tree intersect vs naive intersect timing") From 2312aeef7b4b34e3814738d156cb1619a084cf0c Mon Sep 17 00:00:00 2001 From: remydubois Date: Mon, 16 Jan 2023 20:58:26 +0100 Subject: [PATCH 13/16] remaining hooks --- lsnms/nms.py | 14 +++++++++----- lsnms/rtree.py | 20 +++++++++----------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lsnms/nms.py b/lsnms/nms.py index a5bb574..2b32cfe 100644 --- a/lsnms/nms.py +++ b/lsnms/nms.py @@ -1,9 +1,12 @@ -from typing import Optional import warnings -from numba import njit +from typing import Optional + import numpy as np -from lsnms.rtree import RTree, RNode -from lsnms.util import area, intersection, check_correct_input, offset_bboxes, max_spread_axis +from numba import njit + +from lsnms.rtree import RNode +from lsnms.util import (area, check_correct_input, intersection, + max_spread_axis, offset_bboxes) @njit(cache=False) @@ -19,7 +22,8 @@ def _nms( """ keep = [] - # Discard boxes and scores below score threshold right now to avoid building the tree on useless boxes + # Discard boxes and scores below score threshold right now to avoid building the tree on + # useless boxes score_mask = scores > score_threshold boxes = boxes[score_mask] scores = scores[score_mask] diff --git a/lsnms/rtree.py b/lsnms/rtree.py index 347e6ce..05e01a8 100644 --- a/lsnms/rtree.py +++ b/lsnms/rtree.py @@ -1,16 +1,12 @@ -import numpy as np -from numba import njit from collections import OrderedDict from typing import List + +import numpy as np +from numba import boolean, deferred_type, float64, int64, njit, optional from numba.experimental import jitclass -from numba import deferred_type, optional, int64, float64, boolean -from lsnms.util import ( - intersection, - split_along_axis, - box_englobing_boxes, - max_spread_axis, -) +from lsnms.util import (box_englobing_boxes, intersection, max_spread_axis, + split_along_axis) specs = OrderedDict() node_type = deferred_type() @@ -215,10 +211,12 @@ def intersect( X : np.array Query box (one box). indices_buffer : list - List of currently-gathered neighbors. Stores in-place the neighbor indices along the search process + List of currently-gathered neighbors. Stores in-place the neighbor indices along the + search process intersection_buffer : list List of currently-gathered neighbor intersection with the query box. - Since the redundancy criterion is intersection over union, I store it here to avoid recomputing it later. + Since the redundancy criterion is intersection over union, I store it here to avoid + recomputing it later. inter_UB : float, optional Intersection upper bound: this is the intersection of X with the current node's bbox. By definition, this is the highest intersection a box contained in this node can get with X. From 9f0bb483c6e71f1f0d3c3500569c2a0e3d0aff1a Mon Sep 17 00:00:00 2001 From: remydubois Date: Mon, 16 Jan 2023 21:07:45 +0100 Subject: [PATCH 14/16] Removed redundant test and python3.10 install action --- .github/workflows/python-package.yml | 2 +- tests/test_nms.py | 28 ---------------------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 59e4dc8..2b78b80 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9"] steps: - uses: actions/checkout@v3 diff --git a/tests/test_nms.py b/tests/test_nms.py index a53902e..a64a8df 100644 --- a/tests/test_nms.py +++ b/tests/test_nms.py @@ -127,31 +127,3 @@ def test_warning_tree_arg(instances): boxes, scores = instances with pytest.warns(None): nms(boxes, scores, 0.5, 0.1, tree="faketree") - - -def test_issue_12(): - # From https://github.com/remydubois/lsnms/issues/12 - import numpy as np - - from lsnms import nms # v0.3.1 - - nms_test_dict_fail = { - "scores": np.array([0.03128776, 0.15489164, 0.05489164]), - "boxes": np.array( - [ - [623.47991943, 391.94015503, 675.83850098, 445.0836792], - [11.48574257, 15.99506855, 1053.84313965, 1074.78381348], - [11.48574257, 15.99506855, 1053.84313965, 1074.78381348], - ] - ), - "class_labels": np.array([1, 27, 23]), - "score_thresh": 0.1, - } - - keep = nms( - boxes=nms_test_dict_fail["boxes"], - scores=nms_test_dict_fail["scores"], - score_threshold=nms_test_dict_fail["score_thresh"], - class_ids=nms_test_dict_fail["class_labels"], - ) - assert len(keep) == 1 From 2625375f540572c583cef7305a80b4b92d29f0f8 Mon Sep 17 00:00:00 2001 From: remydubois Date: Sat, 21 Jan 2023 17:45:55 +0100 Subject: [PATCH 15/16] Few docstring fixes + cleaned up deprecated arguments such as cutoff_distance --- .pre-commit-config.yaml | 2 +- lsnms/nms.py | 39 ++++++++++++++--------------------- lsnms/rtree.py | 8 ++++++-- pyproject.toml | 3 +++ tests/test_nms.py | 45 ++--------------------------------------- 5 files changed, 27 insertions(+), 70 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5b47ea5..c74708e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/psf/black - rev: stable + rev: "22.12.0" hooks: - id: black - repo: https://github.com/pycqa/isort diff --git a/lsnms/nms.py b/lsnms/nms.py index 2b32cfe..b988810 100644 --- a/lsnms/nms.py +++ b/lsnms/nms.py @@ -1,12 +1,16 @@ -import warnings from typing import Optional import numpy as np from numba import njit from lsnms.rtree import RNode -from lsnms.util import (area, check_correct_input, intersection, - max_spread_axis, offset_bboxes) +from lsnms.util import ( + area, + check_correct_input, + intersection, + max_spread_axis, + offset_bboxes, +) @njit(cache=False) @@ -15,7 +19,7 @@ def _nms( scores: np.array, iou_threshold: float = 0.5, score_threshold: float = 0.0, - tree_leaf_size: int = 32, + rtree_leaf_size: int = 32, ) -> np.array: """ See `lsnms.nms` docstring. @@ -31,8 +35,8 @@ def _nms( if len(boxes) == 0: return np.zeros(0, dtype=np.int64) - # Build the BallTree - rtree = RNode(boxes, tree_leaf_size, max_spread_axis(boxes), None) + # Build the RTree + rtree = RNode(boxes, rtree_leaf_size, max_spread_axis(boxes), None) rtree.build() # Compute the areas once and for all: avoid recomputing it at each step @@ -54,7 +58,8 @@ def _nms( boxA = boxes[current_idx] # Query the overlapping boxes and return their intersection - query, query_intersections = rtree.intersect(boxA, 0.0) + # return only boxes which have at least one pixel of overlap with the box of interest + query, query_intersections = rtree.intersect(boxA, 1.0) for query_idx, overlap in zip(query, query_intersections): if not to_consider[query_idx]: @@ -75,9 +80,7 @@ def nms( iou_threshold: float = 0.5, score_threshold: float = 0.0, class_ids: Optional[np.array] = None, - cutoff_distance: Optional[int] = None, - tree: Optional[str] = None, - tree_leaf_size: int = 32, + rtree_leaf_size: int = 32, ) -> np.array: """ Sparse NMS, will perform Non Maximum Suppression by only comparing overlapping boxes. @@ -122,14 +125,7 @@ def nms( One-dimensional integer array indicating the respective classes of the bboxes. If this is not None, a class-wise NMS will be applied. If None, all boxes are considered of the same class. - cutoff_distance: int, optional - DEPRECATED, used for compatibility with version 0.1.X. - Since version 0.2.X, it is useless because overlapping boxes are queried using a R-Tree, - which is parameter free. - tree: str, optional - DEPRECATED, used for compatibility with version 0.1.X. - Since version 0.2.X, the tree used is a R-Tree. - tree_leaf_size: int, optional + rtree_leaf_size: int, optional The leaf size parameter of the underlying R-Tree built for box query. Returns @@ -137,11 +133,6 @@ def nms( np.array Indices of boxes kept, in decreasing order of confidence score. """ - if cutoff_distance is not None or tree is not None: - warnings.warn( - "Both `cutoff_distance` and `tree` are deprecated and effect-less from version" - "0.2.X, since R-Tree is used by default to query overlapping boxes." - ) if class_ids is None: class_ids = np.zeros(len(boxes), dtype=np.int64) @@ -160,7 +151,7 @@ def nms( scores, iou_threshold=iou_threshold, score_threshold=score_threshold, - tree_leaf_size=tree_leaf_size, + rtree_leaf_size=rtree_leaf_size, ) return keep diff --git a/lsnms/rtree.py b/lsnms/rtree.py index 05e01a8..4acb0a9 100644 --- a/lsnms/rtree.py +++ b/lsnms/rtree.py @@ -5,8 +5,12 @@ from numba import boolean, deferred_type, float64, int64, njit, optional from numba.experimental import jitclass -from lsnms.util import (box_englobing_boxes, intersection, max_spread_axis, - split_along_axis) +from lsnms.util import ( + box_englobing_boxes, + intersection, + max_spread_axis, + split_along_axis, +) specs = OrderedDict() node_type = deferred_type() diff --git a/pyproject.toml b/pyproject.toml index ff2525e..bebdedf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,9 @@ pillow = "9.3.0" [tool.black] line-length = 100 +[tool.isort] +profile = "black" + [build-system] requires = ["poetry-core>=1.0.0", "setuptools>=30.3.0,<50",] build-backend = "poetry.core.masonry.api" diff --git a/tests/test_nms.py b/tests/test_nms.py index a64a8df..c391306 100644 --- a/tests/test_nms.py +++ b/tests/test_nms.py @@ -7,20 +7,7 @@ from lsnms.nms import naive_nms -def test_rtree_nms(instances): - - boxes, scores = instances - - # Compare against torch - k1 = box_ops.nms(torch.tensor(boxes), torch.tensor(scores), 0.5).numpy() - - # Compare sparse NMS - k2 = nms(boxes, scores, 0.5, 0.0) - - assert np.allclose(k1, k2) - - -def test_rtree_nms_non_null_threshold(instances, score_threshold): +def test_rtree_nms(instances, score_threshold): boxes, scores = instances @@ -45,23 +32,7 @@ def test_empty_nms(instances): assert keep.size == 0 -def test_rtree_multiclass_nms(instances): - - boxes, scores = instances - class_ids = np.random.randint(0, 50, size=len(boxes)) - - # Compare against torch - k1 = box_ops.batched_nms( - torch.tensor(boxes), torch.tensor(scores), torch.tensor(class_ids), 0.5 - ).numpy() - - # Compare sparse NMS - k2 = nms(boxes, scores, 0.5, 0.0, class_ids=class_ids) - - assert np.allclose(k1, k2) - - -def test_rtree_multiclass_nms_non_null_threshold(instances, score_threshold): +def test_rtree_multiclass_nms(instances, score_threshold): boxes, scores = instances class_ids = np.random.randint(0, 50, size=len(boxes)) @@ -115,15 +86,3 @@ def test_box_encoding(instances): boxes[0, 0] = boxes[0, 2] + 1 with pytest.raises(ValueError): nms(boxes, scores, 0.5, 0.1) - - -def test_warning_dist_arg(instances): - boxes, scores = instances - with pytest.warns(None): - nms(boxes, scores, 0.5, 0.1, cutoff_distance=64) - - -def test_warning_tree_arg(instances): - boxes, scores = instances - with pytest.warns(None): - nms(boxes, scores, 0.5, 0.1, tree="faketree") From f9c142965923621bd255dcdb8cd041720e026d40 Mon Sep 17 00:00:00 2001 From: remydubois Date: Sat, 21 Jan 2023 17:57:00 +0100 Subject: [PATCH 16/16] Assert leaf_size > 0 --- changelog.md | 4 +++- lsnms/rtree.py | 1 + tests/{test_boxtree.py => test_rtree.py} | 15 ++++++++++++++- tests/test_util.py | 18 ++++-------------- 4 files changed, 22 insertions(+), 16 deletions(-) rename tests/{test_boxtree.py => test_rtree.py} (78%) diff --git a/changelog.md b/changelog.md index 4c6b813..e95ed35 100644 --- a/changelog.md +++ b/changelog.md @@ -5,7 +5,9 @@ Version 0.3.2 ------------ - Fixed issue https://github.com/remydubois/lsnms/issues/12, by masking scores as well as boxes. - Added torch and torchvision as proper dev dependencies -- Fixed Pillow version to 9.3.0 in dev dependencies because 9.4.0 does not compile on my mbp (see https://github.com/python-pillow/Pillow/issues/6862) +- Fixed Pillow version (dev dep) to 9.3.0 in dev dependencies because 9.4.0 does not compile on my mbp (see https://github.com/python-pillow/Pillow/issues/6862) +- Removed deprecated arguments: `cutoff_distance` and `tree`. Removed associated tests. +- Added sanity check to ensure `leaf_size` is strictly positive. Version 0.3.1 diff --git a/lsnms/rtree.py b/lsnms/rtree.py index 4acb0a9..50d0d53 100644 --- a/lsnms/rtree.py +++ b/lsnms/rtree.py @@ -57,6 +57,7 @@ def __init__(self, data, leaf_size=16, axis=0, indices=None): self.data = data self.axis = axis # Quick sanity checks + assert leaf_size > 0, "Leaf size must be strictly positive" assert len(data) > 0, "Empty dataset" assert self.data.shape[-1] % 2 == 0, "odd dimensionality" assert data.ndim == 2, "Boxes to index should be (n_boxes, 4)" diff --git a/tests/test_boxtree.py b/tests/test_rtree.py similarity index 78% rename from tests/test_boxtree.py rename to tests/test_rtree.py index c43f4b6..8f42dd6 100644 --- a/tests/test_boxtree.py +++ b/tests/test_rtree.py @@ -1,6 +1,7 @@ import numpy as np +import pytest -from lsnms.rtree import RTree +from lsnms.rtree import RNode, RTree def intersection(boxA, boxB): @@ -35,3 +36,15 @@ def test_intersect_tree(): out_inter = np.array([inter for i, inter in enumerate(np_intersect) if i not in indices]) np.testing.assert_allclose(in_inter, intersections) np.testing.assert_array_less(out_inter, min_area) + + +def test_build_odd_tree(instances): + boxes, _ = instances + with pytest.raises(AssertionError): + _ = RTree(boxes, leaf_size=0) + + +def test_build_rnode_default_args(instances): + boxes, _ = instances + + _ = RNode(boxes) diff --git a/tests/test_util.py b/tests/test_util.py index 375d363..0976bf2 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -5,16 +5,6 @@ from lsnms.util import box_englobing_boxes, intersection, offset_bboxes -def datagen(n=10_000): - topleft = np.random.uniform(0.0, high=1_000, size=(n, 2)) - wh = np.random.uniform(15, 45, size=topleft.shape) - - boxes = np.concatenate([topleft, topleft + wh], axis=1) - scores = np.random.uniform(0.1, 1.0, size=len(topleft)) - - return boxes, scores - - @njit def intersect_many(tree, boxes): intersections = [] @@ -31,8 +21,8 @@ def assert_no_intersect(boxesA, boxesB): assert intersection(boxA, boxB) == 0.0 -def test_offset_bboxes(): - boxes, _ = datagen() +def test_offset_bboxes(instances): + boxes, _ = instances rng = np.random.default_rng(0) class_ids = rng.integers(0, 2, size=len(boxes)) @@ -50,10 +40,10 @@ def test_offset_bboxes(): @pytest.mark.skip(reason="Visual test") -def test_visual_offset_bboxes(): +def test_visual_offset_bboxes(instances): import matplotlib.pyplot as plt - boxes, _ = datagen() + boxes, _ = instances # boxes = boxes.reshape(-1, 2, 2).mean(1) rng = np.random.default_rng(0) class_ids = rng.integers(0, 3, size=len(boxes))