├── .gitignore ├── Pipfile ├── Pipfile.lock ├── README.md ├── detect_barcodes ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── django_opencv_barcodes ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── generate_barcodes ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── images ├── Sample_Barcodes.pdf ├── ajax_request.gif ├── detect_barcodes.gif ├── detect_qr_code.gif └── generate_barcodes.gif ├── manage.py ├── requirements.txt ├── templates ├── base.html ├── detect_barcodes │ └── detect.html └── generate_barcodes │ └── generate.html └── utils ├── camera_streaming_widget.py └── check_camera.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/python,django,dotenv,vscode,visualstudiocode,git 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=python,django,dotenv,vscode,visualstudiocode,git 4 | 5 | ### Django ### 6 | *.log 7 | *.pot 8 | *.pyc 9 | __pycache__/ 10 | local_settings.py 11 | db.sqlite3 12 | db.sqlite3-journal 13 | media 14 | 15 | # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ 16 | # in your Git repository. Update and uncomment the following line accordingly. 17 | # /staticfiles/ 18 | 19 | ### Django.Python Stack ### 20 | # Byte-compiled / optimized / DLL files 21 | *.py[cod] 22 | *$py.class 23 | 24 | # C extensions 25 | *.so 26 | 27 | # Distribution / packaging 28 | .Python 29 | build/ 30 | develop-eggs/ 31 | dist/ 32 | downloads/ 33 | eggs/ 34 | .eggs/ 35 | lib/ 36 | lib64/ 37 | parts/ 38 | sdist/ 39 | var/ 40 | wheels/ 41 | pip-wheel-metadata/ 42 | share/python-wheels/ 43 | *.egg-info/ 44 | .installed.cfg 45 | *.egg 46 | MANIFEST 47 | 48 | # PyInstaller 49 | # Usually these files are written by a python script from a template 50 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 51 | *.manifest 52 | *.spec 53 | 54 | # Installer logs 55 | pip-log.txt 56 | pip-delete-this-directory.txt 57 | 58 | # Unit test / coverage reports 59 | htmlcov/ 60 | .tox/ 61 | .nox/ 62 | .coverage 63 | .coverage.* 64 | .cache 65 | nosetests.xml 66 | coverage.xml 67 | *.cover 68 | *.py,cover 69 | .hypothesis/ 70 | .pytest_cache/ 71 | pytestdebug.log 72 | 73 | # Translations 74 | *.mo 75 | 76 | # Django stuff: 77 | 78 | # Flask stuff: 79 | instance/ 80 | .webassets-cache 81 | 82 | # Scrapy stuff: 83 | .scrapy 84 | 85 | # Sphinx documentation 86 | docs/_build/ 87 | doc/_build/ 88 | 89 | # PyBuilder 90 | target/ 91 | 92 | # Jupyter Notebook 93 | .ipynb_checkpoints 94 | 95 | # IPython 96 | profile_default/ 97 | ipython_config.py 98 | 99 | # pyenv 100 | .python-version 101 | 102 | # pipenv 103 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 104 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 105 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 106 | # install all needed dependencies. 107 | #Pipfile.lock 108 | 109 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 110 | __pypackages__/ 111 | 112 | # Celery stuff 113 | celerybeat-schedule 114 | celerybeat.pid 115 | 116 | # SageMath parsed files 117 | *.sage.py 118 | 119 | # Environments 120 | .env 121 | .venv 122 | env/ 123 | venv/ 124 | ENV/ 125 | env.bak/ 126 | venv.bak/ 127 | pythonenv* 128 | 129 | # Spyder project settings 130 | .spyderproject 131 | .spyproject 132 | 133 | # Rope project settings 134 | .ropeproject 135 | 136 | # mkdocs documentation 137 | /site 138 | 139 | # mypy 140 | .mypy_cache/ 141 | .dmypy.json 142 | dmypy.json 143 | 144 | # Pyre type checker 145 | .pyre/ 146 | 147 | # pytype static type analyzer 148 | .pytype/ 149 | 150 | # profiling data 151 | .prof 152 | 153 | ### dotenv ### 154 | 155 | ### Git ### 156 | # Created by git for backups. To disable backups in Git: 157 | # $ git config --global mergetool.keepBackup false 158 | *.orig 159 | 160 | # Created by git when using merge tools for conflicts 161 | *.BACKUP.* 162 | *.BASE.* 163 | *.LOCAL.* 164 | *.REMOTE.* 165 | *_BACKUP_*.txt 166 | *_BASE_*.txt 167 | *_LOCAL_*.txt 168 | *_REMOTE_*.txt 169 | 170 | ### Python ### 171 | # Byte-compiled / optimized / DLL files 172 | 173 | # C extensions 174 | 175 | # Distribution / packaging 176 | 177 | # PyInstaller 178 | # Usually these files are written by a python script from a template 179 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 180 | 181 | # Installer logs 182 | 183 | # Unit test / coverage reports 184 | 185 | # Translations 186 | 187 | # Django stuff: 188 | 189 | # Flask stuff: 190 | 191 | # Scrapy stuff: 192 | 193 | # Sphinx documentation 194 | 195 | # PyBuilder 196 | 197 | # Jupyter Notebook 198 | 199 | # IPython 200 | 201 | # pyenv 202 | 203 | # pipenv 204 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 205 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 206 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 207 | # install all needed dependencies. 208 | 209 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 210 | 211 | # Celery stuff 212 | 213 | # SageMath parsed files 214 | 215 | # Environments 216 | 217 | # Spyder project settings 218 | 219 | # Rope project settings 220 | 221 | # mkdocs documentation 222 | 223 | # mypy 224 | 225 | # Pyre type checker 226 | 227 | # pytype static type analyzer 228 | 229 | # profiling data 230 | 231 | ### VisualStudioCode ### 232 | .vscode/* 233 | !.vscode/tasks.json 234 | !.vscode/launch.json 235 | *.code-workspace 236 | 237 | ### VisualStudioCode Patch ### 238 | # Ignore all local history of files 239 | .history 240 | .ionide 241 | 242 | ### vscode ### 243 | .vscode 244 | !.vscode/settings.json 245 | !.vscode/extensions.json 246 | 247 | # End of https://www.toptal.com/developers/gitignore/api/python,django,dotenv,vscode,visualstudiocode,git 248 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | django = "*" 10 | opencv-python = "*" 11 | pyzbar = "*" 12 | numpy = "*" 13 | python-dotenv = "*" 14 | python-barcode = "*" 15 | pillow = "*" 16 | qrcode = {extras = ["pil"], version = "*"} 17 | 18 | [requires] 19 | python_version = "3.7" 20 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "9587cbbffaee1eba8c6ed2be3dee227985b2f5985fb44c1b1ee92160b2654b24" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "asgiref": { 20 | "hashes": [ 21 | "sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17", 22 | "sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0" 23 | ], 24 | "markers": "python_version >= '3.5'", 25 | "version": "==3.3.1" 26 | }, 27 | "colorama": { 28 | "hashes": [ 29 | "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", 30 | "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" 31 | ], 32 | "markers": "platform_system == 'Windows'", 33 | "version": "==0.4.4" 34 | }, 35 | "django": { 36 | "hashes": [ 37 | "sha256:5c866205f15e7a7123f1eec6ab939d22d5bde1416635cab259684af66d8e48a2", 38 | "sha256:edb10b5c45e7e9c0fb1dc00b76ec7449aca258a39ffd613dbd078c51d19c9f03" 39 | ], 40 | "index": "pypi", 41 | "version": "==3.1.4" 42 | }, 43 | "numpy": { 44 | "hashes": [ 45 | "sha256:08308c38e44cc926bdfce99498b21eec1f848d24c302519e64203a8da99a97db", 46 | "sha256:09c12096d843b90eafd01ea1b3307e78ddd47a55855ad402b157b6c4862197ce", 47 | "sha256:13d166f77d6dc02c0a73c1101dd87fdf01339febec1030bd810dcd53fff3b0f1", 48 | "sha256:141ec3a3300ab89c7f2b0775289954d193cc8edb621ea05f99db9cb181530512", 49 | "sha256:16c1b388cc31a9baa06d91a19366fb99ddbe1c7b205293ed072211ee5bac1ed2", 50 | "sha256:18bed2bcb39e3f758296584337966e68d2d5ba6aab7e038688ad53c8f889f757", 51 | "sha256:1aeef46a13e51931c0b1cf8ae1168b4a55ecd282e6688fdb0a948cc5a1d5afb9", 52 | "sha256:27d3f3b9e3406579a8af3a9f262f5339005dd25e0ecf3cf1559ff8a49ed5cbf2", 53 | "sha256:2a2740aa9733d2e5b2dfb33639d98a64c3b0f24765fed86b0fd2aec07f6a0a08", 54 | "sha256:4377e10b874e653fe96985c05feed2225c912e328c8a26541f7fc600fb9c637b", 55 | "sha256:448ebb1b3bf64c0267d6b09a7cba26b5ae61b6d2dbabff7c91b660c7eccf2bdb", 56 | "sha256:50e86c076611212ca62e5a59f518edafe0c0730f7d9195fec718da1a5c2bb1fc", 57 | "sha256:5734bdc0342aba9dfc6f04920988140fb41234db42381cf7ccba64169f9fe7ac", 58 | "sha256:64324f64f90a9e4ef732be0928be853eee378fd6a01be21a0a8469c4f2682c83", 59 | "sha256:6ae6c680f3ebf1cf7ad1d7748868b39d9f900836df774c453c11c5440bc15b36", 60 | "sha256:6d7593a705d662be5bfe24111af14763016765f43cb6923ed86223f965f52387", 61 | "sha256:8cac8790a6b1ddf88640a9267ee67b1aee7a57dfa2d2dd33999d080bc8ee3a0f", 62 | "sha256:8ece138c3a16db8c1ad38f52eb32be6086cc72f403150a79336eb2045723a1ad", 63 | "sha256:9eeb7d1d04b117ac0d38719915ae169aa6b61fca227b0b7d198d43728f0c879c", 64 | "sha256:a09f98011236a419ee3f49cedc9ef27d7a1651df07810ae430a6b06576e0b414", 65 | "sha256:a5d897c14513590a85774180be713f692df6fa8ecf6483e561a6d47309566f37", 66 | "sha256:ad6f2ff5b1989a4899bf89800a671d71b1612e5ff40866d1f4d8bcf48d4e5764", 67 | "sha256:c42c4b73121caf0ed6cd795512c9c09c52a7287b04d105d112068c1736d7c753", 68 | "sha256:cb1017eec5257e9ac6209ac172058c430e834d5d2bc21961dceeb79d111e5909", 69 | "sha256:d6c7bb82883680e168b55b49c70af29b84b84abb161cbac2800e8fcb6f2109b6", 70 | "sha256:e452dc66e08a4ce642a961f134814258a082832c78c90351b75c41ad16f79f63", 71 | "sha256:e5b6ed0f0b42317050c88022349d994fe72bfe35f5908617512cd8c8ef9da2a9", 72 | "sha256:e9b30d4bd69498fc0c3fe9db5f62fffbb06b8eb9321f92cc970f2969be5e3949", 73 | "sha256:ec149b90019852266fec2341ce1db513b843e496d5a8e8cdb5ced1923a92faab", 74 | "sha256:edb01671b3caae1ca00881686003d16c2209e07b7ef8b7639f1867852b948f7c", 75 | "sha256:f0d3929fe88ee1c155129ecd82f981b8856c5d97bcb0d5f23e9b4242e79d1de3", 76 | "sha256:f29454410db6ef8126c83bd3c968d143304633d45dc57b51252afbd79d700893", 77 | "sha256:fe45becb4c2f72a0907c1d0246ea6449fe7a9e2293bb0e11c4e9a32bb0930a15", 78 | "sha256:fedbd128668ead37f33917820b704784aff695e0019309ad446a6d0b065b57e4" 79 | ], 80 | "index": "pypi", 81 | "version": "==1.19.4" 82 | }, 83 | "opencv-python": { 84 | "hashes": [ 85 | "sha256:0548981fe189e0d57b9cc65066b66fd70d4bc84ea906f349a63d9098e1b911c6", 86 | "sha256:117dbb2fd184de28d831f14c1da17864efcee7bb7895e43adf40f5e1da9137fb", 87 | "sha256:135e05b69ab9665cbe2589f56e60895219bc2443a632bdc4bde72fb95eda1582", 88 | "sha256:14df77490c8aedceae74e660564d48c04761658aecc93895ac5e974006a89606", 89 | "sha256:17581c68400f828700e5c6b3b082f50c781bf74cb9a7b972a04f05d26c8e894a", 90 | "sha256:4af0053c6a70f127a52c26b112341826d3dbfce6955beb9044d3eabd7e14d1cd", 91 | "sha256:51baebb0f8f3cae4cccd30daf018a5bb75cb759d5658aea29100d34cd5cac106", 92 | "sha256:6022609b67f9c0f14e6807e782660d1d1be94d4f0c7bc1794d7d8f600014acb2", 93 | "sha256:68a9ec7e32f82cab267b6f757d9862a9a930371062739f9d00472e7c850c5854", 94 | "sha256:6b1d85cbb64ce20ac5f79ad8e3e76a3dbff53d258c65f2fc0b9411321147a0be", 95 | "sha256:6b6d23de6d5ddc55e865ac8532bf8062b26ba70305fa1c87c671717027dcd370", 96 | "sha256:744e9ae2fb4c8574e6d4a762146b4d0984bdec60b98480fc54a363c03a07a1ac", 97 | "sha256:7fe81d08df4eb5dc4c6aa5f09888b6fd390fce5fa7d5624a98cac890b9aa6181", 98 | "sha256:8a8ebd7ceebc0be9c14ca3e25a1c4ae086016b469848258e998247f2fc855314", 99 | "sha256:8aeda9b2c37bf91fa88d67f09b85f2250661eec43d72184ec544783de204e96a", 100 | "sha256:9659e80059c9f39728c7dcc22032dff0d1d467f07b6cd8e036613393e4b7c71a", 101 | "sha256:c1382209a771ca8a25fe89d4a2377875538c6ed3cf8745280e65636cbd0988f2", 102 | "sha256:d80db278a07f51811dbf0f9c31ff7cd5b2501822fb7a7587e71f9ff27d5c04bd", 103 | "sha256:db874c65654465ef71d6e8618bed8c725722bc90624132b9512bf061abb4eec0", 104 | "sha256:e4c072cf4260063ebadc70e34d622fa1127a88e364475ed757709e249ebe990f", 105 | "sha256:f69a56e958ecb549ba84e0497a438080932b4d52ded441cec04d80afde71dc0a" 106 | ], 107 | "index": "pypi", 108 | "version": "==4.4.0.46" 109 | }, 110 | "pillow": { 111 | "hashes": [ 112 | "sha256:006de60d7580d81f4a1a7e9f0173dc90a932e3905cc4d47ea909bc946302311a", 113 | "sha256:0a2e8d03787ec7ad71dc18aec9367c946ef8ef50e1e78c71f743bc3a770f9fae", 114 | "sha256:0eeeae397e5a79dc088d8297a4c2c6f901f8fb30db47795113a4a605d0f1e5ce", 115 | "sha256:11c5c6e9b02c9dac08af04f093eb5a2f84857df70a7d4a6a6ad461aca803fb9e", 116 | "sha256:2fb113757a369a6cdb189f8df3226e995acfed0a8919a72416626af1a0a71140", 117 | "sha256:4b0ef2470c4979e345e4e0cc1bbac65fda11d0d7b789dbac035e4c6ce3f98adb", 118 | "sha256:59e903ca800c8cfd1ebe482349ec7c35687b95e98cefae213e271c8c7fffa021", 119 | "sha256:5abd653a23c35d980b332bc0431d39663b1709d64142e3652890df4c9b6970f6", 120 | "sha256:5f9403af9c790cc18411ea398a6950ee2def2a830ad0cfe6dc9122e6d528b302", 121 | "sha256:6b4a8fd632b4ebee28282a9fef4c341835a1aa8671e2770b6f89adc8e8c2703c", 122 | "sha256:6c1aca8231625115104a06e4389fcd9ec88f0c9befbabd80dc206c35561be271", 123 | "sha256:795e91a60f291e75de2e20e6bdd67770f793c8605b553cb6e4387ce0cb302e09", 124 | "sha256:7ba0ba61252ab23052e642abdb17fd08fdcfdbbf3b74c969a30c58ac1ade7cd3", 125 | "sha256:7c9401e68730d6c4245b8e361d3d13e1035cbc94db86b49dc7da8bec235d0015", 126 | "sha256:81f812d8f5e8a09b246515fac141e9d10113229bc33ea073fec11403b016bcf3", 127 | "sha256:895d54c0ddc78a478c80f9c438579ac15f3e27bf442c2a9aa74d41d0e4d12544", 128 | "sha256:8de332053707c80963b589b22f8e0229f1be1f3ca862a932c1bcd48dafb18dd8", 129 | "sha256:92c882b70a40c79de9f5294dc99390671e07fc0b0113d472cbea3fde15db1792", 130 | "sha256:95edb1ed513e68bddc2aee3de66ceaf743590bf16c023fb9977adc4be15bd3f0", 131 | "sha256:b63d4ff734263ae4ce6593798bcfee6dbfb00523c82753a3a03cbc05555a9cc3", 132 | "sha256:bd7bf289e05470b1bc74889d1466d9ad4a56d201f24397557b6f65c24a6844b8", 133 | "sha256:cc3ea6b23954da84dbee8025c616040d9aa5eaf34ea6895a0a762ee9d3e12e11", 134 | "sha256:cc9ec588c6ef3a1325fa032ec14d97b7309db493782ea8c304666fb10c3bd9a7", 135 | "sha256:d3d07c86d4efa1facdf32aa878bd508c0dc4f87c48125cc16b937baa4e5b5e11", 136 | "sha256:d8a96747df78cda35980905bf26e72960cba6d355ace4780d4bdde3b217cdf1e", 137 | "sha256:e38d58d9138ef972fceb7aeec4be02e3f01d383723965bfcef14d174c8ccd039", 138 | "sha256:eb472586374dc66b31e36e14720747595c2b265ae962987261f044e5cce644b5", 139 | "sha256:fbd922f702582cb0d71ef94442bfca57624352622d75e3be7a1e7e9360b07e72" 140 | ], 141 | "index": "pypi", 142 | "version": "==8.0.1" 143 | }, 144 | "python-barcode": { 145 | "hashes": [ 146 | "sha256:daa32fb999a843812fbb1c75ff909638811af7c465f0a991e9e41d26d2a44a24", 147 | "sha256:fafba4aa24e9d969777be521c294ff18f6c2b36ad63b5fc2f2108d972e23b252" 148 | ], 149 | "index": "pypi", 150 | "version": "==0.13.1" 151 | }, 152 | "python-dotenv": { 153 | "hashes": [ 154 | "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e", 155 | "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0" 156 | ], 157 | "index": "pypi", 158 | "version": "==0.15.0" 159 | }, 160 | "pytz": { 161 | "hashes": [ 162 | "sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268", 163 | "sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd" 164 | ], 165 | "version": "==2020.4" 166 | }, 167 | "pyzbar": { 168 | "hashes": [ 169 | "sha256:0e204b904e093e5e75aa85e0203bb0e02888105732a509b51f31cff400f34265", 170 | "sha256:496249b546be70ec98c0ff0ad9151e73daaffff129266df86150a15dcd8dac4c", 171 | "sha256:7d6c01d2c0a352fa994aa91b5540d1caeaeaac466656eb41468ca5df33be9f2e" 172 | ], 173 | "index": "pypi", 174 | "version": "==0.1.8" 175 | }, 176 | "qrcode": { 177 | "extras": [ 178 | "pil" 179 | ], 180 | "hashes": [ 181 | "sha256:3996ee560fc39532910603704c82980ff6d4d5d629f9c3f25f34174ce8606cf5", 182 | "sha256:505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369" 183 | ], 184 | "index": "pypi", 185 | "version": "==6.1" 186 | }, 187 | "six": { 188 | "hashes": [ 189 | "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", 190 | "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" 191 | ], 192 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 193 | "version": "==1.15.0" 194 | }, 195 | "sqlparse": { 196 | "hashes": [ 197 | "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", 198 | "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" 199 | ], 200 | "markers": "python_version >= '3.5'", 201 | "version": "==0.4.1" 202 | } 203 | }, 204 | "develop": {} 205 | } 206 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django web application to detect and generate barcodes 2 | 3 | ## Introduction 4 | 5 | A simple django web application to detect and generate both 1D and QR codes on the fly. 6 | 7 | I noticed that there are not that many articles or blog posts about a web app consuming camera feed into the OpenCV's API. Although there are many helpful articles with examples like taking a picture, pre-process it in the backend and create an image file, identifying faces in an image or identifying an object in an image etc. 8 | 9 | The goal is to integrate OpenCV and Django as a complete web application, draw polylines on the identified barcode, and render the real-time stream from your webcam or any camera source. You will notice after that project setup that the video stream is continuously rendered to the frontend from the backend in almost real-time without any lag. 10 | 11 | **Note: In this project I have used a USB connected camera to demonstrate the examples. Please feel free to fiddle around your own hardware resources or even go few steps further to feed an IP camera feed to the backend.** 12 | 13 | ## Examples 14 | 15 | > Update: 15-Sept-2021 16 | > 17 | > Features: 18 | > 19 | > - added feature to detect and fetch QR code data using Ajax 20 | 21 | ### *Detect QR code and fetch barcode data* 22 | 23 | #### Point QR code and fetch data using Ajax 24 | 25 | ![detect_qr_code.gif](images/detect_qr_code.gif) 26 | 27 | #### Ajax fetching - console log example 28 | 29 | ![ajax_request.gif](images/ajax_request.gif) 30 | 31 | ### *Detect barcodes example* 32 | 33 | ![detect_barcodes.gif](images/detect_barcodes.gif) 34 | 35 | ### *Generate barcodes example* 36 | 37 | ![Generate Barcodes](images/generate_barcodes.gif) 38 | 39 | ## Project setup 40 | 41 | ### *Software requirements* 42 | 43 | Please make sure that you have python along with pip (or pipenv) setup on your computer. 44 | 45 | - Python 3.7 (tested), will work for python > 3.7 I believe 46 | - pipenv or pip 47 | 48 | ### *Hardware requirements* 49 | 50 | - an in-built web camera (if you have a laptop) or a USB powered camera plugged into your computer 51 | 52 | ### *Installation and setup* 53 | 54 | Open windows command line or bash shell and run: 55 | 56 | ```shell 57 | - pipenv sync (if using pipenv) or pip install -r requirements.txt 58 | ``` 59 | 60 | If you prefer virtual environment for your project, use pipenv. If not use plain pip to setup. 61 | 62 | Or download or clone this git repo and run pip or pipenv commands to setup the project locally. 63 | 64 | After the dependencies are installed, create a ".env" file in the project root and add the following key-value pairs. 65 | 66 | ```dotenv 67 | TARGET_ENV= # 'dev' or 'prod' => dev displays debug info, prod doesn't 68 | SECRET_KEY= # a secret key for django application 69 | CAMERA= # an integer value; for example 1 for camera 1, 2 for second camera 70 | ``` 71 | 72 | To generate a secret key for your django application, I recommend a secret key generator tools like [https://djecrety.ir/](https://djecrety.ir/) 73 | 74 | Generate your secret key and add this to your .env file. 75 | 76 | And after that run the command: 77 | 78 | ```shell 79 | python manage.py runserver (or) pipenv run python manage.py runserver 80 | ``` 81 | 82 | Open your web browser and go to project URL at: [http://127.0.0.1:8000/](http://127.0.0.1:8000/) 83 | 84 | ### *Trying out the web application* 85 | 86 | I have added a sample PDF document with some barcodes in it. Download the file and see the web app in action. 87 | 88 | ### *Project dependencies* 89 | 90 | Thanks to these amazing and great libraries! 91 | 92 | - django [https://www.djangoproject.com/](https://www.djangoproject.com/) 93 | - opencv-python [https://github.com/skvark/opencv-python](https://github.com/skvark/opencv-python) 94 | - pyzbar [https://github.com/NaturalHistoryMuseum/pyzbar](https://github.com/NaturalHistoryMuseum/pyzbar) 95 | - numpy [https://numpy.org/](https://numpy.org/) 96 | - python-dotenv [https://github.com/theskumar/python-dotenv](https://github.com/theskumar/python-dotenv) 97 | - python-barcode [https://github.com/WhyNotHugo/python-barcode](https://github.com/WhyNotHugo/python-barcode) 98 | - pillow [https://github.com/python-pillow/Pillow](https://github.com/python-pillow/Pillow) 99 | - qrcode [https://github.com/lincolnloop/python-qrcode](https://github.com/lincolnloop/python-qrcode) 100 | 101 | ## Intentional limitation of the proejct 102 | 103 | I have intentionally limited the number of types of barcodes in the dropdown with a list comprehension. 104 | 105 | Check out views.py, line 21 106 | 107 | ```python 108 | generate_barcodes/views.py 109 | ``` 110 | 111 | "python-barcode" library has only 1D barcode types, which is why I have used LincolnLoop's qrcode library to generate the QR codes. 112 | 113 | In the list comprehension I have only considered using code128 and code39 as they are easy and doesn't require string length checks or extra validation checks. For this I have appended ['qrcode'] for generating QR codes. 114 | 115 | Please feel free to tweak the code as you wish and play around with all the kinds of barcode types in the python-barcode library. 116 | 117 | You might want to checkout another barcode library that I found recently, "treepoem" - [https://github.com/adamchainz/treepoem](https://github.com/adamchainz/treepoem) 118 | 119 | ## What next? 120 | 121 | There is a lot of room to develop this sample project into your own ideas. 122 | 123 | For example, in the backend when a barcode is detected you could do something like: 124 | 125 | - do a lookup matching the barcode in a database 126 | - send the barcode data to an API to verify user authentication 127 | - authorize or don't authorize the user based on the barcode data etc. and many more 128 | -------------------------------------------------------------------------------- /detect_barcodes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py404/Django-OpenCV-Barcodes/fa9a8bd7a98c68e403b2cd86fa883d9ed13402c9/detect_barcodes/__init__.py -------------------------------------------------------------------------------- /detect_barcodes/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /detect_barcodes/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DetectBarcodesConfig(AppConfig): 5 | name = 'detect_barcodes' 6 | -------------------------------------------------------------------------------- /detect_barcodes/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py404/Django-OpenCV-Barcodes/fa9a8bd7a98c68e403b2cd86fa883d9ed13402c9/detect_barcodes/migrations/__init__.py -------------------------------------------------------------------------------- /detect_barcodes/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /detect_barcodes/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /detect_barcodes/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.detect, name='detect_barcodes'), 7 | path('camera_feed', views.camera_feed, name='camera_feed'), 8 | ] 9 | -------------------------------------------------------------------------------- /detect_barcodes/views.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | from datetime import datetime 4 | 5 | from django.http import JsonResponse, StreamingHttpResponse 6 | from django.shortcuts import render 7 | from PIL import Image 8 | from pyzbar.pyzbar import decode 9 | from utils.camera_streaming_widget import CameraStreamingWidget 10 | 11 | 12 | # Camera feed 13 | def camera_feed(request): 14 | stream = CameraStreamingWidget() 15 | frames = stream.get_frames() 16 | 17 | # if ajax request is sent 18 | if request.is_ajax(): 19 | print('Ajax request received') 20 | time_stamp = str(datetime.now().strftime("%d-%m-%y")) 21 | image = os.path.join(os.getcwd(), "media", 22 | "images", f"img_{time_stamp}.png") 23 | if os.path.exists(image): 24 | # open image if exists 25 | im = Image.open(image) 26 | # decode barcode 27 | if decode(im): 28 | for barcode in decode(im): 29 | barcode_data = (barcode.data).decode('utf-8') 30 | file_saved_at = time.ctime(os.path.getmtime(image)) 31 | # return decoded barcode as json response 32 | return JsonResponse(data={'barcode_data': barcode_data, 'file_saved_at': file_saved_at}) 33 | else: 34 | return JsonResponse(data={'barcode_data': None}) 35 | else: 36 | return JsonResponse(data={'barcode_data': None}) 37 | # else stream the frames from camera feed 38 | else: 39 | return StreamingHttpResponse(frames, content_type='multipart/x-mixed-replace; boundary=frame') 40 | 41 | 42 | def detect(request): 43 | stream = CameraStreamingWidget() 44 | success, frame = stream.camera.read() 45 | if success: 46 | status = True 47 | else: 48 | status = False 49 | return render(request, 'detect_barcodes/detect.html', context={'cam_status': status}) 50 | -------------------------------------------------------------------------------- /django_opencv_barcodes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py404/Django-OpenCV-Barcodes/fa9a8bd7a98c68e403b2cd86fa883d9ed13402c9/django_opencv_barcodes/__init__.py -------------------------------------------------------------------------------- /django_opencv_barcodes/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for django_opencv_barcodes project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_opencv_barcodes.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /django_opencv_barcodes/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for django_opencv_barcodes project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.1.4. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.1/ref/settings/ 11 | """ 12 | 13 | import os 14 | from pathlib import Path 15 | 16 | # Messages 17 | from django.contrib.messages import constants as messages 18 | from django.core.exceptions import ImproperlyConfigured 19 | from dotenv import load_dotenv 20 | 21 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 22 | BASE_DIR = Path(__file__).resolve().parent.parent 23 | 24 | dotenv_path = os.path.join(BASE_DIR, '.env') 25 | load_dotenv(dotenv_path) 26 | 27 | # Quick-start development settings - unsuitable for production 28 | # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ 29 | 30 | # SECURITY WARNING: keep the secret key used in production secret! 31 | SECRET_KEY = os.environ.get('SECRET_KEY') 32 | 33 | if os.environ.get('TARGET_ENV').lower() == 'prod': 34 | DEBUG = False 35 | else: 36 | DEBUG = True 37 | 38 | ALLOWED_HOSTS = ['localhost', '127.0.0.1', '[::1]'] 39 | 40 | 41 | # Application definition 42 | 43 | INSTALLED_APPS = [ 44 | 'django.contrib.admin', 45 | 'django.contrib.auth', 46 | 'django.contrib.contenttypes', 47 | 'django.contrib.sessions', 48 | 'django.contrib.messages', 49 | 'django.contrib.staticfiles', 50 | ] 51 | 52 | MIDDLEWARE = [ 53 | 'django.middleware.security.SecurityMiddleware', 54 | 'django.contrib.sessions.middleware.SessionMiddleware', 55 | 'django.middleware.common.CommonMiddleware', 56 | 'django.middleware.csrf.CsrfViewMiddleware', 57 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 58 | 'django.contrib.messages.middleware.MessageMiddleware', 59 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 60 | ] 61 | 62 | ROOT_URLCONF = 'django_opencv_barcodes.urls' 63 | 64 | TEMPLATES = [ 65 | { 66 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 67 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 68 | 'APP_DIRS': True, 69 | 'OPTIONS': { 70 | 'context_processors': [ 71 | 'django.template.context_processors.debug', 72 | 'django.template.context_processors.request', 73 | 'django.contrib.auth.context_processors.auth', 74 | 'django.contrib.messages.context_processors.messages', 75 | ], 76 | }, 77 | }, 78 | ] 79 | 80 | WSGI_APPLICATION = 'django_opencv_barcodes.wsgi.application' 81 | 82 | 83 | # Database 84 | # https://docs.djangoproject.com/en/3.1/ref/settings/#databases 85 | 86 | DATABASES = { 87 | 'default': { 88 | 'ENGINE': 'django.db.backends.sqlite3', 89 | 'NAME': BASE_DIR / 'db.sqlite3', 90 | } 91 | } 92 | 93 | 94 | # Password validation 95 | # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators 96 | 97 | AUTH_PASSWORD_VALIDATORS = [ 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 100 | }, 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 103 | }, 104 | { 105 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 106 | }, 107 | { 108 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 109 | }, 110 | ] 111 | 112 | 113 | # Internationalization 114 | # https://docs.djangoproject.com/en/3.1/topics/i18n/ 115 | 116 | LANGUAGE_CODE = 'en-us' 117 | 118 | TIME_ZONE = 'UTC' 119 | 120 | USE_I18N = True 121 | 122 | USE_L10N = True 123 | 124 | USE_TZ = True 125 | 126 | 127 | # Static files (CSS, JavaScript, Images) 128 | # https://docs.djangoproject.com/en/3.1/howto/static-files/ 129 | 130 | STATIC_URL = '/static/' 131 | -------------------------------------------------------------------------------- /django_opencv_barcodes/urls.py: -------------------------------------------------------------------------------- 1 | """django_opencv_barcodes URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import include, path 18 | 19 | urlpatterns = [ 20 | path('admin/', admin.site.urls), 21 | path('', include('detect_barcodes.urls')), 22 | path('generate/', include('generate_barcodes.urls')), 23 | ] 24 | -------------------------------------------------------------------------------- /django_opencv_barcodes/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_opencv_barcodes project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_opencv_barcodes.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /generate_barcodes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py404/Django-OpenCV-Barcodes/fa9a8bd7a98c68e403b2cd86fa883d9ed13402c9/generate_barcodes/__init__.py -------------------------------------------------------------------------------- /generate_barcodes/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /generate_barcodes/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class GenerateBarcodesConfig(AppConfig): 5 | name = 'generate_barcodes' 6 | -------------------------------------------------------------------------------- /generate_barcodes/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py404/Django-OpenCV-Barcodes/fa9a8bd7a98c68e403b2cd86fa883d9ed13402c9/generate_barcodes/migrations/__init__.py -------------------------------------------------------------------------------- /generate_barcodes/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /generate_barcodes/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /generate_barcodes/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.generate, name='generate_barcodes'), 7 | ] 8 | -------------------------------------------------------------------------------- /generate_barcodes/views.py: -------------------------------------------------------------------------------- 1 | import barcode 2 | import qrcode 3 | from barcode.writer import ImageWriter 4 | from django.http import HttpResponse 5 | from django.shortcuts import render 6 | from django.utils.encoding import smart_str 7 | 8 | 9 | def generate_file(barcode_file): 10 | # output = HttpResponse(content_type="image/jpeg") 11 | output = HttpResponse(content_type="application/force-download") 12 | barcode_file.save(output, "JPEG") 13 | output['Content-Disposition'] = 'attachment; filename=%s' % smart_str('barcode.jpg') 14 | output['X-Sendfile'] = smart_str('barcode.jpg') 15 | return output 16 | 17 | 18 | # Create your views here. 19 | def generate(request): 20 | context = { 21 | 'barcode_types': [b for b in barcode.PROVIDED_BARCODES if str(b).startswith('code')] + ['qrcode'] 22 | } 23 | 24 | if request.method == 'POST': 25 | b_type = request.POST['typeOfBarcode'] 26 | b_data = request.POST['barcodeData'] 27 | 28 | if b_type == 'qrcode': 29 | qr = qrcode.QRCode( 30 | version=1, 31 | error_correction=qrcode.constants.ERROR_CORRECT_L, 32 | box_size=10, 33 | border=1, 34 | ) 35 | qr.add_data(b_data) 36 | qr.make(fit=True) 37 | qr_file = qr.make_image(fill_color="black", back_color="white") # render qr image 38 | return generate_file(qr_file) # generate the file 39 | 40 | else: 41 | bar = barcode.get_barcode(name=b_type, code=b_data, writer=ImageWriter()) 42 | barcode_file = bar.render() # creates a PIL class image object 43 | return generate_file(barcode_file) # generate the file 44 | else: 45 | return render(request, 'generate_barcodes/generate.html', context=context) 46 | -------------------------------------------------------------------------------- /images/Sample_Barcodes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py404/Django-OpenCV-Barcodes/fa9a8bd7a98c68e403b2cd86fa883d9ed13402c9/images/Sample_Barcodes.pdf -------------------------------------------------------------------------------- /images/ajax_request.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py404/Django-OpenCV-Barcodes/fa9a8bd7a98c68e403b2cd86fa883d9ed13402c9/images/ajax_request.gif -------------------------------------------------------------------------------- /images/detect_barcodes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py404/Django-OpenCV-Barcodes/fa9a8bd7a98c68e403b2cd86fa883d9ed13402c9/images/detect_barcodes.gif -------------------------------------------------------------------------------- /images/detect_qr_code.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py404/Django-OpenCV-Barcodes/fa9a8bd7a98c68e403b2cd86fa883d9ed13402c9/images/detect_qr_code.gif -------------------------------------------------------------------------------- /images/generate_barcodes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py404/Django-OpenCV-Barcodes/fa9a8bd7a98c68e403b2cd86fa883d9ed13402c9/images/generate_barcodes.gif -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_opencv_barcodes.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py404/Django-OpenCV-Barcodes/fa9a8bd7a98c68e403b2cd86fa883d9ed13402c9/requirements.txt -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | Detect Barcodes 10 | 16 | 17 | 18 | 19 | 34 |
35 | 38 | 39 | {% block content %} 40 | {% endblock content %} 41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /templates/detect_barcodes/detect.html: -------------------------------------------------------------------------------- 1 | {% extends '../base.html' %} 2 | 3 | {% load static %} 4 | 5 | {% block content %} 6 |
7 |

