diff --git a/Pipfile b/Pipfile index 50c6ea5..cad4778 100644 --- a/Pipfile +++ b/Pipfile @@ -9,6 +9,7 @@ omegaconf = "*" numpy = "*" tk = "*" python-rospkg = "*" +Pillow = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index a32af54..b94ca4a 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "de0fdc3b461c618fe29b332e6a8a8bc79f8fdddd34a8794fbb05693489399e01" + "sha256": "7f8d6aec1f0e34cf3689d4c0572c47a2dbbed8521d01a028a25baea455be60ed" }, "pipfile-spec": 6, "requires": { @@ -18,98 +18,151 @@ "default": { "antlr4-python3-runtime": { "hashes": [ - "sha256:15793f5d0512a372b4e7d2284058ad32ce7dd27126b105fb0b2245130445db33" + "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b" ], - "version": "==4.8" - }, - "dataclasses": { - "hashes": [ - "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf", - "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97" - ], - "markers": "python_version == '3.6'", - "version": "==0.8" + "version": "==4.9.3" }, "numpy": { "hashes": [ - "sha256:012426a41bc9ab63bb158635aecccc7610e3eff5d31d1eb43bc099debc979d94", - "sha256:06fab248a088e439402141ea04f0fffb203723148f6ee791e9c75b3e9e82f080", - "sha256:0eef32ca3132a48e43f6a0f5a82cb508f22ce5a3d6f67a8329c81c8e226d3f6e", - "sha256:1ded4fce9cfaaf24e7a0ab51b7a87be9038ea1ace7f34b841fe3b6894c721d1c", - "sha256:2e55195bc1c6b705bfd8ad6f288b38b11b1af32f3c8289d6c50d47f950c12e76", - "sha256:2ea52bd92ab9f768cc64a4c3ef8f4b2580a17af0a5436f6126b08efbd1838371", - "sha256:36674959eed6957e61f11c912f71e78857a8d0604171dfd9ce9ad5cbf41c511c", - "sha256:384ec0463d1c2671170901994aeb6dce126de0a95ccc3976c43b0038a37329c2", - "sha256:39b70c19ec771805081578cc936bbe95336798b7edf4732ed102e7a43ec5c07a", - "sha256:400580cbd3cff6ffa6293df2278c75aef2d58d8d93d3c5614cd67981dae68ceb", - "sha256:43d4c81d5ffdff6bae58d66a3cd7f54a7acd9a0e7b18d97abb255defc09e3140", - "sha256:50a4a0ad0111cc1b71fa32dedd05fa239f7fb5a43a40663269bb5dc7877cfd28", - "sha256:603aa0706be710eea8884af807b1b3bc9fb2e49b9f4da439e76000f3b3c6ff0f", - "sha256:6149a185cece5ee78d1d196938b2a8f9d09f5a5ebfbba66969302a778d5ddd1d", - "sha256:759e4095edc3c1b3ac031f34d9459fa781777a93ccc633a472a5468587a190ff", - "sha256:7fb43004bce0ca31d8f13a6eb5e943fa73371381e53f7074ed21a4cb786c32f8", - "sha256:811daee36a58dc79cf3d8bdd4a490e4277d0e4b7d103a001a4e73ddb48e7e6aa", - "sha256:8b5e972b43c8fc27d56550b4120fe6257fdc15f9301914380b27f74856299fea", - "sha256:99abf4f353c3d1a0c7a5f27699482c987cf663b1eac20db59b8c7b061eabd7fc", - "sha256:a0d53e51a6cb6f0d9082decb7a4cb6dfb33055308c4c44f53103c073f649af73", - "sha256:a12ff4c8ddfee61f90a1633a4c4afd3f7bcb32b11c52026c92a12e1325922d0d", - "sha256:a4646724fba402aa7504cd48b4b50e783296b5e10a524c7a6da62e4a8ac9698d", - "sha256:a76f502430dd98d7546e1ea2250a7360c065a5fdea52b2dffe8ae7180909b6f4", - "sha256:a9d17f2be3b427fbb2bce61e596cf555d6f8a56c222bd2ca148baeeb5e5c783c", - "sha256:ab83f24d5c52d60dbc8cd0528759532736b56db58adaa7b5f1f76ad551416a1e", - "sha256:aeb9ed923be74e659984e321f609b9ba54a48354bfd168d21a2b072ed1e833ea", - "sha256:c843b3f50d1ab7361ca4f0b3639bf691569493a56808a0b0c54a051d260b7dbd", - "sha256:cae865b1cae1ec2663d8ea56ef6ff185bad091a5e33ebbadd98de2cfa3fa668f", - "sha256:cc6bd4fd593cb261332568485e20a0712883cf631f6f5e8e86a52caa8b2b50ff", - "sha256:cf2402002d3d9f91c8b01e66fbb436a4ed01c6498fffed0e4c7566da1d40ee1e", - "sha256:d051ec1c64b85ecc69531e1137bb9751c6830772ee5c1c426dbcfe98ef5788d7", - "sha256:d6631f2e867676b13026e2846180e2c13c1e11289d67da08d71cacb2cd93d4aa", - "sha256:dbd18bcf4889b720ba13a27ec2f2aac1981bd41203b3a3b27ba7a33f88ae4827", - "sha256:df609c82f18c5b9f6cb97271f03315ff0dbe481a2a02e56aeb1b1a985ce38e60" + "sha256:003a9f530e880cb2cd177cba1af7220b9aa42def9c4afc2a2fc3ee6be7eb2b22", + "sha256:150947adbdfeceec4e5926d956a06865c1c690f2fd902efede4ca6fe2e657c3f", + "sha256:2620e8592136e073bd12ee4536149380695fbe9ebeae845b81237f986479ffc9", + "sha256:2eabd64ddb96a1239791da78fa5f4e1693ae2dadc82a76bc76a14cbb2b966e96", + "sha256:4173bde9fa2a005c2c6e2ea8ac1618e2ed2c1c6ec8a7657237854d42094123a0", + "sha256:4199e7cfc307a778f72d293372736223e39ec9ac096ff0a2e64853b866a8e18a", + "sha256:4cecaed30dc14123020f77b03601559fff3e6cd0c048f8b5289f4eeabb0eb281", + "sha256:557d42778a6869c2162deb40ad82612645e21d79e11c1dc62c6e82a2220ffb04", + "sha256:63e45511ee4d9d976637d11e6c9864eae50e12dc9598f531c035265991910468", + "sha256:6524630f71631be2dabe0c541e7675db82651eb998496bbe16bc4f77f0772253", + "sha256:76807b4063f0002c8532cfeac47a3068a69561e9c8715efdad3c642eb27c0756", + "sha256:7de8fdde0003f4294655aa5d5f0a89c26b9f22c0a58790c38fae1ed392d44a5a", + "sha256:889b2cc88b837d86eda1b17008ebeb679d82875022200c6e8e4ce6cf549b7acb", + "sha256:92011118955724465fb6853def593cf397b4a1367495e0b59a7e69d40c4eb71d", + "sha256:97cf27e51fa078078c649a51d7ade3c92d9e709ba2bfb97493007103c741f1d0", + "sha256:9a23f8440561a633204a67fb44617ce2a299beecf3295f0d13c495518908e910", + "sha256:a51725a815a6188c662fb66fb32077709a9ca38053f0274640293a14fdd22978", + "sha256:a77d3e1163a7770164404607b7ba3967fb49b24782a6ef85d9b5f54126cc39e5", + "sha256:adbdce121896fd3a17a77ab0b0b5eedf05a9834a18699db6829a64e1dfccca7f", + "sha256:c29e6bd0ec49a44d7690ecb623a8eac5ab8a923bce0bea6293953992edf3a76a", + "sha256:c72a6b2f4af1adfe193f7beb91ddf708ff867a3f977ef2ec53c0ffb8283ab9f5", + "sha256:d0a2db9d20117bf523dde15858398e7c0858aadca7c0f088ac0d6edd360e9ad2", + "sha256:e3ab5d32784e843fc0dd3ab6dcafc67ef806e6b6828dc6af2f689be0eb4d781d", + "sha256:e428c4fbfa085f947b536706a2fc349245d7baa8334f0c5723c56a10595f9b95", + "sha256:e8d2859428712785e8a8b7d2b3ef0a1d1565892367b32f915c4a4df44d0e64f5", + "sha256:eef70b4fc1e872ebddc38cddacc87c19a3709c0e3e5d20bf3954c147b1dd941d", + "sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780", + "sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa" ], "index": "pypi", - "version": "==1.19.5" + "version": "==1.24.2" }, "omegaconf": { "hashes": [ - "sha256:be93d73eaa2564fbe52d88ee13e3b79f4c6e04876b2f326551a21391f7dc6367", - "sha256:c65e05530369484e074a24038fe31812c73561aa9d916abfd1209e4073136ae5" + "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b", + "sha256:d5d4b6d29955cc50ad50c46dc269bcd92c6e00f5f90d23ab5fee7bfca4ba4cc7" ], "index": "pypi", - "version": "==2.1.1" + "version": "==2.3.0" }, "opencv-python": { "hashes": [ - "sha256:05c5139d620e8d02f7ce0921796d55736fa19fa15e2ec00a388db2eb1ae1e9a1", - "sha256:085232718f28bddd265da480874c37db5c7354cb08f23f4a68a8639b16276a89", - "sha256:18a4a14015eee30d9cd514db8cdefbf594b1d5c234762d27abe512d62a333bc3", - "sha256:205a73adb29c37e42475645519e612e843a985475da993d10b4d5daa6afec36a", - "sha256:3c001d3feec7f3140f1fb78dfc52ca28122db8240826882d175a208a89d2731b", - "sha256:437f30e300725e1d1b3744dbfbc66a523a4744792b58f3dbe1e9140c8f4dfba5", - "sha256:5366fcd6eae4243add3c8c92142045850f1db8e464bcf0b75313e1596b2e3671", - "sha256:54c64e86a087841869901fd34462bb6bec01cd4652800fdf5d92fe7b0596c82f", - "sha256:6763729fcfee2a08e069aa1982c9a8c1abf55b9cdf2fb9640eda1d85bdece19a", - "sha256:68813b720b88e4951e84399b9a8a7b532d45a07a96ea8f539636242f862e32e0", - "sha256:7f41b97d84ac66bdf13cb4d9f4dad3e159525ba1e3f421e670c787ce536eb70a", - "sha256:831b92fe63ce18dd628f71104da7e60596658b75e2fa16b83aefa3eb10c115e2", - "sha256:881f3d85269500e0c7d72b140a6ebb5c14a089f8140fb9da7ce01f12a245858e", - "sha256:8852be06c0749fef0d9c58f532bbcb0570968c59e41cf56b90f5c92593c6e108", - "sha256:8b5bc61be7fc8565140b746288b370a4bfdb4edb9d680b66bb914e7690485db1", - "sha256:8d3282138f3a8646941089aae142684910ebe40776266448eab5f4bb609fc63f", - "sha256:9a78558b5ae848386edbb843c761e5fed5a8480be9af16274a5a78838529edeb", - "sha256:b42bbba9f5421865377c7960bd4f3dd881003b322a6bf46ed2302b89224d102b", - "sha256:c360cb76ad1ddbd5d2d3e730b42f2ff6e4be08ea6f4a6eefacca175d27467e8f", - "sha256:cdc3363c2911d7cfc6c9f55308c51c2841a7aecbf0bf5e791499d220ce89d880", - "sha256:e1f54736272830a1e895cedf7a4ee67737e31e966d380c82a81ef22515d043a3", - "sha256:e42c644a70d5c54f53a4b114dbd88b4eb83f42a9ca998f07bd5682f3f404efcc", - "sha256:f1bda4d144f5204e077ca4571453ebb2015e5748d5e0043386c92c2bbf7f52eb", - "sha256:f3ac2355217114a683f3f72a9c40a5890914a59c4a2df62e4083c66ff65c9cf9" + "sha256:3424794a711f33284581f3c1e4b071cfc827d02b99d6fd9a35391f517c453306", + "sha256:7a297e7651e22eb17c265ddbbc80e2ba2a8ff4f4a1696a67c45e5f5798245842", + "sha256:812af57553ec1c6709060c63f6b7e9ad07ddc0f592f3ccc6d00c71e0fe0e6376", + "sha256:cd08343654c6b88c5a8c25bf425f8025aed2e3189b4d7306b5861d32affaf737", + "sha256:d4f8880440c433a0025d78804dda6901d1e8e541a561dda66892d90290aef881", + "sha256:ebfc0a3a2f57716e709028b992e4de7fd8752105d7a768531c4f434043c6f9ff", + "sha256:eda115797b114fc16ca6f182b91c5d984f0015c19bec3145e55d33d708e9bae1" + ], + "index": "pypi", + "version": "==4.7.0.72" + }, + "pillow": { + "hashes": [ + "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33", + "sha256:0845adc64fe9886db00f5ab68c4a8cd933ab749a87747555cec1c95acea64b0b", + "sha256:0884ba7b515163a1a05440a138adeb722b8a6ae2c2b33aea93ea3118dd3a899e", + "sha256:09b89ddc95c248ee788328528e6a2996e09eaccddeeb82a5356e92645733be35", + "sha256:0dd4c681b82214b36273c18ca7ee87065a50e013112eea7d78c7a1b89a739153", + "sha256:0e51f608da093e5d9038c592b5b575cadc12fd748af1479b5e858045fff955a9", + "sha256:0f3269304c1a7ce82f1759c12ce731ef9b6e95b6df829dccd9fe42912cc48569", + "sha256:16a8df99701f9095bea8a6c4b3197da105df6f74e6176c5b410bc2df2fd29a57", + "sha256:19005a8e58b7c1796bc0167862b1f54a64d3b44ee5d48152b06bb861458bc0f8", + "sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1", + "sha256:28676836c7796805914b76b1837a40f76827ee0d5398f72f7dcc634bae7c6264", + "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157", + "sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9", + "sha256:3fa1284762aacca6dc97474ee9c16f83990b8eeb6697f2ba17140d54b453e133", + "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9", + "sha256:451f10ef963918e65b8869e17d67db5e2f4ab40e716ee6ce7129b0cde2876eab", + "sha256:46c259e87199041583658457372a183636ae8cd56dbf3f0755e0f376a7f9d0e6", + "sha256:46f39cab8bbf4a384ba7cb0bc8bae7b7062b6a11cfac1ca4bc144dea90d4a9f5", + "sha256:519e14e2c49fcf7616d6d2cfc5c70adae95682ae20f0395e9280db85e8d6c4df", + "sha256:53dcb50fbdc3fb2c55431a9b30caeb2f7027fcd2aeb501459464f0214200a503", + "sha256:54614444887e0d3043557d9dbc697dbb16cfb5a35d672b7a0fcc1ed0cf1c600b", + "sha256:575d8912dca808edd9acd6f7795199332696d3469665ef26163cd090fa1f8bfa", + "sha256:5dd5a9c3091a0f414a963d427f920368e2b6a4c2f7527fdd82cde8ef0bc7a327", + "sha256:5f532a2ad4d174eb73494e7397988e22bf427f91acc8e6ebf5bb10597b49c493", + "sha256:60e7da3a3ad1812c128750fc1bc14a7ceeb8d29f77e0a2356a8fb2aa8925287d", + "sha256:653d7fb2df65efefbcbf81ef5fe5e5be931f1ee4332c2893ca638c9b11a409c4", + "sha256:6663977496d616b618b6cfa43ec86e479ee62b942e1da76a2c3daa1c75933ef4", + "sha256:6abfb51a82e919e3933eb137e17c4ae9c0475a25508ea88993bb59faf82f3b35", + "sha256:6c6b1389ed66cdd174d040105123a5a1bc91d0aa7059c7261d20e583b6d8cbd2", + "sha256:6d9dfb9959a3b0039ee06c1a1a90dc23bac3b430842dcb97908ddde05870601c", + "sha256:765cb54c0b8724a7c12c55146ae4647e0274a839fb6de7bcba841e04298e1011", + "sha256:7a21222644ab69ddd9967cfe6f2bb420b460dae4289c9d40ff9a4896e7c35c9a", + "sha256:7ac7594397698f77bce84382929747130765f66406dc2cd8b4ab4da68ade4c6e", + "sha256:7cfc287da09f9d2a7ec146ee4d72d6ea1342e770d975e49a8621bf54eaa8f30f", + "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848", + "sha256:847b114580c5cc9ebaf216dd8c8dbc6b00a3b7ab0131e173d7120e6deade1f57", + "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f", + "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c", + "sha256:8f127e7b028900421cad64f51f75c051b628db17fb00e099eb148761eed598c9", + "sha256:94cdff45173b1919350601f82d61365e792895e3c3a3443cf99819e6fbf717a5", + "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9", + "sha256:9a3049a10261d7f2b6514d35bbb7a4dfc3ece4c4de14ef5876c4b7a23a0e566d", + "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0", + "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1", + "sha256:a1c2d7780448eb93fbcc3789bf3916aa5720d942e37945f4056680317f1cd23e", + "sha256:a2e0f87144fcbbe54297cae708c5e7f9da21a4646523456b00cc956bd4c65815", + "sha256:a4dfdae195335abb4e89cc9762b2edc524f3c6e80d647a9a81bf81e17e3fb6f0", + "sha256:a96e6e23f2b79433390273eaf8cc94fec9c6370842e577ab10dabdcc7ea0a66b", + "sha256:aabdab8ec1e7ca7f1434d042bf8b1e92056245fb179790dc97ed040361f16bfd", + "sha256:b222090c455d6d1a64e6b7bb5f4035c4dff479e22455c9eaa1bdd4c75b52c80c", + "sha256:b52ff4f4e002f828ea6483faf4c4e8deea8d743cf801b74910243c58acc6eda3", + "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab", + "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858", + "sha256:b9b752ab91e78234941e44abdecc07f1f0d8f51fb62941d32995b8161f68cfe5", + "sha256:ba6612b6548220ff5e9df85261bddc811a057b0b465a1226b39bfb8550616aee", + "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343", + "sha256:c3c4ed2ff6760e98d262e0cc9c9a7f7b8a9f61aa4d47c58835cdaf7b0b8811bb", + "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47", + "sha256:cb362e3b0976dc994857391b776ddaa8c13c28a16f80ac6522c23d5257156bed", + "sha256:d197df5489004db87d90b918033edbeee0bd6df3848a204bca3ff0a903bef837", + "sha256:d3b56206244dc8711f7e8b7d6cad4663917cd5b2d950799425076681e8766286", + "sha256:d5b2f8a31bd43e0f18172d8ac82347c8f37ef3e0b414431157718aa234991b28", + "sha256:d7081c084ceb58278dd3cf81f836bc818978c0ccc770cbbb202125ddabec6628", + "sha256:db74f5562c09953b2c5f8ec4b7dfd3f5421f31811e97d1dbc0a7c93d6e3a24df", + "sha256:df41112ccce5d47770a0c13651479fbcd8793f34232a2dd9faeccb75eb5d0d0d", + "sha256:e1339790c083c5a4de48f688b4841f18df839eb3c9584a770cbd818b33e26d5d", + "sha256:e621b0246192d3b9cb1dc62c78cfa4c6f6d2ddc0ec207d43c0dedecb914f152a", + "sha256:e8c5cf126889a4de385c02a2c3d3aba4b00f70234bfddae82a5eaa3ee6d5e3e6", + "sha256:e9d7747847c53a16a729b6ee5e737cf170f7a16611c143d95aa60a109a59c336", + "sha256:eaef5d2de3c7e9b21f1e762f289d17b726c2239a42b11e25446abf82b26ac132", + "sha256:ed3e4b4e1e6de75fdc16d3259098de7c6571b1a6cc863b1a49e7d3d53e036070", + "sha256:ef21af928e807f10bf4141cad4746eee692a0dd3ff56cfb25fce076ec3cc8abe", + "sha256:f09598b416ba39a8f489c124447b007fe865f786a89dbfa48bb5cf395693132a", + "sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd", + "sha256:f6e78171be3fb7941f9910ea15b4b14ec27725865a73c15277bc39f5ca4f8391", + "sha256:f715c32e774a60a337b2bb8ad9839b4abf75b267a0f18806f6f4f5f1688c4b5a", + "sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12" ], "index": "pypi", - "version": "==4.5.3.56" + "version": "==9.4.0" }, "pyyaml": { "hashes": [ + "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", @@ -121,26 +174,32 @@ "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", + "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", + "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", + "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", + "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", + "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", + "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" ], diff --git a/README.md b/README.md index 2f0adc0..30ca9f3 100644 --- a/README.md +++ b/README.md @@ -38,18 +38,17 @@ - example : `python create_dataset.py -v sim` or `python create_dataset.py -v crop -i 60 -o` `test_pf.py:` - -- runs performance tests on any of the algorithms against any video +- runs performance tests on algorithms against any video with an optional GUI - reports `framerate`, `time-to-finish`, `vanishing point uptime`, `avg time to process frame` - arguments : - `-a/--alg` : name of algorithm - - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `check_row_end` or `center_down` + - `hough`, `center_row`, `mini_contour`, `mini_contour_downward`, `scanning`, `seesaw`, `seesaw_v2`, or `check_row_end` - `-v/--vid` : name of video - video has to be stored in `./videos/` - video configuration has to be stored as a `yaml` at `./config/video/` - - `-s/--show` : process drawings and show frame -- example : `python test_pf.py -a center_row -v farm4` - + - `-s/--show` : creates a GUI that shows the frame, allow filter, toggle between views, adjust saturation, brightness, upper, and lower HSV +- example : `python test_pf.py -a seesaw -v crop -s` + ### Commands to Start World in Gazebo Run the following Commands the first time: @@ -120,4 +119,4 @@ image shows the resulting lines we detected. We are currently using these lines and calculating their intersection which occurs at vanishing point. Then we are using a PID controller to minimimize the distance of this vanishing point from the center of our frame. Using this method, we -are able to centre our chassis over the crop rows we are traversing. +are able to centre our chassis over the crop rows we are traversing. \ No newline at end of file diff --git a/algorithms/CenterRowAlgorithm.py b/algorithms/CenterRowAlgorithm.py index fd29486..3e6759f 100644 --- a/algorithms/CenterRowAlgorithm.py +++ b/algorithms/CenterRowAlgorithm.py @@ -2,8 +2,8 @@ import cv2 as cv import numpy as np -from algorithms.Algorithm import Algorithm +from algorithms.Algorithm import Algorithm from algorithms.utils import Lines @@ -18,6 +18,7 @@ def __init__(self, config): # masking range for green self.LOW_GREEN = np.array(config.lower_hsv_threshold) self.HIGH_GREEN = np.array(config.upper_hsv_threshold) + # print(self.LOW_GREEN, self.HIGH_GREEN) # filtering parameters self.averaging_kernel_size = config.averaging_kernel_size @@ -44,6 +45,17 @@ def __init__(self, config): self.center = None self.center_angle = 0 + def get_extra_content(self, frame, show): + maskf = self.create_binary_mask(frame) + item1, item2 = self.process_frame(frame, show) + return item1, item2, maskf + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) + def process_frame(self, frame, show): """Uses contouring to create contours around each crop row and uses these contours to find centroid lines, row vanishing point, a center contour and the angle between the center contour and vanishing point\n diff --git a/algorithms/CheckRowEnd.py b/algorithms/CheckRowEnd.py index d13a9ad..3f1588a 100644 --- a/algorithms/CheckRowEnd.py +++ b/algorithms/CheckRowEnd.py @@ -17,6 +17,17 @@ def __init__(self, config): # Percentage of empty rows required to register as row end self.percentage_of_empty_rows = config.percentage_of_empty_rows + def get_extra_content(self, frame, show): + maskf = self.create_binary_mask(frame) + item1, item2 = self.process_frame(frame, show) + return item1, item2, maskf + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) + def process_frame(self, frame, show): """Averages values in each row in a mask of the frame. If the number of rows with an average value of zero is greater than req_rows_empty, then frame is row end\n diff --git a/algorithms/HoughAlgorithm.py b/algorithms/HoughAlgorithm.py index 6239505..18d4223 100644 --- a/algorithms/HoughAlgorithm.py +++ b/algorithms/HoughAlgorithm.py @@ -32,6 +32,17 @@ def __init__(self, config): # resize factor self.RESIZE_FACTOR = config.resize_factor + def get_extra_content(self, frame, show): + maskf = self.create_mask(frame) + item1, item2 = self.process_frame(frame, show) + return item1, item2, maskf + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) + # processFrame function that is called to process a frame of a video # takes in frame mat object obtained from cv2 video.read() def process_frame(self, frame, show=True): diff --git a/algorithms/MiniContoursAlgorithm.py b/algorithms/MiniContoursAlgorithm.py index 77712e1..ebd021f 100644 --- a/algorithms/MiniContoursAlgorithm.py +++ b/algorithms/MiniContoursAlgorithm.py @@ -1,13 +1,16 @@ -import cv2 -import numpy as np -import time +import math import operator import sys -import math -from algorithms.utils import Lines +import time + +import cv2 +import numpy as np + from algorithms.Algorithm import Algorithm +from algorithms.utils import Lines +cmask = "" class MiniContoursAlgorithm(Algorithm): # applies hsv binarization to the image # slices the image into horizontal strips and finds all the contours in each strip @@ -106,6 +109,8 @@ def get_center_hough_lines( mask = cv2.inRange(cv2.cvtColor(frame, cv2.COLOR_BGR2HSV), self.low_green, self.high_green) mask = cv2.medianBlur(mask, 9) + global cmask + cmask = mask # mask = cv2.GaussianBlur(mask, (9,9), 10) # mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, self.morphology_kernel) centroids = self.get_centroids(mask, num_strips=num_strips) @@ -199,6 +204,18 @@ def get_center_hough_lines( return frame, lines, point_lines + def get_extra_content(self, frame, show): + global cmask + # maskf = self.create_binary_mask(frame) + item1, item2 = self.process_frame(frame, show) + return item1, item2, cmask + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) + def process_frame(self, original_frame, num_strips=60, show=False): # original_frame: BGR frame diff --git a/algorithms/MiniContoursDownwards.py b/algorithms/MiniContoursDownwards.py index 05a9278..4ededeb 100644 --- a/algorithms/MiniContoursDownwards.py +++ b/algorithms/MiniContoursDownwards.py @@ -6,6 +6,7 @@ import math from algorithms.utils import Lines +cmask = "" class MiniContoursDownwards(): @@ -144,6 +145,8 @@ def get_best_fit_line(self, frame, show, num_strips=60, dist_type=cv2.DIST_L2, p else: line = None + global cmask + cmask = c_mask if show: cv2.imshow('frame', frame) cv2.imshow('mask', mask) @@ -152,6 +155,18 @@ def get_best_fit_line(self, frame, show, num_strips=60, dist_type=cv2.DIST_L2, p return frame, line + def get_extra_content(self, frame, show): + global cmask + # maskf = self.create_binary_mask(frame) + item1, item2 = self.process_frame(frame, show) + return item1, item2, cmask + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) + def process_frame(self, original_frame, num_strips=60, show=False): """"" parameters: diff --git a/algorithms/ScanningAlgorithm.py b/algorithms/ScanningAlgorithm.py index 5522fb5..637e04a 100644 --- a/algorithms/ScanningAlgorithm.py +++ b/algorithms/ScanningAlgorithm.py @@ -74,6 +74,17 @@ def create_line(self, start_x, start_y, end_x, end_y): return line + def get_extra_content(self, frame, show): + item1, item2 = self.process_frame(frame, show) + maskf = frame + return item1, item2, maskf + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) + def process_frame(self, frame, show): hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) diff --git a/algorithms/SeesawAlgorithm.py b/algorithms/SeesawAlgorithm.py index c5f38e3..0ff6b94 100644 --- a/algorithms/SeesawAlgorithm.py +++ b/algorithms/SeesawAlgorithm.py @@ -23,6 +23,17 @@ def __init__(self, config): self.HEIGHT = int(config.frame_length) self.WIDTH = int(config.frame_width) + def get_extra_content(self, frame, show): + maskf = self.create_binary_mask(frame) + item1, item2 = self.process_frame(frame, show) + return item1, item2, maskf + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) + def process_frame(self, frame, show): black_frame, points, both_points = self.plot_points(frame) diff --git a/algorithms/SeesawAlgorithmVersionTwo.py b/algorithms/SeesawAlgorithmVersionTwo.py new file mode 100644 index 0000000..ff6d786 --- /dev/null +++ b/algorithms/SeesawAlgorithmVersionTwo.py @@ -0,0 +1,164 @@ +import math +import cv2 as cv +import numpy +import numpy as np +from algorithms.Algorithm import Algorithm +from helper_scripts.change_res import change_res + + +class SeesawAlgorithmVersionTwo(Algorithm): + + def __init__(self, config): + """ + Sets seesaw algorithm configurations + :param config: config params + """ + # masking range for green + self.LOW_GREEN = np.array(config.lower_hsv_threshold) + self.HIGH_GREEN = np.array(config.upper_hsv_threshold) + + # filtering parameters + self.averaging_kernel_size = config.averaging_kernel_size + self.gauss_kernel_size = list(map(int, config.gauss_kernel_size.split(','))) + self.dilate_kernel_size = config.dilate_kernel_size + self.sigma_x = config.sigma_x + + # dimensions + self.HEIGHT = int(config.frame_length) + self.WIDTH = int(config.frame_width) + + # visual parameters + self.BAR_HEIGHT = config.bar_height + self.RES_FACTOR = config.resolution_factor + + def get_extra_content(self, frame, show): + maskf = self.create_binary_mask(frame) + item1, item2 = self.process_frame(frame, show) + return item1, item2, maskf + + def update_lower_hsv(self, next): + self.LOW_GREEN = np.array(next) + + def update_upper_hsv(self, next): + self.HIGH_GREEN = np.array(next) + + def process_frame(self, frame, show): + """ + Divides screen into horizontal strips and find average location of green for each strip. + Draw the best fit line based the average location, + then calculate the angle between the best fit line and a horizontal line. + :param frame: current frame (mat) + :param show: show/hide frames on screen for debugging + :type show: bool + :return: processed frame (mat), angle [-90, 90] + """ + + frame = change_res(frame, self.RES_FACTOR) + black_frame, average_points, overall_bias = self.plot_points(frame) + average_points = np.array(average_points) + + if average_points.any(): + """get best fit line for centre points""" + [vx, vy, x, y] = cv.fitLine(average_points, cv.DIST_L2, 0, 0.01, 0.01) + x1 = int(x - vx * self.WIDTH) + x2 = int(x + vx * self.WIDTH) + y1 = int(y - vy * self.HEIGHT) + y2 = int(y + vy * self.HEIGHT) + black_frame = cv.line(black_frame, (x1, y1), (x2, y2), (0, 255, 255), 9) + + # calculate angle + if y1 - y2 != 0: + angle = round(math.degrees(math.atan(int(x2 - x1) / int(y1 - y2))), 1) + bias = round(numpy.average(overall_bias)/(self.WIDTH/2)*90, 2) + + if abs(angle) > abs(bias): + output = angle + else: + output = bias + else: + output = None + else: + output = None + + black_frame = self.print_text(frame, str(output)) + + return black_frame, output + + def plot_points(self, frame): + """ + Divides screen into horizontal strips, and find average location of green for each strip. + :param frame: current frame (mat) + :return: processed frame (mat), list of centre points + """ + frame = cv.line(frame, (int(self.WIDTH/2), 0), (int(self.WIDTH/2), int(self.HEIGHT)), (255, 0, 255), 9) + bar_height = int(self.BAR_HEIGHT) + mask = self.create_binary_mask(frame) + + square_low = 0 + square_high = bar_height + + black_frame = frame + + centre_points = [] + overall_bias = [] + + while square_low < self.HEIGHT: + points = [] + + seg = mask[int(square_low) + 1:int(square_high), 0:self.WIDTH] + + condition = (seg == 255) + points = np.where(condition)[1] + + if points.any(): + centre = int(numpy.median(points)) + bias = int(centre - self.WIDTH/2) + black_frame = cv.circle(black_frame, [int(centre), int((square_high + square_low) / 2)], + radius=0, color=(0, 0, 255), thickness=15) + centre_points.append([centre, int((square_high + square_low) / 2)]) + overall_bias.append(bias) + + square_high += bar_height + square_low += bar_height + + return frame, centre_points, overall_bias + + def print_text(self, frame, text): + + # Define the font and text size + font = cv.FONT_HERSHEY_SIMPLEX + font_scale = 1 + + # Define the color and thickness of the text + color = (255, 255, 255) # in BGR format + thickness = 2 + + # Get the size of the text box + text_size, _ = cv.getTextSize(text, font, font_scale, thickness) + + # Calculate the position of the text box + x = int((frame.shape[1] - text_size[0]) / 4) + y = int((frame.shape[0] + text_size[1]) / 4) + + # Draw the text on the image + cv.putText(frame, text, (x, y), font, font_scale, color, thickness) + + return frame + + def create_binary_mask(self, frame): + """ + :param frame: current frame + :return: binary mask of frame made using self.low_green and self.high_green as the HSV range + """ + + # Run averaging filter to blur the frame + kernel = np.ones((5, 5), np.float32) / 25 + frame = cv.filter2D(frame, -1, kernel) + + # Convert to hsv format to allow for easier colour filtering + hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV) + + # Filter image and allow only shades of green to pass + mask = cv.inRange(hsv, self.LOW_GREEN, self.HIGH_GREEN) + + return mask diff --git a/config/algorithm/seesaw_v2.yaml b/config/algorithm/seesaw_v2.yaml new file mode 100644 index 0000000..405c2d1 --- /dev/null +++ b/config/algorithm/seesaw_v2.yaml @@ -0,0 +1,18 @@ +#filtering parameters +averaging_kernel_size: (5,5) +gauss_kernel_size: 3,3 +dilate_kernel_size: (5,5) +sigma_x: 0 + +contour_color: 0,129,255 +bar_height: 30 +resolution_factor: 100 + +#### size of frame +frame_width: 1280 +frame_length: 720 + +#### hsv thresholds for crop video +lower_hsv_threshold: [35, 80, 80] +upper_hsv_threshold: [80, 255, 255] + diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..b7aa6a9 --- /dev/null +++ b/gui.py @@ -0,0 +1,411 @@ +import tkinter as tk # change to Tkinter for python2 + +import cv2 as cv +import numpy as np +# import sys +# sys.path.insert(1, '') +from PIL import Image, ImageTk + +import pre_process + +img_dict = {} +isActive = False + + +# scanning not working + no binary_mask +class GUI: + """Creates GUI with tkinter: initializes a master window, creates the same number of radiobuttons as the length of img_dict, + creates 2 sliders for brightness and saturation, then render images accordingly\n + :param `master` = tk.Tk() + :param `img_dict` = { "frameType1": numpy.ndarray } + :param `window_name` = String + """ + + def __init__(self, master, img_dict, window_name): + self.master = master + self.master.title(window_name) + self.master.geometry("600x700") + + self.current_avg_brightness = 110 + self.current_avg_saturation = 105 + self.fps = 0 + + self.var = tk.IntVar() + self.curr_selected = 1 + + self.container = tk.Frame(self.master) + self.container.pack(side='top', anchor='nw', fill="both", expand=True) + + self.canvas = tk.Canvas(self.container) + self.scrollbar = tk.Scrollbar( + self.container, orient="vertical", command=self.canvas.yview) + self.scrollable_frame = tk.Frame(self.canvas) + self.scrollable_frame.bind("", lambda e: self.canvas.configure( + scrollregion=self.canvas.bbox("all"))) + + self.img_container = tk.Label(self.scrollable_frame, padx=10, pady=10) + self.img_container.pack(side='top', fill="both", expand=True) + # self.img_container.config(width=600, height=400) + + self.canvas.create_window( + (0, 0), window=self.scrollable_frame, anchor='c') + self.canvas.configure(yscrollcommand=self.scrollbar.set) + + self.canvas.pack(side="left", fill="both", expand=True) + # self.canvas.config(width=600, height=300) + self.scrollbar.pack(side="right", fill="y") + + self.fps_label = tk.Label( + self.scrollable_frame, text="Frames Per Second: " + str(self.fps)) + self.fps_label.pack(pady=5, side="bottom") + + for i, (img_name, img_array) in enumerate(img_dict.items(), 1): + radiobutton = tk.Radiobutton( + self.scrollable_frame, text=img_name, variable=self.var, value=i, command=lambda i=i: self.update_frameType(i)) + radiobutton.pack(pady=5, side="bottom") + + self.brightness_scale = tk.Scale(self.scrollable_frame, from_=0, to=255, orient="horizontal", + label="Brightness", command=lambda x: self.update_brightness(x)) + self.brightness_scale.set(self.current_avg_brightness) + self.brightness_scale.pack(pady=5, side="bottom") + + self.saturation_scale = tk.Scale(self.scrollable_frame, from_=0, to=255, orient="horizontal", + label="Saturation", command=lambda x: self.update_saturation(x)) + self.saturation_scale.set(self.current_avg_saturation) + self.saturation_scale.pack(pady=5, side="bottom") + # + # Create a Frame to hold both the upper and lower frames + self.center_frame = tk.Frame(self.scrollable_frame) + self.center_frame.pack(in_=self.scrollable_frame, anchor="c", side="bottom") + + self.lower_frame = tk.Frame(self.center_frame) + self.lower_frame.pack(in_=self.center_frame, anchor="c", side="left", ipady=15) + self.upper_frame = tk.Frame(self.center_frame) + self.upper_frame.pack(in_=self.center_frame, anchor="c", side="right", ipadx=15) + + self.alert_for_hsv = tk.Label( + self.lower_frame, text="", fg="red") + self.alert_for_hsv.pack(side="top", anchor="c") + # + # + self.LOWER_GREEN = [35, 80, 80] + # self.low_h_entry_label = tk.Label(self.lower_frame, text="LOWER-H:") + # self.low_h_entry_label.pack(pady=5, side="left") + # self.low_h_entry = tk.Entry( + # self.lower_frame, width=5, justify="center", font="Courier 12") + # self.low_h_entry.insert(0, str(self.LOWER_GREEN[0])) + # self.low_h_entry.pack(pady=5, side="left") + # # + # self.low_s_entry_label = tk.Label(self.lower_frame, text="S:") + # self.low_s_entry_label.pack(pady=5, side="left") + # self.low_s_entry = tk.Entry( + # self.lower_frame, width=5, justify="center", font="Courier 12") + # self.low_s_entry.insert(0, str(self.LOWER_GREEN[1])) + # self.low_s_entry.pack(pady=5, side="left") + # # + # self.low_v_entry_label = tk.Label(self.lower_frame, text="V:") + # self.low_v_entry_label.pack(pady=5, side="left") + # self.low_v_entry = tk.Entry( + # self.lower_frame, width=5, justify="center", font="Courier 12") + # self.low_v_entry.insert(0, str(self.LOWER_GREEN[2])) + # self.low_v_entry.pack(pady=5, side="left") + # # + # self.update_btn_low = tk.Button( + # self.lower_frame, text="Update", command=self.update_lower_hsv) + # self.update_btn_low.pack(pady=0, side="left") + # + # + self.UPPER_GREEN = [80, 255, 255] + # self.up_h_entry_label = tk.Label(self.upper_frame, text="UPPER-H:") + # self.up_h_entry_label.pack(pady=5, side="left") + # self.up_h_entry = tk.Entry( + # self.upper_frame, width=5, justify="center", font="Courier 12") + # self.up_h_entry.insert(0, str(self.UPPER_GREEN[0])) + # self.up_h_entry.pack(pady=5, side="left") + # # + # self.up_s_entry_label = tk.Label(self.upper_frame, text="S:") + # self.up_s_entry_label.pack(pady=5, side="left") + # self.up_s_entry = tk.Entry( + # self.upper_frame, width=5, justify="center", font="Courier 12") + # self.up_s_entry.insert(0, str(self.UPPER_GREEN[1])) + # self.up_s_entry.pack(pady=5, side="left") + # # + # self.up_v_entry_label = tk.Label(self.upper_frame, text="V:") + # self.up_v_entry_label.pack(pady=5, side="left") + # self.up_v_entry = tk.Entry( + # self.upper_frame, width=5, justify="center", font="Courier 12") + # self.up_v_entry.insert(0, str(self.UPPER_GREEN[2])) + # self.up_v_entry.pack(pady=5, side="left") + # # + # self.update_btn_high = tk.Button( + # self.upper_frame, text="Update", command=self.update_upper_hsv) + # self.update_btn_high.pack(pady=0, side="left") + + # Create the three sliders for MIN Hue, Saturation, and Value + self.min_hue_slider = tk.Scale(self.lower_frame, from_=0, to=255, orient="horizontal", label="Min Hue", command=self.update_min_color_canvas) + self.min_hue_slider.set(self.LOWER_GREEN[0]) + self.min_hue_slider.pack() + + self.min_saturation_slider = tk.Scale(self.lower_frame, from_=0, to=255, orient="horizontal", label="Min Saturation", command=self.update_min_color_canvas) + self.min_saturation_slider.set(self.LOWER_GREEN[1]) + self.min_saturation_slider.pack() + + self.min_value_slider = tk.Scale(self.lower_frame, from_=0, to=255, orient="horizontal", label="Min Value", command=self.update_min_color_canvas) + self.min_value_slider.set(self.LOWER_GREEN[2]) + self.min_value_slider.pack() + + self.min_color_canvas = tk.Canvas(self.lower_frame, width=20, height=20, bg="#000000") + self.min_color_canvas.pack() + self.min_color_update_button = tk.Button(self.lower_frame, text="Update Min Color", command=self.update_min_color) + self.min_color_update_button.pack() + + # Create the three sliders for MAX Hue, Saturation, and Value + self.max_hue_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Max Hue", command=self.update_max_color_canvas) + self.max_hue_slider.set(self.UPPER_GREEN[0]) + self.max_hue_slider.pack() + + self.max_saturation_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Max Saturation", command=self.update_max_color_canvas) + self.max_saturation_slider.set(self.UPPER_GREEN[1]) + self.max_saturation_slider.pack() + + self.max_value_slider = tk.Scale(self.upper_frame, from_=0, to=255, orient="horizontal", label="Max Value", command=self.update_max_color_canvas) + self.max_value_slider.set(self.UPPER_GREEN[2]) + self.max_value_slider.pack() + + self.max_color_canvas = tk.Canvas(self.upper_frame, width=20, height=20, bg="#000000") + self.max_color_canvas.pack() + self.max_color_update_button = tk.Button(self.upper_frame, text="Update Max Color", command=self.update_max_color) + self.max_color_update_button.pack() + # + # + # + self.render_image() + + def update_min_color_canvas(self, event=None): + r, g, b = self.min_hue_slider.get(), self.min_saturation_slider.get(), self.min_value_slider.get() + color = f"#{r:02x}{g:02x}{b:02x}" + self.min_color_canvas.config(bg=color) + + def update_max_color_canvas(self, event=None): + r, g, b = self.max_hue_slider.get(), self.max_saturation_slider.get(), self.max_value_slider.get() + color = f"#{r:02x}{g:02x}{b:02x}" + self.max_color_canvas.config(bg=color) + + def update_min_color(self): + lower_h = self.min_hue_slider.get() + lower_s = self.min_saturation_slider.get() + lower_v = self.min_value_slider.get() + if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2] and lower_h >= 0 and lower_s >= 0 and lower_v >= 0: + self.LOWER_GREEN = [lower_h, lower_s, lower_v] + self.alert_for_hsv.config( + text="new LOWER-HSV: " + str(self.LOWER_GREEN)) + else: + warning = "INVALID: " + if lower_h >= self.UPPER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif lower_s >= self.UPPER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif lower_v >= self.UPPER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "LOWER_V < 0\t" + + self.alert_for_hsv.config( + text=warning) + + def update_max_color(self): + upper_h = self.max_hue_slider.get() + upper_s = self.max_saturation_slider.get() + upper_v = self.max_value_slider.get() + if upper_h > self.LOWER_GREEN[0] and upper_s > self.LOWER_GREEN[1] and upper_v > self.LOWER_GREEN[2] and upper_h <= 255 and upper_s <= 255 and upper_v <= 255: + self.UPPER_GREEN = [upper_h, upper_s, upper_v] + self.alert_for_hsv.config( + text="new UPPER-HSV: " + str(self.UPPER_GREEN)) + else: + warning = "INVALID: " + if upper_h <= self.LOWER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif upper_s <= self.LOWER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif upper_v <= self.LOWER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "UPPER_V > 255\t" + + self.alert_for_hsv.config( + text=warning) + + + + def update_upper_hsv(self): + upper_h = int(self.up_h_entry.get()) + upper_s = int(self.up_s_entry.get()) + upper_v = int(self.up_v_entry.get()) + if upper_h > self.LOWER_GREEN[0] and upper_s > self.LOWER_GREEN[1] and upper_v > self.LOWER_GREEN[2] and upper_h <= 255 and upper_s <= 255 and upper_v <= 255: + self.UPPER_GREEN = (upper_h, upper_s, upper_v) + self.alert_for_hsv.config( + text="new UPPER-HSV: " + str(self.UPPER_GREEN)) + else: + warning = "INVALID: " + if upper_h <= self.LOWER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif upper_s <= self.LOWER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif upper_v <= self.LOWER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "UPPER_V > 255\t" + + self.alert_for_hsv.config( + text=warning) + + self.up_h_entry.delete(0, tk.END) + self.up_h_entry.insert( + 0, str(self.UPPER_GREEN[0])) + self.up_s_entry.delete(0, tk.END) + self.up_s_entry.insert( + 0, str(self.UPPER_GREEN[1])) + self.up_v_entry.delete(0, tk.END) + self.up_v_entry.insert( + 0, str(self.UPPER_GREEN[2])) + # print(self.UPPER_GREEN) + + def update_lower_hsv(self): + lower_h = int(self.low_h_entry.get()) + lower_s = int(self.low_s_entry.get()) + lower_v = int(self.low_v_entry.get()) + if lower_h < self.UPPER_GREEN[0] and lower_s < self.UPPER_GREEN[1] and lower_v < self.UPPER_GREEN[2] and lower_h >= 0 and lower_s >= 0 and lower_v >= 0: + self.LOWER_GREEN = (lower_h, lower_s, lower_v) + self.alert_for_hsv.config( + text="new LOWER-HSV: " + str(self.LOWER_GREEN)) + else: + warning = "INVALID: " + if lower_h >= self.UPPER_GREEN[0]: + warning += "UPPER_H <= LOWER_H\t" + elif lower_s >= self.UPPER_GREEN[1]: + warning += "UPPER_S <= LOWER_S\t" + elif lower_v >= self.UPPER_GREEN[2]: + warning += "UPPER_V <= LOWER_V\t" + else: + warning += "LOWER_V < 0\t" + + self.alert_for_hsv.config( + text=warning) + + self.low_h_entry.delete(0, tk.END) + self.low_h_entry.insert( + 0, str(self.LOWER_GREEN[0])) + self.low_s_entry.delete(0, tk.END) + self.low_s_entry.insert( + 0, str(self.LOWER_GREEN[1])) + self.low_v_entry.delete(0, tk.END) + self.low_v_entry.insert( + 0, str(self.LOWER_GREEN[2])) + # print(self.LOWER_GREEN) + + def getLowerHSV(self): + return self.LOWER_GREEN + + def getUpperHSV(self): + return self.UPPER_GREEN + + def update_fps(self, next): + self.fps = next + + def update_frameType(self, next): + self.curr_selected = next + + def update_brightness(self, value): + self.current_avg_brightness = int(value) + + def update_saturation(self, value): + self.current_avg_saturation = int(value) + + def update_dict(self, key_value): + global img_dict + img_dict.update(key_value) + + def apply_filter(self, img): + pre_process.ACCEPTABLE_DIFFERENCE = 0 + pre_process.BRIGHTNESS_BASELINE = self.current_avg_brightness + pre_process.SATURATION_BASELINE = self.current_avg_saturation + return pre_process.standardize_frame(img) + + def render_image(self): + global img_dict + curr_img = list(img_dict.values())[self.curr_selected - 1] + if np.any(curr_img != np.zeros((100, 100))): + curr_img = self.apply_filter(curr_img) + + curr_img = np.array(curr_img).astype(np.uint8) + curr_img = cv.cvtColor(curr_img, cv.COLOR_BGR2RGB) + + curr_img = Image.fromarray(curr_img) + + # Get the dimensions of the available space + max_width = self.canvas.winfo_width() + max_height = self.canvas.winfo_height() + + # Get the dimensions of the image + img_width, img_height = curr_img.size + + # Calculate the scale factor to fit the image within the available space + scale_factor = min(max_width / img_width, max_height / img_height) + + # Resize the image to fit within the available space, while maintaining aspect ratio + new_width = int(img_width * scale_factor) + new_height = int(img_height * scale_factor) + if new_height > 10 and new_width > 10: + new_width -= 10 + new_height -= 10 + curr_img = curr_img.resize((new_width, new_height), Image.ANTIALIAS) + + curr_img = ImageTk.PhotoImage(curr_img) + self.img_container.config(image=curr_img, padx=10, pady=10) + self.img_container.image = curr_img + self.fps_label.config( + text="Frames Per Second: ~" + str(self.fps), padx=10, pady=10) + + self.master.update() + + def isActive(self): + global isActive + return isActive + + +global root + + +def onClose(): + global isActive + isActive = False + root.destroy() + + +def startGUI(window_name, **kwargs): + """Constructs GUI: pass in a `window name` and at least 1 name of frame: `name1="my_chosen_name_1", name2="my_chosen_name_2", ...`\n + use `update_dict({"my_chosen_name_1": numpy.ndarray})` to update image\n + call `render_image()` after updating dict to render\n + :param `window_name` of type String + :(param >= 1) `name1="some string"`, `name2="some string"`, `name3="some string"`, ... + """ + global img_dict, isActive + curr_name = "name" + curr_index = 1 + if kwargs == {}: + raise KeyError("kwargs is empty in startGUI(), start with name1= ... ") + + for key, value in kwargs.items(): + if key == curr_name+str(curr_index): + frame_type = value + img_dict[frame_type] = np.zeros((100, 100)) + curr_index += 1 + else: + raise KeyError("argument " + curr_name + + str(curr_index) + " is missing in startGUI()") + global root + root = tk.Tk() + isActive = True + root.protocol("WM_DELETE_WINDOW", onClose) + app = GUI(root, img_dict, window_name) + return app \ No newline at end of file diff --git a/helper_scripts/change_res.py b/helper_scripts/change_res.py new file mode 100644 index 0000000..afbb319 --- /dev/null +++ b/helper_scripts/change_res.py @@ -0,0 +1,20 @@ +import cv2 as cv + + +def change_res(frame, scale_percent): + scale_percent = scale_percent + width = int(frame.shape[1] * scale_percent / 100) + height = int(frame.shape[0] * scale_percent / 100) + dim = (width, height) + + frame = cv.resize(frame, dim, interpolation=cv.INTER_AREA) + + return frame + + +def change_res_2(frame, resolution): + height, width, channels = frame.shape + new_width = int(width / height * resolution) + frame = cv.resize(frame, (new_width, resolution), interpolation=cv.INTER_AREA) + + return frame diff --git a/pre_process.py b/pre_process.py index 102d1d1..3cc92d7 100644 --- a/pre_process.py +++ b/pre_process.py @@ -1,6 +1,8 @@ -import cv2 import math +import cv2 +import numpy as np + BRIGHTNESS_BASELINE = 110 SATURATION_BASELINE = 105 ACCEPTABLE_DIFFERENCE = 25 @@ -145,6 +147,15 @@ def increase_saturation(img, value): def standardize_frame(img): + if len(img.shape) < 3: + img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + + try: + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + except: + img_float32 = np.float32(img) + hsv = cv2.cvtColor(img_float32, cv2.COLOR_BGR2HSV) + """ Main function that modifies the saturation and brightness of a frame based on it's mean s and v values. @@ -163,15 +174,33 @@ def standardize_frame(img): if mean_brightness < BRIGHTNESS_BASELINE: if mean_brightness + ACCEPTABLE_DIFFERENCE < BRIGHTNESS_BASELINE: - img = alter_brightness(img, math.floor(BRIGHTNESS_BASELINE - (mean_brightness + ACCEPTABLE_DIFFERENCE))) + img = alter_brightness(img, math.floor( + BRIGHTNESS_BASELINE - (mean_brightness + ACCEPTABLE_DIFFERENCE))) else: if mean_brightness - ACCEPTABLE_DIFFERENCE > BRIGHTNESS_BASELINE: - img = alter_brightness(img, math.floor(BRIGHTNESS_BASELINE - (mean_brightness - ACCEPTABLE_DIFFERENCE))) + img = alter_brightness(img, math.floor( + BRIGHTNESS_BASELINE - (mean_brightness - ACCEPTABLE_DIFFERENCE))) + if mean_saturation < SATURATION_BASELINE: + if mean_saturation + ACCEPTABLE_DIFFERENCE < SATURATION_BASELINE: + img = alter_saturation(img, math.floor( + SATURATION_BASELINE - (mean_saturation + ACCEPTABLE_DIFFERENCE))) + else: + if mean_saturation - ACCEPTABLE_DIFFERENCE > SATURATION_BASELINE: + img = alter_saturation(img, math.floor( + SATURATION_BASELINE - (mean_saturation - ACCEPTABLE_DIFFERENCE))) + img = alter_brightness(img, math.floor( + BRIGHTNESS_BASELINE - (mean_brightness + ACCEPTABLE_DIFFERENCE))) + else: + if mean_brightness - ACCEPTABLE_DIFFERENCE > BRIGHTNESS_BASELINE: + img = alter_brightness(img, math.floor( + BRIGHTNESS_BASELINE - (mean_brightness - ACCEPTABLE_DIFFERENCE))) if mean_saturation < SATURATION_BASELINE: if mean_saturation + ACCEPTABLE_DIFFERENCE < SATURATION_BASELINE: - img = alter_saturation(img, math.floor(SATURATION_BASELINE - (mean_saturation + ACCEPTABLE_DIFFERENCE))) + img = alter_saturation(img, math.floor( + SATURATION_BASELINE - (mean_saturation + ACCEPTABLE_DIFFERENCE))) else: if mean_saturation - ACCEPTABLE_DIFFERENCE > SATURATION_BASELINE: - img = alter_saturation(img, math.floor(SATURATION_BASELINE - (mean_saturation - ACCEPTABLE_DIFFERENCE))) + img = alter_saturation(img, math.floor( + SATURATION_BASELINE - (mean_saturation - ACCEPTABLE_DIFFERENCE))) return img diff --git a/test_algorithms.py b/test_algorithms.py index 1ad1564..381d797 100644 --- a/test_algorithms.py +++ b/test_algorithms.py @@ -14,6 +14,8 @@ import pre_process from algorithms.SeesawAlgorithm import SeesawAlgorithm from algorithms.CenterDownwards import CenterDownward +from algorithms.SeesawAlgorithmVersionTwo import SeesawAlgorithmVersionTwo +from helper_scripts.change_res import change_res_2 # parser for command line arguments parser = argparse.ArgumentParser() @@ -24,7 +26,8 @@ # list of algorithms algo_list = [('hough', HoughAlgorithm), ('center_row', CenterRowAlgorithm), ('mini_contour', MiniContoursAlgorithm), ('mini_contour_downward', MiniContoursDownwards), - ('scanning', ScanningAlgorithm), ('check_row_end', CheckRowEnd), ('seesaw', SeesawAlgorithm), ("center_down", CenterDownward)] + ('scanning', ScanningAlgorithm), ('check_row_end', CheckRowEnd), ('seesaw', SeesawAlgorithm), + ('seesaw_v2', SeesawAlgorithmVersionTwo), ("center_down", CenterDownward)] def main(): @@ -74,6 +77,7 @@ def run_algorithm(alg, vid_file): while vid.isOpened(): ret, frame = vid.read() + frame = change_res_2(frame, 720) if not ret: print('No More Frames Remaining') diff --git a/test_pf.py b/test_pf.py index 9a59f42..b77c784 100644 --- a/test_pf.py +++ b/test_pf.py @@ -1,9 +1,11 @@ -import time import argparse import os.path as path import sys +import time +import tkinter as tk import cv2 as cv +import numpy as np from omegaconf import OmegaConf from algorithms.CenterRowAlgorithm import CenterRowAlgorithm @@ -12,7 +14,9 @@ from algorithms.MiniContoursAlgorithm import MiniContoursAlgorithm from algorithms.MiniContoursDownwards import MiniContoursDownwards from algorithms.ScanningAlgorithm import ScanningAlgorithm - +from algorithms.SeesawAlgorithm import SeesawAlgorithm +from algorithms.SeesawAlgorithmVersionTwo import SeesawAlgorithmVersionTwo +from gui import startGUI # parser for command line arguments parser = argparse.ArgumentParser() @@ -33,7 +37,14 @@ ('scanning', ScanningAlgorithm), ('check_row_end', - CheckRowEnd)] + CheckRowEnd), + ('seesaw', + SeesawAlgorithm), + ('seesaw_v2', + SeesawAlgorithmVersionTwo) + ] +# +number_of_frames = 0 # ability to pass in args for reusability @@ -106,38 +117,63 @@ def run_algorithm(alg, vid_file): total_run = 0 uptime = 0 - all_frame = [] + all_frame_time = [] + time_till_second = 0 + frame_within_second = 0 + # + if args.show: + window_name = f'{args.alg}s algorithm on {args.vid}s video' + app = startGUI(window_name, name1="standard", + name2="processed", name3='masked') + while vid.isOpened(): ret, frame = vid.read() if not ret: print('No More Frames Remaining\n') break - + # + if args.show and app.isActive(): + app.update_dict({'standard': frame}) + alg.update_lower_hsv(app.getLowerHSV()) + alg.update_upper_hsv(app.getUpperHSV()) + # uncomment if want the input to be affected + frame = app.apply_filter(frame) + # start_time_frame = time.time() - processed_image, angle = alg.process_frame(frame, show=args.show) + # + processed, angle, maskf = alg.get_extra_content( + frame, show=args.show) + # end_time_frame = time.time() - all_frame.append(end_time_frame - start_time_frame) - + # + all_frame_time.append(end_time_frame - start_time_frame) + # print(angle) - + time_till_second += end_time_frame - start_time_frame + frame_within_second += 1 + # + if args.show and app.isActive(): + app.update_dict({'processed': processed}) + app.update_dict({'masked': maskf}) + x = time_till_second # replace with the integer you want to check + tolerance = 0.05 + if abs(x - 1) <= tolerance: + app.update_fps(frame_within_second) + frame_within_second = 0 + time_till_second = 0 + app.render_image() # counters if angle is not None: uptime += 1 total_run += 1 - - if args.show: - cv.imshow( - f'{args.alg}s algorithm on {args.vid}s video', processed_image) - - key = cv.waitKey(25) - - # Exit if Esc key is pressed + # + key = cv.waitKey(1) if key == 27: break - + # vid.release() cv.destroyAllWindows() - return uptime, total_run, all_frame + return uptime, total_run, all_frame_time if __name__ == '__main__':