diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..9aa279f --- /dev/null +++ b/Pipfile @@ -0,0 +1,18 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +pyqt6 = "*" +numpy = "*" +matplotlib = "*" +scikit-learn = "*" +boto3 = "*" +opencv-python = "*" +black = "*" + +[dev-packages] + +[requires] +python_version = "3.13" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..dee8ee9 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,716 @@ +{ + "_meta": { + "hash": { + "sha256": "8030168f777edcbdfd2b954998e61cd68f938e2c6484ec1ed624794d81b17499" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.13" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "black": { + "hashes": [ + "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", + "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd", + "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea", + "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", + "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", + "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7", + "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", + "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175", + "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", + "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392", + "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad", + "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f", + "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f", + "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", + "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", + "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", + "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800", + "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", + "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", + "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812", + "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50", + "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==24.10.0" + }, + "boto3": { + "hashes": [ + "sha256:6d473f0f340d02b4e9ad5b8e68786a09728101a8b950231b89ebdaf72b6dca21", + "sha256:b36feae061dc0793cf311468956a0a9e99215ce38bc99a1a4e55a5b105f16297" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.36.6" + }, + "botocore": { + "hashes": [ + "sha256:4864c53d638da191a34daf3ede3ff1371a3719d952cc0c6bd24ce2836a38dd77", + "sha256:f77bbbb03fb420e260174650fb5c0cc142ec20a96967734eed2b0ef24334ef34" + ], + "markers": "python_version >= '3.8'", + "version": "==1.36.6" + }, + "click": { + "hashes": [ + "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.8" + }, + "contourpy": { + "hashes": [ + "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1", + "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda", + "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d", + "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509", + "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6", + "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f", + "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e", + "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751", + "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86", + "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b", + "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc", + "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546", + "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec", + "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f", + "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82", + "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c", + "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b", + "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c", + "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c", + "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53", + "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80", + "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242", + "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85", + "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124", + "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5", + "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2", + "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3", + "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d", + "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc", + "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342", + "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1", + "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1", + "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595", + "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30", + "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab", + "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3", + "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2", + "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd", + "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7", + "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277", + "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453", + "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697", + "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b", + "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454", + "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9", + "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1", + "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6", + "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291", + "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750", + "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699", + "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e", + "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81", + "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9", + "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375" + ], + "markers": "python_version >= '3.10'", + "version": "==1.3.1" + }, + "cycler": { + "hashes": [ + "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", + "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c" + ], + "markers": "python_version >= '3.8'", + "version": "==0.12.1" + }, + "fonttools": { + "hashes": [ + "sha256:03701e7de70c71eb5965cb200986b0c11dfa3cf8e843e4f517ee30a0f43f0a25", + "sha256:07478132407736ee5e54f9f534e73923ae28e9bb6dba17764a35e3caf7d7fea3", + "sha256:0879f99eabbf2171dfadd9c8c75cec2b7b3aa9cd1f3955dd799c69d60a5189ef", + "sha256:09ed667c4753e1270994e5398cce8703e6423c41702a55b08f843b2907b1be65", + "sha256:0ee6ed68af8d57764d69da099db163aaf37d62ba246cfd42f27590e3e6724b55", + "sha256:1062daa0390b32bfd062ded2b450db9e9cf10e5a9919561c13f535e818b1952b", + "sha256:127999618afe3a2490fad54bab0650c5fbeab1f8109bdc0205f6ad34306deb8b", + "sha256:132fa22be8a99784de8cb171b30425a581f04a40ec1c05183777fb2b1fe3bac9", + "sha256:1beb4647a0df5ceaea48015656525eb8081af226fe96554089fd3b274d239ef0", + "sha256:2d15e02b93a46982a8513a208e8f89148bca8297640527365625be56151687d0", + "sha256:2d419483a6295e83cabddb56f1c7b7bfdc8169de2fcb5c68d622bd11140355f9", + "sha256:34405f1314f1e88b1877a9f9e497fe45190e8c4b29a6c7cd85ed7f666a57d702", + "sha256:3aa6c684007723895aade9b2fe76d07008c9dc90fd1ef6c310b3ca9c8566729f", + "sha256:3f4e88f15f5ed4d2e4bdfcc98540bb3987ae25904f9be304be9a604e7a7050a1", + "sha256:4259159715142c10b0f4d121ef14da3fa6eafc719289d9efa4b20c15e57fef82", + "sha256:471961af7a4b8461fac0c8ee044b4986e6fe3746d4c83a1aacbdd85b4eb53f93", + "sha256:51120695ee13001533e50abd40eec32c01b9c6f44c5567db38a7acd3eedcd19d", + "sha256:57d55fc965e5dd20c8a60d880e0f43bafb506be87af0b650bdc42591e41e0d0d", + "sha256:5b7535a5ac386e549e2b00b34c59b53f805e2423000676723b6867df3c10df04", + "sha256:61aa1997c520bee4cde14ffabe81efc4708c500c8c81dce37831551627a2be56", + "sha256:73a4aaf672e7b2265c6354a69cbbadf71b7f3133ecb74e98fec4c67c366698a3", + "sha256:73bdff9c44d36c57ea84766afc20517eda0c9bb1571b4a09876646264bd5ff3b", + "sha256:76ac5a595f86892b49ba86ba2e46185adc76328ce6eff0583b30e5c3ab02a914", + "sha256:791e0cf862cdd3a252df395f1bb5f65e3a760f1da3c7ce184d0f7998c266614d", + "sha256:7954ea66a8d835f279c17d8474597a001ddd65a2c1ca97e223041bfbbe11f65e", + "sha256:8398928acb8a57073606feb9a310682d4a7e2d7536f2c61719261f4c0974504c", + "sha256:860ab9ed3f9e088d3bdb77b9074e656635f173b039e77d550b603cba052a0dca", + "sha256:88f74bc19dbab3dee6a00ca67ca54bb4793e44ff0c4dcf1fa61d68651ae3fa0a", + "sha256:8a8004a19195eb8a8a13de69e26ec9ed60a5bc1fde336d0021b47995b368fac9", + "sha256:8c9de8d16d02ecc8b65e3f3d2d1e3002be2c4a3f094d580faf76d7f768bd45fe", + "sha256:9394813cc73fa22c5413ec1c5745c0a16f68dd2b890f7c55eaba5cb40187ed55", + "sha256:94f7f2c5c5f3a6422e954ecb6d37cc363e27d6f94050a7ed3f79f12157af6bb2", + "sha256:9f99e7876518b2d059a9cc67c506168aebf9c71ac8d81006d75e684222f291d2", + "sha256:9fb545f3a4ebada908fa717ec732277de18dd10161f03ee3b3144d34477804de", + "sha256:a55489c7e9d5ea69690a2afad06723c3d0c48c6d276a25391ea97cb31a16b37c", + "sha256:a632f85bd73e002b771bcbcdc512038fa5d2e09bb18c03a22fb8d400ea492ddf", + "sha256:ac817559a7d245454231374e194b4e457dca6fefa5b52af466ab0516e9a09c6e", + "sha256:acc74884afddc2656bffc50100945ff407574538c152931c402fccddc46f0abc", + "sha256:af5469bbf555047efd8752d85faeb2a3510916ddc6c50dd6fb168edf1677408f", + "sha256:bc6f58976ffc19fe1630119a2736153b66151d023c6f30065f31c9e8baed1303", + "sha256:c2f78ebfdef578d4db7c44bc207ac5f9a5c1f22c9db606460dcc8ad48e183338", + "sha256:c42009177d3690894288082d5e3dac6bdc9f5d38e25054535e341a19cf5183a4", + "sha256:d20ab5a78d0536c26628eaadba661e7ae2427b1e5c748a0a510a44d914e1b155", + "sha256:d3226d40cb92787e09dcc3730f54b3779dfe56bdfea624e263685ba17a6faac4", + "sha256:d77d83ca77a4c3156a2f4cbc7f09f5a8503795da658fa255b987ad433a191266", + "sha256:d91fce2e9a87cc0db9f8042281b6458f99854df810cfefab2baf6ab2acc0f4b4", + "sha256:e1c06fbc2fd76b9bab03eddfd8aa9fb7c0981d314d780e763c80aa76be1c9982", + "sha256:e82772f70b84e17aa36e9f236feb2a4f73cb686ec1e162557a36cf759d1acd58", + "sha256:edf159a8f1e48dc4683a715b36da76dd2f82954b16bfe11a215d58e963d31cfc", + "sha256:f66561fbfb75785d06513b8025a50be37bf970c3c413e87581cc6eff10bc78f1" + ], + "markers": "python_version >= '3.8'", + "version": "==4.55.6" + }, + "jmespath": { + "hashes": [ + "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", + "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" + ], + "markers": "python_version >= '3.7'", + "version": "==1.0.1" + }, + "joblib": { + "hashes": [ + "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", + "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e" + ], + "markers": "python_version >= '3.8'", + "version": "==1.4.2" + }, + "kiwisolver": { + "hashes": [ + "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", + "sha256:034d2c891f76bd3edbdb3ea11140d8510dca675443da7304205a2eaa45d8334c", + "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", + "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc", + "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", + "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79", + "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6", + "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2", + "sha256:1732e065704b47c9afca7ffa272f845300a4eb959276bf6970dc07265e73b605", + "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09", + "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab", + "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", + "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", + "sha256:286b18e86682fd2217a48fc6be6b0f20c1d0ed10958d8dc53453ad58d7be0bf8", + "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7", + "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880", + "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b", + "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b", + "sha256:369b75d40abedc1da2c1f4de13f3482cb99e3237b38726710f4a793432b1c5ff", + "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", + "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c", + "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0", + "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6", + "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30", + "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47", + "sha256:4191ee8dfd0be1c3666ccbac178c5a05d5f8d689bbe3fc92f3c4abec817f8fe0", + "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1", + "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90", + "sha256:641f2ddf9358c80faa22e22eb4c9f54bd3f0e442e038728f500e3b978d00aa7d", + "sha256:65ea09a5a3faadd59c2ce96dc7bf0f364986a315949dc6374f04396b0d60e09b", + "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c", + "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", + "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e", + "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc", + "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16", + "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", + "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712", + "sha256:7cd2785b9391f2873ad46088ed7599a6a71e762e1ea33e87514b1a441ed1da1c", + "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3", + "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc", + "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561", + "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", + "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc", + "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db", + "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed", + "sha256:89c107041f7b27844179ea9c85d6da275aa55ecf28413e87624d033cf1f6b751", + "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957", + "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165", + "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2", + "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", + "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84", + "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246", + "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", + "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25", + "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d", + "sha256:b5773efa2be9eb9fcf5415ea3ab70fc785d598729fd6057bea38d539ead28271", + "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", + "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", + "sha256:bcb1ebc3547619c3b58a39e2448af089ea2ef44b37988caf432447374941574e", + "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", + "sha256:c07b29089b7ba090b6f1a669f1411f27221c3662b3a1b7010e67b59bb5a6f10b", + "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7", + "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03", + "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b", + "sha256:c8bf637892dc6e6aad2bc6d4d69d08764166e5e3f69d469e55427b6ac001b19d", + "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", + "sha256:ce2cf1e5688edcb727fdf7cd1bbd0b6416758996826a8be1d958f91880d0809d", + "sha256:d47b28d1dfe0793d5e96bce90835e17edf9a499b53969b03c6c47ea5985844c3", + "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67", + "sha256:d5536185fce131780ebd809f8e623bf4030ce1b161353166c49a3c74c287897f", + "sha256:d561d2d8883e0819445cfe58d7ddd673e4015c3c57261d7bdcd3710d0d14005c", + "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", + "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062", + "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954", + "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb", + "sha256:e7a019419b7b510f0f7c9dceff8c5eae2392037eae483a7f9162625233802b0a", + "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", + "sha256:eb158fe28ca0c29f2260cca8c43005329ad58452c36f0edf298204de32a9a3ed", + "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34", + "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794" + ], + "markers": "python_version >= '3.10'", + "version": "==1.4.8" + }, + "matplotlib": { + "hashes": [ + "sha256:01d2b19f13aeec2e759414d3bfe19ddfb16b13a1250add08d46d5ff6f9be83c6", + "sha256:12eaf48463b472c3c0f8dbacdbf906e573013df81a0ab82f0616ea4b11281908", + "sha256:2c5829a5a1dd5a71f0e31e6e8bb449bc0ee9dbfb05ad28fc0c6b55101b3a4be6", + "sha256:2fbbabc82fde51391c4da5006f965e36d86d95f6ee83fb594b279564a4c5d0d2", + "sha256:3547d153d70233a8496859097ef0312212e2689cdf8d7ed764441c77604095ae", + "sha256:359f87baedb1f836ce307f0e850d12bb5f1936f70d035561f90d41d305fdacea", + "sha256:3b427392354d10975c1d0f4ee18aa5844640b512d5311ef32efd4dd7db106ede", + "sha256:4659665bc7c9b58f8c00317c3c2a299f7f258eeae5a5d56b4c64226fca2f7c59", + "sha256:4673ff67a36152c48ddeaf1135e74ce0d4bce1bbf836ae40ed39c29edf7e2765", + "sha256:503feb23bd8c8acc75541548a1d709c059b7184cde26314896e10a9f14df5f12", + "sha256:5439f4c5a3e2e8eab18e2f8c3ef929772fd5641876db71f08127eed95ab64683", + "sha256:5cdbaf909887373c3e094b0318d7ff230b2ad9dcb64da7ade654182872ab2593", + "sha256:5e6c6461e1fc63df30bf6f80f0b93f5b6784299f721bc28530477acd51bfc3d1", + "sha256:5fd41b0ec7ee45cd960a8e71aea7c946a28a0b8a4dcee47d2856b2af051f334c", + "sha256:607b16c8a73943df110f99ee2e940b8a1cbf9714b65307c040d422558397dac5", + "sha256:7e8632baebb058555ac0cde75db885c61f1212e47723d63921879806b40bec6a", + "sha256:81713dd0d103b379de4516b861d964b1d789a144103277769238c732229d7f03", + "sha256:845d96568ec873be63f25fa80e9e7fae4be854a66a7e2f0c8ccc99e94a8bd4ef", + "sha256:95b710fea129c76d30be72c3b38f330269363fbc6e570a5dd43580487380b5ff", + "sha256:96f2886f5c1e466f21cc41b70c5a0cd47bfa0015eb2d5793c88ebce658600e25", + "sha256:994c07b9d9fe8d25951e3202a68c17900679274dadfc1248738dcfa1bd40d7f3", + "sha256:9ade1003376731a971e398cc4ef38bb83ee8caf0aee46ac6daa4b0506db1fd06", + "sha256:9b0558bae37f154fffda54d779a592bc97ca8b4701f1c710055b609a3bac44c8", + "sha256:a2a43cbefe22d653ab34bb55d42384ed30f611bcbdea1f8d7f431011a2e1c62e", + "sha256:a994f29e968ca002b50982b27168addfd65f0105610b6be7fa515ca4b5307c95", + "sha256:ad2e15300530c1a94c63cfa546e3b7864bd18ea2901317bae8bbf06a5ade6dcf", + "sha256:ae80dc3a4add4665cf2faa90138384a7ffe2a4e37c58d83e115b54287c4f06ef", + "sha256:b886d02a581b96704c9d1ffe55709e49b4d2d52709ccebc4be42db856e511278", + "sha256:c40ba2eb08b3f5de88152c2333c58cee7edcead0a2a0d60fcafa116b17117adc", + "sha256:c55b20591ced744aa04e8c3e4b7543ea4d650b6c3c4b208c08a05b4010e8b442", + "sha256:c58a9622d5dbeb668f407f35f4e6bfac34bb9ecdcc81680c04d0258169747997", + "sha256:d44cb942af1693cced2604c33a9abcef6205601c445f6d0dc531d813af8a2f5a", + "sha256:d907fddb39f923d011875452ff1eca29a9e7f21722b873e90db32e5d8ddff12e", + "sha256:fd44fc75522f58612ec4a33958a7e5552562b7705b42ef1b4f8c0818e304a363" + ], + "index": "pypi", + "markers": "python_version >= '3.10'", + "version": "==3.10.0" + }, + "mypy-extensions": { + "hashes": [ + "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", + "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.0" + }, + "numpy": { + "hashes": [ + "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f", + "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0", + "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd", + "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2", + "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4", + "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648", + "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be", + "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb", + "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160", + "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd", + "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a", + "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84", + "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e", + "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748", + "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825", + "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60", + "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957", + "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715", + "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317", + "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e", + "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283", + "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278", + "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9", + "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de", + "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369", + "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb", + "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189", + "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014", + "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323", + "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e", + "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49", + "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50", + "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d", + "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37", + "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39", + "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576", + "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a", + "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba", + "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7", + "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826", + "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467", + "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495", + "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc", + "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391", + "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0", + "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97", + "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c", + "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac", + "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369", + "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8", + "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2", + "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff", + "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a", + "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df", + "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f" + ], + "index": "pypi", + "markers": "python_version >= '3.10'", + "version": "==2.2.2" + }, + "opencv-python": { + "hashes": [ + "sha256:03d60ccae62304860d232272e4a4fda93c39d595780cb40b161b310244b736a4", + "sha256:085ad9b77c18853ea66283e98affefe2de8cc4c1f43eda4c100cf9b2721142ec", + "sha256:1b92ae2c8852208817e6776ba1ea0d6b1e0a1b5431e971a2a0ddd2a8cc398202", + "sha256:432f67c223f1dc2824f5e73cdfcd9db0efc8710647d4e813012195dc9122a52a", + "sha256:6b02611523803495003bd87362db3e1d2a0454a6a63025dc6658a9830570aa0d", + "sha256:810549cb2a4aedaa84ad9a1c92fbfdfc14090e2749cedf2c1589ad8359aa169b", + "sha256:9d05ef13d23fe97f575153558653e2d6e87103995d54e6a35db3f282fe1f9c66" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==4.11.0.86" + }, + "packaging": { + "hashes": [ + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" + ], + "markers": "python_version >= '3.8'", + "version": "==24.2" + }, + "pathspec": { + "hashes": [ + "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", + "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" + ], + "markers": "python_version >= '3.8'", + "version": "==0.12.1" + }, + "pillow": { + "hashes": [ + "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83", + "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96", + "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", + "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", + "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", + "sha256:3362c6ca227e65c54bf71a5f88b3d4565ff1bcbc63ae72c34b07bbb1cc59a43f", + "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", + "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", + "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", + "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49", + "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", + "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0", + "sha256:4db853948ce4e718f2fc775b75c37ba2efb6aaea41a1a5fc57f0af59eee774b2", + "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", + "sha256:54251ef02a2309b5eec99d151ebf5c9904b77976c8abdcbce7891ed22df53884", + "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e", + "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", + "sha256:5bb94705aea800051a743aa4874bb1397d4695fb0583ba5e425ee0328757f196", + "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", + "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", + "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269", + "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", + "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb", + "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", + "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", + "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1", + "sha256:89dbdb3e6e9594d512780a5a1c42801879628b38e3efc7038094430844e271d8", + "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90", + "sha256:8e275ee4cb11c262bd108ab2081f750db2a1c0b8c12c1897f27b160c8bd57bbc", + "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", + "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", + "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", + "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35", + "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", + "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", + "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2", + "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2", + "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", + "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65", + "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", + "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442", + "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2", + "sha256:ab8a209b8485d3db694fa97a896d96dd6533d63c22829043fd9de627060beade", + "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482", + "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", + "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", + "sha256:b20be51b37a75cc54c2c55def3fa2c65bb94ba859dde241cd0a4fd302de5ae0a", + "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", + "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", + "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a", + "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07", + "sha256:bf902d7413c82a1bfa08b06a070876132a5ae6b2388e2712aab3a7cbc02205c6", + "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f", + "sha256:c1eec9d950b6fe688edee07138993e54ee4ae634c51443cfb7c1e7613322718e", + "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192", + "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", + "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", + "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73", + "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f", + "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6", + "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", + "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", + "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457", + "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8", + "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26", + "sha256:e5449ca63da169a2e6068dd0e2fcc8d91f9558aba89ff6d02121ca8ab11e79e5", + "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", + "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070", + "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71", + "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", + "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761" + ], + "markers": "python_version >= '3.9'", + "version": "==11.1.0" + }, + "platformdirs": { + "hashes": [ + "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", + "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" + ], + "markers": "python_version >= '3.8'", + "version": "==4.3.6" + }, + "pyparsing": { + "hashes": [ + "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", + "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a" + ], + "markers": "python_version >= '3.9'", + "version": "==3.2.1" + }, + "pyqt6": { + "hashes": [ + "sha256:3a4354816f11e812b727206a9ea6e79ff3774f1bb7228ad4b9318442d2c64ff9", + "sha256:452bae5840077bf0f146c798d7777f70d7bdd0c7dcfa9ee7a415c1daf2d10038", + "sha256:48bace7b87676bba5e6114482f3a20ca20be90c7f261b5d340464313f5f2bf5e", + "sha256:6d8628de4c2a050f0b74462e4c9cb97f839bf6ffabbca91711722ffb281570d9", + "sha256:8c5c05f5fdff31a5887dbc29b27615b09df467631238d7b449283809ffca6228", + "sha256:a9913d479f1ffee804bf7f232079baea4fb4b221a8f4890117588917a54ea30d", + "sha256:cf7123caea14e7ecf10bd12cae48e8d9970ef7caf627bc7d7988b0baa209adb3" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==6.8.0" + }, + "pyqt6-qt6": { + "hashes": [ + "sha256:006d786693d0511fbcf184a862edbd339c6ed1bb3bd9de363d73a19ed4b23dff", + "sha256:08065d595f1e6fc2dde9f4450eeff89082f4bad26f600a8e9b9cc5966716bfcf", + "sha256:1eb8460a1fdb38d0b2458c2974c01d471c1e59e4eb19ea63fc447aaba3ad530e", + "sha256:20843cb86bd94942d1cd99e39bf1aeabb875b241a35a8ab273e4bbbfa63776db", + "sha256:2f4b8b55b1414b93f340f22e8c88d25550efcdebc4b65a3927dd947b73bd4358", + "sha256:98aa99fe38ae68c5318284cd28f3479ba538c40bf6ece293980abae0925c1b24", + "sha256:9f3790c4ce4dc576e48b8718d55fb8743057e6cbd53a6ca1dd253ffbac9b7287", + "sha256:a8bc2ed4ee5e7c6ff4dd1c7db0b27705d151fee5dc232bbd1bf17618f937f515", + "sha256:d6ca5d2b9d2ec0ee4a814b2175f641a5c4299cb80b45e0f5f8356632663f89b3" + ], + "version": "==6.8.1" + }, + "pyqt6-sip": { + "hashes": [ + "sha256:14f95c6352e3b85dc26bf59cfbf77a470ecbd5fcdcf00af4b648f0e1b9eefb9e", + "sha256:15be741d1ae8c82bb7afe9a61f3cf8c50457f7d61229a1c39c24cd6e8f4d86dc", + "sha256:1d322ded1d1fea339cc6ac65b768e72c69c486eebb7db6ccde061b5786d74cc5", + "sha256:1ec52e962f54137a19208b6e95b6bd9f7a403eb25d7237768a99306cd9db26d1", + "sha256:1fb405615970e85b622b13b4cad140ff1e4182eb8334a0b27a4698e6217b89b0", + "sha256:22d66256b800f552ade51a463510bf905f3cb318aae00ff4288fae4de5d0e600", + "sha256:2ab85aaf155828331399c59ebdd4d3b0358e42c08250e86b43d56d9873df148a", + "sha256:3c269052c770c09b61fce2f2f9ea934a67dfc65f443d59629b4ccc8f89751890", + "sha256:5004514b08b045ad76425cf3618187091a668d972b017677b1b4b193379ef553", + "sha256:552ff8fdc41f5769d3eccc661f022ed496f55f6e0a214c20aaf56e56385d61b6", + "sha256:5643c92424fe62cb0b33378fef3d28c1525f91ada79e8a15bd9a05414a09503d", + "sha256:56ce0afb19cd8a8c63ff93ae506dffb74f844b88adaa6673ebc0dec43af48a76", + "sha256:57b5312ef13c1766bdf69b317041140b184eb24a51e1e23ce8fc5386ba8dffb2", + "sha256:5d7726556d1ca7a7ed78e19ba53285b64a2a8f6ad7ff4cb18a1832efca1a3102", + "sha256:69a879cfc94f4984d180321b76f52923861cd5bf4969aa885eef7591ee932517", + "sha256:6e6c1e2592187934f4e790c0c099d0033e986dcef7bdd3c06e3895ffa995e9fc", + "sha256:8b2ac36d6e04db6099614b9c1178a2f87788c7ffc3826571fb63d36ddb4c401d", + "sha256:8c207528992d59b0801458aa6fcff118e5c099608ef0fc6ff8bccbdc23f29c04", + "sha256:976c7758f668806d4df7a8853f390ac123d5d1f73591ed368bdb8963574ff589", + "sha256:accab6974b2758296400120fdcc9d1f37785b2ea2591f00656e1776f058ded6c", + "sha256:c1942e107b0243ced9e510d507e0f27aeea9d6b13e0a1b7c06fd52a62e0d41f7", + "sha256:c800db3464481e87b1d2b84523b075df1e8fc7856c6f9623dc243f89be1cb604", + "sha256:e996d320744ca8342cad6f9454345330d4f06bce129812d032bda3bad6967c5c", + "sha256:fa27b51ae4c7013b3700cf0ecf46907d1333ae396fc6511311920485cbce094b" + ], + "markers": "python_version >= '3.9'", + "version": "==13.9.1" + }, + "python-dateutil": { + "hashes": [ + "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", + "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "version": "==2.9.0.post0" + }, + "s3transfer": { + "hashes": [ + "sha256:3b39185cb72f5acc77db1a58b6e25b977f28d20496b6e58d6813d75f464d632f", + "sha256:be6ecb39fadd986ef1701097771f87e4d2f821f27f6071c872143884d2950fbc" + ], + "markers": "python_version >= '3.8'", + "version": "==0.11.2" + }, + "scikit-learn": { + "hashes": [ + "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691", + "sha256:0c8d036eb937dbb568c6242fa598d551d88fb4399c0344d95c001980ec1c7d36", + "sha256:1061b7c028a8663fb9a1a1baf9317b64a257fcb036dae5c8752b2abef31d136f", + "sha256:25fc636bdaf1cc2f4a124a116312d837148b5e10872147bdaf4887926b8c03d8", + "sha256:2c2cae262064e6a9b77eee1c8e768fc46aa0b8338c6a8297b9b6759720ec0ff2", + "sha256:2e69fab4ebfc9c9b580a7a80111b43d214ab06250f8a7ef590a4edf72464dd86", + "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322", + "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f", + "sha256:44a17798172df1d3c1065e8fcf9019183f06c87609b49a124ebdf57ae6cb0107", + "sha256:6849dd3234e87f55dce1db34c89a810b489ead832aaf4d4550b7ea85628be6c1", + "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35", + "sha256:70b1d7e85b1c96383f872a519b3375f92f14731e279a7b4c6cfd650cf5dffc52", + "sha256:72abc587c75234935e97d09aa4913a82f7b03ee0b74111dcc2881cba3c5a7b33", + "sha256:775da975a471c4f6f467725dff0ced5c7ac7bda5e9316b260225b48475279a1b", + "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb", + "sha256:7a73d457070e3318e32bdb3aa79a8d990474f19035464dfd8bede2883ab5dc3b", + "sha256:8634c4bd21a2a813e0a7e3900464e6d593162a29dd35d25bdf0103b3fce60ed5", + "sha256:8a600c31592bd7dab31e1c61b9bbd6dea1b3433e67d264d17ce1017dbdce8002", + "sha256:926f207c804104677af4857b2c609940b743d04c4c35ce0ddc8ff4f053cddc1b", + "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236", + "sha256:b3b00cdc8f1317b5f33191df1386c0befd16625f49d979fe77a8d44cae82410d", + "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e", + "sha256:b8b7a3b86e411e4bce21186e1c180d792f3d99223dcfa3b4f597ecc92fa1a422", + "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348", + "sha256:d056391530ccd1e501056160e3c9673b4da4805eb67eb2bdf4e983e1f9c9204e", + "sha256:dc4765af3386811c3ca21638f63b9cf5ecf66261cc4815c1db3f1e7dc7b79db2", + "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1", + "sha256:e7be3fa5d2eb9be7d77c3734ff1d599151bb523674be9b834e8da6abe132f44e", + "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97", + "sha256:fa909b1a36e000a03c382aade0bd2063fd5680ff8b8e501660c0f59f021a6415" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==1.6.1" + }, + "scipy": { + "hashes": [ + "sha256:033a75ddad1463970c96a88063a1df87ccfddd526437136b6ee81ff0312ebdf6", + "sha256:0458839c9f873062db69a03de9a9765ae2e694352c76a16be44f93ea45c28d2b", + "sha256:070d10654f0cb6abd295bc96c12656f948e623ec5f9a4eab0ddb1466c000716e", + "sha256:09c52320c42d7f5c7748b69e9f0389266fd4f82cf34c38485c14ee976cb8cb04", + "sha256:0ac102ce99934b162914b1e4a6b94ca7da0f4058b6d6fd65b0cef330c0f3346f", + "sha256:0fb57b30f0017d4afa5fe5f5b150b8f807618819287c21cbe51130de7ccdaed2", + "sha256:100193bb72fbff37dbd0bf14322314fc7cbe08b7ff3137f11a34d06dc0ee6b85", + "sha256:14eaa373c89eaf553be73c3affb11ec6c37493b7eaaf31cf9ac5dffae700c2e0", + "sha256:2114a08daec64980e4b4cbdf5bee90935af66d750146b1d2feb0d3ac30613692", + "sha256:21e10b1dd56ce92fba3e786007322542361984f8463c6d37f6f25935a5a6ef52", + "sha256:2722a021a7929d21168830790202a75dbb20b468a8133c74a2c0230c72626b6c", + "sha256:395be70220d1189756068b3173853029a013d8c8dd5fd3d1361d505b2aa58fa7", + "sha256:3fe1d95944f9cf6ba77aa28b82dd6bb2a5b52f2026beb39ecf05304b8392864b", + "sha256:491d57fe89927fa1aafbe260f4cfa5ffa20ab9f1435025045a5315006a91b8f5", + "sha256:4b17d4220df99bacb63065c76b0d1126d82bbf00167d1730019d2a30d6ae01ea", + "sha256:4c9d8fc81d6a3b6844235e6fd175ee1d4c060163905a2becce8e74cb0d7554ce", + "sha256:55cc79ce4085c702ac31e49b1e69b27ef41111f22beafb9b49fea67142b696c4", + "sha256:5b190b935e7db569960b48840e5bef71dc513314cc4e79a1b7d14664f57fd4ff", + "sha256:5bd8d27d44e2c13d0c1124e6a556454f52cd3f704742985f6b09e75e163d20d2", + "sha256:5dff14e75cdbcf07cdaa1c7707db6017d130f0af9ac41f6ce443a93318d6c6e0", + "sha256:5eb0ca35d4b08e95da99a9f9c400dc9f6c21c424298a0ba876fdc69c7afacedf", + "sha256:63b9b6cd0333d0eb1a49de6f834e8aeaefe438df8f6372352084535ad095219e", + "sha256:667f950bf8b7c3a23b4199db24cb9bf7512e27e86d0e3813f015b74ec2c6e3df", + "sha256:6b3e71893c6687fc5e29208d518900c24ea372a862854c9888368c0b267387ab", + "sha256:71ba9a76c2390eca6e359be81a3e879614af3a71dfdabb96d1d7ab33da6f2364", + "sha256:74bb864ff7640dea310a1377d8567dc2cb7599c26a79ca852fc184cc851954ac", + "sha256:82add84e8a9fb12af5c2c1a3a3f1cb51849d27a580cb9e6bd66226195142be6e", + "sha256:837299eec3d19b7e042923448d17d95a86e43941104d33f00da7e31a0f715d3c", + "sha256:900f3fa3db87257510f011c292a5779eb627043dd89731b9c461cd16ef76ab3d", + "sha256:9f151e9fb60fbf8e52426132f473221a49362091ce7a5e72f8aa41f8e0da4f25", + "sha256:af0b61c1de46d0565b4b39c6417373304c1d4f5220004058bdad3061c9fa8a95", + "sha256:bc7136626261ac1ed988dca56cfc4ab5180f75e0ee52e58f1e6aa74b5f3eacd5", + "sha256:be3deeb32844c27599347faa077b359584ba96664c5c79d71a354b80a0ad0ce0", + "sha256:c09aa9d90f3500ea4c9b393ee96f96b0ccb27f2f350d09a47f533293c78ea776", + "sha256:c352c1b6d7cac452534517e022f8f7b8d139cd9f27e6fbd9f3cbd0bfd39f5bef", + "sha256:c64ded12dcab08afff9e805a67ff4480f5e69993310e093434b10e85dc9d43e1", + "sha256:cdde8414154054763b42b74fe8ce89d7f3d17a7ac5dd77204f0e142cdc9239e9", + "sha256:ce3a000cd28b4430426db2ca44d96636f701ed12e2b3ca1f2b1dd7abdd84b39a", + "sha256:f735bc41bd1c792c96bc426dece66c8723283695f02df61dcc4d0a707a42fc54", + "sha256:f82fcf4e5b377f819542fbc8541f7b5fbcf1c0017d0df0bc22c781bf60abc4d8" + ], + "markers": "python_version >= '3.10'", + "version": "==1.15.1" + }, + "six": { + "hashes": [ + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "version": "==1.17.0" + }, + "threadpoolctl": { + "hashes": [ + "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107", + "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467" + ], + "markers": "python_version >= '3.8'", + "version": "==3.5.0" + }, + "urllib3": { + "hashes": [ + "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", + "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" + ], + "markers": "python_version >= '3.9'", + "version": "==2.3.0" + } + }, + "develop": {} +} diff --git a/digitaldog.jpg b/digitaldog.jpg new file mode 100644 index 0000000..2a9ec62 Binary files /dev/null and b/digitaldog.jpg differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..680ea59 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +PyQt6 +numpy +matplotlib +scikit-learn +boto3 +opencv-python diff --git a/src/AWSRekognition.py b/src/AWSRekognition.py new file mode 100755 index 0000000..d17689f --- /dev/null +++ b/src/AWSRekognition.py @@ -0,0 +1,36 @@ +import boto3 +import cv2 + + +class AWSRekognition: + def __init__(self): + self.rekognition_client = boto3.client( + "rekognition", region_name="eu-central-1" + ) + + def label_image(self, img): + result = img.copy() + print("Detect Labels") + # Convert the image to bytes + _, image_bytes = cv2.imencode(".jpg", img) + + # Call the detect_labels method + response = self.rekognition_client.detect_labels( + Image={"Bytes": image_bytes.tobytes()}, MaxLabels=20, MinConfidence=90 + ) + + # Print the labels + for label in response["Labels"]: + print(label["Name"], label["Confidence"]) + + # Draw bounding boxes around the detected objects + for label in response["Labels"]: + for instance in label["Instances"]: + bounding_box = instance["BoundingBox"] + x = int(bounding_box["Left"] * result.shape[1]) + y = int(bounding_box["Top"] * result.shape[0]) + w = int(bounding_box["Width"] * result.shape[1]) + h = int(bounding_box["Height"] * result.shape[0]) + cv2.rectangle(result, (x, y), (x + w, y + h), (0, 255, 0), 2) + + return result diff --git a/src/ColorAnalysis.py b/src/ColorAnalysis.py new file mode 100755 index 0000000..76f1051 --- /dev/null +++ b/src/ColorAnalysis.py @@ -0,0 +1,80 @@ +import cv2 +import numpy as np +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D +from sklearn.cluster import KMeans + + +class ColorAnalysis: + + CLUSTERS = None + IMAGE = None + COLORS = None + LABELS = None + + def __init__(self, img, clusters=3): + self.CLUSTERS = clusters + self.IMAGE = img + + def show3DHistogram(self): + # img = Utilities.resize_image(img, 100) + + # resize image# convert from BGR to RGB + # img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # get rgb values from image to 1D array + r, g, b = cv2.split(self.IMAGE) + r = r.flatten() + g = g.flatten() + b = b.flatten() + + # plotting + fig = plt.figure() + ax = Axes3D(fig) + ax.scatter(r, g, b) + plt.show() + + def dominantColors(self): + img = self.IMAGE + # img = Utilities.resize_image(img, 100) + # reshaping to a list of pixels + img = img.reshape((img.shape[0] * img.shape[1], 3)) + + # using k-means to cluster pixels + kmeans = KMeans(n_clusters=self.CLUSTERS) + kmeans.fit(img) + + # the cluster centers are our dominant colors. + self.COLORS = kmeans.cluster_centers_ + + # save labels + self.LABELS = kmeans.labels_ + + # returning after converting to integer from float + return self.COLORS.astype(int) + + def rgb_to_hex(self, rgb): + return "#%02x%02x%02x" % (int(rgb[0]), int(rgb[1]), int(rgb[2])) + + def plotClusters(self): + # plotting + fig = plt.figure() + ax = Axes3D(fig) + for label, pix in zip(self.LABELS, self.IMAGE): + ax.scatter( + pix[0], pix[1], pix[2], color=self.rgb_to_hex(self.COLORS[label]) + ) + plt.show() + + +def create_visual_output(colors, width, height_max): + n_clusters = len(colors) + visual_output = 255 * np.ones((height_max, width, 3), np.uint8) + height = int(width / n_clusters) + box_width = int(width / n_clusters) + if height < height_max: + visual_output = 255 * np.ones((height, width, 3), np.uint8) + + for i in range(n_clusters): + visual_output[:, box_width * i : box_width * (i + 1)] = colors[i] + return visual_output diff --git a/src/HistogramManipulation.py b/src/HistogramManipulation.py new file mode 100755 index 0000000..b39544c --- /dev/null +++ b/src/HistogramManipulation.py @@ -0,0 +1,60 @@ +import cv2 +import numpy as np +import Utilities + + +# Task 1 +# function to stretch an image +def stretchHistogram(img): + result = img.copy() + return result + + +# Task 2 +# function to equalize an image +def equalizeHistogram(img): + result = img.copy() + return result + + +# Hilfsfunktion +# function to apply a look-up table onto an image +def applyLUT(img, LUT): + result = img.copy() + return result + + +# Hilfsfunktion +# function to find the minimum an maximum in a histogram +def findMinMaxPos(histogram): + minPos = 0 + maxPos = 255 + return minPos, maxPos + + +# Hilfsfunktion +# function to create a vector containing the histogram +def calculateHistogram(img, nrBins): + # create histogram vector + histogram = np.zeros([nrBins], dtype=int) + return histogram + + +def apply_log(img): + result = img.copy() + return result + + +def apply_exp(img): + result = img.copy() + return result + + +def apply_inverse(img): + result = img.copy() + return result + + +def apply_threshold(img, threshold): + result = img.copy() + return result diff --git a/src/ImageFiltering.py b/src/ImageFiltering.py new file mode 100644 index 0000000..0080547 --- /dev/null +++ b/src/ImageFiltering.py @@ -0,0 +1,67 @@ +import numpy as np +import matplotlib.pyplot as plt +import datetime as dt +import cv2 +import Utilities + + +# apply median filter +def applyMedianFilter(img, kSize): + filtered_img = img.copy() + return filtered_img + + +# create a moving average kernel of arbitrary size +def createMovingAverageKernel(kSize): + kernel = np.zeros((kSize, kSize)) + return kernel + + +def gaussian(x, y, sigmaX, sigmaY, meanX, meanY): + result = 1 + return result + + +# create a gaussian kernel of arbitrary size +def createGaussianKernel(kSize, sigma=None): + kernel = np.zeros((kSize, kSize)) + return kernel + + +# create a sobel kernel in x direction of size 3x3 +def createSobelXKernel(): + kernel = np.zeros((3, 3)) + return kernel + + +# create a sobel kernel in y direction of size 3x3 +def createSobelYKernel(): + kernel = np.zeros((3, 3)) + return kernel + + +def applyKernelInSpatialDomain(img, kernel): + filtered_img = img.copy() + return filtered_img + + +# Extra: create an integral image of the given image +def createIntegralImage(img): + integral_image = img.copy() + return integral_image + + +# Extra: apply the moving average filter by using an integral image +def applyMovingAverageFilterWithIntegralImage(img, kSize): + filtered_img = img.copy() + return filtered_img + + +# Extra: +def applyMovingAverageFilterWithSeperatedKernels(img, kSize): + filtered_img = img.copy() + return filtered_img + + +def run_runtime_evaluation(img): + pass diff --git a/src/ImageInformation.py b/src/ImageInformation.py new file mode 100644 index 0000000..dd15feb --- /dev/null +++ b/src/ImageInformation.py @@ -0,0 +1,110 @@ +import numpy as np + + +def luminanceD65(rgb: np.ndarray) -> np.float64: + """ + Compute the luminance value of the specified linear RGB values + according to the D65 white point. + + @param rgb(np.ndarray): sRGB image + + @returns The luminance value + """ + return np.float64(rgb @ [0.2126, 0.7152, 0.0722]) + + +# Task 1: Implement some kind of noticeable image manipulation in this function +# e.g. channel manipulation, filter you already know, drawings on the image etc. +def myFirstImageManipulation(img) -> np.ndarray: + """ + Convert each pixel in the input image using the D65 luminance formula. + + This function takes a NumPy array `img` as input and returns a new array with + each pixel value replaced by its corresponding D65 luminance value. The output + array maintains the same dimensions and data type as the input array, but each + pixel is processed through this specific transformation. + + Parameters: + - img (ndarray): The input image data array of shape (height, width). + + Returns: + - ndarray: An array where each pixel has been replaced by its D65 + luminance value from the original image. + """ + result = img.copy() + + for x in range(0, result.shape[0]): + for y in range(0, result.shape[1]): + + result[x][y] = luminanceD65(img[x][y]) + + return result + + +# Task 2: Return the basic image properties to the console: +# width, height, +# the color of the first pixel of the image, +# Color of the first pixel in the second row +# Color of the first pixel in the second column +# This function should work for images with three channels +def imageSize(img: np.ndarray) -> list[int]: + """Get the height and width dimensions of an image array. + + Args: + img (np.ndarray): A numpy array representing the image. + + Returns: + list[int]: A two-element list containing [height, width] dimensions of the image. + + Note: + The function works for any multi-dimensional numpy array. It returns + a list with the first two dimensions regardless of whether they are 2D or higher. + """ + return [img.shape[0], img.shape[1]] + + +def getPixelColor(img): + """ + Retrieve color information from specific pixels in an image array. + + This function processes an RGB image array and returns a list of color values + based on predefined positions. It is designed to handle multi-channel images, + extracting information such as first pixel of each row, specific channels, + and other notable points within the image data. + + Parameters: + img (np.ndarray): The input image data array containing RGB or grayscale values. + + Returns: + list: A structured list of color values extracted from different positions + in the image. This includes colors from specified rows, columns, + and channels where applicable. + """ + return [img[0][1], img[1][0]] + + +# Task 3: Separate the given channels of a colour image in this function and return it as separate image +def returnChannel(img: np.ndarray, channel: int) -> np.ndarray: + """ + Splits an image into a separate channel or keeps only the specified color channel. + + Parameters: + - img: Input np.ndarray (required). This should be a color image array in RGB format. + If it's grayscale, this method might not work correctly as it expects three channels. + - channel: int (required). The index of the channel to keep. It can be 0 for red, 1 for green, or 2 for blue. + + Returns: + - np.ndarray: A new image array where only the specified color channel is non-zero, + and other channels are zeroed out. + """ + result = img.copy() + + for x in range(0, result.shape[0]): + for y in range(0, result.shape[1]): + for c in range(0, 3): + if c == channel: + continue + + result[x][y][c] = 0 + + return result diff --git a/src/ImagePlotter.py b/src/ImagePlotter.py new file mode 100755 index 0000000..bf2a0c7 --- /dev/null +++ b/src/ImagePlotter.py @@ -0,0 +1,262 @@ +from PyQt6.QtWidgets import QLabel, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem +from PyQt6.QtGui import QTransform +from PyQt6.QtCore import Qt + + +class ImagePlotter(QGraphicsView): + def __init__(self, parent=None): + super(ImagePlotter, self).__init__(parent) + + self.scene = QGraphicsScene() + # self.setAlignment(Qt.AlignCenter) + self.zoom_level = 0 + + def show_image(self, image): + self.scene.clear() + item = QGraphicsPixmapItem(image) + self.scene.addItem(item) + self.setScene(self.scene) + + scene_rect = self.scene.itemsBoundingRect() + + self.fitInView(scene_rect, Qt.AspectRatioMode.KeepAspectRatio) + print(self.scene.sceneRect().width()) + print(self.width()) + self.show() + + def wheelEvent(self, event): + # Get the current zoom level of the view + self.zoom_level = self.transform().m11() + print(self.zoom_level) + zoom = self.transform().m11() + + # Calculate the new zoom level based on the direction of the mouse wheel + if event.angleDelta().y() > 0: + zoom *= 1.2 + else: + zoom /= 1.2 + + # Set the new zoom level of the view + self.setTransform(QTransform().scale(zoom, zoom)) + + +class ImagePlotter2(QLabel): + def __init__(self, parent=None): + super(ImagePlotter2, self).__init__(parent) + self.setMouseTracking(True) + self.setCursor(Qt.CrossCursor) + + # def mouseMoveEvent(self, event): + # cursor_pos = event.pos() + # print("Cursor position:", cursor_pos.x(), cursor_pos.y()) + + def mousePressEvent(self, event): + cursor_pos = event.pos() + pixmap = self.pixmap() + label_pos = self.pos() + pixmap_rect = pixmap.rect() + + pixmap_pos = label_pos + pixmap_rect.topLeft() + + pixel_color = pixmap.toImage().pixelColor(0, 0) + red = pixel_color.red() + green = pixel_color.green() + blue = pixel_color.blue() + + print("RGB value of pixel at position (10, 10):", red, green, blue) + + print( + "Position of pixmap top left:", + pixmap_rect.topLeft().x(), + pixmap_rect.topLeft().y(), + ) + print("Position of pixmap in QLabel:", pixmap_pos.x(), pixmap_pos.y()) + print("Label pos:", label_pos.x(), label_pos.y()) + # image_pos = self.mapFrom(self., cursor_pos) + + +# print("Mouse clicked at:", event.pos().x(), event.pos().y()) +# print("Mouse clicked at:", image_pos.x(), image_pos.y()) + + +from PyQt6.QtGui import QImage, QPixmap, QPainter +from PyQt6 import QtCore, QtGui, QtWidgets + + +__author__ = "Atinderpal Singh" +__license__ = "MIT" +__version__ = "1.0" +__email__ = "atinderpalap@gmail.com" + + +class ImagePlotter3(QLabel): + """Basic image viewer class to show an image with zoom and pan functionaities. + Requirement: Qt's Qlabel widget name where the image will be drawn/displayed. + """ + + def __init__(self, parent=None): + super(ImagePlotter3, self).__init__(parent) + self.qlabel_image = ( + QLabel() + ) # widget/window name where image is displayed (I'm usiing qlabel) + self.qimage_scaled = QImage() # scaled image to fit to the size of qlabel_image + self.qpixmap = QPixmap() # qpixmap to fill the qlabel_image + + self.zoomX = 1 # zoom factor w.r.t size of qlabel_image + self.position = [ + 0, + 0, + ] # position of top left corner of qimage_label w.r.t. qimage_scaled + self.panFlag = False # to enable or disable pan + + self.qlabel_image.setSizePolicy( + QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored + ) + self.__connectEvents() + + def __connectEvents(self): + # Mouse events + self.qlabel_image.mousePressEvent = self.mousePressAction + self.qlabel_image.mouseMoveEvent = self.mouseMoveAction + self.qlabel_image.mouseReleaseEvent = self.mouseReleaseAction + + def onResize(self): + """things to do when qlabel_image is resized""" + self.qpixmap = QPixmap(self.qlabel_image.size()) + self.qpixmap.fill(QtCore.Qt.gray) + self.qimage_scaled = self.qimage.scaled( + self.qlabel_image.width() * self.zoomX, + self.qlabel_image.height() * self.zoomX, + QtCore.Qt.KeepAspectRatio, + ) + self.update() + + def loadImage(self, input_image): + """To load and display new image.""" + self.qimage = input_image.toImage() + print(self.qimage.width()) + self.qpixmap = QPixmap(self.qlabel_image.size()) + if not self.qimage.isNull(): + # reset Zoom factor and Pan position + self.zoomX = 1 + self.position = [0, 0] + self.qimage_scaled = self.qimage.scaled( + self.qlabel_image.width(), + self.qlabel_image.height(), + QtCore.Qt.KeepAspectRatio, + ) + self.update() + else: + self.statusbar.showMessage("Cannot open this image! Try another one.", 5000) + + def update(self): + """This function actually draws the scaled image to the qlabel_image. + It will be repeatedly called when zooming or panning. + So, I tried to include only the necessary operations required just for these tasks. + """ + if not self.qimage_scaled.isNull(): + # check if position is within limits to prevent unbounded panning. + px, py = self.position + px = ( + px + if (px <= self.qimage_scaled.width() - self.qlabel_image.width()) + else (self.qimage_scaled.width() - self.qlabel_image.width()) + ) + py = ( + py + if (py <= self.qimage_scaled.height() - self.qlabel_image.height()) + else (self.qimage_scaled.height() - self.qlabel_image.height()) + ) + px = px if (px >= 0) else 0 + py = py if (py >= 0) else 0 + self.position = (px, py) + + if self.zoomX == 1: + self.qpixmap.fill(QtCore.Qt.white) + + # the act of painting the qpixamp + painter = QPainter() + painter.begin(self.qpixmap) + painter.drawImage( + QtCore.QPoint(0, 0), + self.qimage_scaled, + QtCore.QRect( + self.position[0], + self.position[1], + self.qlabel_image.width(), + self.qlabel_image.height(), + ), + ) + painter.end() + + self.qlabel_image.setPixmap(self.qpixmap) + else: + pass + + def mousePressAction(self, QMouseEvent): + x, y = QMouseEvent.pos().x(), QMouseEvent.pos().y() + # print(x,y) + if self.panFlag: + self.pressed = QMouseEvent.pos() # starting point of drag vector + self.anchor = self.position # save the pan position when panning starts + print("Label pos:") + + def mouseMoveAction(self, QMouseEvent): + x, y = QMouseEvent.pos().x(), QMouseEvent.pos().y() + if self.pressed: + dx, dy = ( + x - self.pressed.x(), + y - self.pressed.y(), + ) # calculate the drag vector + self.position = ( + self.anchor[0] - dx, + self.anchor[1] - dy, + ) # update pan position using drag vector + self.update() + + # show the image with udated pan position + + print("Label pos:") + + def mouseReleaseAction(self, QMouseEvent): + self.pressed = None # clear the starting point of drag vector + + def zoomPlus(self): + self.zoomX += 1 + px, py = self.position + px += self.qlabel_image.width() / 2 + py += self.qlabel_image.height() / 2 + self.position = (px, py) + self.qimage_scaled = self.qimage.scaled( + self.qlabel_image.width() * self.zoomX, + self.qlabel_image.height() * self.zoomX, + QtCore.Qt.KeepAspectRatio, + ) + self.update() + + def zoomMinus(self): + if self.zoomX > 1: + self.zoomX -= 1 + px, py = self.position + px -= self.qlabel_image.width() / 2 + py -= self.qlabel_image.height() / 2 + self.position = (px, py) + self.qimage_scaled = self.qimage.scaled( + self.qlabel_image.width() * self.zoomX, + self.qlabel_image.height() * self.zoomX, + QtCore.Qt.KeepAspectRatio, + ) + self.update() + + def resetZoom(self): + self.zoomX = 1 + self.position = [0, 0] + self.qimage_scaled = self.qimage.scaled( + self.qlabel_image.width() * self.zoomX, + self.qlabel_image.height() * self.zoomX, + QtCore.Qt.KeepAspectRatio, + ) + self.update() + + def enablePan(self, value): + self.panFlag = value diff --git a/src/MplWidget.py b/src/MplWidget.py new file mode 100755 index 0000000..e43e391 --- /dev/null +++ b/src/MplWidget.py @@ -0,0 +1,60 @@ +# Imports +from PyQt6 import QtWidgets +from matplotlib.figure import Figure +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas +import matplotlib + +# Ensure using PyQt5 backend +matplotlib.use("QT5Agg") + + +# Matplotlib canvas class to create figure +class MplCanvas(Canvas): + def __init__(self): + self.fig = Figure(figsize=(5, 2)) + self.ax = self.fig.add_subplot(111) + Canvas.__init__(self, self.fig) + # Canvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + Canvas.updateGeometry(self) + + +# Matplotlib widget +class MplWidget(QtWidgets.QWidget): + _controller = None + + def __init__(self, parent=None): + QtWidgets.QWidget.__init__(self, parent) # Inherit from QWidget + self.canvas = MplCanvas() # Create canvas object + self.vbl = QtWidgets.QVBoxLayout() # Set box for plotting + self.vbl.addWidget(self.canvas) + self.setLayout(self.vbl) + + @property + def controller(self): + return self._image + + @controller.setter + def controller(self, controller): + self._controller = controller + + def drawHistogram(self, img): + self.canvas.ax.cla() + + histogram = self._controller.calculate_histogram(img) + color = ("b", "g", "r") + + if len(histogram) == 1: + color = "b" + + for i, col in enumerate(color): + self.canvas.ax.plot(histogram[i], color=col) + self.canvas.ax.set_xlim([0, 256]) + # self.canvas.figure.set_size_inches(2,2) + + self.canvas.fig.tight_layout() + self.canvas.draw() + + def save_histogram(self, str): + if self.canvas.fig is not None: + # Speichere die Figur als Bild + self.canvas.fig.savefig(str, dpi=300) diff --git a/src/Playground_UI.py b/src/Playground_UI.py new file mode 100755 index 0000000..fe924ca --- /dev/null +++ b/src/Playground_UI.py @@ -0,0 +1,779 @@ +# Form implementation generated from reading ui file 'Playground_UI.ui' +# +# Created by: PyQt6 UI code generator 6.6.0 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(1259, 858) + self.centralwidget = QtWidgets.QWidget(parent=MainWindow) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Maximum, QtWidgets.QSizePolicy.Policy.Maximum + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.centralwidget.sizePolicy().hasHeightForWidth() + ) + self.centralwidget.setSizePolicy(sizePolicy) + self.centralwidget.setObjectName("centralwidget") + self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.centralwidget) + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setSizeConstraint( + QtWidgets.QLayout.SizeConstraint.SetDefaultConstraint + ) + self.horizontalLayout.setSpacing(7) + self.horizontalLayout.setObjectName("horizontalLayout") + self.layout_left = QtWidgets.QVBoxLayout() + self.layout_left.setObjectName("layout_left") + self.tab_widget_image = QtWidgets.QTabWidget(parent=self.centralwidget) + self.tab_widget_image.setMinimumSize(QtCore.QSize(500, 500)) + self.tab_widget_image.setAutoFillBackground(True) + self.tab_widget_image.setObjectName("tab_widget_image") + self.tab_output_image = QtWidgets.QWidget() + self.tab_output_image.setAutoFillBackground(False) + self.tab_output_image.setObjectName("tab_output_image") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tab_output_image) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.label_output_image = QtWidgets.QLabel(parent=self.tab_output_image) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.label_output_image.sizePolicy().hasHeightForWidth() + ) + self.label_output_image.setSizePolicy(sizePolicy) + self.label_output_image.setFrameShape(QtWidgets.QFrame.Shape.Box) + self.label_output_image.setFrameShadow(QtWidgets.QFrame.Shadow.Plain) + self.label_output_image.setScaledContents(False) + self.label_output_image.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.label_output_image.setObjectName("label_output_image") + self.verticalLayout_3.addWidget(self.label_output_image) + self.pushButton_reset_output_image = QtWidgets.QPushButton( + parent=self.tab_output_image + ) + self.pushButton_reset_output_image.setObjectName( + "pushButton_reset_output_image" + ) + self.verticalLayout_3.addWidget(self.pushButton_reset_output_image) + self.pushButton_overwrite_input_image = QtWidgets.QPushButton( + parent=self.tab_output_image + ) + self.pushButton_overwrite_input_image.setObjectName( + "pushButton_overwrite_input_image" + ) + self.verticalLayout_3.addWidget(self.pushButton_overwrite_input_image) + self.tab_widget_image.addTab(self.tab_output_image, "") + self.tab_input_image = QtWidgets.QWidget() + self.tab_input_image.setObjectName("tab_input_image") + self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.tab_input_image) + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.label_input_image = QtWidgets.QLabel(parent=self.tab_input_image) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.label_input_image.sizePolicy().hasHeightForWidth() + ) + self.label_input_image.setSizePolicy(sizePolicy) + self.label_input_image.setAutoFillBackground(False) + self.label_input_image.setFrameShape(QtWidgets.QFrame.Shape.Box) + self.label_input_image.setScaledContents(False) + self.label_input_image.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.label_input_image.setObjectName("label_input_image") + self.verticalLayout_4.addWidget(self.label_input_image) + self.tab_widget_image.addTab(self.tab_input_image, "") + self.layout_left.addWidget(self.tab_widget_image) + self.line = QtWidgets.QFrame(parent=self.centralwidget) + self.line.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line.setObjectName("line") + self.layout_left.addWidget(self.line) + self.text_output = QtWidgets.QTextBrowser(parent=self.centralwidget) + self.text_output.setMinimumSize(QtCore.QSize(0, 150)) + self.text_output.setObjectName("text_output") + self.layout_left.addWidget( + self.text_output, 0, QtCore.Qt.AlignmentFlag.AlignBottom + ) + self.horizontalLayout.addLayout(self.layout_left) + self.vertical_line = QtWidgets.QFrame(parent=self.centralwidget) + self.vertical_line.setFrameShape(QtWidgets.QFrame.Shape.VLine) + self.vertical_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.vertical_line.setObjectName("vertical_line") + self.horizontalLayout.addWidget(self.vertical_line) + self.layout_right = QtWidgets.QVBoxLayout() + self.layout_right.setObjectName("layout_right") + self.label = QtWidgets.QLabel(parent=self.centralwidget) + self.label.setObjectName("label") + self.layout_right.addWidget(self.label) + self.widget_histogram = MplWidget(parent=self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.widget_histogram.sizePolicy().hasHeightForWidth() + ) + self.widget_histogram.setSizePolicy(sizePolicy) + self.widget_histogram.setMinimumSize(QtCore.QSize(400, 250)) + self.widget_histogram.setAutoFillBackground(True) + self.widget_histogram.setObjectName("widget_histogram") + self.layout_right.addWidget(self.widget_histogram) + self.horizontal_line = QtWidgets.QFrame(parent=self.centralwidget) + self.horizontal_line.setContextMenuPolicy( + QtCore.Qt.ContextMenuPolicy.PreventContextMenu + ) + self.horizontal_line.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.horizontal_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.horizontal_line.setObjectName("horizontal_line") + self.layout_right.addWidget(self.horizontal_line) + self.tabWidget_controller = QtWidgets.QTabWidget(parent=self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Maximum, QtWidgets.QSizePolicy.Policy.Expanding + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.tabWidget_controller.sizePolicy().hasHeightForWidth() + ) + self.tabWidget_controller.setSizePolicy(sizePolicy) + self.tabWidget_controller.setMinimumSize(QtCore.QSize(400, 0)) + self.tabWidget_controller.setObjectName("tabWidget_controller") + self.tab_image_analysis = QtWidgets.QWidget() + self.tab_image_analysis.setObjectName("tab_image_analysis") + self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.tab_image_analysis) + self.verticalLayout_7.setObjectName("verticalLayout_7") + self.tabWidget_analysis = QtWidgets.QTabWidget(parent=self.tab_image_analysis) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Maximum, + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.tabWidget_analysis.sizePolicy().hasHeightForWidth() + ) + self.tabWidget_analysis.setSizePolicy(sizePolicy) + self.tabWidget_analysis.setMinimumSize(QtCore.QSize(380, 0)) + self.tabWidget_analysis.setObjectName("tabWidget_analysis") + self.widget_image_information = QtWidgets.QWidget() + self.widget_image_information.setObjectName("widget_image_information") + self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.widget_image_information) + self.verticalLayout_8.setObjectName("verticalLayout_8") + self.widget = QtWidgets.QWidget(parent=self.widget_image_information) + self.widget.setObjectName("widget") + self.formLayout = QtWidgets.QFormLayout(self.widget) + self.formLayout.setObjectName("formLayout") + self.label_height_text = QtWidgets.QLabel(parent=self.widget) + self.label_height_text.setObjectName("label_height_text") + self.formLayout.setWidget( + 0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_height_text + ) + self.label_height_image = QtWidgets.QLabel(parent=self.widget) + self.label_height_image.setObjectName("label_height_image") + self.formLayout.setWidget( + 0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.label_height_image + ) + self.label_width_text = QtWidgets.QLabel(parent=self.widget) + self.label_width_text.setObjectName("label_width_text") + self.formLayout.setWidget( + 1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_width_text + ) + self.label_width_image = QtWidgets.QLabel(parent=self.widget) + self.label_width_image.setObjectName("label_width_image") + self.formLayout.setWidget( + 1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.label_width_image + ) + self.verticalLayout_8.addWidget(self.widget) + self.groupBox_2 = QtWidgets.QGroupBox(parent=self.widget_image_information) + self.groupBox_2.setObjectName("groupBox_2") + self.formLayout_4 = QtWidgets.QFormLayout(self.groupBox_2) + self.formLayout_4.setObjectName("formLayout_4") + self.label_4 = QtWidgets.QLabel(parent=self.groupBox_2) + self.label_4.setObjectName("label_4") + self.formLayout_4.setWidget( + 0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_4 + ) + self.label_color_pixel1 = QtWidgets.QLabel(parent=self.groupBox_2) + self.label_color_pixel1.setObjectName("label_color_pixel1") + self.formLayout_4.setWidget( + 0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.label_color_pixel1 + ) + self.label_5 = QtWidgets.QLabel(parent=self.groupBox_2) + self.label_5.setObjectName("label_5") + self.formLayout_4.setWidget( + 1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_5 + ) + self.label_color_pixel2 = QtWidgets.QLabel(parent=self.groupBox_2) + self.label_color_pixel2.setObjectName("label_color_pixel2") + self.formLayout_4.setWidget( + 1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.label_color_pixel2 + ) + self.verticalLayout_8.addWidget(self.groupBox_2) + self.groupBox = QtWidgets.QGroupBox(parent=self.widget_image_information) + self.groupBox.setObjectName("groupBox") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.groupBox) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.pushButton_show_channel1 = QtWidgets.QPushButton(parent=self.groupBox) + self.pushButton_show_channel1.setObjectName("pushButton_show_channel1") + self.horizontalLayout_4.addWidget(self.pushButton_show_channel1) + self.pushButton_show_channel2 = QtWidgets.QPushButton(parent=self.groupBox) + self.pushButton_show_channel2.setObjectName("pushButton_show_channel2") + self.horizontalLayout_4.addWidget(self.pushButton_show_channel2) + self.pushButton_show_channel3 = QtWidgets.QPushButton(parent=self.groupBox) + self.pushButton_show_channel3.setObjectName("pushButton_show_channel3") + self.horizontalLayout_4.addWidget(self.pushButton_show_channel3) + self.verticalLayout_8.addWidget(self.groupBox) + self.pushButton_do_image_manipulation = QtWidgets.QPushButton( + parent=self.widget_image_information + ) + self.pushButton_do_image_manipulation.setObjectName( + "pushButton_do_image_manipulation" + ) + self.verticalLayout_8.addWidget(self.pushButton_do_image_manipulation) + spacerItem = QtWidgets.QSpacerItem( + 20, + 40, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + self.verticalLayout_8.addItem(spacerItem) + self.tabWidget_analysis.addTab(self.widget_image_information, "") + self.widget_color_analysis = QtWidgets.QWidget() + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.widget_color_analysis.sizePolicy().hasHeightForWidth() + ) + self.widget_color_analysis.setSizePolicy(sizePolicy) + self.widget_color_analysis.setObjectName("widget_color_analysis") + self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.widget_color_analysis) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.verticalLayout_color_analysis = QtWidgets.QVBoxLayout() + self.verticalLayout_color_analysis.setObjectName( + "verticalLayout_color_analysis" + ) + self.label_color_analysis = QtWidgets.QLabel(parent=self.widget_color_analysis) + self.label_color_analysis.setMaximumSize(QtCore.QSize(16777215, 20)) + self.label_color_analysis.setScaledContents(False) + self.label_color_analysis.setAlignment( + QtCore.Qt.AlignmentFlag.AlignLeading + | QtCore.Qt.AlignmentFlag.AlignLeft + | QtCore.Qt.AlignmentFlag.AlignTop + ) + self.label_color_analysis.setObjectName("label_color_analysis") + self.verticalLayout_color_analysis.addWidget(self.label_color_analysis) + self.horizontalSlider_color_clusters = QtWidgets.QSlider( + parent=self.widget_color_analysis + ) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Maximum, QtWidgets.QSizePolicy.Policy.Fixed + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.horizontalSlider_color_clusters.sizePolicy().hasHeightForWidth() + ) + self.horizontalSlider_color_clusters.setSizePolicy(sizePolicy) + self.horizontalSlider_color_clusters.setMinimumSize(QtCore.QSize(370, 0)) + self.horizontalSlider_color_clusters.setSizeIncrement(QtCore.QSize(380, 0)) + self.horizontalSlider_color_clusters.setMinimum(1) + self.horizontalSlider_color_clusters.setMaximum(10) + self.horizontalSlider_color_clusters.setSliderPosition(3) + self.horizontalSlider_color_clusters.setOrientation( + QtCore.Qt.Orientation.Horizontal + ) + self.horizontalSlider_color_clusters.setTickPosition( + QtWidgets.QSlider.TickPosition.TicksBelow + ) + self.horizontalSlider_color_clusters.setTickInterval(1) + self.horizontalSlider_color_clusters.setObjectName( + "horizontalSlider_color_clusters" + ) + self.verticalLayout_color_analysis.addWidget( + self.horizontalSlider_color_clusters + ) + self.line_2 = QtWidgets.QFrame(parent=self.widget_color_analysis) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Maximum, QtWidgets.QSizePolicy.Policy.Fixed + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.line_2.sizePolicy().hasHeightForWidth()) + self.line_2.setSizePolicy(sizePolicy) + self.line_2.setMinimumSize(QtCore.QSize(370, 0)) + self.line_2.setSizeIncrement(QtCore.QSize(370, 0)) + self.line_2.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_2.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_2.setObjectName("line_2") + self.verticalLayout_color_analysis.addWidget(self.line_2) + self.label_image_color_analysis_output = QtWidgets.QLabel( + parent=self.widget_color_analysis + ) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Maximum, QtWidgets.QSizePolicy.Policy.Preferred + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.label_image_color_analysis_output.sizePolicy().hasHeightForWidth() + ) + self.label_image_color_analysis_output.setSizePolicy(sizePolicy) + self.label_image_color_analysis_output.setMinimumSize(QtCore.QSize(370, 0)) + self.label_image_color_analysis_output.setAlignment( + QtCore.Qt.AlignmentFlag.AlignCenter + ) + self.label_image_color_analysis_output.setObjectName( + "label_image_color_analysis_output" + ) + self.verticalLayout_color_analysis.addWidget( + self.label_image_color_analysis_output + ) + self.verticalLayout_5.addLayout(self.verticalLayout_color_analysis) + self.tabWidget_analysis.addTab(self.widget_color_analysis, "") + self.verticalLayout_7.addWidget(self.tabWidget_analysis) + self.tabWidget_controller.addTab(self.tab_image_analysis, "") + self.tab_geometric_adjustments = QtWidgets.QWidget() + self.tab_geometric_adjustments.setObjectName("tab_geometric_adjustments") + self.gridLayout_2 = QtWidgets.QGridLayout(self.tab_geometric_adjustments) + self.gridLayout_2.setObjectName("gridLayout_2") + self.lineEdit_image_width = QtWidgets.QLineEdit( + parent=self.tab_geometric_adjustments + ) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.lineEdit_image_width.sizePolicy().hasHeightForWidth() + ) + self.lineEdit_image_width.setSizePolicy(sizePolicy) + self.lineEdit_image_width.setObjectName("lineEdit_image_width") + self.gridLayout_2.addWidget(self.lineEdit_image_width, 1, 1, 1, 1) + self.pushButton_adjust_image_size = QtWidgets.QPushButton( + parent=self.tab_geometric_adjustments + ) + self.pushButton_adjust_image_size.setObjectName("pushButton_adjust_image_size") + self.gridLayout_2.addWidget(self.pushButton_adjust_image_size, 4, 0, 1, 2) + self.checkBox_fix_image_size = QtWidgets.QCheckBox( + parent=self.tab_geometric_adjustments + ) + self.checkBox_fix_image_size.setChecked(True) + self.checkBox_fix_image_size.setObjectName("checkBox_fix_image_size") + self.gridLayout_2.addWidget(self.checkBox_fix_image_size, 2, 1, 1, 1) + self.label_3 = QtWidgets.QLabel(parent=self.tab_geometric_adjustments) + self.label_3.setObjectName("label_3") + self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1) + self.lineEdit_image_height = QtWidgets.QLineEdit( + parent=self.tab_geometric_adjustments + ) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.lineEdit_image_height.sizePolicy().hasHeightForWidth() + ) + self.lineEdit_image_height.setSizePolicy(sizePolicy) + self.lineEdit_image_height.setObjectName("lineEdit_image_height") + self.gridLayout_2.addWidget(self.lineEdit_image_height, 0, 1, 1, 1) + self.label_2 = QtWidgets.QLabel(parent=self.tab_geometric_adjustments) + self.label_2.setObjectName("label_2") + self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1) + spacerItem1 = QtWidgets.QSpacerItem( + 20, + 40, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + self.gridLayout_2.addItem(spacerItem1, 5, 1, 1, 1) + self.tabWidget_controller.addTab(self.tab_geometric_adjustments, "") + self.tab_histogram_manip = QtWidgets.QWidget() + self.tab_histogram_manip.setObjectName("tab_histogram_manip") + self.verticalLayout_9 = QtWidgets.QVBoxLayout(self.tab_histogram_manip) + self.verticalLayout_9.setObjectName("verticalLayout_9") + self.pushButton_hist_stretch = QtWidgets.QPushButton( + parent=self.tab_histogram_manip + ) + self.pushButton_hist_stretch.setObjectName("pushButton_hist_stretch") + self.verticalLayout_9.addWidget(self.pushButton_hist_stretch) + self.pushButton_hist_equalization = QtWidgets.QPushButton( + parent=self.tab_histogram_manip + ) + self.pushButton_hist_equalization.setObjectName("pushButton_hist_equalization") + self.verticalLayout_9.addWidget(self.pushButton_hist_equalization) + self.line_3 = QtWidgets.QFrame(parent=self.tab_histogram_manip) + self.line_3.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_3.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_3.setObjectName("line_3") + self.verticalLayout_9.addWidget(self.line_3) + self.pushButton_hist_log = QtWidgets.QPushButton( + parent=self.tab_histogram_manip + ) + self.pushButton_hist_log.setObjectName("pushButton_hist_log") + self.verticalLayout_9.addWidget(self.pushButton_hist_log) + self.pushButton_hist_exp = QtWidgets.QPushButton( + parent=self.tab_histogram_manip + ) + self.pushButton_hist_exp.setObjectName("pushButton_hist_exp") + self.verticalLayout_9.addWidget(self.pushButton_hist_exp) + self.pushButton_hist_inv = QtWidgets.QPushButton( + parent=self.tab_histogram_manip + ) + self.pushButton_hist_inv.setObjectName("pushButton_hist_inv") + self.verticalLayout_9.addWidget(self.pushButton_hist_inv) + self.groupBox_6 = QtWidgets.QGroupBox(parent=self.tab_histogram_manip) + self.groupBox_6.setObjectName("groupBox_6") + self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox_6) + self.verticalLayout.setContentsMargins(-1, 0, -1, 0) + self.verticalLayout.setObjectName("verticalLayout") + self.horizontalSlider_hist_threshold = QtWidgets.QSlider(parent=self.groupBox_6) + self.horizontalSlider_hist_threshold.setMaximum(255) + self.horizontalSlider_hist_threshold.setProperty("value", 128) + self.horizontalSlider_hist_threshold.setOrientation( + QtCore.Qt.Orientation.Horizontal + ) + self.horizontalSlider_hist_threshold.setTickPosition( + QtWidgets.QSlider.TickPosition.TicksBelow + ) + self.horizontalSlider_hist_threshold.setTickInterval(0) + self.horizontalSlider_hist_threshold.setObjectName( + "horizontalSlider_hist_threshold" + ) + self.verticalLayout.addWidget(self.horizontalSlider_hist_threshold) + self.verticalLayout_9.addWidget(self.groupBox_6) + self.pushButton_hist_fill = QtWidgets.QPushButton( + parent=self.tab_histogram_manip + ) + self.pushButton_hist_fill.setObjectName("pushButton_hist_fill") + self.verticalLayout_9.addWidget(self.pushButton_hist_fill) + spacerItem2 = QtWidgets.QSpacerItem( + 20, + 40, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + self.verticalLayout_9.addItem(spacerItem2) + self.tabWidget_controller.addTab(self.tab_histogram_manip, "") + self.tab_filter = QtWidgets.QWidget() + self.tab_filter.setObjectName("tab_filter") + self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.tab_filter) + self.verticalLayout_6.setObjectName("verticalLayout_6") + self.groupBox_3 = QtWidgets.QGroupBox(parent=self.tab_filter) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.groupBox_3.sizePolicy().hasHeightForWidth()) + self.groupBox_3.setSizePolicy(sizePolicy) + self.groupBox_3.setObjectName("groupBox_3") + self.verticalLayout_11 = QtWidgets.QVBoxLayout(self.groupBox_3) + self.verticalLayout_11.setObjectName("verticalLayout_11") + self.widget_2 = QtWidgets.QWidget(parent=self.groupBox_3) + self.widget_2.setObjectName("widget_2") + self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.widget_2) + self.horizontalLayout_7.setContentsMargins(-1, 0, -1, 0) + self.horizontalLayout_7.setObjectName("horizontalLayout_7") + self.label_8 = QtWidgets.QLabel(parent=self.widget_2) + self.label_8.setObjectName("label_8") + self.horizontalLayout_7.addWidget(self.label_8) + self.spinBox_filter_avg_size = QtWidgets.QSpinBox(parent=self.widget_2) + self.spinBox_filter_avg_size.setReadOnly(False) + self.spinBox_filter_avg_size.setButtonSymbols( + QtWidgets.QAbstractSpinBox.ButtonSymbols.UpDownArrows + ) + self.spinBox_filter_avg_size.setAccelerated(False) + self.spinBox_filter_avg_size.setCorrectionMode( + QtWidgets.QAbstractSpinBox.CorrectionMode.CorrectToNearestValue + ) + self.spinBox_filter_avg_size.setKeyboardTracking(False) + self.spinBox_filter_avg_size.setProperty("showGroupSeparator", False) + self.spinBox_filter_avg_size.setMinimum(3) + self.spinBox_filter_avg_size.setSingleStep(2) + self.spinBox_filter_avg_size.setObjectName("spinBox_filter_avg_size") + self.horizontalLayout_7.addWidget(self.spinBox_filter_avg_size) + self.verticalLayout_11.addWidget(self.widget_2) + self.widget_3 = QtWidgets.QWidget(parent=self.groupBox_3) + self.widget_3.setObjectName("widget_3") + self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.widget_3) + self.horizontalLayout_6.setContentsMargins(-1, 0, -1, 0) + self.horizontalLayout_6.setObjectName("horizontalLayout_6") + self.pushButton_filter_movAvg = QtWidgets.QPushButton(parent=self.widget_3) + self.pushButton_filter_movAvg.setObjectName("pushButton_filter_movAvg") + self.horizontalLayout_6.addWidget(self.pushButton_filter_movAvg) + self.pushButton_filter_gauss = QtWidgets.QPushButton(parent=self.widget_3) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Fixed + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.pushButton_filter_gauss.sizePolicy().hasHeightForWidth() + ) + self.pushButton_filter_gauss.setSizePolicy(sizePolicy) + self.pushButton_filter_gauss.setObjectName("pushButton_filter_gauss") + self.horizontalLayout_6.addWidget(self.pushButton_filter_gauss) + self.pushButton_filter_median = QtWidgets.QPushButton(parent=self.widget_3) + self.pushButton_filter_median.setObjectName("pushButton_filter_median") + self.horizontalLayout_6.addWidget(self.pushButton_filter_median) + self.verticalLayout_11.addWidget(self.widget_3) + self.verticalLayout_6.addWidget(self.groupBox_3) + self.groupBox_4 = QtWidgets.QGroupBox(parent=self.tab_filter) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Preferred + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.groupBox_4.sizePolicy().hasHeightForWidth()) + self.groupBox_4.setSizePolicy(sizePolicy) + self.groupBox_4.setObjectName("groupBox_4") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.groupBox_4) + self.horizontalLayout_3.setContentsMargins(-1, 0, -1, 0) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.pushButton_filter_sobelX = QtWidgets.QPushButton(parent=self.groupBox_4) + self.pushButton_filter_sobelX.setObjectName("pushButton_filter_sobelX") + self.horizontalLayout_3.addWidget(self.pushButton_filter_sobelX) + self.pushButton_filter_sobelY = QtWidgets.QPushButton(parent=self.groupBox_4) + self.pushButton_filter_sobelY.setObjectName("pushButton_filter_sobelY") + self.horizontalLayout_3.addWidget(self.pushButton_filter_sobelY) + self.verticalLayout_6.addWidget(self.groupBox_4) + self.line_4 = QtWidgets.QFrame(parent=self.tab_filter) + self.line_4.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_4.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_4.setObjectName("line_4") + self.verticalLayout_6.addWidget(self.line_4) + self.groupBox_5 = QtWidgets.QGroupBox(parent=self.tab_filter) + self.groupBox_5.setObjectName("groupBox_5") + self.verticalLayout_10 = QtWidgets.QVBoxLayout(self.groupBox_5) + self.verticalLayout_10.setContentsMargins(-1, 0, -1, 0) + self.verticalLayout_10.setObjectName("verticalLayout_10") + self.pushButton_filter_movAvg_conv = QtWidgets.QPushButton( + parent=self.groupBox_5 + ) + self.pushButton_filter_movAvg_conv.setObjectName( + "pushButton_filter_movAvg_conv" + ) + self.verticalLayout_10.addWidget(self.pushButton_filter_movAvg_conv) + self.pushButton_filter_movAvg_sep = QtWidgets.QPushButton( + parent=self.groupBox_5 + ) + self.pushButton_filter_movAvg_sep.setObjectName("pushButton_filter_movAvg_sep") + self.verticalLayout_10.addWidget(self.pushButton_filter_movAvg_sep) + self.pushButton_filter_movAvg_int = QtWidgets.QPushButton( + parent=self.groupBox_5 + ) + self.pushButton_filter_movAvg_int.setObjectName("pushButton_filter_movAvg_int") + self.verticalLayout_10.addWidget(self.pushButton_filter_movAvg_int) + self.line_5 = QtWidgets.QFrame(parent=self.groupBox_5) + self.line_5.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_5.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_5.setObjectName("line_5") + self.verticalLayout_10.addWidget(self.line_5) + self.pushButton_filter_evaluation = QtWidgets.QPushButton( + parent=self.groupBox_5 + ) + self.pushButton_filter_evaluation.setObjectName("pushButton_filter_evaluation") + self.verticalLayout_10.addWidget(self.pushButton_filter_evaluation) + self.verticalLayout_6.addWidget(self.groupBox_5) + spacerItem3 = QtWidgets.QSpacerItem( + 20, + 40, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + self.verticalLayout_6.addItem(spacerItem3) + self.tabWidget_controller.addTab(self.tab_filter, "") + self.tab_AI = QtWidgets.QWidget() + self.tab_AI.setObjectName("tab_AI") + self.formLayout_3 = QtWidgets.QFormLayout(self.tab_AI) + self.formLayout_3.setObjectName("formLayout_3") + self.pushButton_AWS_Labeling = QtWidgets.QPushButton(parent=self.tab_AI) + self.pushButton_AWS_Labeling.setObjectName("pushButton_AWS_Labeling") + self.formLayout_3.setWidget( + 0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.pushButton_AWS_Labeling + ) + self.tabWidget_controller.addTab(self.tab_AI, "") + self.layout_right.addWidget(self.tabWidget_controller) + self.horizontalLayout.addLayout(self.layout_right) + self.horizontalLayout_5.addLayout(self.horizontalLayout) + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(parent=MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1259, 37)) + self.menubar.setObjectName("menubar") + self.menuMen = QtWidgets.QMenu(parent=self.menubar) + self.menuMen.setObjectName("menuMen") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(parent=MainWindow) + self.statusbar.setEnabled(True) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + self.actionBild_laden = QtGui.QAction(parent=MainWindow) + self.actionBild_laden.setObjectName("actionBild_laden") + self.actionBild_speichern = QtGui.QAction(parent=MainWindow) + self.actionBild_speichern.setObjectName("actionBild_speichern") + self.action_save_histogram = QtGui.QAction(parent=MainWindow) + self.action_save_histogram.setObjectName("action_save_histogram") + self.menuMen.addAction(self.actionBild_laden) + self.menuMen.addAction(self.actionBild_speichern) + self.menuMen.addAction(self.action_save_histogram) + self.menubar.addAction(self.menuMen.menuAction()) + + self.retranslateUi(MainWindow) + self.tab_widget_image.setCurrentIndex(0) + self.tabWidget_controller.setCurrentIndex(0) + self.tabWidget_analysis.setCurrentIndex(0) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Image Playground")) + self.label_output_image.setText(_translate("MainWindow", "Ausgabe")) + self.pushButton_reset_output_image.setText( + _translate("MainWindow", "Ausgabebild zurücksetzen") + ) + self.pushButton_overwrite_input_image.setText( + _translate("MainWindow", "Eingabebild durch aktuelle Ausgabe ersetzen") + ) + self.tab_widget_image.setTabText( + self.tab_widget_image.indexOf(self.tab_output_image), + _translate("MainWindow", "Ausgabe"), + ) + self.label_input_image.setText(_translate("MainWindow", "Eingabe")) + self.tab_widget_image.setTabText( + self.tab_widget_image.indexOf(self.tab_input_image), + _translate("MainWindow", "Eingabe"), + ) + self.label.setText(_translate("MainWindow", "Histogramm")) + self.label_height_text.setText(_translate("MainWindow", "Höhe:")) + self.label_height_image.setText(_translate("MainWindow", "0")) + self.label_width_text.setText(_translate("MainWindow", "Breite:")) + self.label_width_image.setText(_translate("MainWindow", "0")) + self.groupBox_2.setTitle( + _translate("MainWindow", "Farbinformation zu einzelnen Pixeln") + ) + self.label_4.setText(_translate("MainWindow", "2. Reihe, 1. Spalte:")) + self.label_color_pixel1.setText(_translate("MainWindow", "[x,y,z]")) + self.label_5.setText(_translate("MainWindow", "1. Reihe, 2. Spalte:")) + self.label_color_pixel2.setText(_translate("MainWindow", "[x,y,z]")) + self.groupBox.setTitle(_translate("MainWindow", "Farbkanäle anzeigen")) + self.pushButton_show_channel1.setText(_translate("MainWindow", "Kanal 1")) + self.pushButton_show_channel2.setText(_translate("MainWindow", "Kanal 2")) + self.pushButton_show_channel3.setText(_translate("MainWindow", "Kanal 3")) + self.pushButton_do_image_manipulation.setText( + _translate("MainWindow", "Meine erste Bildmanipulation anwenden") + ) + self.tabWidget_analysis.setTabText( + self.tabWidget_analysis.indexOf(self.widget_image_information), + _translate("MainWindow", "Bildinformation"), + ) + self.label_color_analysis.setText(_translate("MainWindow", "Anzahl Cluster: ")) + self.label_image_color_analysis_output.setText( + _translate("MainWindow", "Output") + ) + self.tabWidget_analysis.setTabText( + self.tabWidget_analysis.indexOf(self.widget_color_analysis), + _translate("MainWindow", "Farbanalyse"), + ) + self.tabWidget_controller.setTabText( + self.tabWidget_controller.indexOf(self.tab_image_analysis), + _translate("MainWindow", "Bildanalyse"), + ) + self.lineEdit_image_width.setPlaceholderText(_translate("MainWindow", "100")) + self.pushButton_adjust_image_size.setText( + _translate("MainWindow", "Größe anpassen") + ) + self.checkBox_fix_image_size.setText( + _translate("MainWindow", "Seitenverhältnis fixieren") + ) + self.label_3.setText(_translate("MainWindow", "Neue Breite")) + self.lineEdit_image_height.setPlaceholderText(_translate("MainWindow", "100")) + self.label_2.setText(_translate("MainWindow", "Neue Höhe")) + self.tabWidget_controller.setTabText( + self.tabWidget_controller.indexOf(self.tab_geometric_adjustments), + _translate("MainWindow", "Größe"), + ) + self.pushButton_hist_stretch.setText( + _translate("MainWindow", "Histogram Stretch") + ) + self.pushButton_hist_equalization.setText( + _translate("MainWindow", "Histogram Equalization") + ) + self.pushButton_hist_log.setText(_translate("MainWindow", "Logarithmus")) + self.pushButton_hist_exp.setText( + _translate("MainWindow", "Exponentialfunktion") + ) + self.pushButton_hist_inv.setText( + _translate("MainWindow", "Histogramm invertieren") + ) + self.groupBox_6.setTitle(_translate("MainWindow", "Threshold")) + self.pushButton_hist_fill.setText( + _translate("MainWindow", "Histogrammlücken füllen") + ) + self.tabWidget_controller.setTabText( + self.tabWidget_controller.indexOf(self.tab_histogram_manip), + _translate("MainWindow", "Histogramm Mapping"), + ) + self.groupBox_3.setTitle(_translate("MainWindow", "Weichzeichnungsfilter")) + self.label_8.setText(_translate("MainWindow", "Filtergröße:")) + self.pushButton_filter_movAvg.setText( + _translate("MainWindow", "Moving Average") + ) + self.pushButton_filter_gauss.setText(_translate("MainWindow", "Gauß-Filter")) + self.pushButton_filter_median.setText(_translate("MainWindow", "Median-Filter")) + self.groupBox_4.setTitle(_translate("MainWindow", "Kantenerkennung")) + self.pushButton_filter_sobelX.setText(_translate("MainWindow", "Sobel X")) + self.pushButton_filter_sobelY.setText(_translate("MainWindow", "Sobel Y")) + self.groupBox_5.setTitle( + _translate("MainWindow", "Moving Average - Auswertung") + ) + self.pushButton_filter_movAvg_conv.setText(_translate("MainWindow", "Faltung")) + self.pushButton_filter_movAvg_sep.setText( + _translate("MainWindow", "Separierbarer Filter") + ) + self.pushButton_filter_movAvg_int.setText( + _translate("MainWindow", "Integralbild") + ) + self.pushButton_filter_evaluation.setText( + _translate("MainWindow", "Auswertung durchführen") + ) + self.tabWidget_controller.setTabText( + self.tabWidget_controller.indexOf(self.tab_filter), + _translate("MainWindow", "Filter"), + ) + self.pushButton_AWS_Labeling.setText( + _translate("MainWindow", "AWS Rekognition Labeling") + ) + self.tabWidget_controller.setTabText( + self.tabWidget_controller.indexOf(self.tab_AI), + _translate("MainWindow", "Recognition"), + ) + self.menuMen.setTitle(_translate("MainWindow", "Menü")) + self.actionBild_laden.setText(_translate("MainWindow", "Bild laden")) + self.actionBild_speichern.setText(_translate("MainWindow", "Bild speichern")) + self.action_save_histogram.setText( + _translate("MainWindow", "Histogramm speichern") + ) + + +from MplWidget import MplWidget diff --git a/src/Playground_UI.ui b/src/Playground_UI.ui new file mode 100755 index 0000000..089f6fc --- /dev/null +++ b/src/Playground_UI.ui @@ -0,0 +1,954 @@ + + + MainWindow + + + + 0 + 0 + 1259 + 858 + + + + Image Playground + + + + + 0 + 0 + + + + + + + 7 + + + QLayout::SetDefaultConstraint + + + + + + + + 500 + 500 + + + + true + + + 0 + + + + false + + + Ausgabe + + + + + + + 0 + 0 + + + + QFrame::Box + + + QFrame::Plain + + + Ausgabe + + + false + + + Qt::AlignCenter + + + + + + + Ausgabebild zurücksetzen + + + + + + + Eingabebild durch aktuelle Ausgabe ersetzen + + + + + + + + Eingabe + + + + + + + 0 + 0 + + + + false + + + QFrame::Box + + + Eingabe + + + false + + + Qt::AlignCenter + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 150 + + + + + + + + + + Qt::Vertical + + + + + + + + + Histogramm + + + + + + + + 0 + 0 + + + + + 400 + 250 + + + + true + + + + + + + Qt::PreventContextMenu + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 400 + 0 + + + + 0 + + + + Bildanalyse + + + + + + + 0 + 0 + + + + + 380 + 0 + + + + 0 + + + + Bildinformation + + + + + + + + + Höhe: + + + + + + + 0 + + + + + + + Breite: + + + + + + + 0 + + + + + + + + + + Farbinformation zu einzelnen Pixeln + + + + + + 2. Reihe, 1. Spalte: + + + + + + + [x,y,z] + + + + + + + 1. Reihe, 2. Spalte: + + + + + + + [x,y,z] + + + + + + + + + + Farbkanäle anzeigen + + + + + + Kanal 1 + + + + + + + Kanal 2 + + + + + + + Kanal 3 + + + + + + + + + + Meine erste Bildmanipulation anwenden + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + Farbanalyse + + + + + + + + + 16777215 + 20 + + + + Anzahl Cluster: + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + 0 + 0 + + + + + 370 + 0 + + + + + 380 + 0 + + + + 1 + + + 10 + + + 3 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 1 + + + + + + + + 0 + 0 + + + + + 370 + 0 + + + + + 370 + 0 + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 370 + 0 + + + + Output + + + Qt::AlignCenter + + + + + + + + + + + + + + Größe + + + + + + + 0 + 0 + + + + 100 + + + + + + + Größe anpassen + + + + + + + Seitenverhältnis fixieren + + + true + + + + + + + Neue Breite + + + + + + + + 0 + 0 + + + + 100 + + + + + + + Neue Höhe + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Histogramm Mapping + + + + + + Histogram Stretch + + + + + + + Histogram Equalization + + + + + + + Qt::Horizontal + + + + + + + Logarithmus + + + + + + + Exponentialfunktion + + + + + + + Histogramm invertieren + + + + + + + Threshold + + + + 0 + + + 0 + + + + + 255 + + + 128 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 0 + + + + + + + + + + Histogrammlücken füllen + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Filter + + + + + + + 0 + 0 + + + + Weichzeichnungsfilter + + + + + + + 0 + + + 0 + + + + + Filtergröße: + + + + + + + false + + + QAbstractSpinBox::UpDownArrows + + + false + + + QAbstractSpinBox::CorrectToNearestValue + + + false + + + false + + + 3 + + + 2 + + + + + + + + + + + 0 + + + 0 + + + + + Moving Average + + + + + + + + 0 + 0 + + + + Gauß-Filter + + + + + + + Median-Filter + + + + + + + + + + + + + + 0 + 0 + + + + Kantenerkennung + + + + 0 + + + 0 + + + + + Sobel X + + + + + + + Sobel Y + + + + + + + + + + Qt::Horizontal + + + + + + + Moving Average - Auswertung + + + + 0 + + + 0 + + + + + Faltung + + + + + + + Separierbarer Filter + + + + + + + Integralbild + + + + + + + Qt::Horizontal + + + + + + + Auswertung durchführen + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Recognition + + + + + + AWS Rekognition Labeling + + + + + + + + + + + + + + + + + 0 + 0 + 1259 + 37 + + + + + Menü + + + + + + + + + + true + + + + + Bild laden + + + + + Bild speichern + + + + + Histogramm speichern + + + + + + MplWidget + QWidget +
MplWidget
+ 1 +
+
+ + +
diff --git a/src/Utilities.py b/src/Utilities.py new file mode 100755 index 0000000..b189d61 --- /dev/null +++ b/src/Utilities.py @@ -0,0 +1,81 @@ +import cv2 +from matplotlib import pyplot as plt + + +def showHistogram(img): + color = ("b", "g", "r") + if len(img.shape) == 2: + color = "b" + + for i, col in enumerate(color): + histr = cv2.calcHist([img], [i], None, [256], [0, 256]) + plt.plot(histr, color=col) + plt.xlim([0, 256]) + plt.show() + + +def calculate_histogram(img): + color = ("b", "g", "r") + histogram = [0, 0, 0] + if len(img.shape) == 2: + color = "b" + histogram = [0] + + for i, col in enumerate(color): + histogram[i] = cv2.calcHist([img], [i], None, [256], [0, 256]) + + return histogram + + +def plotHistogramVector(histogram): + plt.plot(histogram, color="b") + plt.xlim([0, len(histogram)]) + plt.show() + + +def resize_image(img, height): + aspect_ratio = img.shape[0] / img.shape[1] + dim = (height, int(height * aspect_ratio)) + return cv2.resize(img, dim, interpolation=cv2.INTER_NEAREST) + + +def resize_image(img, width, height, interpolation=cv2.INTER_NEAREST): + dim = (width, height) + return cv2.resize(img, dim, interpolation) + + +def grabWebcam(): + cap = cv2.VideoCapture(0) + while True: + ret, im = cap.read() + cv2.imshow("video test", im) + key = cv2.waitKey(10) + if key == 27: + break + # if key == ord(' '): + # cv2.imwrite('vid_result.jpg',im) + + +def ensure_one_channel_grayscale_image(img): + if len(img.shape) == 2: + # Das Bild ist bereits in Graustufen + return img + elif len(img.shape) == 3 and img.shape[2] == 3: + # Das Bild ist farbig (3 Kanäle: BGR oder RGB) + return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + else: + raise ValueError("Ungültiges Bildformat.") + + +def ensure_three_channel_grayscale_image(gray_scale_img): + if len(gray_scale_img.shape) == 2: + img = cv2.cvtColor(gray_scale_img, cv2.COLOR_GRAY2BGR) + # Das Bild ist bereits in Graustufen + return img + elif len(gray_scale_img.shape) == 3 and gray_scale_img.shape[2] == 3: + # Das Bild ist farbig (3 Kanäle: BGR oder RGB) + img = cv2.cvtColor(gray_scale_img, cv2.COLOR_BGR2GRAY) + img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + else: + raise ValueError("Ungültiges Bildformat.") diff --git a/src/app.py b/src/app.py new file mode 100755 index 0000000..ab6932a --- /dev/null +++ b/src/app.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +import sys +from models import ImageModel +from views import MainView +from controllers import MainController +from PyQt6.QtWidgets import QApplication + + +class App(QApplication): + def __init__(self, sys_argv): + super(App, self).__init__(sys_argv) + self.model = ImageModel() + self.main_controller = MainController(self.model) + self.main_view = MainView(self.model, self.main_controller) + self.main_view.show() + + +if __name__ == "__main__": + app = App(sys.argv) + sys.exit(app.exec()) diff --git a/src/controllers.py b/src/controllers.py new file mode 100755 index 0000000..c95fe75 --- /dev/null +++ b/src/controllers.py @@ -0,0 +1,148 @@ +import logging +from loggers import LogEmitter + +import cv2 +import numpy as np +import Utilities +import ColorAnalysis as CA +import HistogramManipulation as HM +import AWSRekognition as AI +import ImageInformation as II +import ImageFiltering as IF + + +class MainController: + def __init__(self, model): + super().__init__() + self._model = model + + self.logger = logging.getLogger() + self.log_handler = LogEmitter() + self.logger.addHandler(self.log_handler) + + # Set the log level to INFO + self.logger.setLevel(logging.INFO) + + def test_function(self): + print("Test function") + pass + + def loadImage(self, str): + img = cv2.imread(str, 1) + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + self._model.input_image = img + self.logger.info("Image loaded: " + str) + + def saveImage(self, str): + img = cv2.cvtColor(self._model.image, cv2.COLOR_RGB2BGR) + cv2.imwrite(str, img) + self.logger.info("Image written to: " + str) + + # + def changeImage(self): + image = np.zeros((256, 256, 3), np.uint8) + image[0 : 256 // 2, :] = (255, 0, 0) + image[256 // 2 : 256, :] = (0, 0, 255) + Utilities.resize_image(image, 100) + self._model.input_image = image + + def set_image_as_input_image(self): + self._model.input_image = self._model.image.copy() + + def reset_output_image(self): + self._model.image = self._model.input_image + + def resize_image(self, new_width, new_height): + self._model.image = Utilities.resize_image( + self._model.input_image, new_width, new_height + ) + + def analyseColors(self, ncluster): + color_analyzer = CA.ColorAnalysis(self._model.image, ncluster) + return color_analyzer.dominantColors() + + def create_visual_output(self, colors, width, height_max): + return CA.create_visual_output(colors, width, height_max) + + def calculate_histogram(self, img): + return Utilities.calculate_histogram(img) + + def label_image(self): + ai = AI.AWSRekognition() + self._model.image = ai.label_image(self._model.input_image) + self.logger.critical("Labeling successfull") + + ##################################### + # Übung 1 + ##################################### + + def get_image_information(self): + return II.imageSize(self._model.image) + + def get_pixel_information(self): + return II.getPixelColor(self._model.image) + + def show_channel(self, channel): + self._model.image = II.returnChannel(self._model.input_image, channel) + + def do_first_image_manipulation(self): + self._model.image = II.myFirstImageManipulation(self._model.image) + + ##################################### + # Übung 2 + ##################################### + + def stretch_image(self): + self._model.image = HM.stretchHistogram(self._model.input_image) + + def equalize_image(self): + self._model.image = HM.equalizeHistogram(self._model.input_image) + + def apply_log(self): + self._model.image = HM.apply_log(self._model.input_image) + + def apply_exp(self): + self._model.image = HM.apply_exp(self._model.input_image) + + def apply_inv(self): + self._model.image = HM.apply_inverse(self._model.input_image) + + def apply_threshold(self, threshold): + self._model.image = HM.apply_threshold(self._model.input_image, threshold) + + ##################################### + # Übung 3 + ##################################### + + def apply_gaussian_filter(self, kernel_size): + kernel = IF.createGaussianKernel(kernel_size) + img = IF.applyKernelInSpatialDomain(self._model.input_image, kernel) + self._model.image = Utilities.ensure_three_channel_grayscale_image(img) + + def apply_moving_avg_filter(self, kernel_size): + kernel = IF.createMovingAverageKernel(kernel_size) + img = IF.applyKernelInSpatialDomain(self._model.input_image, kernel) + self._model.image = Utilities.ensure_three_channel_grayscale_image(img) + + def apply_moving_avg_filter_integral(self, kernel_size): + img = IF.applyMovingAverageFilterWithIntegralImage( + self._model.input_image, kernel_size + ) + self._model.image = Utilities.ensure_three_channel_grayscale_image(img) + + def apply_median_filter(self, kernel_size): + img = IF.applyMedianFilter(self._model.input_image, kernel_size) + self._model.image = Utilities.ensure_three_channel_grayscale_image(img) + + def apply_filter_sobelX(self): + kernel = IF.createSobelXKernel() + img = IF.applyKernelInSpatialDomain(self._model.input_image, kernel) + self._model.image = Utilities.ensure_three_channel_grayscale_image(img) + + def apply_filter_sobelY(self): + kernel = IF.createSobelYKernel() + img = IF.applyKernelInSpatialDomain(self._model.input_image, kernel) + self._model.image = Utilities.ensure_three_channel_grayscale_image(img) + + def run_runtime_evaluation(self): + IF.run_runtime_evaluation(self._model.input_image) diff --git a/src/loggers.py b/src/loggers.py new file mode 100755 index 0000000..642e8c4 --- /dev/null +++ b/src/loggers.py @@ -0,0 +1,42 @@ +import logging +from PyQt6.QtCore import pyqtSignal, QObject +import html + + +class LogEmitter(QObject, logging.Handler): + """A custom logging handler that emits a signal with the log message.""" + + messageEmitted = pyqtSignal(str) + + def __init__(self): + super().__init__() + + # Define color codes + self.color_codes = { + logging.DEBUG: "gray", + logging.INFO: "black", + logging.WARNING: "orange", + logging.ERROR: "red", + logging.CRITICAL: "darkred", + } + + self.level_names = { + logging.DEBUG: "DEBUG", + logging.INFO: "INFO", + logging.WARNING: "WARNING", + logging.ERROR: "ERROR", + logging.CRITICAL: "CRITICAL", + } + + def emit(self, record): + msg = self.format(record) + + # Get color code and level name based on log level + level_color = self.color_codes.get(record.levelno, "black") + level_name = self.level_names.get(record.levelno, "UNKNOWN") + + # Create HTML string with color code and level name + html_msg = f'{level_name}: {html.escape(msg)}' + + # Emit signal with HTML message + self.messageEmitted.emit(html_msg) diff --git a/src/models.py b/src/models.py new file mode 100755 index 0000000..3b30574 --- /dev/null +++ b/src/models.py @@ -0,0 +1,61 @@ +import numpy as np +import Utilities +import cv2 + +from PyQt6.QtCore import QObject, pyqtSignal + + +class ImageModel(QObject): + image_changed = pyqtSignal(np.ndarray) + input_image_changed = pyqtSignal(np.ndarray) + + _image = None # output image + _input_image = None # input image (backup) + + def __init__(self): + super().__init__() + self.initialize() + + @property + def image(self): + return self._image + + @property + def input_image(self): + return self._input_image + + @image.setter + def image(self, img): + self._image = img.copy() + print("M: image changed!") + # update in model is reflected in view by sending a signal to view + self.image_changed.emit(img) + + @input_image.setter + def input_image(self, img): + self._input_image = img.copy() + self.image = img + self.input_image_changed.emit(img) + + def initialize(self): + image = np.zeros((256, 256, 3), np.uint8) + image[:, 0 : 256 // 2] = (255, 0, 0) + image[:, 256 // 2 : 256] = (0, 0, 255) + self.input_image = image + + class ColorAnalysis: + + _clusters = None + _image = None + _colors = None + _labels = None + + def __init__(self, img, clusters=3): + self.CLUSTERS = clusters + self.IMAGE = Utilities.resize_image(img, 100) + cv2.imshow("Input", self.IMAGE) + + def load_rgb_image(self, path): + image = cv2.imread(path, 1) + # image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + self.image = image diff --git a/src/views.py b/src/views.py new file mode 100755 index 0000000..1e37e4b --- /dev/null +++ b/src/views.py @@ -0,0 +1,383 @@ +from PyQt6 import QtGui +from PyQt6.QtCore import Qt, QRegularExpression +from PyQt6.QtWidgets import QMainWindow +from PyQt6.QtGui import QPixmap, QRegularExpressionValidator + + +from PyQt6.QtCore import pyqtSlot + +from PyQt6.QtWidgets import QFileDialog + + +from Playground_UI import Ui_MainWindow + + +class MainView(QMainWindow): + def __init__(self, model, main_controller): + super().__init__() + self._model = model + self._main_controller = main_controller + self._ui = Ui_MainWindow() + self._ui.setupUi(self) + self.update_image() + self.update_input_image() + + self._ui.widget_histogram.controller = self._main_controller + + #################### + # Input validators + #################### + # Create a regular expression that matches integer values between 1 and infinity + regex = QRegularExpression("[1-9][0-9]*") + # Create a validator based on the regular expression + validator = QRegularExpressionValidator(regex) + # Set the validator for the line edit widget + self._ui.lineEdit_image_height.setValidator(validator) + self._ui.lineEdit_image_width.setValidator(validator) + + #################################################################### + # connect widgets to controllers + #################################################################### + # open file buttons + # self._ui.pushButton.clicked.connect(self._main_controller.test_function) + self._ui.actionBild_laden.triggered.connect(self.on_open_image_from_filesystem) + self._ui.actionBild_speichern.triggered.connect( + self.on_save_image_to_filesystem + ) + self._ui.action_save_histogram.triggered.connect( + self.on_save_histogram_to_filesystem + ) + self._ui.horizontalSlider_color_clusters.sliderReleased.connect( + self.on_color_cluster_slider_changed + ) + self._ui.pushButton_hist_stretch.clicked.connect( + self.on_hist_stretch_button_clicked + ) + self._ui.pushButton_hist_equalization.clicked.connect( + self.on_hist_equal_button_clicked + ) + self._ui.pushButton_AWS_Labeling.clicked.connect( + self.on_AWS_Rekognition_button_clicked + ) + self._ui.pushButton_adjust_image_size.clicked.connect( + self.on_resize_button_clicked + ) + + self._ui.pushButton_reset_output_image.clicked.connect( + self.on_reset_output_image_button_clicked + ) + self._ui.pushButton_overwrite_input_image.clicked.connect( + self.on_overwrite_input_image_button_clicked + ) + self._ui.lineEdit_image_height.editingFinished.connect( + self.on_new_image_height_requested + ) + self._ui.lineEdit_image_width.editingFinished.connect( + self.on_new_image_width_requested + ) + + ######### + # Buttons für Übung + ######### + self._ui.pushButton_show_channel1.clicked.connect( + self.on_channel_1_button_clicked + ) + self._ui.pushButton_show_channel2.clicked.connect( + self.on_channel_2_button_clicked + ) + self._ui.pushButton_show_channel3.clicked.connect( + self.on_channel_3_button_clicked + ) + self._ui.pushButton_do_image_manipulation.clicked.connect( + self.on_do_first_image_manipulation_button_clicked + ) + + self._ui.pushButton_hist_log.clicked.connect( + self.on_apply_log_on_hist_button_clicked + ) + self._ui.pushButton_hist_exp.clicked.connect( + self.on_apply_exp_on_hist_button_clicked + ) + self._ui.pushButton_hist_inv.clicked.connect( + self.on_apply_inverse_on_hist_button_clicked + ) + self._ui.horizontalSlider_hist_threshold.sliderReleased.connect( + self.on_apply_threshold_on_hist_button_clicked + ) + + self._ui.pushButton_filter_sobelX.clicked.connect( + self.on_filter_sobelX_button_clicked + ) + self._ui.pushButton_filter_sobelY.clicked.connect( + self.on_filter_sobelY_button_clicked + ) + self._ui.pushButton_filter_gauss.clicked.connect( + self.on_filter_gauss_button_clicked + ) + self._ui.pushButton_filter_movAvg.clicked.connect( + self.on_filter_moving_avg_button_clicked + ) + self._ui.pushButton_filter_movAvg_int.clicked.connect( + self.on_filter_moving_avg_integral_button_clicked + ) + self._ui.pushButton_filter_median.clicked.connect( + self.on_filter_median_button_clicked + ) + self._ui.pushButton_filter_evaluation.clicked.connect( + self.on_runtime_evaluation_button_clicked + ) + + #################################################################### + # listen for model event signals + #################################################################### + # file name is updated + self._model.image_changed.connect(self.on_image_changed) + self._model.input_image_changed.connect(self.on_input_image_changed) + + ################### + # Connect Logging + ################### + + self._main_controller.log_handler.messageEmitted.connect(self.add_log_message) + + def show(self): + super().show() + self.on_input_image_changed() + + @pyqtSlot(str) + def add_log_message(self, msg): + """Add a log message to the QTextBrowser widget.""" + self._ui.text_output.append(msg) + + def resizeEvent(self, a0): + self.on_input_image_changed() + QMainWindow.resizeEvent(self, a0) + + def on_open_image_from_filesystem(self): + fname = QFileDialog.getOpenFileName( + self, "Open file", "../", "Image Files (*.png *.jpg *.bmp)" + ) + self._main_controller.loadImage(fname[0]) + print(fname[0]) + + def on_save_image_to_filesystem(self): + fname, _ = QFileDialog.getSaveFileName( + self, "Save file", "../", "Image Files (*.png *.jpg *.bmp)" + ) + if fname: + self._main_controller.saveImage(fname) + + def on_save_histogram_to_filesystem(self): + fname, _ = QFileDialog.getSaveFileName( + self, "Save file", "../", "Image Files (*.png *.jpg *.bmp)" + ) + if fname: + self._ui.widget_histogram.save_histogram(fname) + + def on_image_changed(self): + self.update_image() + self.update_histogram() + self.update_image_information() + + def on_input_image_changed(self): + self.update_input_image() + self.on_image_changed() + + def on_image_mouse_pressed(self): + self._main_controller.logger.logger.critical("Mouse pressed") + + def on_overwrite_input_image_button_clicked(self): + self._main_controller.set_image_as_input_image() + + def on_reset_output_image_button_clicked(self): + self._main_controller.reset_output_image() + + def on_new_image_height_requested(self): + if self._ui.checkBox_fix_image_size.isChecked(): + image_size = self._main_controller.get_image_information() + aspect_ratio = image_size[0] / image_size[1] + # dim = (height, int(height * aspect_ratio)) + self._ui.lineEdit_image_width.setText( + str(int(int(self._ui.lineEdit_image_height.text()) / aspect_ratio)) + ) + pass + + def on_new_image_width_requested(self): + if self._ui.checkBox_fix_image_size.isChecked(): + image_size = self._main_controller.get_image_information() + aspect_ratio = image_size[0] / image_size[1] + # dim = (height, int(height * aspect_ratio)) + self._ui.lineEdit_image_height.setText( + str(int(int(self._ui.lineEdit_image_width.text()) * aspect_ratio)) + ) + pass + + def on_color_cluster_slider_changed(self): + dominant_colors = self._main_controller.analyseColors( + self._ui.horizontalSlider_color_clusters.sliderPosition() + ) + self.update_color_cluster_output(dominant_colors) + print(self._ui.horizontalSlider_color_clusters.sliderPosition()) + + def on_hist_stretch_button_clicked(self): + self._main_controller.stretch_image() + self.on_image_changed() + + def on_hist_equal_button_clicked(self): + self._main_controller.equalize_image() + self.on_image_changed() + + def on_AWS_Rekognition_button_clicked(self): + self._main_controller.label_image() + + def update_color_cluster_output(self, dominant_colors): + size = self._ui.label_image_color_analysis_output.size() + visual_output = self._main_controller.create_visual_output( + dominant_colors, size.width(), size.height() + ) + qt_img = convert_cv_qt(visual_output, size.width(), size.height()) + self._ui.label_image_color_analysis_output.setPixmap(qt_img) + + def on_resize_button_clicked(self): + self._main_controller.resize_image( + int(self._ui.lineEdit_image_width.text()), + int(self._ui.lineEdit_image_height.text()), + ) + # self.on_image_changed() + + def update_image(self): + frame = self._model.image + size = self._ui.label_output_image.size() + # qt_img = convert_cv_qt(frame, size.width(), size.height()) + qt_img = convert_cv2scaledqt(frame, size.width(), size.height()) + # self._ui.label_output_image.loadImage(qt_img) + self._ui.label_output_image.setPixmap(qt_img) + + def update_input_image(self): + frame = self._model.input_image + size = self._ui.label_output_image.size() + qt_img = convert_cv2scaledqt(frame, size.width(), size.height()) + # qt_img = convert_cv_qt(frame, size.width(), size.height()) + self._ui.label_input_image.setPixmap(qt_img) + + def update_histogram(self): + self._ui.widget_histogram.drawHistogram(self._model.image) + + def update_image_information(self): + image_size = self._main_controller.get_image_information() + self._ui.label_height_image.setText(str(image_size[0])) + self._ui.label_width_image.setText(str(image_size[1])) + + self._ui.lineEdit_image_height.setText(str(image_size[0])) + self._ui.lineEdit_image_width.setText(str(image_size[1])) + + pixel_colors = self._main_controller.get_pixel_information() + self._ui.label_color_pixel1.setText(str(pixel_colors[0])) + self._ui.label_color_pixel2.setText(str(pixel_colors[1])) + + ##################### + # Übung 1 + ##################### + + def on_channel_1_button_clicked(self): + self._main_controller.show_channel(0) + self.on_image_changed() + + def on_channel_2_button_clicked(self): + self._main_controller.show_channel(1) + self.on_image_changed() + + def on_channel_3_button_clicked(self): + self._main_controller.show_channel(2) + self.on_image_changed() + + def on_do_first_image_manipulation_button_clicked(self): + self._main_controller.do_first_image_manipulation() + self.on_image_changed() + + ##################### + # Übung 2 + ##################### + + def on_apply_log_on_hist_button_clicked(self): + self._main_controller.apply_log() + self.on_image_changed() + + def on_apply_exp_on_hist_button_clicked(self): + self._main_controller.apply_exp() + self.on_image_changed() + + def on_apply_inverse_on_hist_button_clicked(self): + self._main_controller.apply_inv() + self.on_image_changed() + + def on_apply_threshold_on_hist_button_clicked(self): + self._main_controller.apply_threshold( + self._ui.horizontalSlider_hist_threshold.sliderPosition() + ) + self.on_image_changed() + + ##################### + # Übung 3 + ##################### + + def on_filter_sobelX_button_clicked(self): + self._main_controller.apply_filter_sobelX() + self.on_image_changed() + + def on_filter_sobelY_button_clicked(self): + self._main_controller.apply_filter_sobelY() + self.on_image_changed() + + def on_filter_gauss_button_clicked(self): + self._main_controller.apply_gaussian_filter( + self._ui.spinBox_filter_avg_size.value() + ) + self.on_image_changed() + + def on_filter_moving_avg_button_clicked(self): + self._main_controller.apply_moving_avg_filter( + self._ui.spinBox_filter_avg_size.value() + ) + self.on_image_changed() + + def on_filter_moving_avg_integral_button_clicked(self): + self._main_controller.apply_moving_avg_filter_integral( + self._ui.spinBox_filter_avg_size.value() + ) + self.on_image_changed() + + def on_filter_median_button_clicked(self): + self._main_controller.apply_median_filter( + self._ui.spinBox_filter_avg_size.value() + ) + self.on_image_changed() + + def on_runtime_evaluation_button_clicked(self): + self._main_controller.run_runtime_evaluation() + + +def convert_cv_qt(cv_img, display_width, display_height): + """Convert from an opencv image to QPixmap""" + h, w, ch = cv_img.shape + bytes_per_line = ch * w + convert_to_Qt_format = QtGui.QImage( + cv_img.data, w, h, bytes_per_line, QtGui.QImage.Format.Format_RGB888 + ) + convert_to_Qt_format.scaled( + display_width, display_height, Qt.AspectRatioMode.KeepAspectRatio + ) + return QPixmap.fromImage(convert_to_Qt_format) + + +def convert_cv2scaledqt(cv_img, display_width, display_height): + """Convert from an opencv image to QPixmap""" + h, w, ch = cv_img.shape + bytes_per_line = ch * w + convert_to_Qt_format = QtGui.QImage( + cv_img.data, w, h, bytes_per_line, QtGui.QImage.Format.Format_RGB888 + ) + p = convert_to_Qt_format.scaled( + display_width, display_height, Qt.AspectRatioMode.KeepAspectRatio + ) + return QPixmap.fromImage(p)