Detect Barcodes

8 |

9 | Please check README of the project here before you run this django application.
10 | There are some notes like:
11 | 1. Adding a dotevn file
12 | 2. Adding key, value pairs to the .env
13 | 3. Generating a secret key for your django application 14 |

15 |
16 |
17 | {% if cam_status %} 18 |

Camera stream status: connected

19 | 20 | {% else %} 21 |

Camera stream status: Camera is either not accessible or busy

22 |
Things to check:
23 |
    24 |
  • USB connection?
  • 25 |
  • Camera number in your .env file?
  • 26 |
  • Camera is already in use?
  • 27 |
28 | {% endif %} 29 |
30 |
31 |

32 | Point the QR code facing to the camera. After a valid barcode is found, an 33 | image is saved automatically to "media/images" folder. 34 |

35 |

36 | After that, click the button below to analyse and read your barcode data. 37 |

38 | 41 |
42 |
43 | 44 | 45 | 46 |
47 |
48 |
49 | Read QR code data 50 |
51 |
52 | 53 | 54 | 55 | 85 | {% endblock content %} -------------------------------------------------------------------------------- /templates/generate_barcodes/generate.html: -------------------------------------------------------------------------------- 1 | {% extends '../base.html' %} 2 | 3 | {% load static %} 4 | 5 | {% block content %} 6 |
7 |

