From 57793e50267671131fdc9bd81f8c9f2df20b88d4 Mon Sep 17 00:00:00 2001 From: ZacDiggum Date: Mon, 10 Oct 2016 14:08:32 +0200 Subject: [PATCH 01/10] added infinite grabbing and related added function cam.stop_grabbing() added property cam.is_grabbing changed cam.grab_images(int) to infinite grabbing when int<1 --- factory.pyx | 300 ++++++++++++++++++++++++++++++++++++++++++++++++++ pylon_def.pxd | 152 +++++++++++++++++++++++++ 2 files changed, 452 insertions(+) create mode 100644 factory.pyx create mode 100644 pylon_def.pxd diff --git a/factory.pyx b/factory.pyx new file mode 100644 index 0000000..88e235e --- /dev/null +++ b/factory.pyx @@ -0,0 +1,300 @@ +from cython.operator cimport dereference as deref, preincrement as inc +from libcpp cimport bool +from libcpp.string cimport string + +cimport numpy as np +import numpy as np + +from pylon_def cimport * + + +cdef class DeviceInfo: + cdef: + CDeviceInfo dev_info + + @staticmethod + cdef create(CDeviceInfo dev_info): + obj = DeviceInfo() + obj.dev_info = dev_info + return obj + + property serial_number: + def __get__(self): + return ((self.dev_info.GetSerialNumber())).decode('ascii') + + property model_name: + def __get__(self): + return ((self.dev_info.GetModelName())).decode('ascii') + + property user_defined_name: + def __get__(self): + return ((self.dev_info.GetUserDefinedName())).decode('ascii') + + property device_version: + def __get__(self): + return ((self.dev_info.GetDeviceVersion())).decode('ascii') + + property friendly_name: + def __get__(self): + return ((self.dev_info.GetFriendlyName())).decode('ascii') + + property vendor_name: + def __get__(self): + return ((self.dev_info.GetVendorName())).decode('ascii') + + property device_class: + def __get__(self): + return ((self.dev_info.GetDeviceClass())).decode('ascii') + + def __repr__(self): + return ''.format(self.serial_number, self.friendly_name) + +cdef class _PropertyMap: + cdef: + INodeMap* map + + @staticmethod + cdef create(INodeMap* map): + obj = _PropertyMap() + obj.map = map + return obj + + def get_description(self, basestring key): + cdef bytes btes_name = key.encode() + cdef INode* node = self.map.GetNode(gcstring(btes_name)) + + if node == NULL: + raise KeyError('Key does not exist') + + return ((node.GetDescription())).decode() + + + def get_display_name(self, basestring key): + cdef bytes btes_name = key.encode() + cdef INode* node = self.map.GetNode(gcstring(btes_name)) + + if node == NULL: + raise KeyError('Key does not exist') + + return ((node.GetDisplayName())).decode() + + + def __getitem__(self, basestring key): + cdef bytes btes_name = key.encode() + cdef INode* node = self.map.GetNode(gcstring(btes_name)) + + if node == NULL: + raise KeyError('Key does not exist') + + if not node_is_readable(node): + raise IOError('Key is not readable') + + # We need to try different types and check if the dynamic_cast succeeds... UGLY! + # Potentially we could also use GetPrincipalInterfaceType here. + cdef IBoolean* boolean_value = dynamic_cast_iboolean_ptr(node) + if boolean_value != NULL: + return boolean_value.GetValue() + + cdef IInteger* integer_value = dynamic_cast_iinteger_ptr(node) + if integer_value != NULL: + return integer_value.GetValue() + + cdef IFloat* float_value = dynamic_cast_ifloat_ptr(node) + if float_value != NULL: + return float_value.GetValue() + + # TODO: Probably we also need some type of enum to be useful + + # Potentially, we can always get the setting by string + cdef IValue* string_value = dynamic_cast_ivalue_ptr(node) + if string_value == NULL: + return + + return ((string_value.ToString())).decode() + + def __setitem__(self, str key, value): + cdef bytes bytes_name = key.encode() + cdef INode* node = self.map.GetNode(gcstring(bytes_name)) + + if node == NULL: + raise KeyError('Key does not exist') + + if not node_is_writable(node): + raise IOError('Key is not writable') + + # We need to try different types and check if the dynamic_cast succeeds... UGLY! + # Potentially we could also use GetPrincipalInterfaceType here. + cdef IBoolean* boolean_value = dynamic_cast_iboolean_ptr(node) + if boolean_value != NULL: + boolean_value.SetValue(value) + return + + cdef IInteger* integer_value = dynamic_cast_iinteger_ptr(node) + if integer_value != NULL: + if value < integer_value.GetMin() or value > integer_value.GetMax(): + raise ValueError('Parameter value for {} not inside valid range [{}, {}], was {}'.format( + key, integer_value.GetMin(), integer_value.GetMax(), value)) + integer_value.SetValue(value) + return + + cdef IFloat* float_value = dynamic_cast_ifloat_ptr(node) + if float_value != NULL: + if value < float_value.GetMin() or value > float_value.GetMax(): + raise ValueError('Parameter value for {} not inside valid range [{}, {}], was {}'.format( + key, float_value.GetMin(), float_value.GetMax(), value)) + float_value.SetValue(value) + return + + # TODO: Probably we also need some type of enum to be useful + + # Potentially, we can always set the setting by string + cdef IValue* string_value = dynamic_cast_ivalue_ptr(node) + if string_value == NULL: + raise RuntimeError('Can not set key %s by string' % key) + + cdef bytes bytes_value = str(value).encode() + string_value.FromString(gcstring(bytes_value)) + + def keys(self): + node_keys = list() + + # Iterate through the discovered devices + cdef NodeList_t nodes + self.map.GetNodes(nodes) + + cdef NodeList_t.iterator it = nodes.begin() + while it != nodes.end(): + if deref(it).IsFeature() and dynamic_cast_icategory_ptr(deref(it)) == NULL: + name = ((deref(it).GetName())).decode('ascii') + node_keys.append(name) + inc(it) + + return node_keys + + +cdef class Camera: + cdef: + CInstantCamera camera + + @staticmethod + cdef create(IPylonDevice* device): + obj = Camera() + obj.camera.Attach(device) + return obj + + property device_info: + def __get__(self): + dev_inf = DeviceInfo.create(self.camera.GetDeviceInfo()) + return dev_inf + + property opened: + def __get__(self): + return self.camera.IsOpen() + def __set__(self, opened): + if self.opened and not opened: + self.camera.Close() + elif not self.opened and opened: + self.camera.Open() + + property is_grabbing: + def __get__(self): + return self.camera.IsGrabbing() + + def open(self): + self.camera.Open() + + def close(self): + self.camera.Close() + + def stop_grabbing(self): + self.camera.StopGrabbing() + + def __del__(self): + if self.camera.IsGrabbing(): + self.camera.StopGrabbing() + self.close() + self.camera.DetachDevice() + + def __repr__(self): + return ''.format(self.device_info.friendly_name, self.opened) + + def grab_images(self, int nr_images, unsigned int timeout=5000): + if not self.opened: + raise RuntimeError('Camera not opened') + + if nr_images < 1: + self.camera.StartGrabbing() + else: + self.camera.StartGrabbing(nr_images) + + cdef CGrabResultPtr ptr_grab_result + cdef IImage* img + + cdef str image_format = str(self.properties['PixelFormat']) + cdef str bits_per_pixel_prop = str(self.properties['PixelSize']) + assert bits_per_pixel_prop.startswith('Bpp'), 'PixelSize property should start with "Bpp"' + assert image_format.startswith('Mono'), 'Only mono images allowed at this point' + assert not image_format.endswith('p'), 'Packed data not supported at this point' + + while self.camera.IsGrabbing(): + self.camera.RetrieveResult(timeout, ptr_grab_result) + + if not ACCESS_CGrabResultPtr_GrabSucceeded(ptr_grab_result): + error_desc = ((ACCESS_CGrabResultPtr_GetErrorDescription(ptr_grab_result))).decode() + raise RuntimeError(error_desc) + + img = &(ptr_grab_result) + if not img.IsValid(): + raise RuntimeError('Graped IImage is not valid.') + + if img.GetImageSize() % img.GetHeight(): + print('This image buffer is wired. Probably you will see an error soonish.') + print('\tBytes:', img.GetImageSize()) + print('\tHeight:', img.GetHeight()) + print('\tWidth:', img.GetWidth()) + print('\tGetPaddingX:', img.GetPaddingX()) + + assert not img.GetPaddingX(), 'Image padding not supported.' + # TODO: Check GetOrientation to fix oritentation of image if required. + + img_data = np.frombuffer((img.GetBuffer())[:img.GetImageSize()], dtype='uint'+bits_per_pixel_prop[3:]) + + # TODO: How to handle multi-byte data here? + img_data = img_data.reshape((img.GetHeight(), -1)) + # img_data = img_data[:img.GetHeight(), :img.GetWidth()] + yield img_data + + def grab_image(self, unsigned int timeout=5000): + return next(self.grab_images(1, timeout)) + + property properties: + def __get__(self): + return _PropertyMap.create(&self.camera.GetNodeMap()) + + +cdef class Factory: + def __cinit__(self): + PylonInitialize() + + def __dealloc__(self): + PylonTerminate() + + def find_devices(self): + cdef CTlFactory* tl_factory = &GetInstance() + cdef DeviceInfoList_t devices + + cdef int nr_devices = tl_factory.EnumerateDevices(devices) + + found_devices = list() + + # Iterate through the discovered devices + cdef DeviceInfoList_t.iterator it = devices.begin() + while it != devices.end(): + found_devices.append(DeviceInfo.create(deref(it))) + inc(it) + + return found_devices + + def create_device(self, DeviceInfo dev_info): + cdef CTlFactory* tl_factory = &GetInstance() + return Camera.create(tl_factory.CreateDevice(dev_info.dev_info)) diff --git a/pylon_def.pxd b/pylon_def.pxd new file mode 100644 index 0000000..2943390 --- /dev/null +++ b/pylon_def.pxd @@ -0,0 +1,152 @@ +from libcpp cimport bool +from libc.stdint cimport uint32_t, uint64_t, int64_t +from libcpp.string cimport string + +cdef extern from "Base/GCBase.h": + cdef cppclass gcstring: + gcstring(char*) + +cdef extern from "GenApi/GenApi.h" namespace 'GenApi': + + cdef cppclass INode: + gcstring GetName(bool FullQualified=False) + gcstring GetNameSpace() + gcstring GetDescription() + gcstring GetDisplayName() + bool IsFeature() + gcstring GetValue() + + # Types an INode could be + cdef cppclass IValue: + gcstring ToString() + void FromString(gcstring, bool verify=True) except + + + cdef cppclass IBoolean: + bool GetValue() + void SetValue(bool) except + + + cdef cppclass IInteger: + int64_t GetValue() + void SetValue(int64_t) except + + int64_t GetMin() + int64_t GetMax() + + cdef cppclass IString + cdef cppclass IFloat: + double GetValue() + void SetValue(double) except + + double GetMin() + double GetMax() + + cdef cppclass NodeList_t: + cppclass iterator: + INode* operator*() + iterator operator++() + bint operator==(iterator) + bint operator!=(iterator) + NodeList_t() + CDeviceInfo& operator[](int) + CDeviceInfo& at(int) + iterator begin() + iterator end() + + cdef cppclass ICategory + + cdef cppclass INodeMap: + void GetNodes(NodeList_t&) + INode* GetNode(gcstring& ) + uint32_t GetNumNodes() + +cdef extern from *: + IValue* dynamic_cast_ivalue_ptr "dynamic_cast" (INode*) except + + IBoolean* dynamic_cast_iboolean_ptr "dynamic_cast" (INode*) except + + IInteger* dynamic_cast_iinteger_ptr "dynamic_cast" (INode*) except + + IFloat* dynamic_cast_ifloat_ptr "dynamic_cast" (INode*) except + + INodeMap* dynamic_cast_inodemap_ptr "dynamic_cast" (INode*) except + + INodeMap* dynamic_cast_inodemap_ptr "dynamic_cast" (INode*) except + + ICategory* dynamic_cast_icategory_ptr "dynamic_cast" (INode*) except + + + bool node_is_readable "GenApi::IsReadable" (INode*) except + + bool node_is_writable "GenApi::IsWritable" (INode*) except + + bool node_is_implemented "GenApi::IsImplemented" (INode*) except + + +cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon': + # Common special data types + cdef cppclass String_t + cdef cppclass StringList_t + + # Top level init functions + void PylonInitialize() except + + void PylonTerminate() except + + + # cdef enum EPixelType: + + cdef cppclass IImage: + uint32_t GetWidth() + uint32_t GetHeight() + size_t GetPaddingX() + size_t GetImageSize() + void* GetBuffer() + bool IsValid() + + cdef cppclass CGrabResultData: + bool GrabSucceeded() + + cdef cppclass CGrabResultPtr: + IImage& operator() + #CGrabResultData* operator->() + + + cdef cppclass IPylonDevice: + pass + + cdef cppclass CDeviceInfo: + String_t GetSerialNumber() except + + String_t GetUserDefinedName() except + + String_t GetModelName() except + + String_t GetDeviceVersion() except + + String_t GetFriendlyName() except + + String_t GetVendorName() except + + String_t GetDeviceClass() except + + + cdef cppclass CInstantCamera: + CInstantCamera() + void Attach(IPylonDevice*) + CDeviceInfo& GetDeviceInfo() except + + void IsCameraDeviceRemoved() + void Open() except + + void Close() except + + void StopGrabbing() except + + bool IsOpen() except + + IPylonDevice* DetachDevice() except + + void StartGrabbing() except + + void StartGrabbing(size_t maxImages) except + #FIXME: implement different strategies + bool IsGrabbing() + bool RetrieveResult(unsigned int timeout_ms, CGrabResultPtr& grab_result) except + # FIXME: Timout handling + INodeMap& GetNodeMap() + + cdef cppclass DeviceInfoList_t: + cppclass iterator: + CDeviceInfo operator*() + iterator operator++() + bint operator==(iterator) + bint operator!=(iterator) + DeviceInfoList_t() + CDeviceInfo& operator[](int) + CDeviceInfo& at(int) + iterator begin() + iterator end() + + cdef cppclass CTlFactory: + int EnumerateDevices(DeviceInfoList_t&, bool add_to_list=False) + IPylonDevice* CreateDevice(CDeviceInfo&) + +# Hack to define a static member function +cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon::CTlFactory': + CTlFactory& GetInstance() + +# EVIL HACK: We cannot dereference officially with the -> operator. So we use ugly macros... +cdef extern from 'hacks.h': + bool ACCESS_CGrabResultPtr_GrabSucceeded(CGrabResultPtr ptr) + String_t ACCESS_CGrabResultPtr_GetErrorDescription(CGrabResultPtr ptr) + uint32_t ACCESS_CGrabResultPtr_GetErrorCode(CGrabResultPtr ptr) From 393c19c360eb44bd778dc5d556cdb9d6a077fdc2 Mon Sep 17 00:00:00 2001 From: ZacDiggum Date: Mon, 10 Oct 2016 14:09:46 +0200 Subject: [PATCH 02/10] Delete factory.pyx --- factory.pyx | 300 ---------------------------------------------------- 1 file changed, 300 deletions(-) delete mode 100644 factory.pyx diff --git a/factory.pyx b/factory.pyx deleted file mode 100644 index 88e235e..0000000 --- a/factory.pyx +++ /dev/null @@ -1,300 +0,0 @@ -from cython.operator cimport dereference as deref, preincrement as inc -from libcpp cimport bool -from libcpp.string cimport string - -cimport numpy as np -import numpy as np - -from pylon_def cimport * - - -cdef class DeviceInfo: - cdef: - CDeviceInfo dev_info - - @staticmethod - cdef create(CDeviceInfo dev_info): - obj = DeviceInfo() - obj.dev_info = dev_info - return obj - - property serial_number: - def __get__(self): - return ((self.dev_info.GetSerialNumber())).decode('ascii') - - property model_name: - def __get__(self): - return ((self.dev_info.GetModelName())).decode('ascii') - - property user_defined_name: - def __get__(self): - return ((self.dev_info.GetUserDefinedName())).decode('ascii') - - property device_version: - def __get__(self): - return ((self.dev_info.GetDeviceVersion())).decode('ascii') - - property friendly_name: - def __get__(self): - return ((self.dev_info.GetFriendlyName())).decode('ascii') - - property vendor_name: - def __get__(self): - return ((self.dev_info.GetVendorName())).decode('ascii') - - property device_class: - def __get__(self): - return ((self.dev_info.GetDeviceClass())).decode('ascii') - - def __repr__(self): - return ''.format(self.serial_number, self.friendly_name) - -cdef class _PropertyMap: - cdef: - INodeMap* map - - @staticmethod - cdef create(INodeMap* map): - obj = _PropertyMap() - obj.map = map - return obj - - def get_description(self, basestring key): - cdef bytes btes_name = key.encode() - cdef INode* node = self.map.GetNode(gcstring(btes_name)) - - if node == NULL: - raise KeyError('Key does not exist') - - return ((node.GetDescription())).decode() - - - def get_display_name(self, basestring key): - cdef bytes btes_name = key.encode() - cdef INode* node = self.map.GetNode(gcstring(btes_name)) - - if node == NULL: - raise KeyError('Key does not exist') - - return ((node.GetDisplayName())).decode() - - - def __getitem__(self, basestring key): - cdef bytes btes_name = key.encode() - cdef INode* node = self.map.GetNode(gcstring(btes_name)) - - if node == NULL: - raise KeyError('Key does not exist') - - if not node_is_readable(node): - raise IOError('Key is not readable') - - # We need to try different types and check if the dynamic_cast succeeds... UGLY! - # Potentially we could also use GetPrincipalInterfaceType here. - cdef IBoolean* boolean_value = dynamic_cast_iboolean_ptr(node) - if boolean_value != NULL: - return boolean_value.GetValue() - - cdef IInteger* integer_value = dynamic_cast_iinteger_ptr(node) - if integer_value != NULL: - return integer_value.GetValue() - - cdef IFloat* float_value = dynamic_cast_ifloat_ptr(node) - if float_value != NULL: - return float_value.GetValue() - - # TODO: Probably we also need some type of enum to be useful - - # Potentially, we can always get the setting by string - cdef IValue* string_value = dynamic_cast_ivalue_ptr(node) - if string_value == NULL: - return - - return ((string_value.ToString())).decode() - - def __setitem__(self, str key, value): - cdef bytes bytes_name = key.encode() - cdef INode* node = self.map.GetNode(gcstring(bytes_name)) - - if node == NULL: - raise KeyError('Key does not exist') - - if not node_is_writable(node): - raise IOError('Key is not writable') - - # We need to try different types and check if the dynamic_cast succeeds... UGLY! - # Potentially we could also use GetPrincipalInterfaceType here. - cdef IBoolean* boolean_value = dynamic_cast_iboolean_ptr(node) - if boolean_value != NULL: - boolean_value.SetValue(value) - return - - cdef IInteger* integer_value = dynamic_cast_iinteger_ptr(node) - if integer_value != NULL: - if value < integer_value.GetMin() or value > integer_value.GetMax(): - raise ValueError('Parameter value for {} not inside valid range [{}, {}], was {}'.format( - key, integer_value.GetMin(), integer_value.GetMax(), value)) - integer_value.SetValue(value) - return - - cdef IFloat* float_value = dynamic_cast_ifloat_ptr(node) - if float_value != NULL: - if value < float_value.GetMin() or value > float_value.GetMax(): - raise ValueError('Parameter value for {} not inside valid range [{}, {}], was {}'.format( - key, float_value.GetMin(), float_value.GetMax(), value)) - float_value.SetValue(value) - return - - # TODO: Probably we also need some type of enum to be useful - - # Potentially, we can always set the setting by string - cdef IValue* string_value = dynamic_cast_ivalue_ptr(node) - if string_value == NULL: - raise RuntimeError('Can not set key %s by string' % key) - - cdef bytes bytes_value = str(value).encode() - string_value.FromString(gcstring(bytes_value)) - - def keys(self): - node_keys = list() - - # Iterate through the discovered devices - cdef NodeList_t nodes - self.map.GetNodes(nodes) - - cdef NodeList_t.iterator it = nodes.begin() - while it != nodes.end(): - if deref(it).IsFeature() and dynamic_cast_icategory_ptr(deref(it)) == NULL: - name = ((deref(it).GetName())).decode('ascii') - node_keys.append(name) - inc(it) - - return node_keys - - -cdef class Camera: - cdef: - CInstantCamera camera - - @staticmethod - cdef create(IPylonDevice* device): - obj = Camera() - obj.camera.Attach(device) - return obj - - property device_info: - def __get__(self): - dev_inf = DeviceInfo.create(self.camera.GetDeviceInfo()) - return dev_inf - - property opened: - def __get__(self): - return self.camera.IsOpen() - def __set__(self, opened): - if self.opened and not opened: - self.camera.Close() - elif not self.opened and opened: - self.camera.Open() - - property is_grabbing: - def __get__(self): - return self.camera.IsGrabbing() - - def open(self): - self.camera.Open() - - def close(self): - self.camera.Close() - - def stop_grabbing(self): - self.camera.StopGrabbing() - - def __del__(self): - if self.camera.IsGrabbing(): - self.camera.StopGrabbing() - self.close() - self.camera.DetachDevice() - - def __repr__(self): - return ''.format(self.device_info.friendly_name, self.opened) - - def grab_images(self, int nr_images, unsigned int timeout=5000): - if not self.opened: - raise RuntimeError('Camera not opened') - - if nr_images < 1: - self.camera.StartGrabbing() - else: - self.camera.StartGrabbing(nr_images) - - cdef CGrabResultPtr ptr_grab_result - cdef IImage* img - - cdef str image_format = str(self.properties['PixelFormat']) - cdef str bits_per_pixel_prop = str(self.properties['PixelSize']) - assert bits_per_pixel_prop.startswith('Bpp'), 'PixelSize property should start with "Bpp"' - assert image_format.startswith('Mono'), 'Only mono images allowed at this point' - assert not image_format.endswith('p'), 'Packed data not supported at this point' - - while self.camera.IsGrabbing(): - self.camera.RetrieveResult(timeout, ptr_grab_result) - - if not ACCESS_CGrabResultPtr_GrabSucceeded(ptr_grab_result): - error_desc = ((ACCESS_CGrabResultPtr_GetErrorDescription(ptr_grab_result))).decode() - raise RuntimeError(error_desc) - - img = &(ptr_grab_result) - if not img.IsValid(): - raise RuntimeError('Graped IImage is not valid.') - - if img.GetImageSize() % img.GetHeight(): - print('This image buffer is wired. Probably you will see an error soonish.') - print('\tBytes:', img.GetImageSize()) - print('\tHeight:', img.GetHeight()) - print('\tWidth:', img.GetWidth()) - print('\tGetPaddingX:', img.GetPaddingX()) - - assert not img.GetPaddingX(), 'Image padding not supported.' - # TODO: Check GetOrientation to fix oritentation of image if required. - - img_data = np.frombuffer((img.GetBuffer())[:img.GetImageSize()], dtype='uint'+bits_per_pixel_prop[3:]) - - # TODO: How to handle multi-byte data here? - img_data = img_data.reshape((img.GetHeight(), -1)) - # img_data = img_data[:img.GetHeight(), :img.GetWidth()] - yield img_data - - def grab_image(self, unsigned int timeout=5000): - return next(self.grab_images(1, timeout)) - - property properties: - def __get__(self): - return _PropertyMap.create(&self.camera.GetNodeMap()) - - -cdef class Factory: - def __cinit__(self): - PylonInitialize() - - def __dealloc__(self): - PylonTerminate() - - def find_devices(self): - cdef CTlFactory* tl_factory = &GetInstance() - cdef DeviceInfoList_t devices - - cdef int nr_devices = tl_factory.EnumerateDevices(devices) - - found_devices = list() - - # Iterate through the discovered devices - cdef DeviceInfoList_t.iterator it = devices.begin() - while it != devices.end(): - found_devices.append(DeviceInfo.create(deref(it))) - inc(it) - - return found_devices - - def create_device(self, DeviceInfo dev_info): - cdef CTlFactory* tl_factory = &GetInstance() - return Camera.create(tl_factory.CreateDevice(dev_info.dev_info)) From 2ddc7c7fbebe983d77105af26b64b37272bb9b5b Mon Sep 17 00:00:00 2001 From: ZacDiggum Date: Mon, 10 Oct 2016 14:09:54 +0200 Subject: [PATCH 03/10] Delete pylon_def.pxd --- pylon_def.pxd | 152 -------------------------------------------------- 1 file changed, 152 deletions(-) delete mode 100644 pylon_def.pxd diff --git a/pylon_def.pxd b/pylon_def.pxd deleted file mode 100644 index 2943390..0000000 --- a/pylon_def.pxd +++ /dev/null @@ -1,152 +0,0 @@ -from libcpp cimport bool -from libc.stdint cimport uint32_t, uint64_t, int64_t -from libcpp.string cimport string - -cdef extern from "Base/GCBase.h": - cdef cppclass gcstring: - gcstring(char*) - -cdef extern from "GenApi/GenApi.h" namespace 'GenApi': - - cdef cppclass INode: - gcstring GetName(bool FullQualified=False) - gcstring GetNameSpace() - gcstring GetDescription() - gcstring GetDisplayName() - bool IsFeature() - gcstring GetValue() - - # Types an INode could be - cdef cppclass IValue: - gcstring ToString() - void FromString(gcstring, bool verify=True) except + - - cdef cppclass IBoolean: - bool GetValue() - void SetValue(bool) except + - - cdef cppclass IInteger: - int64_t GetValue() - void SetValue(int64_t) except + - int64_t GetMin() - int64_t GetMax() - - cdef cppclass IString - cdef cppclass IFloat: - double GetValue() - void SetValue(double) except + - double GetMin() - double GetMax() - - cdef cppclass NodeList_t: - cppclass iterator: - INode* operator*() - iterator operator++() - bint operator==(iterator) - bint operator!=(iterator) - NodeList_t() - CDeviceInfo& operator[](int) - CDeviceInfo& at(int) - iterator begin() - iterator end() - - cdef cppclass ICategory - - cdef cppclass INodeMap: - void GetNodes(NodeList_t&) - INode* GetNode(gcstring& ) - uint32_t GetNumNodes() - -cdef extern from *: - IValue* dynamic_cast_ivalue_ptr "dynamic_cast" (INode*) except + - IBoolean* dynamic_cast_iboolean_ptr "dynamic_cast" (INode*) except + - IInteger* dynamic_cast_iinteger_ptr "dynamic_cast" (INode*) except + - IFloat* dynamic_cast_ifloat_ptr "dynamic_cast" (INode*) except + - INodeMap* dynamic_cast_inodemap_ptr "dynamic_cast" (INode*) except + - INodeMap* dynamic_cast_inodemap_ptr "dynamic_cast" (INode*) except + - ICategory* dynamic_cast_icategory_ptr "dynamic_cast" (INode*) except + - - bool node_is_readable "GenApi::IsReadable" (INode*) except + - bool node_is_writable "GenApi::IsWritable" (INode*) except + - bool node_is_implemented "GenApi::IsImplemented" (INode*) except + - -cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon': - # Common special data types - cdef cppclass String_t - cdef cppclass StringList_t - - # Top level init functions - void PylonInitialize() except + - void PylonTerminate() except + - - # cdef enum EPixelType: - - cdef cppclass IImage: - uint32_t GetWidth() - uint32_t GetHeight() - size_t GetPaddingX() - size_t GetImageSize() - void* GetBuffer() - bool IsValid() - - cdef cppclass CGrabResultData: - bool GrabSucceeded() - - cdef cppclass CGrabResultPtr: - IImage& operator() - #CGrabResultData* operator->() - - - cdef cppclass IPylonDevice: - pass - - cdef cppclass CDeviceInfo: - String_t GetSerialNumber() except + - String_t GetUserDefinedName() except + - String_t GetModelName() except + - String_t GetDeviceVersion() except + - String_t GetFriendlyName() except + - String_t GetVendorName() except + - String_t GetDeviceClass() except + - - cdef cppclass CInstantCamera: - CInstantCamera() - void Attach(IPylonDevice*) - CDeviceInfo& GetDeviceInfo() except + - void IsCameraDeviceRemoved() - void Open() except + - void Close() except + - void StopGrabbing() except + - bool IsOpen() except + - IPylonDevice* DetachDevice() except + - void StartGrabbing() except + - void StartGrabbing(size_t maxImages) except + #FIXME: implement different strategies - bool IsGrabbing() - bool RetrieveResult(unsigned int timeout_ms, CGrabResultPtr& grab_result) except + # FIXME: Timout handling - INodeMap& GetNodeMap() - - cdef cppclass DeviceInfoList_t: - cppclass iterator: - CDeviceInfo operator*() - iterator operator++() - bint operator==(iterator) - bint operator!=(iterator) - DeviceInfoList_t() - CDeviceInfo& operator[](int) - CDeviceInfo& at(int) - iterator begin() - iterator end() - - cdef cppclass CTlFactory: - int EnumerateDevices(DeviceInfoList_t&, bool add_to_list=False) - IPylonDevice* CreateDevice(CDeviceInfo&) - -# Hack to define a static member function -cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon::CTlFactory': - CTlFactory& GetInstance() - -# EVIL HACK: We cannot dereference officially with the -> operator. So we use ugly macros... -cdef extern from 'hacks.h': - bool ACCESS_CGrabResultPtr_GrabSucceeded(CGrabResultPtr ptr) - String_t ACCESS_CGrabResultPtr_GetErrorDescription(CGrabResultPtr ptr) - uint32_t ACCESS_CGrabResultPtr_GetErrorCode(CGrabResultPtr ptr) From aaf807e327f3204feb72cfcf60e37cfa55d265de Mon Sep 17 00:00:00 2001 From: ZacDiggum Date: Mon, 10 Oct 2016 14:11:29 +0200 Subject: [PATCH 04/10] added infinite grabbing and helpers changed cam.grab_images(nr_images) so that nr_images<1 -> infinite grabbing added function cam.stop_grabbing() added property cam.is_grabbing --- cython/factory.pyx | 20 ++++++++++++++++---- cython/pylon_def.pxd | 4 +++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/cython/factory.pyx b/cython/factory.pyx index b5e00d1..88e235e 100644 --- a/cython/factory.pyx +++ b/cython/factory.pyx @@ -195,14 +195,23 @@ cdef class Camera: self.camera.Close() elif not self.opened and opened: self.camera.Open() - + + property is_grabbing: + def __get__(self): + return self.camera.IsGrabbing() + def open(self): self.camera.Open() def close(self): self.camera.Close() + def stop_grabbing(self): + self.camera.StopGrabbing() + def __del__(self): + if self.camera.IsGrabbing(): + self.camera.StopGrabbing() self.close() self.camera.DetachDevice() @@ -212,8 +221,11 @@ cdef class Camera: def grab_images(self, int nr_images, unsigned int timeout=5000): if not self.opened: raise RuntimeError('Camera not opened') - - self.camera.StartGrabbing(nr_images) + + if nr_images < 1: + self.camera.StartGrabbing() + else: + self.camera.StartGrabbing(nr_images) cdef CGrabResultPtr ptr_grab_result cdef IImage* img @@ -285,4 +297,4 @@ cdef class Factory: def create_device(self, DeviceInfo dev_info): cdef CTlFactory* tl_factory = &GetInstance() - return Camera.create(tl_factory.CreateDevice(dev_info.dev_info)) \ No newline at end of file + return Camera.create(tl_factory.CreateDevice(dev_info.dev_info)) diff --git a/cython/pylon_def.pxd b/cython/pylon_def.pxd index b965a1c..2943390 100644 --- a/cython/pylon_def.pxd +++ b/cython/pylon_def.pxd @@ -116,8 +116,10 @@ cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon': void IsCameraDeviceRemoved() void Open() except + void Close() except + + void StopGrabbing() except + bool IsOpen() except + IPylonDevice* DetachDevice() except + + void StartGrabbing() except + void StartGrabbing(size_t maxImages) except + #FIXME: implement different strategies bool IsGrabbing() bool RetrieveResult(unsigned int timeout_ms, CGrabResultPtr& grab_result) except + # FIXME: Timout handling @@ -147,4 +149,4 @@ cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon::CTlFactory': cdef extern from 'hacks.h': bool ACCESS_CGrabResultPtr_GrabSucceeded(CGrabResultPtr ptr) String_t ACCESS_CGrabResultPtr_GetErrorDescription(CGrabResultPtr ptr) - uint32_t ACCESS_CGrabResultPtr_GetErrorCode(CGrabResultPtr ptr) \ No newline at end of file + uint32_t ACCESS_CGrabResultPtr_GetErrorCode(CGrabResultPtr ptr) From 6a2199d0ff9d19e2e4779fe65dc7a9b19c8eeb2d Mon Sep 17 00:00:00 2001 From: ZacDiggum Date: Fri, 14 Oct 2016 16:50:30 +0200 Subject: [PATCH 05/10] Update factory.pyx check if camera.IsGrabbing() before calling camera.StopGrabbing() --- cython/factory.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cython/factory.pyx b/cython/factory.pyx index 88e235e..aa66228 100644 --- a/cython/factory.pyx +++ b/cython/factory.pyx @@ -207,11 +207,11 @@ cdef class Camera: self.camera.Close() def stop_grabbing(self): - self.camera.StopGrabbing() - - def __del__(self): if self.camera.IsGrabbing(): self.camera.StopGrabbing() + + def __del__(self): + self.stop_grabbing() self.close() self.camera.DetachDevice() From 7050faf5e15cbe30f0a0999104fd5a91359cf27a Mon Sep 17 00:00:00 2001 From: richardl Date: Fri, 21 Oct 2016 08:41:11 +1300 Subject: [PATCH 06/10] Wrap image grabbing from underlying c++ object in exception handler and on any exception being caught stop grabbing before rethrowing --- cython/factory.pyx | 62 +++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/cython/factory.pyx b/cython/factory.pyx index aa7950d..72e35b5 100644 --- a/cython/factory.pyx +++ b/cython/factory.pyx @@ -219,13 +219,9 @@ cdef class Camera: return ''.format(self.device_info.friendly_name, self.opened) def grab_images(self, int nr_images, unsigned int timeout=5000): + if not self.opened: raise RuntimeError('Camera not opened') - - if nr_images < 1: - self.camera.StartGrabbing() - else: - self.camera.StartGrabbing(nr_images) cdef CGrabResultPtr ptr_grab_result cdef IImage* img @@ -236,36 +232,46 @@ cdef class Camera: assert image_format.startswith('Mono'), 'Only mono images allowed at this point' assert not image_format.endswith('p'), 'Packed data not supported at this point' - while self.camera.IsGrabbing(): + try: + if nr_images < 1: + self.camera.StartGrabbing() + else: + self.camera.StartGrabbing(nr_images) + + while self.camera.IsGrabbing(): + + with nogil: + # Blocking call into native Pylon C++ SDK code, release GIL so other python threads can run + self.camera.RetrieveResult(timeout, ptr_grab_result) - with nogil: - # Blocking call into native Pylon C++ SDK code, release GIL so other python threads can run - self.camera.RetrieveResult(timeout, ptr_grab_result) + if not ACCESS_CGrabResultPtr_GrabSucceeded(ptr_grab_result): + error_desc = ((ACCESS_CGrabResultPtr_GetErrorDescription(ptr_grab_result))).decode() + raise RuntimeError(error_desc) - if not ACCESS_CGrabResultPtr_GrabSucceeded(ptr_grab_result): - error_desc = ((ACCESS_CGrabResultPtr_GetErrorDescription(ptr_grab_result))).decode() - raise RuntimeError(error_desc) + img = &(ptr_grab_result) + if not img.IsValid(): + raise RuntimeError('Graped IImage is not valid.') - img = &(ptr_grab_result) - if not img.IsValid(): - raise RuntimeError('Graped IImage is not valid.') + if img.GetImageSize() % img.GetHeight(): + print('This image buffer is wired. Probably you will see an error soonish.') + print('\tBytes:', img.GetImageSize()) + print('\tHeight:', img.GetHeight()) + print('\tWidth:', img.GetWidth()) + print('\tGetPaddingX:', img.GetPaddingX()) - if img.GetImageSize() % img.GetHeight(): - print('This image buffer is wired. Probably you will see an error soonish.') - print('\tBytes:', img.GetImageSize()) - print('\tHeight:', img.GetHeight()) - print('\tWidth:', img.GetWidth()) - print('\tGetPaddingX:', img.GetPaddingX()) + assert not img.GetPaddingX(), 'Image padding not supported.' + # TODO: Check GetOrientation to fix oritentation of image if required. - assert not img.GetPaddingX(), 'Image padding not supported.' - # TODO: Check GetOrientation to fix oritentation of image if required. + img_data = np.frombuffer((img.GetBuffer())[:img.GetImageSize()], dtype='uint'+bits_per_pixel_prop[3:]) - img_data = np.frombuffer((img.GetBuffer())[:img.GetImageSize()], dtype='uint'+bits_per_pixel_prop[3:]) + # TODO: How to handle multi-byte data here? + img_data = img_data.reshape((img.GetHeight(), -1)) + # img_data = img_data[:img.GetHeight(), :img.GetWidth()] + yield img_data - # TODO: How to handle multi-byte data here? - img_data = img_data.reshape((img.GetHeight(), -1)) - # img_data = img_data[:img.GetHeight(), :img.GetWidth()] - yield img_data + except: + self.stop_grabbing() + raise def grab_image(self, unsigned int timeout=5000): return next(self.grab_images(1, timeout)) From 00ae6ae11e7fddedeb1e4778e4906f15736dff17 Mon Sep 17 00:00:00 2001 From: richardl Date: Fri, 21 Oct 2016 08:48:35 +1300 Subject: [PATCH 07/10] Default value for grab_images() nr_images parameter, so when called with no arguments it grabs continuously --- cython/factory.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cython/factory.pyx b/cython/factory.pyx index 72e35b5..8d254aa 100644 --- a/cython/factory.pyx +++ b/cython/factory.pyx @@ -218,7 +218,7 @@ cdef class Camera: def __repr__(self): return ''.format(self.device_info.friendly_name, self.opened) - def grab_images(self, int nr_images, unsigned int timeout=5000): + def grab_images(self, int nr_images = -1, unsigned int timeout=5000): if not self.opened: raise RuntimeError('Camera not opened') From fde51784b256033aacfc461c5ad55e31eb783128 Mon Sep 17 00:00:00 2001 From: richardl Date: Mon, 7 Nov 2016 16:55:41 +1300 Subject: [PATCH 08/10] Expose parameter on Camera::StartGrabbing() method and grab_image/grab_images python wrapper --- cython/factory.pyx | 20 +++++++++++++------- cython/pylon_def.pxd | 11 +++++++++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/cython/factory.pyx b/cython/factory.pyx index 8d254aa..8ad5b72 100644 --- a/cython/factory.pyx +++ b/cython/factory.pyx @@ -7,6 +7,13 @@ import numpy as np from pylon_def cimport * +cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon': + + cpdef enum EGrabStrategy: + GrabStrategy_OneByOne, + GrabStrategy_LatestImageOnly, + GrabStrategy_LatestImages, + GrabStrategy_UpcomingImage cdef class DeviceInfo: cdef: @@ -199,7 +206,7 @@ cdef class Camera: property is_grabbing: def __get__(self): return self.camera.IsGrabbing() - + def open(self): self.camera.Open() @@ -218,7 +225,7 @@ cdef class Camera: def __repr__(self): return ''.format(self.device_info.friendly_name, self.opened) - def grab_images(self, int nr_images = -1, unsigned int timeout=5000): + def grab_images(self, int nr_images = -1, EGrabStrategy grab_strategy=GrabStrategy_OneByOne, unsigned int timeout=5000): if not self.opened: raise RuntimeError('Camera not opened') @@ -234,9 +241,9 @@ cdef class Camera: try: if nr_images < 1: - self.camera.StartGrabbing() + self.camera.StartGrabbing(grab_strategy) else: - self.camera.StartGrabbing(nr_images) + self.camera.StartGrabbing(nr_images, grab_strategy) while self.camera.IsGrabbing(): @@ -273,14 +280,13 @@ cdef class Camera: self.stop_grabbing() raise - def grab_image(self, unsigned int timeout=5000): - return next(self.grab_images(1, timeout)) + def grab_image(self, EGrabStrategy grab_strategy=GrabStrategy_OneByOne, unsigned int timeout=5000): + return next(self.grab_images(1, grab_strategy, timeout)) property properties: def __get__(self): return _PropertyMap.create(&self.camera.GetNodeMap()) - cdef class Factory: def __cinit__(self): PylonInitialize() diff --git a/cython/pylon_def.pxd b/cython/pylon_def.pxd index b791bb9..e54ec4e 100644 --- a/cython/pylon_def.pxd +++ b/cython/pylon_def.pxd @@ -109,6 +109,12 @@ cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon': String_t GetVendorName() except + String_t GetDeviceClass() except + + cdef enum EGrabStrategy: + GrabStrategy_OneByOne, + GrabStrategy_LatestImageOnly, + GrabStrategy_LatestImages, + GrabStrategy_UpcomingImage + cdef cppclass CInstantCamera: CInstantCamera() void Attach(IPylonDevice*) @@ -119,13 +125,14 @@ cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon': void StopGrabbing() except + bool IsOpen() except + IPylonDevice* DetachDevice() except + - void StartGrabbing() except + - void StartGrabbing(size_t maxImages) except + #FIXME: implement different strategies + void StartGrabbing(EGrabStrategy strategy) except + + void StartGrabbing(size_t maxImages, EGrabStrategy strategy) except + bool IsGrabbing() # RetrieveResult() is blocking call into C++ native SDK, allow it to be called without GIL bool RetrieveResult(unsigned int timeout_ms, CGrabResultPtr& grab_result) nogil except + # FIXME: Timout handling INodeMap& GetNodeMap() + cdef cppclass DeviceInfoList_t: cppclass iterator: CDeviceInfo operator*() From 48a0eebf61c020bf6f8ec252bc6f861555524af7 Mon Sep 17 00:00:00 2001 From: richardl Date: Tue, 8 Nov 2016 08:18:13 +1300 Subject: [PATCH 09/10] Properties used in conjunction with different grab strategies --- cython/factory.pyx | 20 ++++++++++++++++++++ cython/pylon_def.pxd | 7 +++++++ 2 files changed, 27 insertions(+) diff --git a/cython/factory.pyx b/cython/factory.pyx index 8ad5b72..6897654 100644 --- a/cython/factory.pyx +++ b/cython/factory.pyx @@ -287,6 +287,26 @@ cdef class Camera: def __get__(self): return _PropertyMap.create(&self.camera.GetNodeMap()) + # Configuration properties associated with various grab strategies + property max_num_buffer: + def __get__(self): + return self.camera.MaxNumBuffer.GetValue() + def __set__(self, value): + self.camera.MaxNumBuffer.SetValue(value) + + property max_num_queued_buffer: + def __get__(self): + return self.camera.MaxNumQueuedBuffer.GetValue() + def __set__(self, value): + self.camera.MaxNumQueuedBuffer.SetValue(value) + + property output_queue_size: + def __get__(self): + return self.camera.OutputQueueSize.GetValue() + def __set__(self, value): + self.camera.OutputQueueSize.SetValue(value) + + cdef class Factory: def __cinit__(self): PylonInitialize() diff --git a/cython/pylon_def.pxd b/cython/pylon_def.pxd index e54ec4e..9f8955a 100644 --- a/cython/pylon_def.pxd +++ b/cython/pylon_def.pxd @@ -132,6 +132,13 @@ cdef extern from "pylon/PylonIncludes.h" namespace 'Pylon': bool RetrieveResult(unsigned int timeout_ms, CGrabResultPtr& grab_result) nogil except + # FIXME: Timout handling INodeMap& GetNodeMap() + # From CInstantCameraParams_Params base class + IInteger &MaxNumBuffer; + IInteger &MaxNumQueuedBuffer; + IInteger &OutputQueueSize; + + + cdef cppclass DeviceInfoList_t: cppclass iterator: From 4a0407c33d5fa420b677691814d6184e48a84856 Mon Sep 17 00:00:00 2001 From: richardl Date: Mon, 14 Nov 2016 12:08:59 +1300 Subject: [PATCH 10/10] Manually define EGrabStrategy values for compatibility with earlier python/cython versions that dont handle exporting it properly --- pypylon/__init__.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/pypylon/__init__.py b/pypylon/__init__.py index a61d829..21ead66 100644 --- a/pypylon/__init__.py +++ b/pypylon/__init__.py @@ -1,5 +1,26 @@ from pypylon.cython.factory import Factory from pypylon.cython.version import PylonVersion +# With earlier versions of python that don't support PEP 435 enums (discovered on an Ubuntu 14.04 with stock python 2.7.6) cython +# seems to silently drop the EGrabStrategy enum. declared with cpdef directive in factory.pyx, so have to define it manually. +GrabStrategy_OneByOne = 0 +GrabStrategy_LatestImageOnly = 1 +GrabStrategy_LatestImages = 2 +GrabStrategy_UpcomingImage = 3 + +try: + # If running on python version where EGrabStrategy is sucessfully exported, sanity check that values match our manual + # definitions + from pypylon.cython.factory import EGrabStrategy + + assert GrabStrategy_OneByOne == EGrabStrategy.GrabStrategy_OneByOne + assert GrabStrategy_LatestImageOnly == EGrabStrategy.GrabStrategy_LatestImageOnly + assert GrabStrategy_LatestImages == EGrabStrategy.GrabStrategy_LatestImages + assert GrabStrategy_UpcomingImage == EGrabStrategy.GrabStrategy_UpcomingImage + +except ImportError: + pass + + factory = Factory() -pylon_version = PylonVersion() \ No newline at end of file +pylon_version = PylonVersion()