generated from dopt-python/py311
Merge pull request 'working demo code with significant speed-up' (#1) from demo into main
Reviewed-on: #1
This commit is contained in:
commit
18fdab60e6
42
README.md
42
README.md
@ -1,26 +1,30 @@
|
||||
# Repository Template for Python 3.11 Projects
|
||||
|
||||
## Tools for Project and Package Management
|
||||
# KSG AOI image anomaly detection
|
||||
|
||||
[](https://pdm-project.org)
|
||||
[](https://github.com/astral-sh/ruff)
|
||||
|
||||
Python projects are managed with **PDM** ([link to GitHub Source](https://github.com/pdm-project/pdm)), a PEP-compliant project and dependency management tool.
|
||||
The applicable settings are contained within the PyProject-TOML file. In order to use a repo which was created with this template is to tell PDM which Python interpreter to use and then to install the whole project into the created virtual environment. If the interpreter is not available you will need to install it via PDM.
|
||||
## General
|
||||
|
||||
### Lib structure by Susanne
|
||||
|
||||
- configs:
|
||||
- ``config.py``: reduced version of original config to work as a small test example
|
||||
- ``config_for_test.py``: special parameters for test environment, minimal working example
|
||||
- main program:
|
||||
- ``main.py``: loads GUI and background worker to observe changes in folder directory
|
||||
- GUI:
|
||||
- ``window_manager.py``
|
||||
- ``gui_ai_on_off.py``
|
||||
- Logic:
|
||||
- ``monitor.py``: worker logic to observe changes in the relevant folders
|
||||
- function ``monitor_folder``
|
||||
- ``preparation.py``: main logic to process data ``class Preparation``, namely:
|
||||
- copy (backup) of found data to this app's saving directory (``config.py -- STORING_PATH``)
|
||||
- method ``copy_ngt_and_checkimg``
|
||||
- colourisation of images (only the first) (yellow)
|
||||
- method ``change_image_to_yellow``
|
||||
- fuse the different RGB layers to one image and save it
|
||||
- method ``create_rgb_images_and_patches``
|
||||
|
||||
```console
|
||||
pdm use 3.11.11 # example of a given version
|
||||
pdm install
|
||||
```
|
||||
|
||||
This installs all mandatory development dependencies such as:
|
||||
- Ruff (formatting and linting)
|
||||
- pytest (unittest framework)
|
||||
- coverage.py (measuring test coverage)
|
||||
- pytest-cov (integration of coverage into pytest)
|
||||
- pytest-xdist (allows to execute the tests on multiple CPU cores)
|
||||
- bump-my-version (CLI tool to manage version bumping)
|
||||
- Nox (Python runner, e.g. to run test suite on multiple Python versions)
|
||||
- pdoc (to auto-generate documentation from docstrings)
|
||||
- Jupyterlab and widgets (to perform fast prototyping and enable exploratory data analysis)
|
||||
|
||||
|
||||
322
pdm.lock
generated
322
pdm.lock
generated
@ -2,13 +2,13 @@
|
||||
# It is not intended for manual editing.
|
||||
|
||||
[metadata]
|
||||
groups = ["default", "dev", "lint", "nb", "tests"]
|
||||
groups = ["default", "dev", "lint", "nb", "open-cv", "tests"]
|
||||
strategy = ["inherit_metadata"]
|
||||
lock_version = "4.5.0"
|
||||
content_hash = "sha256:3a107981dc4305f031f87c89e3a57a6bb823954d397a52d074fef1c72ac639d0"
|
||||
content_hash = "sha256:3c47abf04bea7dfd195f350d89b59416b8492aaaf54257badfb8b4814b20e996"
|
||||
|
||||
[[metadata.targets]]
|
||||
requires_python = ">=3.11"
|
||||
requires_python = ">=3.11,<3.15"
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
@ -271,7 +271,7 @@ name = "cffi"
|
||||
version = "2.0.0"
|
||||
requires_python = ">=3.9"
|
||||
summary = "Foreign Function Interface for Python calling C code."
|
||||
groups = ["nb"]
|
||||
groups = ["default", "nb"]
|
||||
dependencies = [
|
||||
"pycparser; implementation_name != \"PyPy\"",
|
||||
]
|
||||
@ -1542,6 +1542,108 @@ files = [
|
||||
{file = "nox-2026.2.9.tar.gz", hash = "sha256:1bc8a202ee8cd69be7aaada63b2a7019126899a06fc930a7aee75585bf8ee41b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "2.4.2"
|
||||
requires_python = ">=3.11"
|
||||
summary = "Fundamental package for array computing in Python"
|
||||
groups = ["default", "open-cv"]
|
||||
files = [
|
||||
{file = "numpy-2.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7e88598032542bd49af7c4747541422884219056c268823ef6e5e89851c8825"},
|
||||
{file = "numpy-2.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7edc794af8b36ca37ef5fcb5e0d128c7e0595c7b96a2318d1badb6fcd8ee86b1"},
|
||||
{file = "numpy-2.4.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:6e9f61981ace1360e42737e2bae58b27bf28a1b27e781721047d84bd754d32e7"},
|
||||
{file = "numpy-2.4.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cb7bbb88aa74908950d979eeaa24dbdf1a865e3c7e45ff0121d8f70387b55f73"},
|
||||
{file = "numpy-2.4.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f069069931240b3fc703f1e23df63443dbd6390614c8c44a87d96cd0ec81eb1"},
|
||||
{file = "numpy-2.4.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c02ef4401a506fb60b411467ad501e1429a3487abca4664871d9ae0b46c8ba32"},
|
||||
{file = "numpy-2.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2653de5c24910e49c2b106499803124dde62a5a1fe0eedeaecf4309a5f639390"},
|
||||
{file = "numpy-2.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1ae241bbfc6ae276f94a170b14785e561cb5e7f626b6688cf076af4110887413"},
|
||||
{file = "numpy-2.4.2-cp311-cp311-win32.whl", hash = "sha256:df1b10187212b198dd45fa943d8985a3c8cf854aed4923796e0e019e113a1bda"},
|
||||
{file = "numpy-2.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:b9c618d56a29c9cb1c4da979e9899be7578d2e0b3c24d52079c166324c9e8695"},
|
||||
{file = "numpy-2.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:47c5a6ed21d9452b10227e5e8a0e1c22979811cad7dcc19d8e3e2fb8fa03f1a3"},
|
||||
{file = "numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a"},
|
||||
{file = "numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1"},
|
||||
{file = "numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e"},
|
||||
{file = "numpy-2.4.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27"},
|
||||
{file = "numpy-2.4.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548"},
|
||||
{file = "numpy-2.4.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f"},
|
||||
{file = "numpy-2.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460"},
|
||||
{file = "numpy-2.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba"},
|
||||
{file = "numpy-2.4.2-cp312-cp312-win32.whl", hash = "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f"},
|
||||
{file = "numpy-2.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85"},
|
||||
{file = "numpy-2.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa"},
|
||||
{file = "numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c"},
|
||||
{file = "numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979"},
|
||||
{file = "numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98"},
|
||||
{file = "numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef"},
|
||||
{file = "numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7"},
|
||||
{file = "numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499"},
|
||||
{file = "numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb"},
|
||||
{file = "numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7"},
|
||||
{file = "numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110"},
|
||||
{file = "numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622"},
|
||||
{file = "numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71"},
|
||||
{file = "numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262"},
|
||||
{file = "numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913"},
|
||||
{file = "numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab"},
|
||||
{file = "numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82"},
|
||||
{file = "numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f"},
|
||||
{file = "numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554"},
|
||||
{file = "numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257"},
|
||||
{file = "numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657"},
|
||||
{file = "numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b"},
|
||||
{file = "numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1"},
|
||||
{file = "numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b"},
|
||||
{file = "numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000"},
|
||||
{file = "numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1"},
|
||||
{file = "numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74"},
|
||||
{file = "numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a"},
|
||||
{file = "numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325"},
|
||||
{file = "numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909"},
|
||||
{file = "numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a"},
|
||||
{file = "numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a"},
|
||||
{file = "numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75"},
|
||||
{file = "numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05"},
|
||||
{file = "numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308"},
|
||||
{file = "numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef"},
|
||||
{file = "numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d"},
|
||||
{file = "numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8"},
|
||||
{file = "numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5"},
|
||||
{file = "numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e"},
|
||||
{file = "numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a"},
|
||||
{file = "numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443"},
|
||||
{file = "numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236"},
|
||||
{file = "numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181"},
|
||||
{file = "numpy-2.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:89f7268c009bc492f506abd6f5265defa7cb3f7487dc21d357c3d290add45082"},
|
||||
{file = "numpy-2.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6dee3bb76aa4009d5a912180bf5b2de012532998d094acee25d9cb8dee3e44a"},
|
||||
{file = "numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:cd2bd2bbed13e213d6b55dc1d035a4f91748a7d3edc9480c13898b0353708920"},
|
||||
{file = "numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:cf28c0c1d4c4bf00f509fa7eb02c58d7caf221b50b467bcb0d9bbf1584d5c821"},
|
||||
{file = "numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e04ae107ac591763a47398bb45b568fc38f02dbc4aa44c063f67a131f99346cb"},
|
||||
{file = "numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:602f65afdef699cda27ec0b9224ae5dc43e328f4c24c689deaf77133dbee74d0"},
|
||||
{file = "numpy-2.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be71bf1edb48ebbbf7f6337b5bfd2f895d1902f6335a5830b20141fc126ffba0"},
|
||||
{file = "numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opencv-python"
|
||||
version = "4.13.0.92"
|
||||
requires_python = ">=3.6"
|
||||
summary = "Wrapper package for OpenCV python bindings."
|
||||
groups = ["open-cv"]
|
||||
dependencies = [
|
||||
"numpy<2.0; python_version < \"3.9\"",
|
||||
"numpy>=2; python_version >= \"3.9\"",
|
||||
]
|
||||
files = [
|
||||
{file = "opencv_python-4.13.0.92-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:caf60c071ec391ba51ed00a4a920f996d0b64e3e46068aac1f646b5de0326a19"},
|
||||
{file = "opencv_python-4.13.0.92-cp37-abi3-macosx_14_0_x86_64.whl", hash = "sha256:5868a8c028a0b37561579bfb8ac1875babdc69546d236249fff296a8c010ccf9"},
|
||||
{file = "opencv_python-4.13.0.92-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bc2596e68f972ca452d80f444bc404e08807d021fbba40df26b61b18e01838a"},
|
||||
{file = "opencv_python-4.13.0.92-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:402033cddf9d294693094de5ef532339f14ce821da3ad7df7c9f6e8316da32cf"},
|
||||
{file = "opencv_python-4.13.0.92-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bccaabf9eb7f897ca61880ce2869dcd9b25b72129c28478e7f2a5e8dee945616"},
|
||||
{file = "opencv_python-4.13.0.92-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:620d602b8f7d8b8dab5f4b99c6eb353e78d3fb8b0f53db1bd258bb1aa001c1d5"},
|
||||
{file = "opencv_python-4.13.0.92-cp37-abi3-win32.whl", hash = "sha256:372fe164a3148ac1ca51e5f3ad0541a4a276452273f503441d718fab9c5e5f59"},
|
||||
{file = "opencv_python-4.13.0.92-cp37-abi3-win_amd64.whl", hash = "sha256:423d934c9fafb91aad38edf26efb46da91ffbc05f3f59c4b0c72e699720706f5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overrides"
|
||||
version = "7.7.0"
|
||||
@ -1619,6 +1721,95 @@ files = [
|
||||
{file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "12.1.1"
|
||||
requires_python = ">=3.10"
|
||||
summary = "Python Imaging Library (fork)"
|
||||
groups = ["default"]
|
||||
files = [
|
||||
{file = "pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32"},
|
||||
{file = "pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38"},
|
||||
{file = "pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5"},
|
||||
{file = "pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090"},
|
||||
{file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af"},
|
||||
{file = "pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b"},
|
||||
{file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5"},
|
||||
{file = "pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d"},
|
||||
{file = "pillow-12.1.1-cp311-cp311-win32.whl", hash = "sha256:7311c0a0dcadb89b36b7025dfd8326ecfa36964e29913074d47382706e516a7c"},
|
||||
{file = "pillow-12.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563"},
|
||||
{file = "pillow-12.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:b81b5e3511211631b3f672a595e3221252c90af017e399056d0faabb9538aa80"},
|
||||
{file = "pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052"},
|
||||
{file = "pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984"},
|
||||
{file = "pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79"},
|
||||
{file = "pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293"},
|
||||
{file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397"},
|
||||
{file = "pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0"},
|
||||
{file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3"},
|
||||
{file = "pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35"},
|
||||
{file = "pillow-12.1.1-cp312-cp312-win32.whl", hash = "sha256:5d1f9575a12bed9e9eedd9a4972834b08c97a352bd17955ccdebfeca5913fa0a"},
|
||||
{file = "pillow-12.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:21329ec8c96c6e979cd0dfd29406c40c1d52521a90544463057d2aaa937d66a6"},
|
||||
{file = "pillow-12.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:af9a332e572978f0218686636610555ae3defd1633597be015ed50289a03c523"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1"},
|
||||
{file = "pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a"},
|
||||
{file = "pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da"},
|
||||
{file = "pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc"},
|
||||
{file = "pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c"},
|
||||
{file = "pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8"},
|
||||
{file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20"},
|
||||
{file = "pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13"},
|
||||
{file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf"},
|
||||
{file = "pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524"},
|
||||
{file = "pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986"},
|
||||
{file = "pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c"},
|
||||
{file = "pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15"},
|
||||
{file = "pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f"},
|
||||
{file = "pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8"},
|
||||
{file = "pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9"},
|
||||
{file = "pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60"},
|
||||
{file = "pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7"},
|
||||
{file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f"},
|
||||
{file = "pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586"},
|
||||
{file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce"},
|
||||
{file = "pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8"},
|
||||
{file = "pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36"},
|
||||
{file = "pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b"},
|
||||
{file = "pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334"},
|
||||
{file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f"},
|
||||
{file = "pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9"},
|
||||
{file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e"},
|
||||
{file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9"},
|
||||
{file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3"},
|
||||
{file = "pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735"},
|
||||
{file = "pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e"},
|
||||
{file = "pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.9.2"
|
||||
@ -1671,7 +1862,7 @@ name = "psutil"
|
||||
version = "7.2.2"
|
||||
requires_python = ">=3.6"
|
||||
summary = "Cross-platform lib for process and system monitoring."
|
||||
groups = ["nb"]
|
||||
groups = ["default", "nb"]
|
||||
files = [
|
||||
{file = "psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b"},
|
||||
{file = "psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea"},
|
||||
@ -1722,7 +1913,7 @@ name = "pycparser"
|
||||
version = "3.0"
|
||||
requires_python = ">=3.10"
|
||||
summary = "C parser in Python"
|
||||
groups = ["nb"]
|
||||
groups = ["default", "nb"]
|
||||
marker = "implementation_name != \"PyPy\""
|
||||
files = [
|
||||
{file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"},
|
||||
@ -1864,6 +2055,60 @@ files = [
|
||||
{file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyside6"
|
||||
version = "6.10.2"
|
||||
requires_python = "<3.15,>=3.9"
|
||||
summary = "Python bindings for the Qt cross-platform application and UI framework"
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
"PySide6-Addons==6.10.2",
|
||||
"PySide6-Essentials==6.10.2",
|
||||
"shiboken6==6.10.2",
|
||||
]
|
||||
files = [
|
||||
{file = "pyside6-6.10.2-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:4b084293caa7845d0064aaf6af258e0f7caae03a14a33537d0a552131afddaf0"},
|
||||
{file = "pyside6-6.10.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1b89ce8558d4b4f35b85bff1db90d680912e4d3ce9e79ff804d6fef1d1a151ef"},
|
||||
{file = "pyside6-6.10.2-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:0439f5e9b10ebe6177981bac9e219096ec970ac6ec215bef055279802ba50601"},
|
||||
{file = "pyside6-6.10.2-cp39-abi3-win_amd64.whl", hash = "sha256:032bad6b18a17fcbf4dddd0397f49b07f8aae7f1a45b7e4de7037bf7fd6e0edf"},
|
||||
{file = "pyside6-6.10.2-cp39-abi3-win_arm64.whl", hash = "sha256:65a59ad0bc92525639e3268d590948ce07a80ee97b55e7a9200db41d493cac31"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyside6-addons"
|
||||
version = "6.10.2"
|
||||
requires_python = "<3.15,>=3.9"
|
||||
summary = "Python bindings for the Qt cross-platform application and UI framework (Addons)"
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
"PySide6-Essentials==6.10.2",
|
||||
"shiboken6==6.10.2",
|
||||
]
|
||||
files = [
|
||||
{file = "pyside6_addons-6.10.2-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:0de7d0c9535e17d5e3b634b61314a1867f3b0f6d35c3d7cdc99efc353192faff"},
|
||||
{file = "pyside6_addons-6.10.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:030a851163b51dbf0063be59e9ddb6a9e760bde89a28e461ccc81a224d286eaf"},
|
||||
{file = "pyside6_addons-6.10.2-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:fcee0373e3fd7b98f014094e5e37b4a39e4de7c5a47c13f654a7d557d4a426ad"},
|
||||
{file = "pyside6_addons-6.10.2-cp39-abi3-win_amd64.whl", hash = "sha256:c20150068525a17494f3b6576c5d61c417cf9a5870659e29f5ebd83cd20a78ea"},
|
||||
{file = "pyside6_addons-6.10.2-cp39-abi3-win_arm64.whl", hash = "sha256:3d18db739b46946ba7b722d8ad4cc2097135033aa6ea57076e64d591e6a345f3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyside6-essentials"
|
||||
version = "6.10.2"
|
||||
requires_python = "<3.15,>=3.9"
|
||||
summary = "Python bindings for the Qt cross-platform application and UI framework (Essentials)"
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
"shiboken6==6.10.2",
|
||||
]
|
||||
files = [
|
||||
{file = "pyside6_essentials-6.10.2-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:1dee2cb9803ff135f881dadeb5c0edcef793d1ec4f8a9140a1348cecb71074e1"},
|
||||
{file = "pyside6_essentials-6.10.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:660aea45bfa36f1e06f799b934c2a7df963bd31abc5083e8bb8a5bfaef45686b"},
|
||||
{file = "pyside6_essentials-6.10.2-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:c2b028e4c6f8047a02c31f373408e23b4eedfd405f56c6aba8d0525c29472835"},
|
||||
{file = "pyside6_essentials-6.10.2-cp39-abi3-win_amd64.whl", hash = "sha256:0741018c2b6395038cad4c41775cfae3f13a409e87995ac9f7d89e5b1fb6b22a"},
|
||||
{file = "pyside6_essentials-6.10.2-cp39-abi3-win_arm64.whl", hash = "sha256:db5f4913648bb6afddb8b347edae151ee2378f12bceb03c8b2515a530a4b38d9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.0.2"
|
||||
@ -1970,6 +2215,57 @@ files = [
|
||||
{file = "python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyvips"
|
||||
version = "3.1.1"
|
||||
requires_python = ">=3.7"
|
||||
summary = "binding for the libvips image processing library"
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
"cffi>=1.0.0",
|
||||
]
|
||||
files = [
|
||||
{file = "pyvips-3.1.1.tar.gz", hash = "sha256:84fe744d023b1084ac2516bb17064cacd41c7f8aabf8e524dd383534941b9301"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyvips-binary"
|
||||
version = "8.18.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Binary distribution of libvips and dependencies for use with pyvips"
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
"cffi>=1.0.0",
|
||||
]
|
||||
files = [
|
||||
{file = "pyvips_binary-8.18.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:6ff72bd6c60bb6cf75b7827083b64e275a15a7d862628b5716998350c17426c8"},
|
||||
{file = "pyvips_binary-8.18.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a570dbf76bb620efc9745d82d6493da504d56b21b035ccd876e358a0c182e018"},
|
||||
{file = "pyvips_binary-8.18.0-cp37-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dad3012233b7b12f48180f2a407a50854e44654f37168fa8d42583d9e4f15882"},
|
||||
{file = "pyvips_binary-8.18.0-cp37-abi3-manylinux_2_26_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0906be336b8f775e2d33dfe61ffc480ff83c91c08d5eeff904c27c2c5164ff3a"},
|
||||
{file = "pyvips_binary-8.18.0-cp37-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4ddd4d344f758483d1630a9a08f201ab95162599acc6a8e6c62bb1563e94fe0"},
|
||||
{file = "pyvips_binary-8.18.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:076fb0affa2901af0fee90c728ded6eed2c72f00356af9895fa7a1fb6c9a2288"},
|
||||
{file = "pyvips_binary-8.18.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:659ef1e4af04b3472e7762a95caa1038fdeea530807c84a23a0f4c706af0338f"},
|
||||
{file = "pyvips_binary-8.18.0-cp37-abi3-win32.whl", hash = "sha256:fd331bcd75bff8651d73d09687d55ac8fb9014baa5682b770a4ea0fbcedf5f97"},
|
||||
{file = "pyvips_binary-8.18.0-cp37-abi3-win_amd64.whl", hash = "sha256:a67d73683f70c21bf2c336b6d5ddc2bd54ec36db72cc54ab63cb48bc2373feac"},
|
||||
{file = "pyvips_binary-8.18.0-cp37-abi3-win_arm64.whl", hash = "sha256:0c1f9af910866bc8c2d55182e7a6e8684a828ee4d6084dd814e88e2ee9ec4be3"},
|
||||
{file = "pyvips_binary-8.18.0.tar.gz", hash = "sha256:2f9e509de6d0cf04ea9b429ff0649130a9cf04de8a4f0887d2bcb72e3973225a"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyvips"
|
||||
version = "3.1.1"
|
||||
extras = ["binary"]
|
||||
requires_python = ">=3.7"
|
||||
summary = "binding for the libvips image processing library"
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
"pyvips-binary",
|
||||
"pyvips==3.1.1",
|
||||
]
|
||||
files = [
|
||||
{file = "pyvips-3.1.1.tar.gz", hash = "sha256:84fe744d023b1084ac2516bb17064cacd41c7f8aabf8e524dd383534941b9301"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pywinpty"
|
||||
version = "3.0.3"
|
||||
@ -2387,6 +2683,20 @@ files = [
|
||||
{file = "setuptools-82.0.0.tar.gz", hash = "sha256:22e0a2d69474c6ae4feb01951cb69d515ed23728cf96d05513d36e42b62b37cb"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shiboken6"
|
||||
version = "6.10.2"
|
||||
requires_python = "<3.15,>=3.9"
|
||||
summary = "Python/C++ bindings helper module"
|
||||
groups = ["default"]
|
||||
files = [
|
||||
{file = "shiboken6-6.10.2-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:3bd4e94e9a3c8c1fa8362fd752d399ef39265d5264e4e37bae61cdaa2a00c8c7"},
|
||||
{file = "shiboken6-6.10.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ace0790032d9cb0adda644b94ee28d59410180d9773643bb6cf8438c361987ad"},
|
||||
{file = "shiboken6-6.10.2-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:f74d3ed1f92658077d0630c39e694eb043aeb1d830a5d275176c45d07147427f"},
|
||||
{file = "shiboken6-6.10.2-cp39-abi3-win_amd64.whl", hash = "sha256:10f3c8c5e1b8bee779346f21c10dbc14cff068f0b0b4e62420c82a6bf36ac2e7"},
|
||||
{file = "shiboken6-6.10.2-cp39-abi3-win_arm64.whl", hash = "sha256:20c671645d70835af212ee05df60361d734c5305edb2746e9875c6a31283f963"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.17.0"
|
||||
|
||||
@ -6,11 +6,15 @@ authors = [
|
||||
{name = "Susanne Franke", email = "s.franke@d-opt.de"},
|
||||
{name = "Florian Förster", email = "f.foerster@d-opt.com"},
|
||||
]
|
||||
dependencies = []
|
||||
requires-python = ">=3.11"
|
||||
dependencies = ["PySide6>=6.10.2", "numpy>=2.4.2", "pillow>=12.1.1", "pyvips[binary]>=3.1.1", "psutil>=7.2.2"]
|
||||
requires-python = "<3.15,>=3.11"
|
||||
readme = "README.md"
|
||||
license = {text = "LicenseRef-Proprietary"}
|
||||
|
||||
[project.optional-dependencies]
|
||||
open-cv = [
|
||||
"opencv-python>=4.13.0.92",
|
||||
]
|
||||
[build-system]
|
||||
requires = ["pdm-backend"]
|
||||
build-backend = "pdm.backend"
|
||||
|
||||
63
src/KSG_anomaly_detection/_prepare_env.py
Normal file
63
src/KSG_anomaly_detection/_prepare_env.py
Normal file
@ -0,0 +1,63 @@
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from KSG_anomaly_detection.config_for_test import PATH
|
||||
|
||||
BASE_PATH = Path(PATH)
|
||||
|
||||
|
||||
# delete "Daten"
|
||||
def recreate_folder(folder_name: str) -> Path:
|
||||
p_data = BASE_PATH / folder_name
|
||||
if p_data.exists():
|
||||
shutil.rmtree(p_data)
|
||||
p_data.mkdir(parents=True)
|
||||
return p_data
|
||||
|
||||
|
||||
def main() -> None:
|
||||
paths_src: list[Path] = []
|
||||
paths_dst: list[Path] = []
|
||||
|
||||
_ = recreate_folder("Daten")
|
||||
_ = recreate_folder("KI")
|
||||
# packages
|
||||
p_orig_data = (
|
||||
BASE_PATH / "_Originaldaten/614706_helles Entek/614706_helles Entek[3136761]_1"
|
||||
)
|
||||
assert p_orig_data.exists(), "original data not existing"
|
||||
paths_src.append(p_orig_data)
|
||||
|
||||
p_data = recreate_folder(
|
||||
"Verifizierdaten_1/20260225/614706_helles Entek/614706_helles Entek[3136761]_1"
|
||||
)
|
||||
paths_dst.append(p_data)
|
||||
|
||||
p_orig_data = (
|
||||
BASE_PATH / "_Originaldaten/614706_helles Entek/614706_helles Entek[3136761]_2"
|
||||
)
|
||||
assert p_orig_data.exists(), "original data not existing"
|
||||
paths_src.append(p_orig_data)
|
||||
|
||||
p_data = recreate_folder(
|
||||
"Verifizierdaten_1/20260225/614706_helles Entek/614706_helles Entek[3136761]_2"
|
||||
)
|
||||
paths_dst.append(p_data)
|
||||
|
||||
p_orig_data = (
|
||||
BASE_PATH / "_Originaldaten/614706_helles Entek/614706_helles Entek[3136761]_3"
|
||||
)
|
||||
assert p_orig_data.exists(), "original data not existing"
|
||||
paths_src.append(p_orig_data)
|
||||
|
||||
p_data = recreate_folder(
|
||||
"Verifizierdaten_1/20260225/614706_helles Entek/614706_helles Entek[3136761]_3"
|
||||
)
|
||||
paths_dst.append(p_data)
|
||||
|
||||
for src, dst in zip(paths_src, paths_dst):
|
||||
shutil.copytree(src, dst, dirs_exist_ok=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
43
src/KSG_anomaly_detection/_profile.py
Normal file
43
src/KSG_anomaly_detection/_profile.py
Normal file
@ -0,0 +1,43 @@
|
||||
import cProfile
|
||||
import pstats
|
||||
import time
|
||||
|
||||
from KSG_anomaly_detection import _prepare_env, delegator
|
||||
from KSG_anomaly_detection.monitor import monitor_folder_simple
|
||||
|
||||
profiler = cProfile.Profile()
|
||||
|
||||
PROFILE = True
|
||||
USE_NEW_IMPL = False
|
||||
USE_MP = False
|
||||
ONLY_PREPARE = False
|
||||
|
||||
|
||||
def main() -> None:
|
||||
_prepare_env.main()
|
||||
if ONLY_PREPARE:
|
||||
return
|
||||
|
||||
mp_pool = delegator.MPPool()
|
||||
|
||||
try:
|
||||
t1 = time.perf_counter()
|
||||
if PROFILE:
|
||||
profiler.enable()
|
||||
monitor_folder_simple(mp_pool=mp_pool, use_new=USE_NEW_IMPL, use_mp=USE_MP)
|
||||
profiler.disable()
|
||||
|
||||
stats = pstats.Stats(profiler).sort_stats("cumtime")
|
||||
ENTRIES_TO_SHOW = 40 if USE_MP else 20
|
||||
stats.print_stats(ENTRIES_TO_SHOW)
|
||||
else:
|
||||
monitor_folder_simple(mp_pool=mp_pool, use_new=USE_NEW_IMPL, use_mp=USE_MP)
|
||||
t2 = time.perf_counter()
|
||||
finally:
|
||||
mp_pool.close()
|
||||
|
||||
print(f"Elapsed time: {t2 - t1} s")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
9
src/KSG_anomaly_detection/_run.py
Normal file
9
src/KSG_anomaly_detection/_run.py
Normal file
@ -0,0 +1,9 @@
|
||||
from time import perf_counter
|
||||
|
||||
from KSG_anomaly_detection.monitor import monitor_folder_simple
|
||||
|
||||
if __name__ == "__main__":
|
||||
t1 = perf_counter()
|
||||
monitor_folder_simple()
|
||||
t2 = perf_counter()
|
||||
print(f"Elasped time: {(t2 - t1)} s")
|
||||
17
src/KSG_anomaly_detection/config.py
Normal file
17
src/KSG_anomaly_detection/config.py
Normal file
@ -0,0 +1,17 @@
|
||||
import config_for_test
|
||||
|
||||
# Monitoring
|
||||
START_DATE = "20260225"
|
||||
MONITOR_PATH = rf"{config_for_test.PATH}"
|
||||
AGE_THRESHOLD = (
|
||||
10 # how long nothing new is allowed to have been created within the folder (in seconds)
|
||||
)
|
||||
|
||||
|
||||
# Processing: turned depending on AI on/off, we decide whether the folder is used for anomaly detection or not
|
||||
V_1 = "Verifizierdaten_1" # Name des Ordners der Daten von Verifizierstation 1
|
||||
CURRENT_PATH_RGB = rf"{config_for_test.PATH}\Daten" # dort werden die Ordner mit den RGB-AOI-Bildern abgelegt
|
||||
|
||||
|
||||
# Fileserver: Datensicherung
|
||||
STORING_PATH = rf"{config_for_test.PATH}\KI"
|
||||
11
src/KSG_anomaly_detection/config_for_test.py
Normal file
11
src/KSG_anomaly_detection/config_for_test.py
Normal file
@ -0,0 +1,11 @@
|
||||
# BITTE ANPASSEN
|
||||
|
||||
# Pfad zu dem Ordner, in dem die Ordner 'KI' und 'Verifizierdaten_1' liegen
|
||||
PATH = r"B:\projects\KSG\Ordnerstruktur"
|
||||
|
||||
# Pfad zu den einzelnen Päckchen, die untersucht werden sollen
|
||||
FOLDER_LIST = [
|
||||
r"B:\projects\KSG\Ordnerstruktur\Verifizierdaten_1\20260225\614706_helles Entek\614706_helles Entek[3136761]_1",
|
||||
r"B:\projects\KSG\Ordnerstruktur\Verifizierdaten_1\20260225\614706_helles Entek\614706_helles Entek[3136761]_2",
|
||||
r"B:\projects\KSG\Ordnerstruktur\Verifizierdaten_1\20260225\614706_helles Entek\614706_helles Entek[3136761]_3",
|
||||
]
|
||||
77
src/KSG_anomaly_detection/delegator.py
Normal file
77
src/KSG_anomaly_detection/delegator.py
Normal file
@ -0,0 +1,77 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import multiprocessing as mp
|
||||
from collections.abc import Callable, Collection, Iterable
|
||||
from typing import Any, TypeVar
|
||||
|
||||
import psutil
|
||||
|
||||
T = TypeVar("T")
|
||||
D = TypeVar("D")
|
||||
|
||||
|
||||
class MPPool:
|
||||
def __init__(
|
||||
self,
|
||||
) -> None:
|
||||
self.num_workers = psutil.cpu_count(logical=False) or 4
|
||||
print("Set number of workers to: ", self.num_workers)
|
||||
self.pool = mp.Pool(processes=self.num_workers)
|
||||
|
||||
def enrich_data_funcargs(
|
||||
self,
|
||||
data: Iterable[T],
|
||||
arg: D,
|
||||
) -> list[tuple[T, D]]:
|
||||
return [(entry, arg) for entry in data]
|
||||
|
||||
def get_chunksize(
|
||||
self,
|
||||
data: Collection[Any],
|
||||
) -> int:
|
||||
chunk_size = max(1, math.ceil(len(data) / self.num_workers))
|
||||
|
||||
return chunk_size
|
||||
|
||||
def chunk_data(
|
||||
self,
|
||||
data: list[T],
|
||||
chunk_size: int | None = None,
|
||||
) -> list[list[T]]:
|
||||
if chunk_size is None:
|
||||
chunk_size = max(1, len(data) // self.num_workers)
|
||||
chunks = [data[i : i + chunk_size] for i in range(0, len(data), chunk_size)]
|
||||
chunks_assigned = chunks[: self.num_workers]
|
||||
|
||||
if len(chunks) - self.num_workers > 0:
|
||||
open_chunks = chunks[self.num_workers :]
|
||||
open_entries = (entry for chunk in open_chunks for entry in chunk)
|
||||
|
||||
for idx, entry in enumerate(open_entries):
|
||||
chunks_assigned[idx].append(entry)
|
||||
|
||||
return chunks_assigned
|
||||
|
||||
def map(
|
||||
self,
|
||||
func: Callable[[Any], None],
|
||||
chunks: Iterable[Any],
|
||||
) -> None:
|
||||
# assumes pre-batched data with "chunk_data"
|
||||
_ = self.pool.map(func, chunks, chunksize=1)
|
||||
|
||||
def starmap(
|
||||
self,
|
||||
func: Callable[[Any], None],
|
||||
chunks: Iterable[tuple[Any, ...]],
|
||||
) -> None:
|
||||
# assumes pre-batched data with "chunk_data"
|
||||
_ = self.pool.starmap(func, chunks, chunksize=1)
|
||||
|
||||
def close(self) -> None:
|
||||
self.pool.close()
|
||||
self.pool.join()
|
||||
|
||||
def terminate(self) -> None:
|
||||
self.pool.terminate()
|
||||
87
src/KSG_anomaly_detection/gui_ai_on_off.py
Normal file
87
src/KSG_anomaly_detection/gui_ai_on_off.py
Normal file
@ -0,0 +1,87 @@
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QFont
|
||||
from PySide6.QtWidgets import (
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QPushButton,
|
||||
QSizePolicy,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
|
||||
class ToggleGUI(QWidget):
|
||||
def __init__(self, title, background_colour):
|
||||
super().__init__()
|
||||
self.title = title
|
||||
self.bg_colour = background_colour
|
||||
|
||||
self.setWindowTitle("KI-Algorithmus")
|
||||
self.setMinimumSize(300, 200)
|
||||
|
||||
# Label
|
||||
self.label = QLabel(self.title) # Visper 1 or Visper 2
|
||||
self.label.setAlignment(Qt.AlignCenter)
|
||||
font = QFont("Arial", 42, QFont.Bold)
|
||||
self.label.setFont(font)
|
||||
self.label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
|
||||
# Buttons for turning KI on/off
|
||||
self.btn_on = QPushButton("KI eingeschaltet")
|
||||
self.btn_off = QPushButton("KI ausgeschaltet")
|
||||
|
||||
for btn in (self.btn_on, self.btn_off):
|
||||
btn.setCheckable(True)
|
||||
btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
self.set_button_style(btn, "lightgrey")
|
||||
|
||||
self.btn_on.clicked.connect(lambda: self.handle_toggle(self.btn_on))
|
||||
self.btn_off.clicked.connect(lambda: self.handle_toggle(self.btn_off))
|
||||
|
||||
# Layouts for positioning
|
||||
btn_layout = QHBoxLayout()
|
||||
btn_layout.setSpacing(20)
|
||||
btn_layout.addWidget(self.btn_on)
|
||||
btn_layout.addWidget(self.btn_off)
|
||||
|
||||
main_layout = QVBoxLayout()
|
||||
main_layout.setSpacing(20)
|
||||
main_layout.setContentsMargins(40, 40, 40, 40)
|
||||
main_layout.addWidget(self.label)
|
||||
main_layout.addLayout(btn_layout)
|
||||
main_layout.addStretch(1)
|
||||
|
||||
self.setLayout(main_layout)
|
||||
|
||||
# default state: ON
|
||||
self.handle_toggle(self.btn_on)
|
||||
|
||||
# background colour to distinguish the different GUIs from eacht other
|
||||
self.setStyleSheet(f"background-color: {self.bg_colour};")
|
||||
|
||||
def set_button_style(self, button, color):
|
||||
button.setStyleSheet(f"""
|
||||
QPushButton {{
|
||||
background-color: {color};
|
||||
padding: 20px;
|
||||
font-size: 24px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
}}
|
||||
""")
|
||||
|
||||
def handle_toggle(self, clicked_button):
|
||||
if clicked_button == self.btn_on:
|
||||
self.btn_on.setChecked(True)
|
||||
self.btn_off.setChecked(False)
|
||||
self.set_button_style(self.btn_on, "lightgreen")
|
||||
self.set_button_style(self.btn_off, "lightgrey")
|
||||
else:
|
||||
self.btn_off.setChecked(True)
|
||||
self.btn_on.setChecked(False)
|
||||
self.set_button_style(self.btn_off, "lightcoral")
|
||||
self.set_button_style(self.btn_on, "lightgrey")
|
||||
|
||||
def is_enabled(self):
|
||||
# we return True if 'KI eingeschaltet' is active
|
||||
return self.btn_on.isChecked()
|
||||
22
src/KSG_anomaly_detection/main.py
Normal file
22
src/KSG_anomaly_detection/main.py
Normal file
@ -0,0 +1,22 @@
|
||||
import sys
|
||||
import threading
|
||||
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
from KSG_anomaly_detection.monitor import (
|
||||
monitor_folder, # Check für neue Ordner und Auslösen des KI-Algorithmus
|
||||
)
|
||||
from KSG_anomaly_detection.window_manager import WindowManager # zum Erzeugen der GUI
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
manager = WindowManager()
|
||||
manager._create_v1()
|
||||
# manager._create_v2()
|
||||
# manager._create_stats()
|
||||
|
||||
monitor_thread = threading.Thread(target=monitor_folder, args=(manager,), daemon=True)
|
||||
monitor_thread.start()
|
||||
|
||||
sys.exit(app.exec())
|
||||
202
src/KSG_anomaly_detection/monitor.py
Normal file
202
src/KSG_anomaly_detection/monitor.py
Normal file
@ -0,0 +1,202 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
|
||||
from KSG_anomaly_detection import config, config_for_test, delegator
|
||||
from KSG_anomaly_detection.preparation import Preparation
|
||||
from KSG_anomaly_detection.window_manager import WindowManager
|
||||
|
||||
|
||||
# Identifikation aller Unterordner jeweils in Verifizierstation_1 und Verifizierstation_2, eindeutige Lose
|
||||
def get_third_level_subfolders(path):
|
||||
seen_basenames = set()
|
||||
result = set()
|
||||
|
||||
pattern = re.compile(r"^(.*)_\d+$")
|
||||
|
||||
for level1 in os.listdir(path):
|
||||
level1_path = os.path.join(path, level1)
|
||||
if not os.path.isdir(level1_path):
|
||||
continue
|
||||
if not level1 >= config.START_DATE:
|
||||
continue
|
||||
|
||||
for level2 in os.listdir(level1_path):
|
||||
level2_path = os.path.join(level1_path, level2)
|
||||
if not os.path.isdir(level2_path):
|
||||
continue
|
||||
|
||||
for level3 in os.listdir(level2_path):
|
||||
level3_path = os.path.join(level2_path, level3)
|
||||
if not os.path.isdir(level3_path):
|
||||
continue
|
||||
|
||||
match = pattern.match(level3)
|
||||
if match:
|
||||
base_name = level3
|
||||
|
||||
if base_name not in seen_basenames:
|
||||
seen_basenames.add(base_name)
|
||||
|
||||
base_path = os.path.join(level2_path, base_name)
|
||||
result.add(base_path)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# zur Identifikation, ob wir uns in Verifizierstation_1 und Verifizierstation_2 befinden
|
||||
def get_first_level_name(folder_path):
|
||||
# returns the name of the immediate parent folder (e.g., V1 or V2)
|
||||
return Path(folder_path).parts[-4]
|
||||
|
||||
|
||||
def is_entire_folder_unchanged(folder_path, threshold_seconds=300):
|
||||
now = time.time()
|
||||
for root, dirs, files in os.walk(folder_path):
|
||||
for entry in dirs + files:
|
||||
try:
|
||||
entry_path = os.path.join(root, entry)
|
||||
mtime = os.path.getmtime(entry_path)
|
||||
if now - mtime < threshold_seconds:
|
||||
return False
|
||||
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def are_all_matching_folders_unchanged(base_path, name_substring, threshold_seconds):
|
||||
for root, dirs, _ in os.walk(base_path):
|
||||
for d in dirs:
|
||||
if name_substring in d:
|
||||
folder_path = os.path.join(root, d)
|
||||
if not is_entire_folder_unchanged(folder_path, threshold_seconds):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# Hauptfunktion: Check auf neue Ordner und ggf. Auslösen des KI-Algorithmus
|
||||
def monitor_folder(manager: WindowManager):
|
||||
print("starting thread...")
|
||||
|
||||
while True:
|
||||
for folder in config_for_test.FOLDER_LIST:
|
||||
try:
|
||||
if are_all_matching_folders_unchanged(
|
||||
os.path.dirname(folder),
|
||||
os.path.basename(folder),
|
||||
threshold_seconds=config.AGE_THRESHOLD,
|
||||
):
|
||||
# prüfen, ob Verifizierstation_1 oder Verifizierstation_2
|
||||
first_level = get_first_level_name(folder)
|
||||
|
||||
# ein zu Verifizierstation_1 zugehöriger Ordner und KI-Algorithmus soll durchgeführt werden
|
||||
if first_level == config.V_1 and manager.get_checkbox_state_v1():
|
||||
# Vorbereitung
|
||||
preparation = Preparation(folder)
|
||||
|
||||
# Aufgabe 2: NGT und check_img von Originalordner in KI kopieren
|
||||
# Rückgabewert: Ordner Fileserver/KI auf dem Fileserver, wo dann die Heatmaps abgelegt werden
|
||||
file_ki_folder, result = preparation.copy_ngt_and_checkimg()
|
||||
if result: # d. h. Fehler ist aufgetreten
|
||||
continue # zu nächstem neuen folder springen
|
||||
|
||||
# Aufgabe 3: check_img im Originalordner anpassen (d. h. gelbe Farbe: work in progress)
|
||||
preparation.change_image_to_yellow()
|
||||
|
||||
# Aufgabe 4: AOI-Bilder in RGB überführen und zwischenspeichern
|
||||
# wir erhalten hier den Speicherort sowie ggf. Fehlermeldungen zurück
|
||||
current_folder, result = preparation.create_rgb_images_and_patches()
|
||||
|
||||
print("finished routine")
|
||||
|
||||
if result is not None:
|
||||
print(result)
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
tb = traceback.extract_tb(e.__traceback__)
|
||||
no = tb[-1].lineno
|
||||
print(e, no)
|
||||
|
||||
time.sleep(60)
|
||||
|
||||
|
||||
def monitor_folder_simple(mp_pool: delegator.MPPool, use_new: bool, use_mp: bool):
|
||||
print("starting procedure...")
|
||||
|
||||
for folder in config_for_test.FOLDER_LIST:
|
||||
# try:
|
||||
if are_all_matching_folders_unchanged(
|
||||
os.path.dirname(folder),
|
||||
os.path.basename(folder),
|
||||
threshold_seconds=config.AGE_THRESHOLD,
|
||||
):
|
||||
# prüfen, ob Verifizierstation_1 oder Verifizierstation_2
|
||||
first_level = get_first_level_name(folder)
|
||||
|
||||
# ein zu Verifizierstation_1 zugehöriger Ordner und KI-Algorithmus soll durchgeführt werden
|
||||
if first_level == config.V_1:
|
||||
# Vorbereitung
|
||||
preparation = Preparation(folder)
|
||||
|
||||
# Aufgabe 2: NGT und check_img von Originalordner in KI kopieren
|
||||
# Rückgabewert: Ordner Fileserver/KI auf dem Fileserver, wo dann die Heatmaps abgelegt werden
|
||||
print("'copy_ngt_and_checkimg'...")
|
||||
if use_new:
|
||||
file_ki_folder, result = preparation.copy_ngt_and_checkimg_new()
|
||||
else:
|
||||
file_ki_folder, result = preparation.copy_ngt_and_checkimg()
|
||||
|
||||
if result: # d. h. Fehler ist aufgetreten
|
||||
continue # zu nächstem neuen folder springen
|
||||
|
||||
# Aufgabe 3: check_img im Originalordner anpassen (d. h. gelbe Farbe: work in progress)
|
||||
SKIP_NEXT = False
|
||||
if not SKIP_NEXT:
|
||||
print("'change_image_to_yellow'...")
|
||||
if use_new:
|
||||
preparation.change_image_to_yellow_new()
|
||||
else:
|
||||
preparation.change_image_to_yellow()
|
||||
|
||||
# sys.exit(0)
|
||||
|
||||
# Aufgabe 4: AOI-Bilder in RGB überführen und zwischenspeichern
|
||||
# wir erhalten hier den Speicherort sowie ggf. Fehlermeldungen zurück
|
||||
SKIP_NEXT = False
|
||||
if not use_mp and not SKIP_NEXT:
|
||||
print("'create_rgb_images_and_patches'...")
|
||||
if use_new:
|
||||
current_folder, result = (
|
||||
preparation.create_rgb_images_and_patches_new()
|
||||
)
|
||||
else:
|
||||
current_folder, result = preparation.create_rgb_images_and_patches()
|
||||
|
||||
SKIP_NEXT = False
|
||||
if use_mp and not SKIP_NEXT:
|
||||
print("'create_rgb_images_and_patches' multiprocessing...")
|
||||
if use_new:
|
||||
current_folder, result = (
|
||||
preparation.create_rgb_images_and_patches_new2(mp_pool)
|
||||
)
|
||||
else:
|
||||
current_folder, result = preparation.create_rgb_images_and_patches()
|
||||
|
||||
print("finished routine")
|
||||
|
||||
if result is not None:
|
||||
print(result)
|
||||
continue
|
||||
|
||||
# except Exception as e:
|
||||
# tb = traceback.extract_tb(e.__traceback__)
|
||||
# no = tb[-1].lineno
|
||||
# print(e, no)
|
||||
|
||||
# time.sleep(60)
|
||||
446
src/KSG_anomaly_detection/preparation.py
Normal file
446
src/KSG_anomaly_detection/preparation.py
Normal file
@ -0,0 +1,446 @@
|
||||
import multiprocessing
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
from pprint import pprint
|
||||
from shutil import copytree
|
||||
from typing import Literal, TypeAlias, cast
|
||||
|
||||
import pyvips
|
||||
from PIL import Image
|
||||
from pyvips import Image as vipsImage
|
||||
|
||||
from KSG_anomaly_detection import config, delegator
|
||||
|
||||
Image.MAX_IMAGE_PIXELS = None
|
||||
COLOUR_ASSIGNMENT = {"R": [255, 0, 0], "G": [0, 255, 0], "B": [0, 0, 0]}
|
||||
RE_CHANNEL_MAPPING = re.compile(r"R_NG(\d+)_(\d+)\.jpg$")
|
||||
|
||||
|
||||
class Preparation:
|
||||
def __init__(self, folder):
|
||||
# der aktuelle Ordner mit neuen AOI-Bilddateien auf dem KI-Rechner
|
||||
self.folder_path = folder
|
||||
|
||||
self.path = Path(self.folder_path)
|
||||
# ?? verify existence --> fail early?
|
||||
|
||||
self.visper = self.path.parts[-4]
|
||||
# self.original_data_path = (
|
||||
# Path(config.STORING_PATH)
|
||||
# / self.visper
|
||||
# / Path(self.folder_path).parents[1].name
|
||||
# / Path(self.folder_path).parent.name
|
||||
# ) # Pfad zu Fileserver/KI/...
|
||||
|
||||
# the original path's last component is the product type name instead of the package
|
||||
self.original_data_base_path = Path(config.STORING_PATH).joinpath(
|
||||
*self.path.parts[-4:-1]
|
||||
)
|
||||
self.original_data_path = self.original_data_base_path / self.path.name
|
||||
|
||||
print(f"[{self.visper}] {Path(self.folder_path).name} Vorbereitung gestartet...")
|
||||
|
||||
# ------------------------------------- Zweite Aufgabe: ngt und check_img kopieren ------------------------------------
|
||||
def copy_ngt_and_checkimg(self):
|
||||
try:
|
||||
# extract last level name because we need to copy all folders containing this name
|
||||
target_name = os.path.basename(self.folder_path)
|
||||
base_path = Path(self.folder_path).parent
|
||||
|
||||
folders_to_copy = []
|
||||
|
||||
with os.scandir(base_path) as entries:
|
||||
for entry in entries:
|
||||
if entry.is_dir() and target_name in entry.name:
|
||||
folders_to_copy.append(entry.path)
|
||||
|
||||
for src in folders_to_copy:
|
||||
dst = (
|
||||
Path(config.STORING_PATH)
|
||||
/ self.visper
|
||||
/ Path(self.folder_path).parent.parent.name
|
||||
/ Path(self.folder_path).parent.name
|
||||
/ Path(src).name
|
||||
)
|
||||
copytree(src, dst, dirs_exist_ok=True)
|
||||
|
||||
return Path(config.STORING_PATH) / self.visper / Path(
|
||||
self.folder_path
|
||||
).parent.parent / Path(self.folder_path).parent, None
|
||||
|
||||
except FileExistsError:
|
||||
return (
|
||||
None,
|
||||
f"Fehlermeldung: Ordner {Path(self.folder_path).parts[-1]} existiert bereits.",
|
||||
)
|
||||
except Exception as e:
|
||||
tb = traceback.extract_tb(e.__traceback__)
|
||||
no = tb[-1].lineno
|
||||
return None, f"Fehlermeldung: {e}, {no}"
|
||||
|
||||
def copy_ngt_and_checkimg_new(self):
|
||||
try:
|
||||
# extract last level name because we need to copy all folders containing this name
|
||||
# target_name = os.path.basename(self.folder_path)
|
||||
# target_name = self.path.name
|
||||
# base_path = self.path.parent
|
||||
|
||||
# ?? Warum kopieren wir nicht gleich den ganzen Ordner? --> Das Ergebnis der unten stehenden
|
||||
# ?? Operation ist eine Liste, in der nur der Name des Ordners (Päckchen-Ebene) steht, die also eigentlich
|
||||
# ?? immer nur einen Eintrag enthält --> self.path
|
||||
# Einzige Ausnahme: mehr als 10 Einträge/Päckchen, dann enthalten die Päckchen 10-19 auch den Namen des
|
||||
# ersten Päckchens
|
||||
# Annahme hier: Es soll nur die Daten des entsprechenden Päckchens kopiert werden
|
||||
|
||||
# folders_to_copy = []
|
||||
# with os.scandir(base_path) as entries:
|
||||
# for entry in entries:
|
||||
# if entry.is_dir() and target_name in entry.name:
|
||||
# folders_to_copy.append(entry.path)
|
||||
|
||||
# for src in folders_to_copy:
|
||||
# # duplicate -> self.original_data_path
|
||||
# dst = (
|
||||
# Path(config.STORING_PATH)
|
||||
# / self.visper
|
||||
# / Path(self.folder_path).parent.parent.name
|
||||
# / Path(self.folder_path).parent.name
|
||||
# / Path(src).name
|
||||
# )
|
||||
# copytree(src, dst, dirs_exist_ok=True)
|
||||
|
||||
src = self.path
|
||||
# dst = self.original_data_base_path / self.path.name
|
||||
copytree(src, self.original_data_path, dirs_exist_ok=True)
|
||||
|
||||
# ?? Soll hier der Pfad mit den kopierten Originaldaten zurückgegeben werden?
|
||||
# Das ist aktuell nicht der Fall, da die letzten Pfadkomponenten als absolute Pfade das
|
||||
# Ergebnis komplett überschreiben
|
||||
# Zudem wird der Basispfad (also Produktebene, nicht Päckchen zurückgegeben)!
|
||||
# return Path(config.STORING_PATH) / self.visper / Path(
|
||||
# self.folder_path
|
||||
# ).parent.parent / Path(self.folder_path).parent, None
|
||||
return self.original_data_base_path, None
|
||||
|
||||
except FileExistsError:
|
||||
return (
|
||||
None,
|
||||
f"Fehlermeldung: Ordner {Path(self.folder_path).parts[-1]} existiert bereits.",
|
||||
)
|
||||
except Exception as e:
|
||||
tb = traceback.extract_tb(e.__traceback__)
|
||||
no = tb[-1].lineno
|
||||
return None, f"Fehlermeldung: {e}, {no}"
|
||||
|
||||
# --------------------------- Dritte Aufgabe: check_img auf dem Fileserver auf Gelb ("Work in Progress") ändern --------------------------
|
||||
def change_image_to_yellow(self):
|
||||
# first we define for R, G and B which coour has to be adapted
|
||||
colour_assignment = {"R": (255, 0, 0), "G": (0, 255, 0), "B": (0, 0, 0)}
|
||||
|
||||
base_path = Path(self.folder_path).parent
|
||||
|
||||
# iterate over all 'checkimg' folders recursively
|
||||
for img_folder in base_path.rglob("checkimg"):
|
||||
if not img_folder.is_dir():
|
||||
continue
|
||||
|
||||
# iterate over image files inside this 'checkimg' folder and only change first ones (because these are the ones to be shown at the Verifizierstation)
|
||||
for image_file in img_folder.glob("????1_1*"):
|
||||
# from the file name, we extract whether it is the R, G or B part of the RGB image
|
||||
# i.e. these are still the Einzelkanalfarbbilder
|
||||
colour_channel = image_file.stem[0]
|
||||
|
||||
# change image
|
||||
####################### ist eigentlich bekannt über DB #######################
|
||||
with Image.open(image_file) as img:
|
||||
size = img.size
|
||||
####################### ist eigentlich bekannt über DB #######################
|
||||
new_img = Image.new("RGB", size, colour_assignment[colour_channel])
|
||||
# save the modified image
|
||||
new_img.save(image_file)
|
||||
|
||||
def pyvips_blank_image(
|
||||
self,
|
||||
size: tuple[int, int],
|
||||
channel: Literal["R", "G", "B"],
|
||||
) -> vipsImage:
|
||||
img = pyvips.Image.black(size[0], size[1], bands=3) + COLOUR_ASSIGNMENT[channel] # type: ignore
|
||||
img = img.cast("uchar") # type: ignore
|
||||
img = img.copy(interpretation="srgb")
|
||||
return img
|
||||
|
||||
def change_image_to_yellow_new(self):
|
||||
# ?? Hier sind wir nicht mehr auf Ebene des Päckchens, sondern im übergeordneteten Bereich. Ist das
|
||||
# ?? richtig so? Beim Kopieren waren wir nur auf Päckchen-Ebene unterwegs.
|
||||
# Wenn jetzt neue Instanzen dieser Klasse mit anderen Päckchen desselben Basisordners erstellt werden, werden
|
||||
# die Berechnungen an dieser Stelle immer erneut durchgeführt, auch für die anderen Päckchen, egal ob bereits
|
||||
# durchgeführt oder durch Nutzer abgewählt
|
||||
# Annahme hier: Es sollen nur Dateien des aktuellen Päckchens bearbeitet werden.
|
||||
base_path = self.path
|
||||
# base_path = Path(self.folder_path).parent
|
||||
|
||||
# iterate over all 'checkimg' folders recursively
|
||||
# !! check needed
|
||||
# sizes should be the same for the same camera, only obtain once
|
||||
# dict: {camera_number: (width, height)}
|
||||
cam_img_sizes: dict[str, tuple[int, int]] = {}
|
||||
cam_rgb_pictures: dict[str, dict[str, vipsImage]] = {
|
||||
cam_no: {} for cam_no in ("1", "2")
|
||||
}
|
||||
|
||||
for image_file in base_path.rglob("checkimg/????1_1*"):
|
||||
# print("processing image file: ", image_file)
|
||||
# from the file name, we extract whether it is the R, G or B part of the RGB image
|
||||
# i.e. these are still the Einzelkanalfarbbilder
|
||||
colour_channel = image_file.stem[0]
|
||||
camera_num = str(image_file.parents[1])[-1]
|
||||
|
||||
if camera_num not in cam_img_sizes:
|
||||
img_read = pyvips.Image.new_from_file(image_file, access="random")
|
||||
size = img_read.width, img_read.height
|
||||
cam_img_sizes[camera_num] = size
|
||||
if (
|
||||
camera_num not in cam_rgb_pictures
|
||||
or colour_channel not in cam_rgb_pictures[camera_num]
|
||||
):
|
||||
blank_img = self.pyvips_blank_image(size, colour_channel)
|
||||
cam_rgb_pictures[camera_num][colour_channel] = blank_img
|
||||
|
||||
img = cam_rgb_pictures[camera_num][colour_channel]
|
||||
img_file_temp = (image_file.parent / (image_file.stem + "_temp")).with_suffix(
|
||||
image_file.suffix
|
||||
)
|
||||
img.write_to_file(img_file_temp)
|
||||
os.replace(img_file_temp, image_file)
|
||||
|
||||
# --------------------------- Vierte Aufgabe: AOI-Einzelkanalfarbbilder zur RGB-Bildern zusammenfügen --------------------------
|
||||
# within the current folder to be inspected (i.e., self.folder_path), there are subfolders for the different A/B sides
|
||||
# we have to extract the relative path starting from self.folder_path
|
||||
def extract_folder_path_within_one_AOI_folder(
|
||||
self, current_folder_to_inspect, checkimg_folders, folder_type
|
||||
):
|
||||
for folder_to_inspect in checkimg_folders:
|
||||
print(
|
||||
f"\n-------------------------------\n{folder_to_inspect=}\n,{current_folder_to_inspect=}\n"
|
||||
)
|
||||
check_1 = Path(folder_to_inspect).parts[-2]
|
||||
check_2 = Path(current_folder_to_inspect).parts[-2]
|
||||
print(f"{check_1=},\n{check_2=}\n------------------------------------")
|
||||
|
||||
if folder_to_inspect.is_dir() and (
|
||||
Path(folder_to_inspect).parts[-2] == Path(current_folder_to_inspect).parts[-2]
|
||||
):
|
||||
relative_path = current_folder_to_inspect.relative_to(
|
||||
self.original_data_base_path
|
||||
)
|
||||
relative_path = Path(*relative_path.parts[1:])
|
||||
return relative_path
|
||||
|
||||
def create_rgb_images_and_patches(self):
|
||||
# in the folders of interest, we iterate over all images and search for the three that belong together
|
||||
# (because in advance we do not know how many there are)
|
||||
pattern = re.compile(r"R_NG(\d+)_(\d+)\.jpg$")
|
||||
|
||||
# create folder name in our temp folder "Backup" and store it
|
||||
# therefore, first extract the name of the current folder from the whole path
|
||||
folder_name = Path(self.folder_path).name
|
||||
new_folder_path = Path(config.CURRENT_PATH_RGB) / folder_name
|
||||
|
||||
try:
|
||||
new_folder_path.mkdir(parents=True, exist_ok=False)
|
||||
except FileExistsError:
|
||||
return (
|
||||
None,
|
||||
f"Fehlermeldung: Ordner {Path(self.folder_path).parts[-1]} existiert bereits.",
|
||||
)
|
||||
except Exception as e:
|
||||
return None, f"Fehlermeldung: {e}"
|
||||
|
||||
# find all checkimg folders within the folder
|
||||
checkimg_folders = [
|
||||
p for p in self.original_data_base_path.rglob("checkimg") if p.is_dir()
|
||||
]
|
||||
|
||||
# iterate through all 'checkimg' folders recursively
|
||||
for current_folder_to_inspect in checkimg_folders:
|
||||
# identify the path starting from self.folder_path until the checkimg folder
|
||||
relative_path = self.extract_folder_path_within_one_AOI_folder(
|
||||
current_folder_to_inspect, checkimg_folders, "checkimg"
|
||||
)
|
||||
|
||||
save_path_rgb = new_folder_path / relative_path
|
||||
save_path_rgb.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for file_path in current_folder_to_inspect.glob("R_NG*_*.jpg"):
|
||||
# find match according to pattern defined at the very beginning
|
||||
match = pattern.match(file_path.name)
|
||||
if not match:
|
||||
continue
|
||||
|
||||
num1, num2 = match.groups()
|
||||
|
||||
# find all three images belonging together
|
||||
r_path = file_path
|
||||
g_path = current_folder_to_inspect / f"G_NG{num1}_{num2}.jpg"
|
||||
b_path = current_folder_to_inspect / f"B_NG{num1}_{num2}.jpg"
|
||||
|
||||
# open all three images and combine them to RGB
|
||||
with (
|
||||
Image.open(r_path) as r,
|
||||
Image.open(g_path) as g,
|
||||
Image.open(b_path) as b,
|
||||
):
|
||||
r = r.convert("L")
|
||||
g = g.convert("L")
|
||||
b = b.convert("L")
|
||||
|
||||
rgb_image = Image.merge("RGB", (r, g, b))
|
||||
|
||||
filename = f"RGB_NG{num1}_{num2}.png"
|
||||
rgb_image.save(save_path_rgb / filename)
|
||||
|
||||
return "folder_name", None
|
||||
|
||||
def create_rgb_images_and_patches_new(self):
|
||||
# in the folders of interest, we iterate over all images and search for the three that belong together
|
||||
# (because in advance we do not know how many there are)
|
||||
|
||||
# create folder name in our temp folder "Backup" and store it
|
||||
# therefore, first extract the name of the current folder from the whole path
|
||||
# folder_name = self.path.name
|
||||
# print("folder name: ", folder_name)
|
||||
new_folder_path = Path(config.CURRENT_PATH_RGB) / self.path.name
|
||||
# print("new_folder_path: ", new_folder_path)
|
||||
|
||||
try:
|
||||
new_folder_path.mkdir(parents=True, exist_ok=False)
|
||||
except FileExistsError:
|
||||
return (
|
||||
None,
|
||||
f"Fehlermeldung: Ordner {Path(self.folder_path).parts[-1]} existiert bereits.",
|
||||
)
|
||||
except Exception as e:
|
||||
return None, f"Fehlermeldung: {e}"
|
||||
|
||||
# find all checkimg folders within the folder
|
||||
# ?? Hier gewinnen wir wieder alle Verzeichnisse oberhalb der Paketebene, d.h.
|
||||
# ?? unabhängig vom Päckchen
|
||||
# Annahme: Wir wollen tatsächlich nur auf Päckchenebene arbeiten
|
||||
# Sollten i.d.R. vier Ordner sein, 2 Kameras je 1x Vorder-/Rückseite
|
||||
checkimg_folders = tuple(
|
||||
p for p in self.original_data_path.rglob("checkimg") if p.is_dir()
|
||||
)
|
||||
# print(f">>> {checkimg_folders=}")
|
||||
|
||||
# images = tuple(self.original_data_path.rglob("checkimg/R_NG*_*.jpg"))
|
||||
# print(f">>> {len(images)=}")
|
||||
# pprint(images)
|
||||
# sys.exit(0)
|
||||
|
||||
# iterate through all 'checkimg' folders recursively
|
||||
for checkimg_folder in checkimg_folders:
|
||||
# ?? Das scheint unnötig, da wir nun nur für das Päckchen relevante checkimg
|
||||
# ?? Ordner durchsuchen bei jeder Iteration. Damit können wir direkt auf den
|
||||
# ?? Pfaden arbeiten.
|
||||
# identify the path starting from self.folder_path until the checkimg folder
|
||||
# relative_path = self.extract_folder_path_within_one_AOI_folder(
|
||||
# checkimg_folder, checkimg_folders, "checkimg"
|
||||
# )
|
||||
# save_path_rgb = new_folder_path / relative_path
|
||||
# save_path_rgb.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
relative_path = checkimg_folder.parts[-2:]
|
||||
save_path_rgb = new_folder_path.joinpath(*relative_path)
|
||||
save_path_rgb.mkdir(parents=True, exist_ok=True)
|
||||
# print(f">>>> {relative_path=}")
|
||||
# print(f">>>> {save_path_rgb=}")
|
||||
|
||||
for file_path in checkimg_folder.glob("R_NG*_*.jpg"):
|
||||
# find match according to pattern defined at the very beginning
|
||||
match = RE_CHANNEL_MAPPING.match(file_path.name)
|
||||
if not match:
|
||||
continue
|
||||
num1, num2 = match.groups()
|
||||
|
||||
# find all three images belonging together
|
||||
r_path = file_path
|
||||
g_path = checkimg_folder / f"G_NG{num1}_{num2}.jpg"
|
||||
b_path = checkimg_folder / f"B_NG{num1}_{num2}.jpg"
|
||||
|
||||
# open all three images and combine them to RGB
|
||||
r = pyvips.Image.new_from_file(r_path, access="sequential")
|
||||
g = pyvips.Image.new_from_file(g_path, access="sequential")
|
||||
b = pyvips.Image.new_from_file(b_path, access="sequential")
|
||||
rgb_image = r.bandjoin([g, b]) # type: ignore
|
||||
rgb_image = rgb_image.copy(interpretation="srgb")
|
||||
filename = f"RGB_NG{num1}_{num2}.png"
|
||||
rgb_image.write_to_file(save_path_rgb / filename)
|
||||
|
||||
return "folder_name", None
|
||||
|
||||
def create_rgb_images_and_patches_new2(self, pool: delegator.MPPool):
|
||||
# in the folders of interest, we iterate over all images and search for the three that belong together
|
||||
# (because in advance we do not know how many there are)
|
||||
|
||||
# create folder name in our temp folder "Backup" and store it
|
||||
# therefore, first extract the name of the current folder from the whole path
|
||||
rgb_saving_path = cast(Path, Path(config.CURRENT_PATH_RGB) / self.path.name)
|
||||
|
||||
try:
|
||||
rgb_saving_path.mkdir(parents=True, exist_ok=False)
|
||||
except FileExistsError:
|
||||
return (
|
||||
None,
|
||||
f"Fehlermeldung: Ordner {Path(self.folder_path).parts[-1]} existiert bereits.",
|
||||
)
|
||||
except Exception as e:
|
||||
return None, f"Fehlermeldung: {e}"
|
||||
|
||||
images = cast(
|
||||
tuple[Path, ...], tuple(self.original_data_path.rglob("checkimg/R_NG*_*.jpg"))
|
||||
)
|
||||
images = pool.enrich_data_funcargs(images, rgb_saving_path)
|
||||
chunks = pool.chunk_data(images)
|
||||
# these are all images which must be processed
|
||||
pool.map(transform_to_rgb, chunks)
|
||||
|
||||
return "folder_name", None
|
||||
|
||||
|
||||
def transform_to_rgb(
|
||||
files: Iterable[tuple[Path, Path]],
|
||||
) -> None:
|
||||
# iterable contains path to image file and the base saving path
|
||||
# for RGB images
|
||||
# saving_path is "new_folder_path" from above
|
||||
# must be included in function call
|
||||
for image, saving_path in files:
|
||||
relative_path = image.parts[-3:-1]
|
||||
save_path_rgb = saving_path.joinpath(*relative_path)
|
||||
|
||||
save_path_rgb.mkdir(parents=True, exist_ok=True)
|
||||
base_folder = image.parent
|
||||
assert base_folder.is_dir(), "base folder of image not a directory"
|
||||
|
||||
match = re.match(r"R_NG(\d+)_(\d+)\.jpg$", image.name)
|
||||
if not match:
|
||||
continue
|
||||
num1, num2 = match.groups()
|
||||
|
||||
# find all three images belonging together
|
||||
r_path = image
|
||||
g_path = base_folder / f"G_NG{num1}_{num2}.jpg"
|
||||
b_path = base_folder / f"B_NG{num1}_{num2}.jpg"
|
||||
|
||||
# open all three images and combine them to RGB
|
||||
r = pyvips.Image.new_from_file(r_path, access="sequential")
|
||||
g = pyvips.Image.new_from_file(g_path, access="sequential")
|
||||
b = pyvips.Image.new_from_file(b_path, access="sequential")
|
||||
rgb_image = r.bandjoin([g, b]) # type: ignore
|
||||
rgb_image = rgb_image.copy(interpretation="srgb")
|
||||
filename = f"RGB_NG{num1}_{num2}.png"
|
||||
rgb_image.write_to_file(save_path_rgb / filename)
|
||||
22
src/KSG_anomaly_detection/window_manager.py
Normal file
22
src/KSG_anomaly_detection/window_manager.py
Normal file
@ -0,0 +1,22 @@
|
||||
from PySide6.QtCore import QObject, Signal, Slot
|
||||
|
||||
from KSG_anomaly_detection.gui_ai_on_off import ToggleGUI
|
||||
|
||||
|
||||
class WindowManager(QObject):
|
||||
recreate_v1 = Signal()
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.v1 = None
|
||||
|
||||
self.recreate_v1.connect(self._create_v1)
|
||||
|
||||
@Slot()
|
||||
def _create_v1(self):
|
||||
self.v1 = ToggleGUI("Visper 1", "#e6e6fa")
|
||||
self.v1.move(100, 100)
|
||||
self.v1.show()
|
||||
|
||||
def get_checkbox_state_v1(self):
|
||||
return self.v1 and self.v1.is_enabled()
|
||||
Loading…
x
Reference in New Issue
Block a user