Generate Barcodes

8 |

9 | Use the options below to generate a barcode.
10 | The generated barcode will be downloaded with a prompt window asking you to save locally to your computer.
11 | Barcode types are currently limited to 3 for demonstration purpose only.
12 | For notes and project documentation, please check README of the project here. 13 |

14 |
15 | 16 |
17 |
18 | {% csrf_token %} 19 |
20 |
21 | 22 | 27 |
28 |
29 | 30 | 31 |
32 |
33 | 34 | 35 |
36 |
37 |
38 |
39 |
40 | {% endblock content %} -------------------------------------------------------------------------------- /utils/camera_streaming_widget.py: -------------------------------------------------------------------------------- 1 | import os 2 | from datetime import datetime 3 | 4 | import cv2 5 | import numpy as np 6 | from pyzbar.pyzbar import decode 7 | 8 | 9 | class CameraStreamingWidget: 10 | def __init__(self): 11 | self.camera = cv2.VideoCapture(int(os.environ.get('CAMERA'))) 12 | self.media_path = os.path.join(os.getcwd(), "media", "images") 13 | # create "media/images" folder if doesn't exist 14 | if not self.media_path: 15 | os.mkdir(self.media_path) 16 | 17 | def get_frames(self): 18 | while True: 19 | # Capture frame-by-frame 20 | success, frame = self.camera.read() 21 | if not success: 22 | break 23 | else: 24 | ret, buffer = cv2.imencode('.jpg', frame) 25 | 26 | # Add text on top of the barcode if there is a barcode in the stream using opencv 27 | # convert camera frame to numpy array 28 | color_image = np.asanyarray(frame) 29 | 30 | # decode numpy array to check if there is a barcode in color_image 31 | # you can add a custom check here to verify if the qr code has right identifier information 32 | if decode(color_image): 33 | for barcode in decode(color_image): 34 | barcode_data = (barcode.data).decode('utf-8') 35 | # if barcode data exists 36 | if barcode_data: 37 | # save file as PNG if a QR code is detected 38 | today = str(datetime.now().strftime("%d-%m-%y")) 39 | image = os.path.join(self.media_path, f"img_{today}.png") 40 | cv2.imwrite(image, frame) 41 | 42 | # print info for debugging purpose 43 | print('-'*50) 44 | print('Saving image to media...') 45 | print('-'*50) 46 | print(f'Saved as: {image}') 47 | print('-'*50) 48 | 49 | pts = np.array([barcode.polygon], np.int32) 50 | pts = pts.reshape((-1,1,2)) 51 | # draw polylines on the barcode 52 | cv2.polylines( 53 | img=color_image, 54 | pts=[pts], 55 | isClosed=True, 56 | color=(0,255,0), 57 | thickness=3 58 | ) 59 | pts2 = barcode.rect 60 | # put text on top of polylines 61 | barcode_frame = cv2.putText( 62 | img=color_image, 63 | text=barcode_data, 64 | org=(pts2[0], pts2[1]), 65 | fontFace=cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 66 | fontScale=0.9, 67 | color=(0,0,255), 68 | thickness=2 69 | ) 70 | # encode the new barcode_frame that has polylines and barcode data text 71 | _, buffer_ = cv2.imencode('.jpg', barcode_frame) 72 | # convert barcode_frame to bytes 73 | barcode_frame = buffer_.tobytes() 74 | # yield output stream with polylines and barcode data text 75 | yield (b'--frame\r\n' 76 | b'Content-Type: image/jpeg\r\n\r\n' + barcode_frame + b'\r\n\r\n') 77 | # else, yield the normal camera stream 78 | else: 79 | frame = buffer.tobytes() 80 | yield (b'--frame\r\n' 81 | b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n') 82 | -------------------------------------------------------------------------------- /utils/check_camera.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | def return_camera_indexes(): 4 | # checks the first 10 indexes. 5 | index = 0 6 | arr = [] 7 | i = 10 8 | while i > 0: 9 | cap = cv2.VideoCapture(index) 10 | if cap.read()[0]: 11 | arr.append(index) 12 | cap.release() 13 | index += 1 14 | i -= 1 15 | return arr 16 | 17 | print('Available camera indexes:', return_camera_indexes()) --------------------------------------------------------------------------------