├── .gitignore ├── LICENSE ├── README.md ├── cython ├── factory.pyx ├── hacks.h ├── pylon_def.pxd └── version.pyx ├── examples └── list_cameras.py ├── pypylon ├── __init__.py └── cython │ └── __init__.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Sphinx documentation 59 | docs/_build/ 60 | 61 | # PyBuilder 62 | target/ 63 | 64 | #Ipython Notebook 65 | .ipynb_checkpoints 66 | 67 | # pyenv 68 | .python-version -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Matthias Blaicher (Karlsruhe Institute of Technology, KIT) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the KIT nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyPylon 2 | An experimental python wrapper around the Basler Pylon 5 library. 3 | Its initial ideas were inspired by the [python-pylon](https://github.com/srgblnch/python-pylon) which is also a cython based wrapper around the Basler pylon library. 4 | 5 | However, in contrast to `python-pylon` this code directly instanciates the Pylon C++ classes inside the cython code instead of adding another C++ abstraction layer. In addition it tries to automagically configure your build environment and to provide you with a PEP8 conform pythonic access to your camera. 6 | 7 | While the basic code seems to work, I'd like to point out, that it still in early alpha stage. You will probably stumble over bugs. 8 | 9 | ## Current TODO list and development targets 10 | - [ ] Test with color cameras 11 | - [x] Handle different image packing other than Mono8 12 | - [ ] Make cython code more modular 13 | - [ ] Support commands 14 | - [ ] Try triggered images and such 15 | - [ ] Add some callbacks on events 16 | - [x] Test code under Windows 17 | 18 | ## Simple usage example 19 | ```python 20 | >>> import pypylon 21 | >>> pypylon.pylon_version.version 22 | '5.0.1.build_6388' 23 | >>> available_cameras = pypylon.factory.find_devices() 24 | >>> available_cameras 25 | [] 26 | >>> cam = pypylon.factory.create_device(available_cameras[0]) 27 | >>> cam.opened 28 | False 29 | >>> cam.open() 30 | 31 | >>> cam.properties['ExposureTime'] 32 | 10000.0 33 | >>> cam.properties['ExposureTime'] = 1000 34 | >>> # Go to full available speed 35 | ... cam.properties['DeviceLinkThroughputLimitMode'] = 'Off' 36 | >>> 37 | 38 | >>> import matplotlib.pyplot as plt 39 | >>> for image in cam.grab_images(1): 40 | ... plt.imshow(image) 41 | ... plt.show() 42 | ``` 43 | -------------------------------------------------------------------------------- /cython/factory.pyx: -------------------------------------------------------------------------------- 1 | from cython.operator cimport dereference as deref, preincrement as inc 2 | from libcpp cimport bool 3 | from libcpp.string cimport string 4 | 5 | cimport numpy as np 6 | import numpy as np 7 | 8 | from pylon_def cimport * 9 | 10 | 11 | cdef class DeviceInfo: 12 | cdef: 13 | CDeviceInfo dev_info 14 | 15 | @staticmethod 16 | cdef create(CDeviceInfo dev_info): 17 | obj = DeviceInfo() 18 | obj.dev_info = dev_info 19 | return obj 20 | 21 | property serial_number: 22 | def __get__(self): 23 | return ((self.dev_info.GetSerialNumber())).decode('ascii') 24 | 25 | property model_name: 26 | def __get__(self): 27 | return ((self.dev_info.GetModelName())).decode('ascii') 28 | 29 | property user_defined_name: 30 | def __get__(self): 31 | return ((self.dev_info.GetUserDefinedName())).decode('ascii') 32 | 33 | property device_version: 34 | def __get__(self): 35 | return ((self.dev_info.GetDeviceVersion())).decode('ascii') 36 | 37 | property friendly_name: 38 | def __get__(self): 39 | return ((self.dev_info.GetFriendlyName())).decode('ascii') 40 | 41 | property vendor_name: 42 | def __get__(self): 43 | return ((self.dev_info.GetVendorName())).decode('ascii') 44 | 45 | property device_class: 46 | def __get__(self): 47 | return ((self.dev_info.GetDeviceClass())).decode('ascii') 48 | 49 | def __repr__(self): 50 | return ''.format(self.serial_number, self.friendly_name) 51 | 52 | cdef class _PropertyMap: 53 | cdef: 54 | INodeMap* map 55 | 56 | @staticmethod 57 | cdef create(INodeMap* map): 58 | obj = _PropertyMap() 59 | obj.map = map 60 | return obj 61 | 62 | def get_description(self, basestring key): 63 | cdef bytes btes_name = key.encode() 64 | cdef INode* node = self.map.GetNode(gcstring(btes_name)) 65 | 66 | if node == NULL: 67 | raise KeyError('Key does not exist') 68 | 69 | return ((node.GetDescription())).decode() 70 | 71 | 72 | def get_display_name(self, basestring key): 73 | cdef bytes btes_name = key.encode() 74 | cdef INode* node = self.map.GetNode(gcstring(btes_name)) 75 | 76 | if node == NULL: 77 | raise KeyError('Key does not exist') 78 | 79 | return ((node.GetDisplayName())).decode() 80 | 81 | 82 | def __getitem__(self, basestring key): 83 | cdef bytes btes_name = key.encode() 84 | cdef INode* node = self.map.GetNode(gcstring(btes_name)) 85 | 86 | if node == NULL: 87 | raise KeyError('Key does not exist') 88 | 89 | if not node_is_readable(node): 90 | raise IOError('Key is not readable') 91 | 92 | # We need to try different types and check if the dynamic_cast succeeds... UGLY! 93 | # Potentially we could also use GetPrincipalInterfaceType here. 94 | cdef IBoolean* boolean_value = dynamic_cast_iboolean_ptr(node) 95 | if boolean_value != NULL: 96 | return boolean_value.GetValue() 97 | 98 | cdef IInteger* integer_value = dynamic_cast_iinteger_ptr(node) 99 | if integer_value != NULL: 100 | return integer_value.GetValue() 101 | 102 | cdef IFloat* float_value = dynamic_cast_ifloat_ptr(node) 103 | if float_value != NULL: 104 | return float_value.GetValue() 105 | 106 | # TODO: Probably we also need some type of enum to be useful 107 | 108 | # Potentially, we can always get the setting by string 109 | cdef IValue* string_value = dynamic_cast_ivalue_ptr(node) 110 | if string_value == NULL: 111 | return 112 | 113 | return ((string_value.ToString())).decode() 114 | 115 | def __setitem__(self, str key, value): 116 | cdef bytes bytes_name = key.encode() 117 | cdef INode* node = self.map.GetNode(gcstring(bytes_name)) 118 | 119 | if node == NULL: 120 | raise KeyError('Key does not exist') 121 | 122 | if not node_is_writable(node): 123 | raise IOError('Key is not writable') 124 | 125 | # We need to try different types and check if the dynamic_cast succeeds... UGLY! 126 | # Potentially we could also use GetPrincipalInterfaceType here. 127 | cdef IBoolean* boolean_value = dynamic_cast_iboolean_ptr(node) 128 | if boolean_value != NULL: 129 | boolean_value.SetValue(value) 130 | return 131 | 132 | cdef IInteger* integer_value = dynamic_cast_iinteger_ptr(node) 133 | if integer_value != NULL: 134 | if value < integer_value.GetMin() or value > integer_value.GetMax(): 135 | raise ValueError('Parameter value for {} not inside valid range [{}, {}], was {}'.format( 136 | key, integer_value.GetMin(), integer_value.GetMax(), value)) 137 | integer_value.SetValue(value) 138 | return 139 | 140 | cdef IFloat* float_value = dynamic_cast_ifloat_ptr(node) 141 | if float_value != NULL: 142 | if value < float_value.GetMin() or value > float_value.GetMax(): 143 | raise ValueError('Parameter value for {} not inside valid range [{}, {}], was {}'.format( 144 | key, float_value.GetMin(), float_value.GetMax(), value)) 145 | float_value.SetValue(value) 146 | return 147 | 148 | # TODO: Probably we also need some type of enum to be useful 149 | 150 | # Potentially, we can always set the setting by string 151 | cdef IValue* string_value = dynamic_cast_ivalue_ptr(node) 152 | if string_value == NULL: 153 | raise RuntimeError('Can not set key %s by string' % key) 154 | 155 | cdef bytes bytes_value = str(value).encode() 156 | string_value.FromString(gcstring(bytes_value)) 157 | 158 | def keys(self): 159 | node_keys = list() 160 | 161 | # Iterate through the discovered devices 162 | cdef NodeList_t nodes 163 | self.map.GetNodes(nodes) 164 | 165 | cdef NodeList_t.iterator it = nodes.begin() 166 | while it != nodes.end(): 167 | if deref(it).IsFeature() and dynamic_cast_icategory_ptr(deref(it)) == NULL: 168 | name = ((deref(it).GetName())).decode('ascii') 169 | node_keys.append(name) 170 | inc(it) 171 | 172 | return node_keys 173 | 174 | 175 | cdef class Camera: 176 | cdef: 177 | CInstantCamera camera 178 | 179 | @staticmethod 180 | cdef create(IPylonDevice* device): 181 | obj = Camera() 182 | obj.camera.Attach(device) 183 | return obj 184 | 185 | property device_info: 186 | def __get__(self): 187 | dev_inf = DeviceInfo.create(self.camera.GetDeviceInfo()) 188 | return dev_inf 189 | 190 | property opened: 191 | def __get__(self): 192 | return self.camera.IsOpen() 193 | def __set__(self, opened): 194 | if self.opened and not opened: 195 | self.camera.Close() 196 | elif not self.opened and opened: 197 | self.camera.Open() 198 | 199 | def open(self): 200 | self.camera.Open() 201 | 202 | def close(self): 203 | self.camera.Close() 204 | 205 | def __del__(self): 206 | self.close() 207 | self.camera.DetachDevice() 208 | 209 | def __repr__(self): 210 | return ''.format(self.device_info.friendly_name, self.opened) 211 | 212 | def grab_images(self, int nr_images, unsigned int timeout=5000): 213 | if not self.opened: 214 | raise RuntimeError('Camera not opened') 215 | 216 | self.camera.StartGrabbing(nr_images) 217 | 218 | cdef CGrabResultPtr ptr_grab_result 219 | cdef IImage* img 220 | 221 | cdef str image_format = str(self.properties['PixelFormat']) 222 | cdef str bits_per_pixel_prop = str(self.properties['PixelSize']) 223 | assert bits_per_pixel_prop.startswith('Bpp'), 'PixelSize property should start with "Bpp"' 224 | assert image_format.startswith('Mono'), 'Only mono images allowed at this point' 225 | assert not image_format.endswith('p'), 'Packed data not supported at this point' 226 | 227 | while self.camera.IsGrabbing(): 228 | 229 | with nogil: 230 | # Blocking call into native Pylon C++ SDK code, release GIL so other python threads can run 231 | self.camera.RetrieveResult(timeout, ptr_grab_result) 232 | 233 | if not ACCESS_CGrabResultPtr_GrabSucceeded(ptr_grab_result): 234 | error_desc = ((ACCESS_CGrabResultPtr_GetErrorDescription(ptr_grab_result))).decode() 235 | raise RuntimeError(error_desc) 236 | 237 | img = &(ptr_grab_result) 238 | if not img.IsValid(): 239 | raise RuntimeError('Graped IImage is not valid.') 240 | 241 | if img.GetImageSize() % img.GetHeight(): 242 | print('This image buffer is wired. Probably you will see an error soonish.') 243 | print('\tBytes:', img.GetImageSize()) 244 | print('\tHeight:', img.GetHeight()) 245 | print('\tWidth:', img.GetWidth()) 246 | print('\tGetPaddingX:', img.GetPaddingX()) 247 | 248 | assert not img.GetPaddingX(), 'Image padding not supported.' 249 | # TODO: Check GetOrientation to fix oritentation of image if required. 250 | 251 | img_data = np.frombuffer((img.GetBuffer())[:img.GetImageSize()], dtype='uint'+bits_per_pixel_prop[3:]) 252 | 253 | # TODO: How to handle multi-byte data here? 254 | img_data = img_data.reshape((img.GetHeight(), -1)) 255 | # img_data = img_data[:img.GetHeight(), :img.GetWidth()] 256 | yield img_data 257 | 258 | def grab_image(self, unsigned int timeout=5000): 259 | return next(self.grab_images(1, timeout)) 260 | 261 | property properties: 262 | def __get__(self): 263 | return _PropertyMap.create(&self.camera.GetNodeMap()) 264 | 265 | 266 | cdef class Factory: 267 | def __cinit__(self): 268 | PylonInitialize() 269 | 270 | def __dealloc__(self): 271 | PylonTerminate() 272 | 273 | def find_devices(self): 274 | cdef CTlFactory* tl_factory = &GetInstance() 275 | cdef DeviceInfoList_t devices 276 | 277 | cdef int nr_devices = tl_factory.EnumerateDevices(devices) 278 | 279 | found_devices = list() 280 | 281 | # Iterate through the discovered devices 282 | cdef DeviceInfoList_t.iterator it = devices.begin() 283 | while it != devices.end(): 284 | found_devices.append(DeviceInfo.create(deref(it))) 285 | inc(it) 286 | 287 | return found_devices 288 | 289 | def create_device(self, DeviceInfo dev_info): 290 | cdef CTlFactory* tl_factory = &GetInstance() 291 | return Camera.create(tl_factory.CreateDevice(dev_info.dev_info)) -------------------------------------------------------------------------------- /cython/hacks.h: -------------------------------------------------------------------------------- 1 | #ifndef HACK_H 2 | #define HACK_H 3 | 4 | #define ACCESS_CGrabResultPtr_GrabSucceeded(x) x->GrabSucceeded() 5 | #define ACCESS_CGrabResultPtr_GetErrorDescription(x) x->GetErrorDescription() 6 | #define ACCESS_CGrabResultPtr_GetErrorCode(x) x->GetErrorCode() 7 | 8 | #endif -------------------------------------------------------------------------------- /cython/pylon_def.pxd: -------------------------------------------------------------------------------- 1 | from libcpp cimport bool 2 | from libc.stdint cimport uint32_t, uint64_t, int64_t 3 | from libcpp.string cimport string 4 | 5 | cdef extern from "Base/GCBase.h": 6 | cdef cppclass gcstring: 7 | gcstring(char*) 8 | 9 | cdef extern from "GenApi/GenApi.h" namespace 'GenApi': 10 | 11 | cdef cppclass INode: 12 | gcstring GetName(bool FullQualified=False) 13 | gcstring GetNameSpace() 14 | gcstring GetDescription() 15 | gcstring GetDisplayName() 16 | bool IsFeature() 17 | gcstring GetValue() 18 | 19 | # Types an INode could be 20 | cdef cppclass IValue: 21 | gcstring ToString() 22 | void FromString(gcstring, bool verify=True) except + 23 | 24 | cdef cppclass IBoolean: 25 | bool GetValue() 26 | void SetValue(bool) except + 27 | 28 | cdef cppclass IInteger: 29 | int64_t GetValue() 30 | void SetValue(int64_t) except + 31 | int64_t GetMin() 32 | int64_t GetMax() 33 | 34 | cdef cppclass IString 35 | cdef cppclass IFloat: 36 | double GetValue() 37 | void SetValue(double) except + 38 | double GetMin() 39 | double GetMax() 40 | 41 | cdef cppclass NodeList_t: 42 | cppclass iterator: 43 | INode* operator*() 44 | iterator operator++() 45 | bint operator==(iterator) 46 | bint operator!=(iterator) 47 | NodeList_t() 48 | CDeviceInfo& operator[](int) 49 | CDeviceInfo& at(int) 50 | iterator begin() 51 | iterator end() 52 | 53 | cdef cppclass ICategory 54 | 55 | cdef cppclass INodeMap: 56 | void GetNodes(NodeList_t&) 57 | INode* GetNode(gcstring& ) 58 | uint32_t GetNumNodes() 59 | 60 | cdef extern from *: 61 | IValue* dynamic_cast_ivalue_ptr "dynamic_cast" (INode*) except + 62 | IBoolean* dynamic_cast_iboolean_ptr "dynamic_cast" (INode*) except + 63 | IInteger* dynamic_cast_iinteger_ptr "dynamic_cast" (INode*) except + 64 | IFloat* dynamic_cast_ifloat_ptr "dynamic_cast" (INode*) except + 65 | INodeMap* dynamic_cast_inodemap_ptr "dynamic_cast" (INode*) except + 66 | INodeMap* dynamic_cast_inodemap_ptr "dynamic_cast" (INode*) except + 67 | ICategory* dynamic_cast_icategory_ptr "dynamic_cast" (INode*) except + 68 | 69 | bool node_is_readable "GenApi::IsReadable" (INode*) except + 70 | bool node_is_writable "GenApi::IsWritable" (INode*) except + 71 | bool node_is_implemented "GenApi::IsImplemented" (INode*) except + 72 | 73 | cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon': 74 | # Common special data types 75 | cdef cppclass String_t 76 | cdef cppclass StringList_t 77 | 78 | # Top level init functions 79 | void PylonInitialize() except + 80 | void PylonTerminate() except + 81 | 82 | # cdef enum EPixelType: 83 | 84 | cdef cppclass IImage: 85 | uint32_t GetWidth() 86 | uint32_t GetHeight() 87 | size_t GetPaddingX() 88 | size_t GetImageSize() 89 | void* GetBuffer() 90 | bool IsValid() 91 | 92 | cdef cppclass CGrabResultData: 93 | bool GrabSucceeded() 94 | 95 | cdef cppclass CGrabResultPtr: 96 | IImage& operator() 97 | #CGrabResultData* operator->() 98 | 99 | 100 | cdef cppclass IPylonDevice: 101 | pass 102 | 103 | cdef cppclass CDeviceInfo: 104 | String_t GetSerialNumber() except + 105 | String_t GetUserDefinedName() except + 106 | String_t GetModelName() except + 107 | String_t GetDeviceVersion() except + 108 | String_t GetFriendlyName() except + 109 | String_t GetVendorName() except + 110 | String_t GetDeviceClass() except + 111 | 112 | cdef cppclass CInstantCamera: 113 | CInstantCamera() 114 | void Attach(IPylonDevice*) 115 | CDeviceInfo& GetDeviceInfo() except + 116 | void IsCameraDeviceRemoved() 117 | void Open() except + 118 | void Close() except + 119 | bool IsOpen() except + 120 | IPylonDevice* DetachDevice() except + 121 | void StartGrabbing(size_t maxImages) except + #FIXME: implement different strategies 122 | bool IsGrabbing() 123 | # RetrieveResult() is blocking call into C++ native SDK, allow it to be called without GIL 124 | bool RetrieveResult(unsigned int timeout_ms, CGrabResultPtr& grab_result) nogil except + # FIXME: Timout handling 125 | INodeMap& GetNodeMap() 126 | 127 | cdef cppclass DeviceInfoList_t: 128 | cppclass iterator: 129 | CDeviceInfo operator*() 130 | iterator operator++() 131 | bint operator==(iterator) 132 | bint operator!=(iterator) 133 | DeviceInfoList_t() 134 | CDeviceInfo& operator[](int) 135 | CDeviceInfo& at(int) 136 | iterator begin() 137 | iterator end() 138 | 139 | cdef cppclass CTlFactory: 140 | int EnumerateDevices(DeviceInfoList_t&, bool add_to_list=False) 141 | IPylonDevice* CreateDevice(CDeviceInfo&) 142 | 143 | # Hack to define a static member function 144 | cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon::CTlFactory': 145 | CTlFactory& GetInstance() 146 | 147 | # EVIL HACK: We cannot dereference officially with the -> operator. So we use ugly macros... 148 | cdef extern from 'hacks.h': 149 | bool ACCESS_CGrabResultPtr_GrabSucceeded(CGrabResultPtr ptr) 150 | String_t ACCESS_CGrabResultPtr_GetErrorDescription(CGrabResultPtr ptr) 151 | uint32_t ACCESS_CGrabResultPtr_GetErrorCode(CGrabResultPtr ptr) -------------------------------------------------------------------------------- /cython/version.pyx: -------------------------------------------------------------------------------- 1 | cdef extern from "pylon/PylonVersionNumber.h": 2 | int PYLON_VERSION_MAJOR 3 | int PYLON_VERSION_MINOR 4 | int PYLON_VERSION_SUBMINOR 5 | int PYLON_VERSION_BUILD 6 | 7 | cdef class PylonVersion: 8 | property major: 9 | def __get__(self): 10 | return PYLON_VERSION_MAJOR 11 | 12 | property minor: 13 | def __get__(self): 14 | return PYLON_VERSION_MINOR 15 | 16 | property subminor: 17 | def __get__(self): 18 | return PYLON_VERSION_SUBMINOR 19 | 20 | property build: 21 | def __get__(self): 22 | return PYLON_VERSION_BUILD 23 | 24 | property version_tuple: 25 | def __get__(self): 26 | return self.major, self.minor, self.subminor, self.build 27 | 28 | property version: 29 | def __get__(self): 30 | return '%i.%i.%i.build_%i' % self.version_tuple -------------------------------------------------------------------------------- /examples/list_cameras.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, print_function, division 2 | 3 | import pypylon 4 | import matplotlib.pyplot as plt 5 | import tqdm 6 | import numpy as np 7 | 8 | print('Build against pylon library version:', pypylon.pylon_version.version) 9 | 10 | available_cameras = pypylon.factory.find_devices() 11 | print('Available cameras are', available_cameras) 12 | 13 | # Grep the first one and create a camera for it 14 | cam = pypylon.factory.create_device(available_cameras[-1]) 15 | 16 | # We can still get information of the camera back 17 | print('Camera info of camera object:', cam.device_info) 18 | 19 | # Open camera and grep some images 20 | cam.open() 21 | 22 | # Hard code exposure time 23 | # cam.properties['ExposureTime'] = 10000.0 24 | cam.properties['PixelFormat'] = 'Mono12' 25 | print(cam.properties['PixelSize']) 26 | 27 | # Go to full available speed 28 | # cam.properties['DeviceLinkThroughputLimitMode'] = 'Off' 29 | 30 | for key in cam.properties.keys(): 31 | try: 32 | value = cam.properties[key] 33 | except IOError: 34 | value = '' 35 | 36 | print('{0} ({1}):\t{2}'.format(key, cam.properties.get_description(key), value)) 37 | 38 | 39 | # while True: 40 | # cam.grap_image() 41 | 42 | # for image in tqdm.tqdm(cam.grap_images(200), leave=True): 43 | # pass 44 | 45 | # plt.figure() 46 | # plt.imshow(np.mean([img for img in cam.grap_images(100)], axis=0, dtype=np.float)) 47 | 48 | plt.figure() 49 | for image in cam.grab_images(1): 50 | print(image.shape) 51 | plt.imshow(image) 52 | plt.show() 53 | -------------------------------------------------------------------------------- /pypylon/__init__.py: -------------------------------------------------------------------------------- 1 | from pypylon.cython.factory import Factory 2 | from pypylon.cython.version import PylonVersion 3 | 4 | factory = Factory() 5 | pylon_version = PylonVersion() -------------------------------------------------------------------------------- /pypylon/cython/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabl/PyPylon/7306818a4bb2f8cb2ab370def2a7700d830db406/pypylon/cython/__init__.py -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from Cython.Distutils import build_ext, Extension 3 | import subprocess 4 | import os 5 | import os.path 6 | import sys 7 | import numpy 8 | import glob 9 | 10 | def detect_pylon(config_config='/opt/pylon5/bin/pylon-config'): 11 | compiler_config = dict() 12 | compiler_config['library_dirs'] = [subprocess.check_output([config_config, '--libdir']).decode().strip(), ] 13 | compiler_config['include_dirs'] = [_.strip() for _ in 14 | subprocess.check_output([config_config, 15 | '--cflags-only-I']).decode().strip().split('-I') if _] 16 | compiler_config['libraries'] = [_.strip() for _ in 17 | subprocess.check_output([config_config, 18 | '--libs-only-l']).decode().strip().split('-l') if _] 19 | compiler_config['runtime_library_dirs'] = compiler_config['library_dirs'] 20 | return compiler_config 21 | 22 | def is_windows_64bit(): 23 | if 'PROCESSOR_ARCHITEW6432' in os.environ: 24 | return True 25 | return os.environ['PROCESSOR_ARCHITECTURE'].endswith('64') 26 | 27 | def fake_detect_pylon_windows(pylon_dir=r'C:\Program Files\Basler\pylon 5'): 28 | if not os.path.isdir(pylon_dir): 29 | raise RuntimeError('Pylon directory not found') 30 | 31 | if not os.path.isdir(os.path.join(pylon_dir, 'Development')): 32 | raise RuntimeError('You need to install Pylon with the development options') 33 | 34 | arch = 'x64' if is_windows_64bit() else 'Win32' 35 | 36 | compiler_config = dict() 37 | compiler_config['include_dirs'] = [os.path.join(pylon_dir, 'Development', 'include')] 38 | compiler_config['library_dirs'] = [os.path.join(pylon_dir, 'Runtime', arch), 39 | os.path.join(pylon_dir, 'Development', 'lib', arch)] 40 | compiler_config['libraries'] = list([_[:-4] for _ in os.listdir(os.path.join(pylon_dir, 'Development', 'lib', arch)) 41 | if _.endswith('.lib')]) 42 | return compiler_config 43 | 44 | def fake_detect_pylon_osx(pylon_dir='/Library/Frameworks/pylon.framework'): 45 | if not os.path.isdir(pylon_dir): 46 | raise RuntimeError('Pylon framework not found') 47 | 48 | compiler_config = dict() 49 | compiler_config['include_dirs'] = [os.path.join(pylon_dir, 'Headers'), 50 | os.path.join(pylon_dir, 'Headers', 'GenICam')] 51 | 52 | compiler_config['extra_link_args'] = ['-rpath', os.path.join(*os.path.split(pylon_dir)[:-1]), 53 | '-framework', 'pylon'] 54 | return compiler_config 55 | 56 | 57 | if sys.platform == 'win32': 58 | build_options = fake_detect_pylon_windows() 59 | elif sys.platform == 'darwin': 60 | build_options = fake_detect_pylon_osx() 61 | else: 62 | build_options = detect_pylon() 63 | 64 | # Set build language 65 | build_options['language'] = 'c++' 66 | 67 | # Add numpy build options 68 | build_options['include_dirs'].append(numpy.get_include()) 69 | 70 | pypylon_extensions = [Extension('pypylon.cython.version', ['cython/version.pyx', ], **build_options), 71 | Extension('pypylon.cython.factory', ['cython/factory.pyx', ], **build_options), 72 | ] 73 | 74 | setup(name='pypylon', 75 | license="custom", 76 | description="Cython module to provide access to Pylon's SDK.", 77 | version='0.0.1', 78 | author="Matthias Blaicher", 79 | author_email="matthias@blaicher.com", 80 | cmdclass={'build_ext': build_ext}, 81 | ext_modules=pypylon_extensions, 82 | packages=find_packages(exclude=['contrib', 'docs', 'tests', 'examples', 'cython']), 83 | 84 | # for the classifiers review see: 85 | # https://pypi.python.org/pypi?%3Aaction=list_classifiers 86 | classifiers=[ 87 | 'Development Status :: 3 - Alpha', 88 | 89 | 'Intended Audience :: Developers', 90 | 'Topic :: Multimedia :: Graphics :: Capture :: Digital Camera' 91 | 92 | 'License :: OSI Approved :: BSD License', 93 | 94 | 'Programming Language :: Python :: 3', 95 | 'Programming Language :: Python :: 3.2', 96 | 'Programming Language :: Python :: 3.3', 97 | 'Programming Language :: Python :: 3.4', 98 | 'Programming Language :: Python :: 3.5', 99 | ], 100 | ) 101 | --------------------------------------------------------------------------------