deapi 5.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- deapi/__init__.py +33 -0
- deapi/buffer_protocols/__init__.py +29 -0
- deapi/buffer_protocols/pb_2_3_0.py +666 -0
- deapi/buffer_protocols/pb_3_11_4.py +741 -0
- deapi/buffer_protocols/pb_3_19_3.py +114 -0
- deapi/buffer_protocols/pb_3_23_3.py +41 -0
- deapi/buffer_protocols/pb_3_6_1.py +745 -0
- deapi/client.py +2159 -0
- deapi/conf.py +184 -0
- deapi/data_types.py +586 -0
- deapi/fake_data/__init__.py +7 -0
- deapi/fake_data/base_fake_data.py +113 -0
- deapi/fake_data/grains.py +87 -0
- deapi/index.rst +10 -0
- deapi/prop_dump.json +450 -0
- deapi/python_3_instruction.txt +31 -0
- deapi/release_notes.txt +34 -0
- deapi/simulated_server/__init__.py +0 -0
- deapi/simulated_server/fake_server.py +721 -0
- deapi/simulated_server/initialize_server.py +66 -0
- deapi/tests/__init__.py +0 -0
- deapi/tests/conftest.py +95 -0
- deapi/tests/original_tests/01_fps.py +102 -0
- deapi/tests/original_tests/02_hwRoisize.py +155 -0
- deapi/tests/original_tests/03_hwBinning.py +117 -0
- deapi/tests/original_tests/04_swBinning.py +141 -0
- deapi/tests/original_tests/05_swhwBinning.py +113 -0
- deapi/tests/original_tests/06_patternPixel.py +139 -0
- deapi/tests/original_tests/07_reference.py +92 -0
- deapi/tests/original_tests/08_virtmask.py +114 -0
- deapi/tests/original_tests/09_scanRoi.py +104 -0
- deapi/tests/original_tests/10_imageStatistics.py +247 -0
- deapi/tests/original_tests/__init__.py +0 -0
- deapi/tests/original_tests/func.py +76 -0
- deapi/tests/original_tests/propertyName.py +725 -0
- deapi/tests/original_tests/test_legacy.py +132 -0
- deapi/tests/speed_tests/__init__.py +0 -0
- deapi/tests/speed_tests/test_internal_file_saving.py +54 -0
- deapi/tests/speed_tests/test_movie_buffer_transfer.py +54 -0
- deapi/tests/test_client.py +270 -0
- deapi/tests/test_fake_server/__init__.py +0 -0
- deapi/tests/test_fake_server/test_server.py +35 -0
- deapi/tests/test_file_saving/__init__.py +0 -0
- deapi/tests/test_file_saving/test_file_loading_libertem.py +49 -0
- deapi/tests/test_file_saving/test_file_loading_rsciio.py +53 -0
- deapi/tests/test_file_saving/test_scan_pattern_saving.py +68 -0
- deapi/version.py +3 -0
- deapi-5.2.0.dist-info/METADATA +44 -0
- deapi-5.2.0.dist-info/RECORD +52 -0
- deapi-5.2.0.dist-info/WHEEL +5 -0
- deapi-5.2.0.dist-info/entry_points.txt +2 -0
- deapi-5.2.0.dist-info/top_level.txt +1 -0
deapi/client.py
ADDED
|
@@ -0,0 +1,2159 @@
|
|
|
1
|
+
# File containing the Client for connecting to the DE-Server
|
|
2
|
+
#
|
|
3
|
+
# Last update: 2024-08-07
|
|
4
|
+
# cfrancis@directelectron.com
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Python System imports
|
|
8
|
+
import socket
|
|
9
|
+
import sys
|
|
10
|
+
import struct
|
|
11
|
+
import time
|
|
12
|
+
import os
|
|
13
|
+
import logging
|
|
14
|
+
import mmap
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from time import sleep
|
|
17
|
+
import re
|
|
18
|
+
from typing import List
|
|
19
|
+
from enum import Enum
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
# External package imports
|
|
24
|
+
from PIL import Image
|
|
25
|
+
import numpy
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Internal package imports
|
|
29
|
+
from deapi.data_types import (
|
|
30
|
+
FrameType,
|
|
31
|
+
PixelFormat,
|
|
32
|
+
Attributes,
|
|
33
|
+
Histogram,
|
|
34
|
+
PropertySpec,
|
|
35
|
+
MovieBufferStatus,
|
|
36
|
+
MovieBufferInfo,
|
|
37
|
+
DataType,
|
|
38
|
+
PropertyCollection,
|
|
39
|
+
VirtualMask,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
from deapi.buffer_protocols import pb
|
|
44
|
+
from deapi.version import version, commandVersion
|
|
45
|
+
from deapi.version import commandVersion as cVersion
|
|
46
|
+
import functools
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
## the commandInfo contains [VERSION_MAJOR.VERSION_MINOR.VERSION_PATCH.VERSION_REVISION]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
logLevel = logging.INFO
|
|
53
|
+
logging.basicConfig(format="%(asctime)s DE %(levelname)-8s %(message)s", level=logLevel)
|
|
54
|
+
log = logging.getLogger("DECameraClientLib")
|
|
55
|
+
log.info("Python : " + sys.version.split("(")[0])
|
|
56
|
+
log.info("DEClient : " + version)
|
|
57
|
+
log.info("CommandVer: " + str(commandVersion))
|
|
58
|
+
log.info("logLevel : " + str(logging.getLevelName(logLevel)))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def write_only(func):
|
|
62
|
+
def wrapper(*args, **kwargs):
|
|
63
|
+
if args[0].read_only:
|
|
64
|
+
log.error("Client is read-only. Cannot set property.")
|
|
65
|
+
return
|
|
66
|
+
else:
|
|
67
|
+
return func(*args, **kwargs)
|
|
68
|
+
|
|
69
|
+
return wrapper
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def disable_scan(func):
|
|
73
|
+
def wrapper(*args, **kwargs):
|
|
74
|
+
print("Disabling scan")
|
|
75
|
+
initial_scan = args[0]["Scan - Enable"]
|
|
76
|
+
args[0].set_property("Scan - Enable", False)
|
|
77
|
+
ans = func(*args, **kwargs)
|
|
78
|
+
args[0].set_property("Scan - Enable", initial_scan)
|
|
79
|
+
return ans
|
|
80
|
+
|
|
81
|
+
return wrapper
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class Client:
|
|
85
|
+
"""A class for connecting to the DE-Server
|
|
86
|
+
|
|
87
|
+
Examples
|
|
88
|
+
--------
|
|
89
|
+
>>> client = Client()
|
|
90
|
+
>>> client.connect()
|
|
91
|
+
>>> client["Exposure Time (seconds)"]
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def __init__(self):
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
def set_log_level(self, level):
|
|
98
|
+
log = logging.getLogger("DECameraClientLib")
|
|
99
|
+
log.setLevel(level)
|
|
100
|
+
log.info("Log level set to %s", level)
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
def __str__(self):
|
|
104
|
+
return f"Client(host={self.host}, port={self.port}, camera={self.get_current_camera()})"
|
|
105
|
+
|
|
106
|
+
def _ipython_key_completions_(self):
|
|
107
|
+
return self.list_properties()
|
|
108
|
+
|
|
109
|
+
def _repr_html_(self):
|
|
110
|
+
table = f"""
|
|
111
|
+
<table>
|
|
112
|
+
<tr>
|
|
113
|
+
<th>Host</th>
|
|
114
|
+
<th>Port</th>
|
|
115
|
+
<th>Current Camera</th>
|
|
116
|
+
</tr>
|
|
117
|
+
<tr>
|
|
118
|
+
<td>{self.host}</td>
|
|
119
|
+
<td>{self.port}</td>
|
|
120
|
+
<td>{self.currCamera}</td>
|
|
121
|
+
</tr>
|
|
122
|
+
</table>
|
|
123
|
+
<details>
|
|
124
|
+
<summary>Current Info</summary>
|
|
125
|
+
<pre>
|
|
126
|
+
{self.get_current_info()}
|
|
127
|
+
</pre>
|
|
128
|
+
</details>
|
|
129
|
+
"""
|
|
130
|
+
return table
|
|
131
|
+
|
|
132
|
+
def gui(self):
|
|
133
|
+
from IPython.display import display
|
|
134
|
+
|
|
135
|
+
display(self)
|
|
136
|
+
|
|
137
|
+
def __setitem__(self, key, value):
|
|
138
|
+
self.set_property(key, value)
|
|
139
|
+
|
|
140
|
+
def __getitem__(self, key):
|
|
141
|
+
return self.get_property(key)
|
|
142
|
+
|
|
143
|
+
def get_current_info(self):
|
|
144
|
+
prop_list = self.list_properties()
|
|
145
|
+
values = self.get_properties(prop_list)
|
|
146
|
+
text = ""
|
|
147
|
+
for p, v in zip(prop_list, values):
|
|
148
|
+
text += f"{p}: {v} \n"
|
|
149
|
+
return text
|
|
150
|
+
|
|
151
|
+
def _initialize_attributes(self):
|
|
152
|
+
all_properties = self.list_properties()
|
|
153
|
+
collections = [p.split(" - ")[0] for p in all_properties if " - " in p]
|
|
154
|
+
unique_collections = np.unique(collections)
|
|
155
|
+
for collection in unique_collections:
|
|
156
|
+
stripped = collection.lower().strip().replace(" ", "_")
|
|
157
|
+
props = [p for p in all_properties if collection + " -" in p]
|
|
158
|
+
setattr(
|
|
159
|
+
self,
|
|
160
|
+
stripped,
|
|
161
|
+
PropertyCollection(client=self, name=collection, properties=props),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def connect(self, host: str = "127.0.0.1", port: int = 13240, read_only=False):
|
|
165
|
+
"""Connect to DE-Server
|
|
166
|
+
|
|
167
|
+
Parameters
|
|
168
|
+
----------
|
|
169
|
+
host : str, optional
|
|
170
|
+
The host to connect to, by default "127.0.0.1" for local connection
|
|
171
|
+
port : int, optional
|
|
172
|
+
The port to connect to, by default 13240
|
|
173
|
+
"""
|
|
174
|
+
if host == "localhost" or host == "127.0.0.1":
|
|
175
|
+
tcpNoDelay = 0 # on loopback interface, nodelay causes delay
|
|
176
|
+
|
|
177
|
+
if self.usingMmf:
|
|
178
|
+
self.mmf = mmap.mmap(0, MMF_DATA_BUFFER_SIZE, "ImageFileMappingObject")
|
|
179
|
+
self.mmf[0] = True
|
|
180
|
+
else:
|
|
181
|
+
self.usingMmf = False # Disabled MMF if connected remotely
|
|
182
|
+
tcpNoDelay = 1
|
|
183
|
+
|
|
184
|
+
if logLevel == logging.DEBUG:
|
|
185
|
+
log.debug("Connecting to server: %s", host)
|
|
186
|
+
|
|
187
|
+
self.socket = socket.socket(
|
|
188
|
+
socket.AF_INET, socket.SOCK_STREAM
|
|
189
|
+
) # Create a socket (SOCK_STREAM means a TCP socket)
|
|
190
|
+
self.socket.connect(
|
|
191
|
+
(host, port)
|
|
192
|
+
) # Connect to server reading port for sending data
|
|
193
|
+
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, tcpNoDelay)
|
|
194
|
+
self.socket.setblocking(False)
|
|
195
|
+
self.socket.settimeout(2)
|
|
196
|
+
|
|
197
|
+
self.cameras = self.__getStrings(self.LIST_CAMERAS)
|
|
198
|
+
if logLevel == logging.DEBUG:
|
|
199
|
+
log.debug("Available cameras: %s", self.cameras)
|
|
200
|
+
|
|
201
|
+
self.currCamera = self.cameras[0]
|
|
202
|
+
if logLevel == logging.DEBUG:
|
|
203
|
+
log.debug("Current camera: %s", self.currCamera)
|
|
204
|
+
|
|
205
|
+
self.connected = True
|
|
206
|
+
self.host = host
|
|
207
|
+
self.port = port
|
|
208
|
+
log.info("Connected to server: %s, port: %d", host, port)
|
|
209
|
+
|
|
210
|
+
serverVersion = self.GetProperty("Server Software Version")
|
|
211
|
+
serverVersion = re.findall(r"\d+", serverVersion)
|
|
212
|
+
|
|
213
|
+
version = [int(part) for part in serverVersion[:4]]
|
|
214
|
+
temp = version[2] + version[1] * 1000 + version[0] * 1000000
|
|
215
|
+
|
|
216
|
+
if cVersion >= 12:
|
|
217
|
+
self.set_client_read_only(read_only)
|
|
218
|
+
|
|
219
|
+
if temp >= 2007004:
|
|
220
|
+
## version after 2.7.4
|
|
221
|
+
commandVersion = 12
|
|
222
|
+
elif temp >= 2007003:
|
|
223
|
+
## version after 2.7.3
|
|
224
|
+
commandVersion = 11
|
|
225
|
+
elif temp >= 2007002:
|
|
226
|
+
## version after 2.7.2
|
|
227
|
+
commandVersion = 10
|
|
228
|
+
elif temp >= 2005025:
|
|
229
|
+
##version after 2.5.25
|
|
230
|
+
commandVersion = 4
|
|
231
|
+
elif temp >= 2001017:
|
|
232
|
+
## version after 2.1.17
|
|
233
|
+
commandVersion = 3
|
|
234
|
+
self._initialize_attributes()
|
|
235
|
+
self.update_scan_size()
|
|
236
|
+
self.update_image_size()
|
|
237
|
+
self.virtual_masks = []
|
|
238
|
+
for i in range(4):
|
|
239
|
+
self.virtual_masks.append(VirtualMask(client=self, index=i))
|
|
240
|
+
|
|
241
|
+
def set_client_read_only(self, read_only):
|
|
242
|
+
self.read_only = read_only
|
|
243
|
+
command = self._addSingleCommand(self.SET_CLIENT_READ_ONLY, None, [read_only])
|
|
244
|
+
response = self._sendCommand(command)
|
|
245
|
+
return response
|
|
246
|
+
|
|
247
|
+
def update_scan_size(self):
|
|
248
|
+
self.scan_sizex = self["Scan - Size X"]
|
|
249
|
+
self.scan_sizey = self["Scan - Size Y"]
|
|
250
|
+
|
|
251
|
+
def update_image_size(self):
|
|
252
|
+
self.image_sizex = self["Image Size X (pixels)"]
|
|
253
|
+
self.image_sizey = self["Image Size Y (pixels)"]
|
|
254
|
+
|
|
255
|
+
def disconnect(self):
|
|
256
|
+
"""
|
|
257
|
+
Disconnects from the server.
|
|
258
|
+
Closes the memory-mapped file (mmf) if it is open.
|
|
259
|
+
Closes the socket connection if it is open.
|
|
260
|
+
Sets the 'connected' flag to False.
|
|
261
|
+
"""
|
|
262
|
+
if self.mmf != 0:
|
|
263
|
+
self.mmf.close()
|
|
264
|
+
|
|
265
|
+
if self.connected:
|
|
266
|
+
self.socket.close()
|
|
267
|
+
self.socket.close()
|
|
268
|
+
self.connected = False
|
|
269
|
+
log.info("Disconnected.")
|
|
270
|
+
|
|
271
|
+
def list_cameras(self) -> List[str]:
|
|
272
|
+
"""
|
|
273
|
+
List the available cameras on the server.
|
|
274
|
+
"""
|
|
275
|
+
return self.cameras
|
|
276
|
+
|
|
277
|
+
def get_virtual_mask(self, index):
|
|
278
|
+
mask_name = f"virtual_mask{index}"
|
|
279
|
+
a = Attributes()
|
|
280
|
+
a.windowWidth = self["Image Size X (pixels)"]
|
|
281
|
+
a.windowHeight = self["Image Size Y (pixels)"]
|
|
282
|
+
(
|
|
283
|
+
res,
|
|
284
|
+
_,
|
|
285
|
+
_,
|
|
286
|
+
_,
|
|
287
|
+
) = self.get_result(mask_name, DataType.DE8u, attributes=a)
|
|
288
|
+
return res
|
|
289
|
+
|
|
290
|
+
def get_current_camera(self) -> str:
|
|
291
|
+
"""
|
|
292
|
+
Get the current camera on the server.
|
|
293
|
+
"""
|
|
294
|
+
if self.currCamera is None:
|
|
295
|
+
return "No current camera"
|
|
296
|
+
else:
|
|
297
|
+
return self.currCamera
|
|
298
|
+
|
|
299
|
+
@write_only
|
|
300
|
+
def set_current_camera(self, camera_name: str = None):
|
|
301
|
+
"""
|
|
302
|
+
Set the current camera on the server.
|
|
303
|
+
"""
|
|
304
|
+
if camera_name is None:
|
|
305
|
+
return False
|
|
306
|
+
|
|
307
|
+
self.currCamera = camera_name
|
|
308
|
+
|
|
309
|
+
if logLevel == logging.DEBUG:
|
|
310
|
+
log.debug("current camera: %s", camera_name)
|
|
311
|
+
|
|
312
|
+
self.refreshProperties = True
|
|
313
|
+
return True
|
|
314
|
+
|
|
315
|
+
def list_properties(self, options=None, search=None):
|
|
316
|
+
"""
|
|
317
|
+
Get a list of property names from the current camera on DE-Server
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
options : list, optional
|
|
322
|
+
Options to pass to the server, by default None
|
|
323
|
+
"""
|
|
324
|
+
available_properties = self.__getStrings(self.LIST_PROPERTIES, options)
|
|
325
|
+
if available_properties != False:
|
|
326
|
+
self.available_properties = available_properties
|
|
327
|
+
|
|
328
|
+
if logLevel == logging.DEBUG:
|
|
329
|
+
log.debug("Available camera properties: %s", available_properties)
|
|
330
|
+
if search is not None:
|
|
331
|
+
available_properties = [p for p in available_properties if search in p]
|
|
332
|
+
return available_properties
|
|
333
|
+
|
|
334
|
+
def get_property_spec(self, propertyName: str):
|
|
335
|
+
"""
|
|
336
|
+
Get a list of allowed values for a property of the current camera on DE-Server
|
|
337
|
+
|
|
338
|
+
Parameters
|
|
339
|
+
----------
|
|
340
|
+
propertyName : str
|
|
341
|
+
The name of the property to get the allowed values for
|
|
342
|
+
"""
|
|
343
|
+
t0 = self.GetTime()
|
|
344
|
+
values = False
|
|
345
|
+
command = self._addSingleCommand(self.LIST_ALLOWED_VALUES, propertyName)
|
|
346
|
+
response = self._sendCommand(command)
|
|
347
|
+
if response == False:
|
|
348
|
+
return None
|
|
349
|
+
|
|
350
|
+
values = self.__getParameters(response.acknowledge[0])
|
|
351
|
+
|
|
352
|
+
propSpec = PropertySpec()
|
|
353
|
+
propSpec.dataType = values[0]
|
|
354
|
+
propSpec.valueType = values[1]
|
|
355
|
+
propSpec.category = values[len(values) - 3]
|
|
356
|
+
propSpec.options = list(values[2 : len(values) - 3])
|
|
357
|
+
propSpec.defaultValue = str(values[len(values) - 2])
|
|
358
|
+
propSpec.currentValue = str(values[len(values) - 1])
|
|
359
|
+
|
|
360
|
+
optionsLength = len(propSpec.options)
|
|
361
|
+
|
|
362
|
+
if propSpec.valueType == "Range":
|
|
363
|
+
if optionsLength == 2:
|
|
364
|
+
rangeString = ""
|
|
365
|
+
for i in range(optionsLength):
|
|
366
|
+
if propSpec.dataType == "Integer":
|
|
367
|
+
rangeString += str(int(propSpec.options[i]))
|
|
368
|
+
else:
|
|
369
|
+
rangeString += str(propSpec.options[i])
|
|
370
|
+
if i == 0:
|
|
371
|
+
rangeString += str(" - ")
|
|
372
|
+
|
|
373
|
+
propSpec.options.append(rangeString)
|
|
374
|
+
|
|
375
|
+
if propSpec.valueType == "Set":
|
|
376
|
+
for i in range(optionsLength):
|
|
377
|
+
if propSpec.defaultValue == propSpec.options[i]:
|
|
378
|
+
if propSpec.defaultValue != "":
|
|
379
|
+
propSpec.options[i] = propSpec.defaultValue + str("*")
|
|
380
|
+
else:
|
|
381
|
+
emptyStringIndex = i
|
|
382
|
+
if propSpec.defaultValue == "":
|
|
383
|
+
propSpec.options.pop(emptyStringIndex)
|
|
384
|
+
|
|
385
|
+
if "allow_all" in propSpec.valueType:
|
|
386
|
+
propSpec.options = ""
|
|
387
|
+
elif propSpec.dataType == "String":
|
|
388
|
+
propSpec.options = str(list(map(lambda a: str(a), propSpec.options)))[1:-1]
|
|
389
|
+
else:
|
|
390
|
+
propSpec.options = str(propSpec.options)[1:-1]
|
|
391
|
+
|
|
392
|
+
return propSpec
|
|
393
|
+
|
|
394
|
+
def property_valid_values(self, propertyName: str):
|
|
395
|
+
"""
|
|
396
|
+
Get a list of allowed values for a property of the current camera on DE-Server
|
|
397
|
+
"""
|
|
398
|
+
t0 = self.GetTime()
|
|
399
|
+
values = False
|
|
400
|
+
command = self._addSingleCommand(self.LIST_ALLOWED_VALUES, propertyName)
|
|
401
|
+
response = self._sendCommand(command)
|
|
402
|
+
if response != False:
|
|
403
|
+
values = self.__getParameters(response.acknowledge[0])
|
|
404
|
+
|
|
405
|
+
if logLevel == logging.DEBUG:
|
|
406
|
+
log.debug(
|
|
407
|
+
"get allowed property values: %s = %s, completed in %.1f ms",
|
|
408
|
+
propertyName,
|
|
409
|
+
values,
|
|
410
|
+
(self.GetTime() - t0) * 1000,
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
return values
|
|
414
|
+
|
|
415
|
+
def get_property(self, propertyName: str):
|
|
416
|
+
"""
|
|
417
|
+
Get the value of a property of the current camera on DE-Server
|
|
418
|
+
|
|
419
|
+
Parameters
|
|
420
|
+
----------
|
|
421
|
+
propertyName : str
|
|
422
|
+
The name of the property to get the value of
|
|
423
|
+
"""
|
|
424
|
+
t0 = self.GetTime()
|
|
425
|
+
ret = False
|
|
426
|
+
|
|
427
|
+
if propertyName is not None:
|
|
428
|
+
command = self._addSingleCommand(self.GET_PROPERTY, propertyName)
|
|
429
|
+
response = self._sendCommand(command)
|
|
430
|
+
if response != False:
|
|
431
|
+
values = self.__getParameters(response.acknowledge[0])
|
|
432
|
+
if type(values) is list:
|
|
433
|
+
if len(values) > 0:
|
|
434
|
+
ret = values[0] # always return the first value
|
|
435
|
+
else:
|
|
436
|
+
ret = values
|
|
437
|
+
|
|
438
|
+
if logLevel == logging.DEBUG:
|
|
439
|
+
log.debug(
|
|
440
|
+
"GetProperty: %s = %s, completed in %.1f ms",
|
|
441
|
+
propertyName,
|
|
442
|
+
values,
|
|
443
|
+
(self.GetTime() - t0) * 1000,
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
return ret
|
|
447
|
+
|
|
448
|
+
def get_server_version(self):
|
|
449
|
+
"""
|
|
450
|
+
Get the server software version
|
|
451
|
+
"""
|
|
452
|
+
server_version = self.GetProperty("Server Software Version")
|
|
453
|
+
server_version = re.findall(r"\d+", server_version)
|
|
454
|
+
|
|
455
|
+
ver = [int(part) for part in server_version[:4]]
|
|
456
|
+
res = ver[2] + ver[1] * 1000 + ver[0] * 1000000
|
|
457
|
+
return res
|
|
458
|
+
|
|
459
|
+
def get_properties(self, names=None):
|
|
460
|
+
if names is None:
|
|
461
|
+
names = self.list_properties()
|
|
462
|
+
return [self.get_property(p) for p in names]
|
|
463
|
+
|
|
464
|
+
@property
|
|
465
|
+
def acquiring(self):
|
|
466
|
+
"""Check if the camera is currently acquiring images. (bool)"""
|
|
467
|
+
return self.get_property("Acquisition Status") == "Acquiring"
|
|
468
|
+
|
|
469
|
+
@write_only
|
|
470
|
+
def set_property(self, name: str, value):
|
|
471
|
+
"""
|
|
472
|
+
Set the value of a property of the current camera on DE-Server
|
|
473
|
+
|
|
474
|
+
Parameters
|
|
475
|
+
----------
|
|
476
|
+
name : str
|
|
477
|
+
The name of the property to set the value of
|
|
478
|
+
value : any
|
|
479
|
+
The value to set the property to
|
|
480
|
+
"""
|
|
481
|
+
t0 = self.GetTime()
|
|
482
|
+
ret = False
|
|
483
|
+
|
|
484
|
+
if name is not None and value is not None:
|
|
485
|
+
command = self._addSingleCommand(self.SET_PROPERTY, name, [value])
|
|
486
|
+
response = self._sendCommand(command)
|
|
487
|
+
if response != False:
|
|
488
|
+
ret = response.acknowledge[0].error != True
|
|
489
|
+
self.refreshProperties = True
|
|
490
|
+
|
|
491
|
+
if logLevel == logging.DEBUG:
|
|
492
|
+
log.debug(
|
|
493
|
+
"SetProperty: %s = %s, completed in %.1f ms",
|
|
494
|
+
name,
|
|
495
|
+
value,
|
|
496
|
+
(self.GetTime() - t0) * 1000,
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
return ret
|
|
500
|
+
|
|
501
|
+
@write_only
|
|
502
|
+
def set_property_and_get_changed_properties(self, name, value, changedProperties):
|
|
503
|
+
"""
|
|
504
|
+
Set the value of a property of the current camera on DE-Server and get all of
|
|
505
|
+
the changed properties. This is useful for testing and determining how certain
|
|
506
|
+
properties affect others.
|
|
507
|
+
|
|
508
|
+
Parameters
|
|
509
|
+
----------
|
|
510
|
+
name : str
|
|
511
|
+
The name of the property to set the value of
|
|
512
|
+
value : any
|
|
513
|
+
The value to set the property to
|
|
514
|
+
changedProperties : list
|
|
515
|
+
List of properties that have changed
|
|
516
|
+
"""
|
|
517
|
+
t0 = self.GetTime()
|
|
518
|
+
ret = False
|
|
519
|
+
|
|
520
|
+
if name is not None and value is not None:
|
|
521
|
+
command = self._addSingleCommand(
|
|
522
|
+
self.SET_PROPERTY_AND_GET_CHANGED_PROPERTIES, name, [value]
|
|
523
|
+
)
|
|
524
|
+
response = self._sendCommand(command)
|
|
525
|
+
if response != False:
|
|
526
|
+
ret = response.acknowledge[0].error != True
|
|
527
|
+
self.refreshProperties = True
|
|
528
|
+
|
|
529
|
+
if ret:
|
|
530
|
+
ret = self.ParseChangedProperties(changedProperties, response)
|
|
531
|
+
if logLevel == logging.DEBUG:
|
|
532
|
+
log.debug(
|
|
533
|
+
"SetProperty: %s = %s, completed in %.1f ms",
|
|
534
|
+
name,
|
|
535
|
+
value,
|
|
536
|
+
(self.GetTime() - t0) * 1000,
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
return ret
|
|
540
|
+
|
|
541
|
+
@write_only
|
|
542
|
+
def set_engineering_mode(self, enable, password):
|
|
543
|
+
"""
|
|
544
|
+
Set the engineering mode of the current camera on DE-Server. Mostly for internal testing.
|
|
545
|
+
|
|
546
|
+
Parameters
|
|
547
|
+
----------
|
|
548
|
+
enable : bool
|
|
549
|
+
Enable or disable engineering mode
|
|
550
|
+
password : str
|
|
551
|
+
The password to enable engineering mode
|
|
552
|
+
"""
|
|
553
|
+
ret = False
|
|
554
|
+
|
|
555
|
+
command = self._addSingleCommand(self.SET_ENG_MODE, None, [enable, password])
|
|
556
|
+
response = self._sendCommand(command)
|
|
557
|
+
if response != False:
|
|
558
|
+
ret = response.acknowledge[0].error != True
|
|
559
|
+
self.refreshProperties = True
|
|
560
|
+
return ret
|
|
561
|
+
|
|
562
|
+
@write_only
|
|
563
|
+
def setEngModeAndGetChangedProperties(self, enable, password, changedProperties):
|
|
564
|
+
|
|
565
|
+
ret = False
|
|
566
|
+
|
|
567
|
+
command = self.__addSingleCommand(
|
|
568
|
+
self.SET_ENG_MODE_GET_CHANGED_PROPERTIES, None, [enable, password]
|
|
569
|
+
)
|
|
570
|
+
response = self.__sendCommand(command)
|
|
571
|
+
if response != False:
|
|
572
|
+
ret = response.acknowledge[0].error != True
|
|
573
|
+
self.refreshProperties = True
|
|
574
|
+
|
|
575
|
+
if ret:
|
|
576
|
+
ret = self.ParseChangedProperties(changedProperties, response)
|
|
577
|
+
|
|
578
|
+
return ret
|
|
579
|
+
|
|
580
|
+
@write_only
|
|
581
|
+
def set_hw_roi(self, offsetX: int, offsetY: int, sizeX: int, sizeY: int):
|
|
582
|
+
"""
|
|
583
|
+
Set the hardware region of interest (ROI) of the current camera on DE-Server.
|
|
584
|
+
|
|
585
|
+
Parameters
|
|
586
|
+
----------
|
|
587
|
+
offsetX : int
|
|
588
|
+
The x offset of the ROI
|
|
589
|
+
offsetY : int
|
|
590
|
+
The y offset of the ROI
|
|
591
|
+
sizeX : int
|
|
592
|
+
The width of the ROI
|
|
593
|
+
sizeY : int
|
|
594
|
+
The height of the ROI
|
|
595
|
+
"""
|
|
596
|
+
t0 = self.GetTime()
|
|
597
|
+
ret = False
|
|
598
|
+
|
|
599
|
+
command = self._addSingleCommand(
|
|
600
|
+
self.SET_HW_ROI, None, [offsetX, offsetY, sizeX, sizeY]
|
|
601
|
+
)
|
|
602
|
+
response = self._sendCommand(command)
|
|
603
|
+
if response != False:
|
|
604
|
+
ret = response.acknowledge[0].error != True
|
|
605
|
+
self.refreshProperties = True
|
|
606
|
+
|
|
607
|
+
if logLevel == logging.DEBUG:
|
|
608
|
+
log.debug(
|
|
609
|
+
"SetHwRoi: (%i,%i,%i,%i) , completed in %.1f ms",
|
|
610
|
+
offsetX,
|
|
611
|
+
offsetY,
|
|
612
|
+
sizeX,
|
|
613
|
+
sizeY,
|
|
614
|
+
(self.GetTime() - t0) * 1000,
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
return ret
|
|
618
|
+
|
|
619
|
+
@write_only
|
|
620
|
+
def SetScanSize(self, sizeX, sizeY):
|
|
621
|
+
|
|
622
|
+
t0 = self.GetTime()
|
|
623
|
+
ret = False
|
|
624
|
+
|
|
625
|
+
command = self.__addSingleCommand(self.SET_SCAN_SIZE, None, [sizeX, sizeY])
|
|
626
|
+
response = self.__sendCommand(command)
|
|
627
|
+
if response != False:
|
|
628
|
+
ret = response.acknowledge[0].error != True
|
|
629
|
+
self.refreshProperties = True
|
|
630
|
+
|
|
631
|
+
if logLevel == logging.DEBUG:
|
|
632
|
+
log.debug(
|
|
633
|
+
"SetScanSize: (%i,%i) , completed in %.1f ms",
|
|
634
|
+
sizeX,
|
|
635
|
+
sizeY,
|
|
636
|
+
(self.GetTime() - t0) * 1000,
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
return ret
|
|
640
|
+
|
|
641
|
+
@write_only
|
|
642
|
+
def SetScanSizeAndGetChangedProperties(self, sizeX, sizeY, changedProperties):
|
|
643
|
+
t0 = self.GetTime()
|
|
644
|
+
ret = False
|
|
645
|
+
|
|
646
|
+
command = self.__addSingleCommand(
|
|
647
|
+
self.SET_SCAN_SIZE_AND_GET_CHANGED_PROPERTIES, None, [sizeX, sizeY]
|
|
648
|
+
)
|
|
649
|
+
response = self.__sendCommand(command)
|
|
650
|
+
if response != False:
|
|
651
|
+
ret = response.acknowledge[0].error != True
|
|
652
|
+
self.refreshProperties = True
|
|
653
|
+
|
|
654
|
+
if ret:
|
|
655
|
+
ret = self.ParseChangedProperties(changedProperties, response)
|
|
656
|
+
|
|
657
|
+
if logLevel == logging.DEBUG:
|
|
658
|
+
log.debug(
|
|
659
|
+
"SetScanSize: (%i,%i) , completed in %.1f ms",
|
|
660
|
+
sizeX,
|
|
661
|
+
sizeY,
|
|
662
|
+
(self.GetTime() - t0) * 1000,
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
return ret
|
|
666
|
+
|
|
667
|
+
@write_only
|
|
668
|
+
def SetScanROI(self, enable, offsetX, offsetY, sizeX, sizeY):
|
|
669
|
+
|
|
670
|
+
t0 = self.GetTime()
|
|
671
|
+
ret = False
|
|
672
|
+
|
|
673
|
+
command = self.__addSingleCommand(
|
|
674
|
+
self.SET_SCAN_SIZE, None, [enable, offsetX, offsetY, sizeX, sizeY]
|
|
675
|
+
)
|
|
676
|
+
response = self.__sendCommand(command)
|
|
677
|
+
if response != False:
|
|
678
|
+
ret = response.acknowledge[0].error != True
|
|
679
|
+
self.refreshProperties = True
|
|
680
|
+
|
|
681
|
+
if logLevel == logging.DEBUG:
|
|
682
|
+
log.debug(
|
|
683
|
+
"SetScanROI: (%i,%i,%i,%i) , completed in %.1f ms",
|
|
684
|
+
offsetX,
|
|
685
|
+
offsetY,
|
|
686
|
+
sizeX,
|
|
687
|
+
sizeY,
|
|
688
|
+
(self.GetTime() - t0) * 1000,
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
return ret
|
|
692
|
+
|
|
693
|
+
@write_only
|
|
694
|
+
def SetScanROI(self, enable, offsetX, offsetY, sizeX, sizeY, changedProperties):
|
|
695
|
+
t0 = self.GetTime()
|
|
696
|
+
ret = False
|
|
697
|
+
|
|
698
|
+
command = self.__addSingleCommand(
|
|
699
|
+
self.SET_SCAN_ROI__AND_GET_CHANGED_PROPERTIES,
|
|
700
|
+
None,
|
|
701
|
+
[enable, offsetX, offsetY, sizeX, sizeY],
|
|
702
|
+
)
|
|
703
|
+
response = self.__sendCommand(command)
|
|
704
|
+
if response != False:
|
|
705
|
+
ret = response.acknowledge[0].error != True
|
|
706
|
+
self.refreshProperties = True
|
|
707
|
+
|
|
708
|
+
if ret:
|
|
709
|
+
ret = self.ParseChangedProperties(changedProperties, response)
|
|
710
|
+
|
|
711
|
+
if logLevel == logging.DEBUG:
|
|
712
|
+
log.debug(
|
|
713
|
+
"SetScanROI: (%i,%i,%i,%i) , completed in %.1f ms",
|
|
714
|
+
offsetX,
|
|
715
|
+
offsetY,
|
|
716
|
+
sizeX,
|
|
717
|
+
sizeY,
|
|
718
|
+
(self.GetTime() - t0) * 1000,
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
return ret
|
|
722
|
+
|
|
723
|
+
@write_only
|
|
724
|
+
def set_hw_roi_and_get_changed_properties(
|
|
725
|
+
self, offsetX: int, offsetY: int, sizeX: int, sizeY: int, changedProperties
|
|
726
|
+
):
|
|
727
|
+
"""
|
|
728
|
+
Set the hardware region of interest (ROI) of the current camera on DE-Server and get all of
|
|
729
|
+
the changed properties. This is useful for testing and determining how certain
|
|
730
|
+
properties affect others.
|
|
731
|
+
|
|
732
|
+
Parameters
|
|
733
|
+
----------
|
|
734
|
+
offsetX : int
|
|
735
|
+
The x offset of the ROI
|
|
736
|
+
offsetY : int
|
|
737
|
+
The y offset of the ROI
|
|
738
|
+
sizeX : int
|
|
739
|
+
The width of the ROI
|
|
740
|
+
sizeY : int
|
|
741
|
+
The height of the ROI
|
|
742
|
+
changedProperties : list
|
|
743
|
+
List of properties that have changed
|
|
744
|
+
"""
|
|
745
|
+
t0 = self.GetTime()
|
|
746
|
+
ret = False
|
|
747
|
+
|
|
748
|
+
command = self._addSingleCommand(
|
|
749
|
+
self.SET_HW_ROI_AND_GET_CHANGED_PROPERTIES,
|
|
750
|
+
None,
|
|
751
|
+
[offsetX, offsetY, sizeX, sizeY],
|
|
752
|
+
)
|
|
753
|
+
response = self._sendCommand(command)
|
|
754
|
+
if response != False:
|
|
755
|
+
ret = response.acknowledge[0].error != True
|
|
756
|
+
self.refreshProperties = True
|
|
757
|
+
|
|
758
|
+
if ret:
|
|
759
|
+
ret = self.ParseChangedProperties(changedProperties, response)
|
|
760
|
+
|
|
761
|
+
if logLevel == logging.DEBUG:
|
|
762
|
+
log.debug(
|
|
763
|
+
"SetHwRoi: (%i,%i,%i,%i) , completed in %.1f ms",
|
|
764
|
+
offsetX,
|
|
765
|
+
offsetY,
|
|
766
|
+
sizeX,
|
|
767
|
+
sizeY,
|
|
768
|
+
(self.GetTime() - t0) * 1000,
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
return ret
|
|
772
|
+
|
|
773
|
+
@write_only
|
|
774
|
+
def set_sw_roi(self, offsetX: int, offsetY: int, sizeX: int, sizeY: int):
|
|
775
|
+
"""
|
|
776
|
+
Set the software region of interest (ROI) of the current camera on DE-Server.
|
|
777
|
+
|
|
778
|
+
Parameters
|
|
779
|
+
----------
|
|
780
|
+
offsetX : int
|
|
781
|
+
The x offset of the ROI
|
|
782
|
+
offsetY : int
|
|
783
|
+
The y offset of the ROI
|
|
784
|
+
sizeX : int
|
|
785
|
+
The width of the ROI
|
|
786
|
+
sizeY : int
|
|
787
|
+
The height of the ROI
|
|
788
|
+
"""
|
|
789
|
+
t0 = self.GetTime()
|
|
790
|
+
ret = False
|
|
791
|
+
|
|
792
|
+
command = self._addSingleCommand(
|
|
793
|
+
self.SET_SW_ROI, None, [offsetX, offsetY, sizeX, sizeY]
|
|
794
|
+
)
|
|
795
|
+
response = self._sendCommand(command)
|
|
796
|
+
if response != False:
|
|
797
|
+
ret = response.acknowledge[0].error != True
|
|
798
|
+
self.refreshProperties = True
|
|
799
|
+
|
|
800
|
+
if logLevel == logging.DEBUG:
|
|
801
|
+
log.debug(
|
|
802
|
+
"SetSwRoi: (%i,%i,%i,%i) , completed in %.1f ms",
|
|
803
|
+
offsetX,
|
|
804
|
+
offsetY,
|
|
805
|
+
sizeX,
|
|
806
|
+
sizeY,
|
|
807
|
+
(self.GetTime() - t0) * 1000,
|
|
808
|
+
)
|
|
809
|
+
|
|
810
|
+
return ret
|
|
811
|
+
|
|
812
|
+
@write_only
|
|
813
|
+
def set_sw_roi_and_get_changed_properties(
|
|
814
|
+
self, offsetX, offsetY, sizeX, sizeY, changedProperties
|
|
815
|
+
):
|
|
816
|
+
"""
|
|
817
|
+
Set the software region of interest (ROI) of the current camera on DE-Server and get all of
|
|
818
|
+
the changed properties. This is useful for testing and determining how certain
|
|
819
|
+
properties affect others.
|
|
820
|
+
|
|
821
|
+
Parameters
|
|
822
|
+
----------
|
|
823
|
+
offsetX : int
|
|
824
|
+
The x offset of the ROI
|
|
825
|
+
offsetY : int
|
|
826
|
+
The y offset of the ROI
|
|
827
|
+
sizeX : int
|
|
828
|
+
The width of the ROI
|
|
829
|
+
sizeY : int
|
|
830
|
+
The height of the ROI
|
|
831
|
+
changedProperties : list
|
|
832
|
+
List of properties that have changed
|
|
833
|
+
"""
|
|
834
|
+
t0 = self.GetTime()
|
|
835
|
+
ret = False
|
|
836
|
+
|
|
837
|
+
command = self._addSingleCommand(
|
|
838
|
+
self.SET_SW_ROI_AND_GET_CHANGED_PROPERTIES,
|
|
839
|
+
None,
|
|
840
|
+
[offsetX, offsetY, sizeX, sizeY],
|
|
841
|
+
)
|
|
842
|
+
response = self._sendCommand(command)
|
|
843
|
+
if response != False:
|
|
844
|
+
ret = response.acknowledge[0].error != True
|
|
845
|
+
self.refreshProperties = True
|
|
846
|
+
|
|
847
|
+
if ret:
|
|
848
|
+
ret = self.ParseChangedProperties(changedProperties, response)
|
|
849
|
+
|
|
850
|
+
if logLevel == logging.DEBUG:
|
|
851
|
+
log.debug(
|
|
852
|
+
"SetSWRoi: (%i,%i,%i,%i) , completed in %.1f ms",
|
|
853
|
+
offsetX,
|
|
854
|
+
offsetY,
|
|
855
|
+
sizeX,
|
|
856
|
+
sizeY,
|
|
857
|
+
(self.GetTime() - t0) * 1000,
|
|
858
|
+
)
|
|
859
|
+
|
|
860
|
+
return ret
|
|
861
|
+
|
|
862
|
+
def current_movie_buffer(self):
|
|
863
|
+
movieBufferInfo = self.GetMovieBufferInfo()
|
|
864
|
+
if movieBufferInfo.imageDataType == DataType.DE8u:
|
|
865
|
+
imageType = numpy.uint8
|
|
866
|
+
elif movieBufferInfo.imageDataType == DataType.DE16u:
|
|
867
|
+
imageType = numpy.uint16
|
|
868
|
+
elif movieBufferInfo.imageDataType == DataType.DE32f:
|
|
869
|
+
imageType = numpy.float32
|
|
870
|
+
|
|
871
|
+
## Allocate movie buffers
|
|
872
|
+
totalBytes = movieBufferInfo.headerBytes + movieBufferInfo.imageBufferBytes
|
|
873
|
+
buffer = bytearray(totalBytes)
|
|
874
|
+
return movieBufferInfo, buffer, totalBytes, imageType
|
|
875
|
+
|
|
876
|
+
@write_only
|
|
877
|
+
def start_acquisition(
|
|
878
|
+
self,
|
|
879
|
+
numberOfAcquisitions: int = 1,
|
|
880
|
+
requestMovieBuffer=False,
|
|
881
|
+
update=True,
|
|
882
|
+
):
|
|
883
|
+
"""
|
|
884
|
+
Start acquiring images. Make sure all of the properties are set to the desired values.
|
|
885
|
+
|
|
886
|
+
Parameters
|
|
887
|
+
----------
|
|
888
|
+
numberOfAcquisitions : int, optional
|
|
889
|
+
The number of acquisitions to repeat, by default 1
|
|
890
|
+
requestMovieBuffer : bool, optional
|
|
891
|
+
Request a movie buffer, by default False. If True, the movie buffer will be returned
|
|
892
|
+
with all of the frames.
|
|
893
|
+
|
|
894
|
+
"""
|
|
895
|
+
start_time = self.GetTime()
|
|
896
|
+
step_time = self.GetTime()
|
|
897
|
+
|
|
898
|
+
if update:
|
|
899
|
+
self.update_scan_size()
|
|
900
|
+
self.update_image_size()
|
|
901
|
+
|
|
902
|
+
if self.refreshProperties:
|
|
903
|
+
self.roi_x = self.GetProperty("Crop Size X")
|
|
904
|
+
self.roi_y = self.GetProperty("Crop Size Y")
|
|
905
|
+
self.binning_x = self.GetProperty("Binning X")
|
|
906
|
+
self.binning_y = self.GetProperty("Binning Y")
|
|
907
|
+
self.width = self.GetProperty("Image Size X (pixels)")
|
|
908
|
+
self.height = self.GetProperty("Image Size Y (pixels)")
|
|
909
|
+
self.exposureTime = self.GetProperty("Exposure Time (seconds)")
|
|
910
|
+
self.refreshProperties = False
|
|
911
|
+
|
|
912
|
+
if logLevel == logging.DEBUG:
|
|
913
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
914
|
+
log.debug(" Prepare Time: %.1f ms", lapsed)
|
|
915
|
+
step_time = self.GetTime()
|
|
916
|
+
|
|
917
|
+
if self.width * self.height == 0:
|
|
918
|
+
log.error(" Image size is 0! ")
|
|
919
|
+
else:
|
|
920
|
+
bytesize = 0
|
|
921
|
+
command = self._addSingleCommand(
|
|
922
|
+
self.START_ACQUISITION, None, [numberOfAcquisitions, requestMovieBuffer]
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
if logLevel == logging.DEBUG:
|
|
926
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
927
|
+
log.debug(" Build Time: %.1f ms", lapsed)
|
|
928
|
+
step_time = self.GetTime()
|
|
929
|
+
|
|
930
|
+
response = self._sendCommand(command)
|
|
931
|
+
if logLevel == logging.DEBUG:
|
|
932
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
933
|
+
log.debug(" Command Time: %.1f ms", lapsed)
|
|
934
|
+
step_time = self.GetTime()
|
|
935
|
+
|
|
936
|
+
if response != False:
|
|
937
|
+
ret = response.acknowledge[0].error != True
|
|
938
|
+
self.refreshProperties = True
|
|
939
|
+
|
|
940
|
+
if logLevel == logging.DEBUG:
|
|
941
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
942
|
+
log.debug(" Typing Time: %.1f ms", lapsed)
|
|
943
|
+
step_time = self.GetTime()
|
|
944
|
+
|
|
945
|
+
if logLevel <= logging.DEBUG:
|
|
946
|
+
lapsed = (self.GetTime() - start_time) * 1000
|
|
947
|
+
log.debug(
|
|
948
|
+
" Start Time: %.1f ms, ROI:[%d, %d], Binning:[%d, %d], Image size:[%d, %d]",
|
|
949
|
+
lapsed,
|
|
950
|
+
self.roi_x,
|
|
951
|
+
self.roi_y,
|
|
952
|
+
self.binning_x,
|
|
953
|
+
self.binning_y,
|
|
954
|
+
self.width,
|
|
955
|
+
self.height,
|
|
956
|
+
)
|
|
957
|
+
|
|
958
|
+
@write_only
|
|
959
|
+
def stop_acquisition(self):
|
|
960
|
+
"""
|
|
961
|
+
Stop acquiring images.
|
|
962
|
+
|
|
963
|
+
This can be called in the same thread or another thread to stop the current acquisitions.
|
|
964
|
+
This will cause `get_result` calls to return immediately.
|
|
965
|
+
"""
|
|
966
|
+
start_time = self.GetTime()
|
|
967
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
|
|
968
|
+
sock.sendto(b"PyClientStopAcq", (self.host, self.port))
|
|
969
|
+
respond = sock.recv(32)
|
|
970
|
+
if logLevel == logging.INFO:
|
|
971
|
+
log.info(f"{self.host} {self.port} {respond}")
|
|
972
|
+
if logLevel <= logging.DEBUG:
|
|
973
|
+
lapsed = (self.GetTime() - start_time) * 1000
|
|
974
|
+
log.debug(" Stop Time: %.1f ms", lapsed)
|
|
975
|
+
|
|
976
|
+
return b"Stopped" in respond
|
|
977
|
+
|
|
978
|
+
def get_result(
|
|
979
|
+
self,
|
|
980
|
+
frameType="singleframe_integrated",
|
|
981
|
+
pixelFormat="UINT16",
|
|
982
|
+
attributes="auto",
|
|
983
|
+
histogram=None,
|
|
984
|
+
):
|
|
985
|
+
"""
|
|
986
|
+
Get the specified type of frames in the desired pixel format and associated information.
|
|
987
|
+
|
|
988
|
+
Parameters
|
|
989
|
+
----------
|
|
990
|
+
frameType: FrameType
|
|
991
|
+
The type of frame to get. Use the FrameType enum or a string.
|
|
992
|
+
Most common:
|
|
993
|
+
- virtual_image0 (or 1, 2, 3, 4)
|
|
994
|
+
- external_image1 (or 2, 3, 4)
|
|
995
|
+
- sumtotal
|
|
996
|
+
- singleframe_integrated
|
|
997
|
+
pixelFormat: PixelFormat
|
|
998
|
+
The pixel format to get. Use the PixelFormat enum or a string.
|
|
999
|
+
One of the following:
|
|
1000
|
+
- DE8u
|
|
1001
|
+
- DE16u
|
|
1002
|
+
- DE32f
|
|
1003
|
+
- DE64f
|
|
1004
|
+
attributes: Attributes
|
|
1005
|
+
Defines the image to be returned, some members can be updated.
|
|
1006
|
+
Some members of this parameter are input only, some are input/output.
|
|
1007
|
+
histogram: Histogram
|
|
1008
|
+
Returns the histogram if desired.
|
|
1009
|
+
Some members of this parameter are input only, some are input/output.
|
|
1010
|
+
|
|
1011
|
+
Note
|
|
1012
|
+
----
|
|
1013
|
+
During acquisition, live frames will be returned; after acquisition, the last image will be returned.
|
|
1014
|
+
"""
|
|
1015
|
+
if isinstance(frameType, str):
|
|
1016
|
+
frameType = getattr(FrameType, frameType.upper())
|
|
1017
|
+
if isinstance(pixelFormat, str):
|
|
1018
|
+
pixelFormat = getattr(PixelFormat, pixelFormat)
|
|
1019
|
+
|
|
1020
|
+
if attributes == "auto":
|
|
1021
|
+
attributes = Attributes()
|
|
1022
|
+
scan_images = [17, 18, 19, 20, 21, 22, 23, 24, 25]
|
|
1023
|
+
if frameType.value in scan_images:
|
|
1024
|
+
attributes.windowWidth = self.scan_sizex
|
|
1025
|
+
attributes.windowHeight = self.scan_sizey
|
|
1026
|
+
else:
|
|
1027
|
+
attributes.windowWidth = self.image_sizex
|
|
1028
|
+
attributes.windowHeight = self.image_sizey
|
|
1029
|
+
|
|
1030
|
+
log.debug("GetResult frameType:%s, pixelFormat:%s", frameType, pixelFormat)
|
|
1031
|
+
start_time = self.GetTime()
|
|
1032
|
+
step_time = self.GetTime()
|
|
1033
|
+
|
|
1034
|
+
if attributes == None:
|
|
1035
|
+
attributes = Attributes()
|
|
1036
|
+
|
|
1037
|
+
if histogram == None:
|
|
1038
|
+
histogram = Histogram()
|
|
1039
|
+
|
|
1040
|
+
if attributes.windowWidth > 0:
|
|
1041
|
+
self.width = attributes.windowWidth
|
|
1042
|
+
|
|
1043
|
+
if attributes.windowHeight > 0:
|
|
1044
|
+
self.height = attributes.windowHeight
|
|
1045
|
+
|
|
1046
|
+
image = None
|
|
1047
|
+
imageDataType = numpy.uint16
|
|
1048
|
+
|
|
1049
|
+
if logLevel == logging.DEBUG:
|
|
1050
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
1051
|
+
log.debug(" Prepare Time: %.1f ms", lapsed)
|
|
1052
|
+
step_time = self.GetTime()
|
|
1053
|
+
|
|
1054
|
+
if histogram != None:
|
|
1055
|
+
histoMin = histogram.min
|
|
1056
|
+
histoMax = histogram.max
|
|
1057
|
+
histoBins = histogram.bins
|
|
1058
|
+
else:
|
|
1059
|
+
histoMin = 0
|
|
1060
|
+
histoMax = 0
|
|
1061
|
+
histoBins = 0
|
|
1062
|
+
|
|
1063
|
+
if self.width * self.height == 0:
|
|
1064
|
+
log.error(" Image size is 0! ")
|
|
1065
|
+
else:
|
|
1066
|
+
bytesize = 0
|
|
1067
|
+
command = self._addSingleCommand(
|
|
1068
|
+
self.GET_RESULT,
|
|
1069
|
+
None,
|
|
1070
|
+
[
|
|
1071
|
+
frameType.value,
|
|
1072
|
+
pixelFormat.value,
|
|
1073
|
+
attributes.centerX,
|
|
1074
|
+
attributes.centerY,
|
|
1075
|
+
attributes.zoom,
|
|
1076
|
+
attributes.windowWidth,
|
|
1077
|
+
attributes.windowHeight,
|
|
1078
|
+
attributes.fft,
|
|
1079
|
+
attributes.stretchType,
|
|
1080
|
+
attributes.manualStretchMin,
|
|
1081
|
+
attributes.manualStretchMax,
|
|
1082
|
+
attributes.manualStretchGamma,
|
|
1083
|
+
attributes.outlierPercentage,
|
|
1084
|
+
attributes.timeoutMsec,
|
|
1085
|
+
histoMin,
|
|
1086
|
+
histoMax,
|
|
1087
|
+
histoBins,
|
|
1088
|
+
],
|
|
1089
|
+
)
|
|
1090
|
+
|
|
1091
|
+
if logLevel == logging.DEBUG:
|
|
1092
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
1093
|
+
log.debug(" Build Time: %.1f ms", lapsed)
|
|
1094
|
+
step_time = self.GetTime()
|
|
1095
|
+
response = self._sendCommand(command)
|
|
1096
|
+
if logLevel == logging.DEBUG:
|
|
1097
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
1098
|
+
log.debug(" Command Time: %.1f ms", lapsed)
|
|
1099
|
+
step_time = self.GetTime()
|
|
1100
|
+
|
|
1101
|
+
if response != False:
|
|
1102
|
+
values = self.__getParameters(response.acknowledge[0])
|
|
1103
|
+
if (
|
|
1104
|
+
type(values) is list and len(values) >= 20
|
|
1105
|
+
): # This should be majorly simplified
|
|
1106
|
+
i = 0
|
|
1107
|
+
pixelFormat = PixelFormat(values[i])
|
|
1108
|
+
i += 1
|
|
1109
|
+
attributes.frameWidth = values[i]
|
|
1110
|
+
i += 1
|
|
1111
|
+
attributes.frameHeight = values[i]
|
|
1112
|
+
i += 1
|
|
1113
|
+
attributes.datasetName = values[i]
|
|
1114
|
+
i += 1
|
|
1115
|
+
attributes.acqIndex = values[i]
|
|
1116
|
+
i += 1
|
|
1117
|
+
attributes.acqFinished = values[i]
|
|
1118
|
+
i += 1
|
|
1119
|
+
attributes.imageIndex = values[i]
|
|
1120
|
+
i += 1
|
|
1121
|
+
attributes.frameCount = values[i]
|
|
1122
|
+
i += 1
|
|
1123
|
+
attributes.imageMin = values[i]
|
|
1124
|
+
i += 1
|
|
1125
|
+
attributes.imageMax = values[i]
|
|
1126
|
+
i += 1
|
|
1127
|
+
attributes.imageMean = values[i]
|
|
1128
|
+
i += 1
|
|
1129
|
+
attributes.imageStd = values[i]
|
|
1130
|
+
i += 1
|
|
1131
|
+
attributes.eppix = values[i]
|
|
1132
|
+
i += 1
|
|
1133
|
+
attributes.eps = values[i]
|
|
1134
|
+
i += 1
|
|
1135
|
+
attributes.eppixps = values[i]
|
|
1136
|
+
i += 1
|
|
1137
|
+
attributes.epa2 = values[i]
|
|
1138
|
+
i += 1
|
|
1139
|
+
attributes.eppixpf = values[i]
|
|
1140
|
+
i += 1
|
|
1141
|
+
if commandVersion >= 12:
|
|
1142
|
+
attributes.eppix_incident = values[i]
|
|
1143
|
+
i += 1
|
|
1144
|
+
attributes.eps_incident = values[i]
|
|
1145
|
+
i += 1
|
|
1146
|
+
attributes.eppixps_incident = values[i]
|
|
1147
|
+
i += 1
|
|
1148
|
+
attributes.epa2_incident = values[i]
|
|
1149
|
+
i += 1
|
|
1150
|
+
attributes.eppixpf_incident = values[i]
|
|
1151
|
+
i += 1
|
|
1152
|
+
if commandVersion >= 11:
|
|
1153
|
+
attributes.saturation = values[i]
|
|
1154
|
+
i += 1
|
|
1155
|
+
if commandVersion < 10:
|
|
1156
|
+
attributes.underExposureRate = values[i]
|
|
1157
|
+
i += 1
|
|
1158
|
+
attributes.overExposureRate = values[i]
|
|
1159
|
+
i += 1
|
|
1160
|
+
attributes.timestamp = float(values[i])
|
|
1161
|
+
i += 1
|
|
1162
|
+
if commandVersion >= 10:
|
|
1163
|
+
attributes.autoStretchMin = values[i]
|
|
1164
|
+
i += 1
|
|
1165
|
+
attributes.autoStretchMax = values[i]
|
|
1166
|
+
i += 1
|
|
1167
|
+
attributes.autoStretchGamma = values[i]
|
|
1168
|
+
i += 1
|
|
1169
|
+
|
|
1170
|
+
if (
|
|
1171
|
+
histogram != None
|
|
1172
|
+
and histoBins > 0
|
|
1173
|
+
and len(values) >= i + histogram.bins
|
|
1174
|
+
):
|
|
1175
|
+
histogram.data = [0] * histogram.bins
|
|
1176
|
+
histogram.min = values[i]
|
|
1177
|
+
i += 1
|
|
1178
|
+
histogram.max = values[i]
|
|
1179
|
+
i += 1
|
|
1180
|
+
if commandVersion >= 11:
|
|
1181
|
+
histogram.upperMostLocalMaxima = values[i]
|
|
1182
|
+
i += 1
|
|
1183
|
+
for j in range(histogram.bins):
|
|
1184
|
+
log.debug("Hist %d: %d" % (j, values[i + j]))
|
|
1185
|
+
histogram.data[j] = values[i + j]
|
|
1186
|
+
|
|
1187
|
+
if pixelFormat == PixelFormat.FLOAT32:
|
|
1188
|
+
imageDataType = numpy.float32
|
|
1189
|
+
elif pixelFormat == PixelFormat.UINT16:
|
|
1190
|
+
imageDataType = numpy.uint16
|
|
1191
|
+
else:
|
|
1192
|
+
imageDataType = numpy.uint8
|
|
1193
|
+
|
|
1194
|
+
self.width = attributes.frameWidth
|
|
1195
|
+
self.height = attributes.frameHeight
|
|
1196
|
+
|
|
1197
|
+
recvbyteSizeString = self._recvFromSocket(
|
|
1198
|
+
self.socket, 4
|
|
1199
|
+
) # get the first 4 bytes
|
|
1200
|
+
if len(recvbyteSizeString) == 4:
|
|
1201
|
+
recvbyteSize = struct.unpack(
|
|
1202
|
+
"I", recvbyteSizeString
|
|
1203
|
+
) # interpret as size
|
|
1204
|
+
received_string = self._recvFromSocket(
|
|
1205
|
+
self.socket, recvbyteSize[0]
|
|
1206
|
+
) # get the rest
|
|
1207
|
+
data_header = pb.DEPacket()
|
|
1208
|
+
data_header.ParseFromString(received_string)
|
|
1209
|
+
bytesize = data_header.data_header.bytesize
|
|
1210
|
+
|
|
1211
|
+
if self.usingMmf:
|
|
1212
|
+
image = numpy.frombuffer(
|
|
1213
|
+
self.mmf,
|
|
1214
|
+
offset=MMF_DATA_HEADER_SIZE,
|
|
1215
|
+
dtype=imageDataType,
|
|
1216
|
+
count=self.width * self.height,
|
|
1217
|
+
)
|
|
1218
|
+
image.shape = [self.height, self.width]
|
|
1219
|
+
bytesize = self.width * self.height * 2
|
|
1220
|
+
elif bytesize > 0:
|
|
1221
|
+
packet = self._recvFromSocket(self.socket, bytesize)
|
|
1222
|
+
if len(packet) == bytesize:
|
|
1223
|
+
image = numpy.frombuffer(packet, imageDataType)
|
|
1224
|
+
bytesize = self.height * self.width * 2
|
|
1225
|
+
image.shape = [self.height, self.width]
|
|
1226
|
+
else:
|
|
1227
|
+
log.error(
|
|
1228
|
+
"The size of the image does not match the expected size from "
|
|
1229
|
+
"The header. Expected: %d, Received: %d",
|
|
1230
|
+
bytesize,
|
|
1231
|
+
len(packet),
|
|
1232
|
+
)
|
|
1233
|
+
|
|
1234
|
+
if logLevel == logging.DEBUG:
|
|
1235
|
+
elapsed = self.GetTime() - step_time
|
|
1236
|
+
log.debug(
|
|
1237
|
+
"Transfer time: %.1f ms, %d bytes, %d mbps",
|
|
1238
|
+
elapsed * 1000,
|
|
1239
|
+
bytesize,
|
|
1240
|
+
bytesize * 8 / elapsed / 1024 / 1024,
|
|
1241
|
+
)
|
|
1242
|
+
step_time = self.GetTime()
|
|
1243
|
+
|
|
1244
|
+
if bytesize <= 0:
|
|
1245
|
+
log.error(" GetResut failed! An empty image will be returned.")
|
|
1246
|
+
image = None
|
|
1247
|
+
|
|
1248
|
+
if logLevel == logging.DEBUG:
|
|
1249
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
1250
|
+
log.debug(" Saving Time: %.1f ms", lapsed)
|
|
1251
|
+
step_time = self.GetTime()
|
|
1252
|
+
|
|
1253
|
+
if image is None:
|
|
1254
|
+
log.error(" GetResut failed!")
|
|
1255
|
+
else:
|
|
1256
|
+
image = image.astype(imageDataType)
|
|
1257
|
+
|
|
1258
|
+
if logLevel == logging.DEBUG:
|
|
1259
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
1260
|
+
log.debug(" Typing Time: %.1f ms", lapsed)
|
|
1261
|
+
step_time = self.GetTime()
|
|
1262
|
+
|
|
1263
|
+
if logLevel <= logging.DEBUG:
|
|
1264
|
+
lapsed = (self.GetTime() - start_time) * 1000
|
|
1265
|
+
log.debug(
|
|
1266
|
+
"GetResult frameType:%s, pixelFormat:%s ROI:[%d, %d] Binning:[%d, %d], Return size:[%d, %d], datasetName:%s acqCount:%d, frameCount:%d min:%.1f max:%.1f mean:%.1f std:%.1f %.1f ms",
|
|
1267
|
+
frameType,
|
|
1268
|
+
pixelFormat,
|
|
1269
|
+
attributes.datasetName,
|
|
1270
|
+
attributes.acqIndex,
|
|
1271
|
+
attributes.frameCount,
|
|
1272
|
+
attributes.imageMin,
|
|
1273
|
+
attributes.imageMax,
|
|
1274
|
+
attributes.imageMean,
|
|
1275
|
+
attributes.imageStd,
|
|
1276
|
+
lapsed,
|
|
1277
|
+
)
|
|
1278
|
+
|
|
1279
|
+
return image, pixelFormat, attributes, histogram
|
|
1280
|
+
|
|
1281
|
+
@write_only
|
|
1282
|
+
def set_virtual_mask(self, id, w, h, mask):
|
|
1283
|
+
"""
|
|
1284
|
+
Set the virtual mask of the current camera on DE-Server.
|
|
1285
|
+
|
|
1286
|
+
Parameters
|
|
1287
|
+
----------
|
|
1288
|
+
id : int
|
|
1289
|
+
The id of the mask. 0-3
|
|
1290
|
+
w : int
|
|
1291
|
+
The width of the mask
|
|
1292
|
+
h : int
|
|
1293
|
+
The height of the mask
|
|
1294
|
+
mask : np.ndarray
|
|
1295
|
+
The mask to set
|
|
1296
|
+
"""
|
|
1297
|
+
if id < 1 or id > 4:
|
|
1298
|
+
log.error(
|
|
1299
|
+
" SetVirtualMask The virtual mask id must be selected between 1-4"
|
|
1300
|
+
)
|
|
1301
|
+
ret = False
|
|
1302
|
+
elif w < 0 or h < 0:
|
|
1303
|
+
log.error(
|
|
1304
|
+
" SetVirtualMask The virtual mask width and height must greater than 0"
|
|
1305
|
+
)
|
|
1306
|
+
ret = False
|
|
1307
|
+
else:
|
|
1308
|
+
command = self._addSingleCommand(self.SET_VIRTUAL_MASK, None, [id, w, h])
|
|
1309
|
+
ret = True
|
|
1310
|
+
try:
|
|
1311
|
+
packet = (
|
|
1312
|
+
struct.pack("I", command.ByteSize()) + command.SerializeToString()
|
|
1313
|
+
)
|
|
1314
|
+
self.socket.send(packet)
|
|
1315
|
+
except socket.error as e:
|
|
1316
|
+
ret = False
|
|
1317
|
+
|
|
1318
|
+
if ret:
|
|
1319
|
+
mask_bytes = mask.tobytes()
|
|
1320
|
+
self.__sendToSocket(self.socket, mask_bytes, len(mask_bytes))
|
|
1321
|
+
|
|
1322
|
+
ret = self.__ReceiveResponseForCommand(command) != False
|
|
1323
|
+
|
|
1324
|
+
return ret
|
|
1325
|
+
|
|
1326
|
+
def get_movie_buffer_info(self, movieBufferInfo=None, timeoutMsec=5000):
|
|
1327
|
+
"""
|
|
1328
|
+
Get the movie buffer information of the current camera on DE-Server.
|
|
1329
|
+
|
|
1330
|
+
Parameters
|
|
1331
|
+
----------
|
|
1332
|
+
movieBufferInfo : MovieBufferInfo, optional
|
|
1333
|
+
The movie buffer information to get, by default None. If None
|
|
1334
|
+
a new MovieBufferInfo object will be created.
|
|
1335
|
+
timeoutMsec : int, optional
|
|
1336
|
+
The timeout in milliseconds, by default 5000"""
|
|
1337
|
+
|
|
1338
|
+
if movieBufferInfo == None:
|
|
1339
|
+
movieBufferInfo = MovieBufferInfo()
|
|
1340
|
+
command = self._addSingleCommand(self.GET_MOVIE_BUFFER_INFO, None, None)
|
|
1341
|
+
|
|
1342
|
+
response = self._sendCommand(command)
|
|
1343
|
+
|
|
1344
|
+
if response != False:
|
|
1345
|
+
values = self.__getParameters(response.acknowledge[0])
|
|
1346
|
+
if type(values) is list:
|
|
1347
|
+
movieBufferInfo.headerBytes = values[0]
|
|
1348
|
+
movieBufferInfo.imageBufferBytes = values[1]
|
|
1349
|
+
movieBufferInfo.frameIndexStartPos = values[2]
|
|
1350
|
+
movieBufferInfo.imageStartPos = values[3]
|
|
1351
|
+
movieBufferInfo.imageW = values[4]
|
|
1352
|
+
movieBufferInfo.imageH = values[5]
|
|
1353
|
+
movieBufferInfo.framesInBuffer = values[6]
|
|
1354
|
+
dataType = values[7]
|
|
1355
|
+
movieBufferInfo.imageDataType = DataType(dataType)
|
|
1356
|
+
|
|
1357
|
+
return movieBufferInfo
|
|
1358
|
+
|
|
1359
|
+
def get_movie_buffer(
|
|
1360
|
+
self, movieBuffer, movieBufferSize, numFrames, timeoutMsec=5000
|
|
1361
|
+
):
|
|
1362
|
+
"""
|
|
1363
|
+
Get the movie buffer of the current camera on DE-Server. The movie buffer
|
|
1364
|
+
is a series of frames that are stored in memory and can be retrieved as
|
|
1365
|
+
a single buffer for faster processing.
|
|
1366
|
+
|
|
1367
|
+
"""
|
|
1368
|
+
|
|
1369
|
+
movieBufferStatus = MovieBufferStatus.UNKNOWN
|
|
1370
|
+
retval = True
|
|
1371
|
+
|
|
1372
|
+
command = self._addSingleCommand(self.GET_MOVIE_BUFFER, None, [timeoutMsec])
|
|
1373
|
+
response = self._sendCommand(command)
|
|
1374
|
+
|
|
1375
|
+
if response != False:
|
|
1376
|
+
totalBytes = 0
|
|
1377
|
+
status = 0
|
|
1378
|
+
values = self.__getParameters(response.acknowledge[0])
|
|
1379
|
+
if type(values) is list:
|
|
1380
|
+
status = values[0]
|
|
1381
|
+
totalBytes = values[1]
|
|
1382
|
+
numFrames = values[2]
|
|
1383
|
+
movieBufferStatus = MovieBufferStatus(status)
|
|
1384
|
+
if movieBufferStatus == MovieBufferStatus.OK:
|
|
1385
|
+
if totalBytes == 0 or movieBufferSize < totalBytes:
|
|
1386
|
+
retval = False
|
|
1387
|
+
log.error(
|
|
1388
|
+
f"Image received did not have the expected size."
|
|
1389
|
+
f"expected: {totalBytes}, received: {movieBufferSize}"
|
|
1390
|
+
)
|
|
1391
|
+
else:
|
|
1392
|
+
print("reading movie buffer", totalBytes)
|
|
1393
|
+
movieBuffer = self._recvFromSocket(self.socket, totalBytes)
|
|
1394
|
+
print("Done reading movie buffer")
|
|
1395
|
+
else:
|
|
1396
|
+
retval = False
|
|
1397
|
+
|
|
1398
|
+
if not retval:
|
|
1399
|
+
movieBufferStatus = MovieBufferStatus.FAILED
|
|
1400
|
+
|
|
1401
|
+
return movieBufferStatus, totalBytes, numFrames, movieBuffer
|
|
1402
|
+
|
|
1403
|
+
def save_image(self, image, fileName, textSize=0):
|
|
1404
|
+
t0 = self.GetTime()
|
|
1405
|
+
filePath = self.debugImagesFolder + fileName + ".tif"
|
|
1406
|
+
try:
|
|
1407
|
+
if not os.path.exists(self.debugImagesFolder):
|
|
1408
|
+
os.makedirs(self.debugImagesFolder)
|
|
1409
|
+
|
|
1410
|
+
tiff = Image.fromarray(image)
|
|
1411
|
+
tiff.save(filePath)
|
|
1412
|
+
log.info("Saved %s" % filePath)
|
|
1413
|
+
|
|
1414
|
+
# if textSize > 0:
|
|
1415
|
+
# self.__saveText(image, fileName, textSize)
|
|
1416
|
+
|
|
1417
|
+
except OSError:
|
|
1418
|
+
log.error("Failed to save file")
|
|
1419
|
+
|
|
1420
|
+
if logLevel == logging.DEBUG or True:
|
|
1421
|
+
log.debug("Save time: %.1f ms", (self.GetTime() - t0) * 1000)
|
|
1422
|
+
|
|
1423
|
+
return filePath
|
|
1424
|
+
|
|
1425
|
+
def print_server_info(self, camera=None):
|
|
1426
|
+
"""
|
|
1427
|
+
Print out the server information
|
|
1428
|
+
"""
|
|
1429
|
+
if camera is None:
|
|
1430
|
+
camera = self["Camera Name"]
|
|
1431
|
+
print("Time : " + datetime.now().strftime("%m/%d/%Y, %H:%M:%S"))
|
|
1432
|
+
print("Computer : " + socket.gethostname())
|
|
1433
|
+
print("DE-Server : " + self.GetProperty("Server Software Version"))
|
|
1434
|
+
print("CPU : " + self.GetProperty("Computer CPU Info"))
|
|
1435
|
+
print("GPU : " + self.GetProperty("Computer GPU Info"))
|
|
1436
|
+
print("Memory : " + self.GetProperty("Computer Memory Info"))
|
|
1437
|
+
print("Camera Name : " + camera)
|
|
1438
|
+
print("Camera S/N : " + str(self.GetProperty("Camera SN")))
|
|
1439
|
+
print("Sensor S/N : " + str(self.GetProperty("Sensor Module SN")))
|
|
1440
|
+
print("Firmware : " + str(self.GetProperty("Firmware Version")))
|
|
1441
|
+
print("Python : " + sys.version.split("(")[0])
|
|
1442
|
+
print("Client : " + version)
|
|
1443
|
+
print("Interrupt : " + str(self.GetProperty("Interrupt Status")))
|
|
1444
|
+
|
|
1445
|
+
def print_acquisition_info(self):
|
|
1446
|
+
"""
|
|
1447
|
+
Print out the acquisition information
|
|
1448
|
+
"""
|
|
1449
|
+
hwW = self.GetProperty("Hardware ROI - Size X")
|
|
1450
|
+
hwH = self.GetProperty("Hardware ROI - Size Y")
|
|
1451
|
+
hwX = self.GetProperty("Hardware ROI Offset X")
|
|
1452
|
+
hwY = self.GetProperty("Hardware ROI Offset Y")
|
|
1453
|
+
hwBinX = self.GetProperty("Hardware Binning X")
|
|
1454
|
+
hwBinY = self.GetProperty("Hardware Binning Y")
|
|
1455
|
+
|
|
1456
|
+
swW = self.GetProperty("ROI Size X")
|
|
1457
|
+
swH = self.GetProperty("ROI Size Y")
|
|
1458
|
+
swX = self.GetProperty("ROI Offset X")
|
|
1459
|
+
swY = self.GetProperty("ROI Offset Y")
|
|
1460
|
+
swBinX = self.GetProperty("Binning X")
|
|
1461
|
+
swBinY = self.GetProperty("Binning Y")
|
|
1462
|
+
|
|
1463
|
+
print("Test Pattern: " + self.GetProperty("Test Pattern"))
|
|
1464
|
+
print("Log Level : " + self.GetProperty("Log Level"))
|
|
1465
|
+
print(
|
|
1466
|
+
"Sensor size : "
|
|
1467
|
+
+ str(self.GetProperty("Sensor Size X (pixels)"))
|
|
1468
|
+
+ " x "
|
|
1469
|
+
+ str(self.GetProperty("Sensor Size Y (pixels)"))
|
|
1470
|
+
)
|
|
1471
|
+
print("HW ROI : " + str("%d x %d at (%d, %d)" % (hwW, hwH, hwX, hwY)))
|
|
1472
|
+
print("HW Binning : " + "%d x %d" % (hwBinX, hwBinY))
|
|
1473
|
+
print("SW ROI : " + str("%d x %d at (%d, %d)" % (swW, swH, swX, swY)))
|
|
1474
|
+
print("SW Binning : " + "%d x %d" % (swBinX, swBinY))
|
|
1475
|
+
print(
|
|
1476
|
+
"Image size : "
|
|
1477
|
+
+ str(self.GetProperty("Image Size X (pixels)"))
|
|
1478
|
+
+ " x "
|
|
1479
|
+
+ str(self.GetProperty("Image Size Y (pixels)"))
|
|
1480
|
+
)
|
|
1481
|
+
print("Max FPS : " + str(self.GetProperty("Frames Per Second (Max)")))
|
|
1482
|
+
print("FPS : " + str(self.GetProperty("Frames Per Second")))
|
|
1483
|
+
print("Grab Buffers: " + str(self.GetProperty("Grab Buffer Size")))
|
|
1484
|
+
|
|
1485
|
+
def print_saving_info(self):
|
|
1486
|
+
"""
|
|
1487
|
+
Print out the saving information
|
|
1488
|
+
"""
|
|
1489
|
+
sys.stdout.write("Saving : ")
|
|
1490
|
+
|
|
1491
|
+
if self.GetProperty("Autosave Crude Frames") == "On":
|
|
1492
|
+
sys.stdout.write("crude ")
|
|
1493
|
+
if self.GetProperty("Autosave Raw Frames") == "On":
|
|
1494
|
+
sys.stdout.write("raw ")
|
|
1495
|
+
if self.GetProperty("Autosave Single Frames") == "On":
|
|
1496
|
+
sys.stdout.write("frames ")
|
|
1497
|
+
if self.GetProperty("Autosave Integrating Frames") == "On":
|
|
1498
|
+
sys.stdout.write("integrating frames ")
|
|
1499
|
+
if self.GetProperty("Autosave Movie") == "On":
|
|
1500
|
+
sys.stdout.write("movie(")
|
|
1501
|
+
sys.stdout.write(str(self.GetProperty("Autosave Movie - Sum Count")))
|
|
1502
|
+
sys.stdout.write(") ")
|
|
1503
|
+
if self.GetProperty("Autosave Final Image") == "On":
|
|
1504
|
+
sys.stdout.write("final ")
|
|
1505
|
+
|
|
1506
|
+
print("")
|
|
1507
|
+
|
|
1508
|
+
def grab(self, frames=1, dataSetName="", fileName=None):
|
|
1509
|
+
"""
|
|
1510
|
+
Grab specified number of frames and print out stats. Mostly used for testing purposes
|
|
1511
|
+
|
|
1512
|
+
Parameters
|
|
1513
|
+
----------
|
|
1514
|
+
frames : int, optional
|
|
1515
|
+
The number of frames requested, by default 1
|
|
1516
|
+
dataSetName : str, optional
|
|
1517
|
+
Data set name to be used, by default ""
|
|
1518
|
+
fileName : str, optional
|
|
1519
|
+
Save the returned image as a file if provided, by default None
|
|
1520
|
+
"""
|
|
1521
|
+
imageW = self.GetProperty("Image Size X (pixels)")
|
|
1522
|
+
imageH = self.GetProperty("Image Size Y (pixels)")
|
|
1523
|
+
fps = self.GetProperty("Frames Per Second")
|
|
1524
|
+
|
|
1525
|
+
self.SetProperty("Exposure Time (seconds)", frames / fps)
|
|
1526
|
+
expoSec = self.GetProperty("Exposure Time (seconds)")
|
|
1527
|
+
maxExpoSec = self.GetProperty("Exposure Time Max (seconds)")
|
|
1528
|
+
prevSuffix = self.GetProperty("Autosave Filename Suffix")
|
|
1529
|
+
|
|
1530
|
+
frames = round(expoSec * fps)
|
|
1531
|
+
# frames = expoSec * fps
|
|
1532
|
+
|
|
1533
|
+
if dataSetName != "" and dataSetName != None:
|
|
1534
|
+
self.SetProperty("Autosave Filename Suffix", dataSetName)
|
|
1535
|
+
dataSetName = dataSetName + " "
|
|
1536
|
+
|
|
1537
|
+
if dataSetName != None:
|
|
1538
|
+
sys.stdout.write("%s%dx%d fps:%.3f " % (dataSetName, imageW, imageH, fps))
|
|
1539
|
+
sys.stdout.flush()
|
|
1540
|
+
|
|
1541
|
+
t0 = self.GetTime()
|
|
1542
|
+
self.StartAcquisition(1)
|
|
1543
|
+
|
|
1544
|
+
frameType = FrameType.SUMTOTAL
|
|
1545
|
+
|
|
1546
|
+
pixelFormat = PixelFormat.AUTO
|
|
1547
|
+
attributes = Attributes()
|
|
1548
|
+
histogram = Histogram()
|
|
1549
|
+
image = self.GetResult(frameType, pixelFormat, attributes, histogram)[0]
|
|
1550
|
+
|
|
1551
|
+
duration = self.GetTime() - t0
|
|
1552
|
+
measuredFps = self.GetProperty("Measured Frame Rate")
|
|
1553
|
+
missedFrames = self.GetProperty("Missed Frames")
|
|
1554
|
+
|
|
1555
|
+
frameCount = self.GetProperty("Number of Frames Processed")
|
|
1556
|
+
sys.stdout.write(
|
|
1557
|
+
"mfps:%.3f et:%.2fs dur:%.2fs frames:%d/%d %s %s, min:%4.1f max:%4.0f mean:%8.3f std:%8.3f timestamp:%10.6f"
|
|
1558
|
+
% (
|
|
1559
|
+
measuredFps,
|
|
1560
|
+
expoSec,
|
|
1561
|
+
duration,
|
|
1562
|
+
frameCount,
|
|
1563
|
+
frames,
|
|
1564
|
+
frameType,
|
|
1565
|
+
pixelFormat,
|
|
1566
|
+
attributes.imageMin,
|
|
1567
|
+
attributes.imageMax,
|
|
1568
|
+
attributes.imageMean,
|
|
1569
|
+
attributes.imageStd,
|
|
1570
|
+
attributes.timestamp,
|
|
1571
|
+
)
|
|
1572
|
+
)
|
|
1573
|
+
sys.stdout.flush()
|
|
1574
|
+
|
|
1575
|
+
if max == 0:
|
|
1576
|
+
sys.stdout.write(", empty")
|
|
1577
|
+
|
|
1578
|
+
if missedFrames > 0:
|
|
1579
|
+
sys.stdout.write(", missed:%d " % (missedFrames))
|
|
1580
|
+
|
|
1581
|
+
sys.stdout.flush()
|
|
1582
|
+
|
|
1583
|
+
self.WaitForSavingFiles(dataSetName == None)
|
|
1584
|
+
self.SetProperty("Autosave Filename Suffix", prevSuffix)
|
|
1585
|
+
|
|
1586
|
+
if fileName and len(fileName) > 0:
|
|
1587
|
+
self.SaveImage(image, fileName)
|
|
1588
|
+
|
|
1589
|
+
return image
|
|
1590
|
+
|
|
1591
|
+
def wait_for_saving_files(self, quiet=True):
|
|
1592
|
+
"""
|
|
1593
|
+
Wait for the saving files to complete
|
|
1594
|
+
"""
|
|
1595
|
+
|
|
1596
|
+
t0 = self.GetTime()
|
|
1597
|
+
saveCrude = True if self.GetProperty("Autosave Crude Frames") == "On" else False
|
|
1598
|
+
saveRaw = True if self.GetProperty("Autosave Raw Frames") == "On" else False
|
|
1599
|
+
saveFrame = (
|
|
1600
|
+
True if self.GetProperty("Autosave Single Frames") == "On" else False
|
|
1601
|
+
)
|
|
1602
|
+
saveMovie = True if self.GetProperty("Autosave Movie") == "On" else False
|
|
1603
|
+
counting = (
|
|
1604
|
+
True
|
|
1605
|
+
if self.GetProperty("Image Processing - Mode") != "Integrating"
|
|
1606
|
+
else False
|
|
1607
|
+
)
|
|
1608
|
+
|
|
1609
|
+
saveIntegratingFrames = saveFrame and (
|
|
1610
|
+
not counting or self.GetProperty("Autosave Integrating Frames") == "On"
|
|
1611
|
+
)
|
|
1612
|
+
saveCountingFrames = saveFrame and counting
|
|
1613
|
+
saveIntegratingMovie = saveMovie and not counting
|
|
1614
|
+
saveCountingMovie = saveMovie and counting
|
|
1615
|
+
|
|
1616
|
+
saveFinal = True if self.GetProperty("Autosave Final Image") == "On" else False
|
|
1617
|
+
expMode = self.GetProperty("Exposure Mode")
|
|
1618
|
+
|
|
1619
|
+
if expMode == "Dark" or expMode == "Gain":
|
|
1620
|
+
repeats = self.GetProperty("Remaining Number of Acquisitions")
|
|
1621
|
+
remaining = self.GetProperty("Remaining Number of Acquisitions")
|
|
1622
|
+
if remaining > 0:
|
|
1623
|
+
for i in range(repeats * 10):
|
|
1624
|
+
remaining = self.GetProperty("Remaining Number of Acquisitions")
|
|
1625
|
+
if remaining > 0:
|
|
1626
|
+
sleep(1)
|
|
1627
|
+
else:
|
|
1628
|
+
break
|
|
1629
|
+
elif (
|
|
1630
|
+
saveCrude
|
|
1631
|
+
or saveRaw
|
|
1632
|
+
or saveIntegratingFrames
|
|
1633
|
+
or saveCountingFrames
|
|
1634
|
+
or saveIntegratingMovie
|
|
1635
|
+
or saveCountingMovie
|
|
1636
|
+
or saveFinal
|
|
1637
|
+
):
|
|
1638
|
+
for i in range(1000):
|
|
1639
|
+
if self.GetProperty("Autosave Status") in ["Starting", "In Progress"]:
|
|
1640
|
+
if not quiet:
|
|
1641
|
+
sys.stdout.write(".")
|
|
1642
|
+
sys.stdout.flush()
|
|
1643
|
+
sleep(1)
|
|
1644
|
+
else:
|
|
1645
|
+
if not quiet:
|
|
1646
|
+
sys.stdout.write(
|
|
1647
|
+
" \tgrab:%4.0fMB/s"
|
|
1648
|
+
% self.GetProperty("Speed - Grabbing (MB/s)")
|
|
1649
|
+
)
|
|
1650
|
+
sys.stdout.write(
|
|
1651
|
+
" proc:%4.0fMB/s"
|
|
1652
|
+
% self.GetProperty("Speed - Processing (MB/s)")
|
|
1653
|
+
)
|
|
1654
|
+
sys.stdout.write(
|
|
1655
|
+
" save:%4.0fMB/s"
|
|
1656
|
+
% self.GetProperty("Speed - Writing (MB/s)")
|
|
1657
|
+
)
|
|
1658
|
+
|
|
1659
|
+
if saveCrude:
|
|
1660
|
+
sys.stdout.write(
|
|
1661
|
+
" crude:%d"
|
|
1662
|
+
% self.GetProperty("Autosave Crude Frames Written")
|
|
1663
|
+
)
|
|
1664
|
+
if saveRaw:
|
|
1665
|
+
sys.stdout.write(
|
|
1666
|
+
" raw:%d"
|
|
1667
|
+
% self.GetProperty("Autosave Raw Frames Written")
|
|
1668
|
+
)
|
|
1669
|
+
if saveIntegratingFrames:
|
|
1670
|
+
# sys.stdout.write(" frames:%d" % self.GetProperty("Autosave Single Frames - Frames Written") )
|
|
1671
|
+
sys.stdout.write(
|
|
1672
|
+
" integrating frames:%d"
|
|
1673
|
+
% self.GetProperty(
|
|
1674
|
+
"Autosave Integrated Single Frames Written"
|
|
1675
|
+
)
|
|
1676
|
+
)
|
|
1677
|
+
if saveCountingFrames:
|
|
1678
|
+
sys.stdout.write(
|
|
1679
|
+
" counting frames:%d"
|
|
1680
|
+
% self.GetProperty(
|
|
1681
|
+
"Autosave Counted Single Frames Written"
|
|
1682
|
+
)
|
|
1683
|
+
)
|
|
1684
|
+
if saveIntegratingMovie:
|
|
1685
|
+
sys.stdout.write(
|
|
1686
|
+
" integrating movie:%d"
|
|
1687
|
+
% self.GetProperty(
|
|
1688
|
+
"Autosave Integrated Movie Frames Written"
|
|
1689
|
+
)
|
|
1690
|
+
)
|
|
1691
|
+
if saveCountingMovie:
|
|
1692
|
+
sys.stdout.write(
|
|
1693
|
+
" counting movie:%d"
|
|
1694
|
+
% self.GetProperty(
|
|
1695
|
+
"Autosave Counted Movie Frames Written"
|
|
1696
|
+
)
|
|
1697
|
+
)
|
|
1698
|
+
if saveFinal:
|
|
1699
|
+
sys.stdout.write(
|
|
1700
|
+
" final sum count:%d"
|
|
1701
|
+
% self.GetProperty("Autosave Final Image - Sum Count")
|
|
1702
|
+
)
|
|
1703
|
+
|
|
1704
|
+
break
|
|
1705
|
+
|
|
1706
|
+
duration = self.GetTime() - t0
|
|
1707
|
+
if not quiet:
|
|
1708
|
+
print(" %.1fs" % duration)
|
|
1709
|
+
sys.stdout.flush()
|
|
1710
|
+
|
|
1711
|
+
# Start acquisition and get a single image
|
|
1712
|
+
# fileName: the image will be saved to disk if a file name is provided
|
|
1713
|
+
# textSize: a text file with pixel values will be saved if textSize is given, for debugging/test
|
|
1714
|
+
def get_image(self, pixelFormat=PixelFormat.AUTO, fileName=None, textSize=0):
|
|
1715
|
+
"""
|
|
1716
|
+
Get a single image and save it to disk if a file name is provided
|
|
1717
|
+
|
|
1718
|
+
Parameters
|
|
1719
|
+
----------
|
|
1720
|
+
pixelFormat : PixelFormat, optional
|
|
1721
|
+
The pixel format of the image, by default PixelFormat.AUTO
|
|
1722
|
+
fileName : str, optional
|
|
1723
|
+
The file name to save the image, by default None
|
|
1724
|
+
textSize : int, optional
|
|
1725
|
+
The text size, by default 0
|
|
1726
|
+
"""
|
|
1727
|
+
self.StartAcquisition(1)
|
|
1728
|
+
frameType = FrameType.SUMTOTAL
|
|
1729
|
+
|
|
1730
|
+
if pixelFormat == "float32":
|
|
1731
|
+
pixelFormat = PixelFormat.FLOAT32
|
|
1732
|
+
|
|
1733
|
+
elif pixelFormat == "uint16":
|
|
1734
|
+
pixelFormat = PixelFormat.UINT16
|
|
1735
|
+
|
|
1736
|
+
attributes = Attributes()
|
|
1737
|
+
histogram = Histogram()
|
|
1738
|
+
image = self.GetResult(frameType, pixelFormat, attributes, histogram)[0]
|
|
1739
|
+
|
|
1740
|
+
if fileName and len(fileName) > 0:
|
|
1741
|
+
self.SaveImage(image, fileName, textSize)
|
|
1742
|
+
|
|
1743
|
+
return image
|
|
1744
|
+
|
|
1745
|
+
@disable_scan
|
|
1746
|
+
def take_dark_reference(self, frameRate: float = 20):
|
|
1747
|
+
"""
|
|
1748
|
+
Take dark reference images
|
|
1749
|
+
|
|
1750
|
+
Parameters
|
|
1751
|
+
----------
|
|
1752
|
+
frameRate : float, optional
|
|
1753
|
+
The frame rate, by default 20 frames per second
|
|
1754
|
+
"""
|
|
1755
|
+
sys.stdout.write("Taking dark references: ")
|
|
1756
|
+
sys.stdout.flush()
|
|
1757
|
+
|
|
1758
|
+
prevExposureMode = self.GetProperty("Exposure Mode")
|
|
1759
|
+
prevExposureTime = self.GetProperty("Exposure Time (seconds)")
|
|
1760
|
+
|
|
1761
|
+
acquisitions = 20
|
|
1762
|
+
self.SetProperty("Exposure Mode", "Dark")
|
|
1763
|
+
self.SetProperty("Frames Per Second", frameRate)
|
|
1764
|
+
self.SetProperty("Exposure Time (seconds)", 1)
|
|
1765
|
+
self.StartAcquisition(acquisitions)
|
|
1766
|
+
|
|
1767
|
+
while True:
|
|
1768
|
+
attributes = Attributes()
|
|
1769
|
+
histogram = Histogram()
|
|
1770
|
+
image = self.GetResult(
|
|
1771
|
+
FrameType.SUMTOTAL, PixelFormat.FLOAT32, attributes, histogram
|
|
1772
|
+
)
|
|
1773
|
+
|
|
1774
|
+
sys.stdout.write(str(attributes.acqIndex) + " ")
|
|
1775
|
+
sys.stdout.flush()
|
|
1776
|
+
remaining = self.GetProperty("Remaining Number of Acquisitions")
|
|
1777
|
+
|
|
1778
|
+
if remaining == 0:
|
|
1779
|
+
print("done.")
|
|
1780
|
+
break
|
|
1781
|
+
|
|
1782
|
+
self.SetProperty("Exposure Mode", prevExposureMode)
|
|
1783
|
+
self.SetProperty("Exposure Time (seconds)", prevExposureTime)
|
|
1784
|
+
|
|
1785
|
+
def get_time(self):
|
|
1786
|
+
"""
|
|
1787
|
+
Get the current time from the system clock
|
|
1788
|
+
"""
|
|
1789
|
+
if sys.version_info[0] < 3:
|
|
1790
|
+
return time.clock()
|
|
1791
|
+
else:
|
|
1792
|
+
return time.perf_counter()
|
|
1793
|
+
|
|
1794
|
+
# private methods
|
|
1795
|
+
|
|
1796
|
+
def __del__(self):
|
|
1797
|
+
if self.connected:
|
|
1798
|
+
self.disconnect()
|
|
1799
|
+
|
|
1800
|
+
# get multiple parameters from a single acknowledge packet
|
|
1801
|
+
def __getParameters(self, single_acknowledge=None):
|
|
1802
|
+
output = []
|
|
1803
|
+
if single_acknowledge is None:
|
|
1804
|
+
return output
|
|
1805
|
+
if single_acknowledge.error == True:
|
|
1806
|
+
return output
|
|
1807
|
+
for one_parameter in single_acknowledge.parameter:
|
|
1808
|
+
if one_parameter.type == pb.AnyParameter.P_BOOL:
|
|
1809
|
+
output.append(one_parameter.p_bool)
|
|
1810
|
+
elif one_parameter.type == pb.AnyParameter.P_STRING:
|
|
1811
|
+
output.append(one_parameter.p_string)
|
|
1812
|
+
elif one_parameter.type == pb.AnyParameter.P_INT:
|
|
1813
|
+
output.append(one_parameter.p_int)
|
|
1814
|
+
elif one_parameter.type == pb.AnyParameter.P_FLOAT:
|
|
1815
|
+
output.append(one_parameter.p_float)
|
|
1816
|
+
return output
|
|
1817
|
+
|
|
1818
|
+
# get strings from a single command response
|
|
1819
|
+
def __getStrings(self, command_id=None, param=None):
|
|
1820
|
+
if command_id is None:
|
|
1821
|
+
return False
|
|
1822
|
+
command = self._addSingleCommand(command_id, param)
|
|
1823
|
+
response = self._sendCommand(command)
|
|
1824
|
+
if response != False:
|
|
1825
|
+
return self.__getParameters(response.acknowledge[0])
|
|
1826
|
+
else:
|
|
1827
|
+
return False
|
|
1828
|
+
|
|
1829
|
+
# add a new command (with optional label and parameter)
|
|
1830
|
+
def _addSingleCommand(self, command_id=None, label=None, params=None):
|
|
1831
|
+
if command_id is None:
|
|
1832
|
+
return False
|
|
1833
|
+
command = pb.DEPacket() # create the command packet
|
|
1834
|
+
command.type = pb.DEPacket.P_COMMAND
|
|
1835
|
+
singlecommand1 = command.command.add() # add the first single command
|
|
1836
|
+
singlecommand1.command_id = command_id + commandVersion * 100
|
|
1837
|
+
if not label is None:
|
|
1838
|
+
str_param = command.command[0].parameter.add()
|
|
1839
|
+
str_param.type = pb.AnyParameter.P_STRING
|
|
1840
|
+
str_param.p_string = label
|
|
1841
|
+
str_param.name = "label"
|
|
1842
|
+
|
|
1843
|
+
if not params is None:
|
|
1844
|
+
for param in params:
|
|
1845
|
+
if isinstance(param, bool):
|
|
1846
|
+
bool_param = command.command[0].parameter.add()
|
|
1847
|
+
bool_param.type = pb.AnyParameter.P_BOOL
|
|
1848
|
+
bool_param.p_bool = bool(param)
|
|
1849
|
+
bool_param.name = "val"
|
|
1850
|
+
elif isinstance(param, int) or isinstance(param, np.int32):
|
|
1851
|
+
int_param = command.command[0].parameter.add()
|
|
1852
|
+
int_param.type = pb.AnyParameter.P_INT
|
|
1853
|
+
int_param.p_int = int(param)
|
|
1854
|
+
int_param.name = "val"
|
|
1855
|
+
elif isinstance(param, float):
|
|
1856
|
+
float_param = command.command[0].parameter.add()
|
|
1857
|
+
float_param.type = pb.AnyParameter.P_FLOAT
|
|
1858
|
+
float_param.p_float = param
|
|
1859
|
+
float_param.name = "val"
|
|
1860
|
+
else:
|
|
1861
|
+
str_param = command.command[0].parameter.add()
|
|
1862
|
+
str_param.type = pb.AnyParameter.P_STRING
|
|
1863
|
+
str_param.p_string = str(param)
|
|
1864
|
+
str_param.name = "val"
|
|
1865
|
+
return command
|
|
1866
|
+
|
|
1867
|
+
# send single command and get a response, if error occurred, return False
|
|
1868
|
+
def _sendCommand(self, command=None):
|
|
1869
|
+
step_time = self.GetTime()
|
|
1870
|
+
|
|
1871
|
+
if command is None:
|
|
1872
|
+
return False
|
|
1873
|
+
|
|
1874
|
+
if len(command.camera_name) == 0:
|
|
1875
|
+
command.camera_name = (
|
|
1876
|
+
self.currCamera
|
|
1877
|
+
) # append the current camera name if necessary
|
|
1878
|
+
|
|
1879
|
+
try:
|
|
1880
|
+
packet = struct.pack("I", command.ByteSize()) + command.SerializeToString()
|
|
1881
|
+
res = self.socket.send(packet)
|
|
1882
|
+
# packet.PrintDebugString()
|
|
1883
|
+
# log.debug("sent result = %d\n", res)
|
|
1884
|
+
except:
|
|
1885
|
+
log.error("Error sending %s\n", command)
|
|
1886
|
+
|
|
1887
|
+
if logLevel == logging.DEBUG:
|
|
1888
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
1889
|
+
log.debug(" Send Time: %.1f ms", lapsed)
|
|
1890
|
+
step_time = self.GetTime()
|
|
1891
|
+
|
|
1892
|
+
return self.__ReceiveResponseForCommand(command)
|
|
1893
|
+
|
|
1894
|
+
def __ReceiveResponseForCommand(self, command):
|
|
1895
|
+
step_time = self.GetTime()
|
|
1896
|
+
|
|
1897
|
+
recvbyteSizeString = self._recvFromSocket(
|
|
1898
|
+
self.socket, 4
|
|
1899
|
+
) # get the first 4 byte
|
|
1900
|
+
|
|
1901
|
+
if len(recvbyteSizeString) == 4:
|
|
1902
|
+
recvbyteSize = struct.unpack("I", recvbyteSizeString) # interpret as size
|
|
1903
|
+
log.debug("-- recvbyteSize: " + str(recvbyteSize))
|
|
1904
|
+
received_string = self._recvFromSocket(
|
|
1905
|
+
self.socket, recvbyteSize[0]
|
|
1906
|
+
) # get the rest
|
|
1907
|
+
if logLevel == logging.DEBUG:
|
|
1908
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
1909
|
+
log.debug(" Recv Time: %.1f ms, %d bytes", lapsed, recvbyteSize[0])
|
|
1910
|
+
step_time = self.GetTime()
|
|
1911
|
+
|
|
1912
|
+
Acknowledge_return = pb.DEPacket()
|
|
1913
|
+
Acknowledge_return.ParseFromString(received_string) # parse the byte string
|
|
1914
|
+
if logLevel == logging.DEBUG:
|
|
1915
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
1916
|
+
log.debug("Parse Time: %.1f ms", lapsed)
|
|
1917
|
+
step_time = self.GetTime()
|
|
1918
|
+
|
|
1919
|
+
if (
|
|
1920
|
+
Acknowledge_return.type == pb.DEPacket.P_ACKNOWLEDGE
|
|
1921
|
+
): # has to be an acknowledge packet
|
|
1922
|
+
if len(command.command) <= len(Acknowledge_return.acknowledge):
|
|
1923
|
+
error = False
|
|
1924
|
+
for one_ack in Acknowledge_return.acknowledge:
|
|
1925
|
+
error = error or one_ack.error
|
|
1926
|
+
if error:
|
|
1927
|
+
message = Acknowledge_return.acknowledge[0].error_message
|
|
1928
|
+
if logLevel == logging.DEBUG:
|
|
1929
|
+
log.error(
|
|
1930
|
+
"Server returned error for request :\n"
|
|
1931
|
+
+ str(command)
|
|
1932
|
+
+ "\n"
|
|
1933
|
+
+ "Response :\n"
|
|
1934
|
+
+ str(message)
|
|
1935
|
+
)
|
|
1936
|
+
elif not message.startswith("Unknown property"):
|
|
1937
|
+
log.error(message)
|
|
1938
|
+
else:
|
|
1939
|
+
if logLevel == logging.DEBUG:
|
|
1940
|
+
lapsed = (self.GetTime() - step_time) * 1000
|
|
1941
|
+
log.debug(" Ack Time: %.1f ms", lapsed)
|
|
1942
|
+
step_time = self.GetTime()
|
|
1943
|
+
return Acknowledge_return
|
|
1944
|
+
else:
|
|
1945
|
+
log.error(
|
|
1946
|
+
"len(command.command):%d != len(Acknowledge_return.acknowledge):%d",
|
|
1947
|
+
len(command.command),
|
|
1948
|
+
len(Acknowledge_return.acknowledge),
|
|
1949
|
+
)
|
|
1950
|
+
else:
|
|
1951
|
+
log.error("Response from server is not ACK")
|
|
1952
|
+
else:
|
|
1953
|
+
log.error(
|
|
1954
|
+
"Server response is %d bytes, shorter than mimumum of 4 bytes",
|
|
1955
|
+
len(recvbyteSizeString),
|
|
1956
|
+
)
|
|
1957
|
+
|
|
1958
|
+
return False
|
|
1959
|
+
|
|
1960
|
+
def _recvFromSocket(self, sock, bytes):
|
|
1961
|
+
timeout = self.exposureTime * 10 + 30
|
|
1962
|
+
startTime = self.GetTime()
|
|
1963
|
+
self.socket.settimeout(timeout)
|
|
1964
|
+
|
|
1965
|
+
buffer = b""
|
|
1966
|
+
|
|
1967
|
+
total_len = len(buffer)
|
|
1968
|
+
upper_lim = 4096 * 4096 * 12 # 4096 #1024*256
|
|
1969
|
+
while total_len < bytes:
|
|
1970
|
+
bytes_left = bytes - total_len
|
|
1971
|
+
if bytes_left < upper_lim:
|
|
1972
|
+
packet_size = bytes_left
|
|
1973
|
+
else:
|
|
1974
|
+
packet_size = upper_lim
|
|
1975
|
+
loopTime = self.GetTime()
|
|
1976
|
+
try:
|
|
1977
|
+
buffer += sock.recv(packet_size)
|
|
1978
|
+
|
|
1979
|
+
except socket.timeout:
|
|
1980
|
+
log.debug(
|
|
1981
|
+
" __recvFromSocket : timeout in trying to receive %d bytes in %.1f ms",
|
|
1982
|
+
bytes,
|
|
1983
|
+
(self.GetTime() - loopTime) * 1000,
|
|
1984
|
+
)
|
|
1985
|
+
if self.GetTime() - startTime > timeout:
|
|
1986
|
+
log.error(" __recvFromSocket: max timeout %d seconds", timeout)
|
|
1987
|
+
break
|
|
1988
|
+
else:
|
|
1989
|
+
pass # continue further
|
|
1990
|
+
except:
|
|
1991
|
+
log.error(
|
|
1992
|
+
"Unknown exception occurred. Current Length: %d in %.1f ms",
|
|
1993
|
+
len(buffer),
|
|
1994
|
+
(self.GetTime() - loopTime) * 1000,
|
|
1995
|
+
)
|
|
1996
|
+
break
|
|
1997
|
+
total_len = len(buffer)
|
|
1998
|
+
|
|
1999
|
+
totalTimeMs = (self.GetTime() - startTime) * 1000
|
|
2000
|
+
Gbps = total_len * 8 / (totalTimeMs / 1000) / 1024 / 1024 / 1024
|
|
2001
|
+
log.debug(
|
|
2002
|
+
" __recvFromSocket :received %d of %d bytes in total in %.1f ms, %.1f Gbps",
|
|
2003
|
+
total_len,
|
|
2004
|
+
bytes,
|
|
2005
|
+
totalTimeMs,
|
|
2006
|
+
Gbps,
|
|
2007
|
+
)
|
|
2008
|
+
|
|
2009
|
+
return buffer
|
|
2010
|
+
|
|
2011
|
+
def __sendToSocket(self, sock, buffer, bytes):
|
|
2012
|
+
timeout = self.exposureTime * 10 + 30
|
|
2013
|
+
startTime = self.GetTime()
|
|
2014
|
+
self.socket.settimeout(timeout)
|
|
2015
|
+
|
|
2016
|
+
retval = True
|
|
2017
|
+
chunkSize = 4096
|
|
2018
|
+
for i in range(0, len(buffer), chunkSize):
|
|
2019
|
+
try:
|
|
2020
|
+
sock.send(buffer[i : min(len(buffer), i + chunkSize)])
|
|
2021
|
+
except socket.timeout:
|
|
2022
|
+
|
|
2023
|
+
log.debug(
|
|
2024
|
+
" __sendToSocket : timeout in trying to send %d bytes in %.1f ms",
|
|
2025
|
+
bytes,
|
|
2026
|
+
(self.GetTime() - loopTime) * 1000,
|
|
2027
|
+
)
|
|
2028
|
+
if self.GetTime() - startTime > timeout:
|
|
2029
|
+
log.error(" __recvFromSocket: max timeout %d seconds", timeout)
|
|
2030
|
+
retval = False
|
|
2031
|
+
break
|
|
2032
|
+
else:
|
|
2033
|
+
pass # continue further
|
|
2034
|
+
except socket.error as e:
|
|
2035
|
+
log.error(f"Error during send: {e}")
|
|
2036
|
+
# Handle the error as needed, e.g., close the connection
|
|
2037
|
+
retval = False
|
|
2038
|
+
break
|
|
2039
|
+
return buffer
|
|
2040
|
+
|
|
2041
|
+
def __saveText(self, image, fileName, textSize):
|
|
2042
|
+
text = open(self.debugImagesFolder + fileName + ".txt", "w+")
|
|
2043
|
+
line = "%s: [%d x %d]\n" % (fileName, image.shape[1], image.shape[0])
|
|
2044
|
+
text.write(line)
|
|
2045
|
+
for i in range(min(image.shape[0], textSize)):
|
|
2046
|
+
for j in range(min(image.shape[1], textSize)):
|
|
2047
|
+
text.write("%8.3f\t" % image[i][j])
|
|
2048
|
+
|
|
2049
|
+
if image.shape[1] > textSize:
|
|
2050
|
+
text.write(" ...\n")
|
|
2051
|
+
else:
|
|
2052
|
+
text.write("\n")
|
|
2053
|
+
|
|
2054
|
+
if image.shape[0] > textSize:
|
|
2055
|
+
text.write("...\n")
|
|
2056
|
+
else:
|
|
2057
|
+
text.write("\n")
|
|
2058
|
+
|
|
2059
|
+
def ParseChangedProperties(self, changedProperties, response):
|
|
2060
|
+
value = self.__getParameters(response.acknowledge[0])[0]
|
|
2061
|
+
|
|
2062
|
+
props = value.split("|")
|
|
2063
|
+
|
|
2064
|
+
try:
|
|
2065
|
+
for prop in props:
|
|
2066
|
+
p = prop.split(":")
|
|
2067
|
+
|
|
2068
|
+
if len(p) == 2:
|
|
2069
|
+
changedProperties[p[0]] = p[1]
|
|
2070
|
+
except Exception as e:
|
|
2071
|
+
log.error("Parse changed properties failed." + e.Message)
|
|
2072
|
+
|
|
2073
|
+
return changedProperties
|
|
2074
|
+
|
|
2075
|
+
# renamed methods to follow python standards
|
|
2076
|
+
GetServerVersion = get_server_version
|
|
2077
|
+
Connect = connect
|
|
2078
|
+
Disconnect = disconnect
|
|
2079
|
+
ListCameras = list_cameras
|
|
2080
|
+
GetCurrentCamera = get_current_camera
|
|
2081
|
+
SetCurrentCamera = set_current_camera
|
|
2082
|
+
ListProperties = list_properties
|
|
2083
|
+
GetPropertySpec = get_property_spec
|
|
2084
|
+
PropertyValidValues = property_valid_values
|
|
2085
|
+
GetProperty = get_property
|
|
2086
|
+
SetProperty = set_property
|
|
2087
|
+
SetPropertyAndGetChangedProperties = set_property_and_get_changed_properties
|
|
2088
|
+
setEngMode = set_engineering_mode
|
|
2089
|
+
SetHWROI = set_hw_roi
|
|
2090
|
+
SetHWROIAndGetChangedProperties = set_hw_roi_and_get_changed_properties
|
|
2091
|
+
SetSWROI = set_sw_roi
|
|
2092
|
+
SetSWROIAndGetChangedProperties = set_sw_roi_and_get_changed_properties
|
|
2093
|
+
StartAcquisition = start_acquisition
|
|
2094
|
+
StopAcquisition = stop_acquisition
|
|
2095
|
+
GetResult = get_result
|
|
2096
|
+
SetVirtualMask = set_virtual_mask
|
|
2097
|
+
GetMovieBufferInfo = get_movie_buffer_info
|
|
2098
|
+
GetMovieBuffer = get_movie_buffer
|
|
2099
|
+
SaveImage = save_image
|
|
2100
|
+
PrintServerInfo = print_server_info
|
|
2101
|
+
PrintAcqInfo = print_acquisition_info
|
|
2102
|
+
PrintSavingInfo = print_saving_info
|
|
2103
|
+
Grab = grab
|
|
2104
|
+
WaitForSavingFiles = wait_for_saving_files
|
|
2105
|
+
GetImage = get_image
|
|
2106
|
+
TakeDarkReference = take_dark_reference
|
|
2107
|
+
GetTime = get_time
|
|
2108
|
+
|
|
2109
|
+
# method setProperty was renamed to SetProperty. please use SetProperty
|
|
2110
|
+
setProperty = SetProperty
|
|
2111
|
+
getProperty = GetProperty
|
|
2112
|
+
|
|
2113
|
+
# private members
|
|
2114
|
+
width = 0
|
|
2115
|
+
height = 0
|
|
2116
|
+
mmf = 0
|
|
2117
|
+
usingMmf = True
|
|
2118
|
+
debugImagesFolder = "D:\\DebugImages\\"
|
|
2119
|
+
connected = False
|
|
2120
|
+
cameras = None
|
|
2121
|
+
currCamera = ""
|
|
2122
|
+
refreshProperties = True
|
|
2123
|
+
exposureTime = 1
|
|
2124
|
+
host = 0
|
|
2125
|
+
port = 0
|
|
2126
|
+
read_only = False
|
|
2127
|
+
|
|
2128
|
+
# command lists
|
|
2129
|
+
LIST_CAMERAS = 0
|
|
2130
|
+
LIST_PROPERTIES = 1
|
|
2131
|
+
LIST_ALLOWED_VALUES = 2
|
|
2132
|
+
GET_PROPERTY = 3
|
|
2133
|
+
SET_PROPERTY = 4
|
|
2134
|
+
GET_IMAGE_16U = 5
|
|
2135
|
+
GET_IMAGE_32F = 10
|
|
2136
|
+
STOP_ACQUISITION = 11
|
|
2137
|
+
GET_RESULT = 14
|
|
2138
|
+
START_ACQUISITION = 15
|
|
2139
|
+
SET_HW_ROI = 16
|
|
2140
|
+
SET_SW_ROI = 17
|
|
2141
|
+
GET_MOVIE_BUFFER_INFO = 18
|
|
2142
|
+
GET_MOVIE_BUFFER = 19
|
|
2143
|
+
SET_PROPERTY_AND_GET_CHANGED_PROPERTIES = 20
|
|
2144
|
+
SET_HW_ROI_AND_GET_CHANGED_PROPERTIES = 21
|
|
2145
|
+
SET_SW_ROI_AND_GET_CHANGED_PROPERTIES = 22
|
|
2146
|
+
SET_VIRTUAL_MASK = 23
|
|
2147
|
+
SAVE_FINAL_AFTER_ACQ = 24
|
|
2148
|
+
SET_ENG_MODE = 25
|
|
2149
|
+
SET_ENG_MODE_GET_CHANGED_PROPERTIES = 26
|
|
2150
|
+
SET_SCAN_SIZE = 27
|
|
2151
|
+
SET_SCAN_ROI = 28
|
|
2152
|
+
SET_SCAN_SIZE_AND_GET_CHANGED_PROPERTIES = 29
|
|
2153
|
+
SET_SCAN_ROI__AND_GET_CHANGED_PROPERTIES = 30
|
|
2154
|
+
SET_CLIENT_READ_ONLY = 31
|
|
2155
|
+
|
|
2156
|
+
|
|
2157
|
+
MMF_DATA_HEADER_SIZE = 24
|
|
2158
|
+
MMF_IMAGE_BUFFER_SIZE = 8192 * 16384 * 4
|
|
2159
|
+
MMF_DATA_BUFFER_SIZE = MMF_IMAGE_BUFFER_SIZE + MMF_DATA_HEADER_SIZE
|