├── __init__.py ├── zbarcam ├── __init__.py ├── zbarcam.kv └── zbarcam.py ├── MANIFEST.in ├── requirements ├── test_requirements.txt └── requirements.txt ├── .gitignore ├── screenshot.gif ├── main.py ├── tox.ini ├── setup.py ├── CHANGELOG.md ├── OpenCV.md ├── LICENSE └── README.md /__init__.py: -------------------------------------------------------------------------------- 1 | from .zbarcam import ZBarCam # noqa 2 | -------------------------------------------------------------------------------- /zbarcam/__init__.py: -------------------------------------------------------------------------------- 1 | from .zbarcam import ZBarCam # noqa 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.md 2 | include LICENSE 3 | include zbarcam/*.kv 4 | -------------------------------------------------------------------------------- /requirements/test_requirements.txt: -------------------------------------------------------------------------------- 1 | isort==4.2.5 2 | flake8==3.3.0 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | __pycache__/ 4 | venv/ 5 | .tox/ 6 | .buildozer/ 7 | bin/ 8 | -------------------------------------------------------------------------------- /screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fitoprincipe/garden.zbarcam/develop/screenshot.gif -------------------------------------------------------------------------------- /requirements/requirements.txt: -------------------------------------------------------------------------------- 1 | zbar==0.10 2 | Kivy==1.10.0 3 | Kivy-Garden==0.1.4 4 | Pillow==4.3.0 5 | numpy==1.13.3 6 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Buildozer is only hooking with a `main.py` file. 4 | """ 5 | from zbarcam.zbarcam import DemoApp 6 | 7 | 8 | if __name__ == '__main__': 9 | DemoApp().run() 10 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = pep8,isort-check 3 | # no setup.py to be ran 4 | skipsdist = True 5 | 6 | [testenv] 7 | deps = 8 | -r{toxinidir}/requirements/test_requirements.txt 9 | 10 | [testenv:pep8] 11 | commands = flake8 zbarcam/ 12 | 13 | [testenv:isort-check] 14 | commands = 15 | isort --check-only --recursive zbarcam/ 16 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | 5 | setup(name='zbarcam', 6 | version='20171220', 7 | description='Real time Barcode and QR Code scanner Edit', 8 | author='Andre Miras', 9 | url='https://github.com/AndreMiras/garden.zbarcam', 10 | packages=['zbarcam'], 11 | package_data={'zbarcam': ['*.kv']}, 12 | install_requires=['zbar', 'kivy', 'pillow', 'numpy']) 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [20171220] 4 | 5 | - Full screen camera 6 | - Kvlang refactoring 7 | - File tree refactoring 8 | 9 | ## [20171117] 10 | 11 | - Integrated to kivy-garden 12 | 13 | ## [20171102] 14 | 15 | - Add camera start/stop 16 | 17 | ## [20171020] 18 | 19 | - Add Android autofocus, refs #2 20 | - Fix Android rotation, refs #3 21 | - Add animated demo gif 22 | 23 | ## [20171019] 24 | 25 | - Add ZBar Android support, refs #1 26 | - Add PIL/Pillow Android workaround 27 | 28 | ## [20171016] 29 | 30 | - Initial release 31 | -------------------------------------------------------------------------------- /OpenCV.md: -------------------------------------------------------------------------------- 1 | # OpenCV 2 | 3 | ## Linux Camera support 4 | In order to be able to use the camera on Linux, you need to compile OpenCV. 5 | Simply installing `opencv-python` from pypi is not enough. 6 | Currently only OpenCV2 works with Kivy on Linux (see https://github.com/kivy/kivy/issues/5404). 7 | Download and extract the OpenCV2 archive: 8 | ``` 9 | wget https://github.com/opencv/opencv/archive/2.4.13.3.tar.gz -O opencv-2.4.13.3.tar.gz 10 | tar -xvzf opencv-2.4.13.3.tar.gz 11 | ``` 12 | Prepare and build: 13 | ``` 14 | cd opencv-2.4.13.3/ 15 | mkdir build && cd build/ 16 | cmake .. 17 | make -j4 18 | ``` 19 | Copy your compiled `cv2.so` to your virtualenv: 20 | ``` 21 | cp lib/cv2.so venv/lib/python2.7/site-packages/cv2.so 22 | ``` 23 | -------------------------------------------------------------------------------- /zbarcam/zbarcam.kv: -------------------------------------------------------------------------------- 1 | #:import XCamera kivy.garden.xcamera.XCamera 2 | : 3 | Widget: 4 | # invert width/height on rotated Android 5 | # https://stackoverflow.com/a/45192295/185510 6 | id: proxy 7 | XCamera: 8 | id: xcamera 9 | play: True 10 | resolution: root.resolution 11 | allow_stretch: True 12 | keep_ratio: True 13 | center: self.size and proxy.center 14 | size: 15 | (proxy.height, proxy.width) if root.is_android() \ 16 | else (proxy.width, proxy.height) 17 | # Android camera rotation workaround, refs: 18 | # https://github.com/AndreMiras/garden.zbarcam/issues/3 19 | canvas.before: 20 | PushMatrix 21 | Rotate: 22 | angle: -90 if root.is_android() else 0 23 | origin: self.center 24 | canvas.after: 25 | PopMatrix 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # garden.zbarcam 2 | 3 | Real time Barcode and QR Code scanner using the camera. 4 | It's built on top of [Kivy](https://github.com/kivy/kivy) and [ZBar](https://github.com/ZBar/ZBar). 5 | 6 | screenshot 7 | 8 | ## How to use 9 | Simply import and instanciate `ZBarCam` in your kvlang file and access its `symbols` property. 10 | ``` 11 | #:import ZBarCam zbarcam 12 | BoxLayout: 13 | orientation: 'vertical' 14 | ZBarCam: 15 | id: zbarcam 16 | Label: 17 | size_y: 20 18 | size_hint_y: None 19 | text: ", ".join([str(symbol.data) for symbol in zbarcam.symbols]) 20 | ``` 21 | 22 | ## Install 23 | Install system requirements (Ubuntu 16.04): 24 | ``` 25 | sudo apt install libzbar-dev 26 | ``` 27 | 28 | Install garden requirements: 29 | ``` 30 | garden install xcamera 31 | ``` 32 | 33 | Install zbarcam: 34 | ``` 35 | garden install zbarcam 36 | ``` 37 | 38 | You may also need to compile/install OpenCV manually, see [OpenCV.md](OpenCV.md). 39 | 40 | ## Troubleshooting 41 | 42 | ### Android `ValueError: Empty module name` 43 | More likely an import issue in your `.kv` file. 44 | Try to `from zbarcam import ZBarCam` in your `main.py` to see the exact error. 45 | It's common to forget `pil` in `buildozer.spec` `requirements` section. 46 | 47 | ## Credits 48 | I borrowed a lot of code from [tito/android-zbar-qrcode](https://github.com/tito/android-zbar-qrcode). 49 | -------------------------------------------------------------------------------- /zbarcam/zbarcam.py: -------------------------------------------------------------------------------- 1 | import os 2 | from collections import namedtuple 3 | 4 | import PIL 5 | import zbar 6 | from kivy.app import App 7 | from kivy.clock import Clock 8 | from kivy.lang import Builder 9 | from kivy.properties import ListProperty 10 | from kivy.uix.anchorlayout import AnchorLayout 11 | from kivy.utils import platform 12 | 13 | # Pillow is not currently available for Android: 14 | # https://github.com/kivy/python-for-android/pull/786 15 | try: 16 | # Pillow 17 | PIL.Image.frombytes 18 | PIL.Image.Image.tobytes 19 | except AttributeError: 20 | # PIL 21 | PIL.Image.frombytes = PIL.Image.frombuffer 22 | PIL.Image.Image.tobytes = PIL.Image.Image.tostring 23 | 24 | MODULE_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) 25 | Builder.load_file(os.path.join(MODULE_DIRECTORY, "zbarcam.kv")) 26 | 27 | 28 | class ZBarCam(AnchorLayout): 29 | """ 30 | Widget that use the Camera and zbar to detect qrcode. 31 | When found, the `symbols` will be updated. 32 | """ 33 | resolution = ListProperty([640, 480]) 34 | 35 | symbols = ListProperty([]) 36 | 37 | Qrcode = namedtuple( 38 | 'Qrcode', ['type', 'data', 'bounds', 'quality', 'count']) 39 | 40 | def __init__(self, **kwargs): 41 | super(ZBarCam, self).__init__(**kwargs) 42 | Clock.schedule_once(lambda dt: self._setup()) 43 | # creates a scanner used for detecting qrcode 44 | self.scanner = zbar.ImageScanner() 45 | 46 | def _setup(self): 47 | """ 48 | Postpones some setup tasks that require self.ids dictionary. 49 | """ 50 | self._remove_shoot_button() 51 | self._enable_android_autofocus() 52 | self.xcamera._camera.bind(on_texture=self._on_texture) 53 | # self.add_widget(self.xcamera) 54 | 55 | def _remove_shoot_button(self): 56 | """ 57 | Removes the "shoot button", see: 58 | https://github.com/kivy-garden/garden.xcamera/pull/3 59 | """ 60 | xcamera = self.xcamera 61 | shoot_button = xcamera.children[0] 62 | xcamera.remove_widget(shoot_button) 63 | 64 | def _enable_android_autofocus(self): 65 | """ 66 | Enables autofocus on Android. 67 | """ 68 | if not self.is_android(): 69 | return 70 | camera = self.xcamera._camera._android_camera 71 | params = camera.getParameters() 72 | params.setFocusMode('continuous-video') 73 | camera.setParameters(params) 74 | 75 | def _on_texture(self, instance): 76 | self._detect_qrcode_frame( 77 | instance=None, camera=instance, texture=instance.texture) 78 | 79 | def _detect_qrcode_frame(self, instance, camera, texture): 80 | image_data = texture.pixels 81 | size = texture.size 82 | fmt = texture.colorfmt.upper() 83 | pil_image = PIL.Image.frombytes(mode=fmt, size=size, data=image_data) 84 | # convert to greyscale; since zbar only works with it 85 | pil_image = pil_image.convert('L') 86 | width, height = pil_image.size 87 | raw_image = pil_image.tobytes() 88 | zimage = zbar.Image(width, height, "Y800", raw_image) 89 | result = self.scanner.scan(zimage) 90 | if result == 0: 91 | self.symbols = [] 92 | return 93 | # we detected qrcode extract and dispatch them 94 | symbols = [] 95 | for symbol in zimage: 96 | qrcode = ZBarCam.Qrcode( 97 | type=symbol.type, 98 | data=symbol.data, 99 | quality=symbol.quality, 100 | count=symbol.count, 101 | bounds=None) 102 | symbols.append(qrcode) 103 | self.symbols = symbols 104 | 105 | @property 106 | def xcamera(self): 107 | return self.ids['xcamera'] 108 | 109 | def start(self): 110 | self.xcamera.play = True 111 | 112 | def stop(self): 113 | self.xcamera.play = False 114 | 115 | def is_android(self): 116 | return platform == 'android' 117 | 118 | 119 | DEMO_APP_KV_LANG = """ 120 | BoxLayout: 121 | orientation: 'vertical' 122 | ZBarCam: 123 | id: zbarcam 124 | Label: 125 | size_hint: None, None 126 | size: self.texture_size[0], 50 127 | text: ", ".join([str(symbol.data) for symbol in zbarcam.symbols]) 128 | """ 129 | 130 | 131 | class DemoApp(App): 132 | 133 | def build(self): 134 | return Builder.load_string(DEMO_APP_KV_LANG) 135 | 136 | 137 | if __name__ == '__main__': 138 | DemoApp().run() 139 | --------------------------------------------------------------------------------