-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathfactory.pyx
More file actions
309 lines (228 loc) · 10.2 KB
/
factory.pyx
File metadata and controls
309 lines (228 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
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 (<string>(self.dev_info.GetSerialNumber())).decode('ascii')
property model_name:
def __get__(self):
return (<string>(self.dev_info.GetModelName())).decode('ascii')
property user_defined_name:
def __get__(self):
return (<string>(self.dev_info.GetUserDefinedName())).decode('ascii')
property device_version:
def __get__(self):
return (<string>(self.dev_info.GetDeviceVersion())).decode('ascii')
property friendly_name:
def __get__(self):
return (<string>(self.dev_info.GetFriendlyName())).decode('ascii')
property vendor_name:
def __get__(self):
return (<string>(self.dev_info.GetVendorName())).decode('ascii')
property device_class:
def __get__(self):
return (<string>(self.dev_info.GetDeviceClass())).decode('ascii')
def __repr__(self):
return '<DeviceInfo {1}>'.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 (<string>(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 (<string>(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>(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 = (<string>(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):
if self.camera.IsGrabbing():
self.camera.StopGrabbing()
def __del__(self):
self.stop_grabbing()
self.close()
self.camera.DetachDevice()
def __repr__(self):
return '<Camera {0} open={1}>'.format(self.device_info.friendly_name, self.opened)
def grab_images(self, int nr_images = -1, unsigned int timeout=5000):
if not self.opened:
raise RuntimeError('Camera not opened')
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'
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)
if not ACCESS_CGrabResultPtr_GrabSucceeded(ptr_grab_result):
error_desc = (<string>(ACCESS_CGrabResultPtr_GetErrorDescription(ptr_grab_result))).decode()
raise RuntimeError(error_desc)
img = &(<IImage&>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((<char*>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
except:
self.stop_grabbing()
raise
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))