Skip to content

Commit afa7b4e

Browse files
committed
FlyCapture2_Camera-bugfixes: Add google style docstrings to the FlyCapture2Camera device.
1 parent 84a92ee commit afa7b4e

File tree

3 files changed

+161
-29
lines changed

3 files changed

+161
-29
lines changed

FlyCapture2Camera/blacs_tabs.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
from labscript_devices.IMAQdxCamera.blacs_tabs import IMAQdxCameraTab
1515

1616
class FlyCapture2CameraTab(IMAQdxCameraTab):
17+
"""Thin sub-class of obj:`IMAQdxCameraTab`.
18+
19+
This sub-class only defines :obj:`worker_class` to point to the correct
20+
:obj:`FlyCapture2CameraWorker`."""
1721

1822
# override worker class
1923
worker_class = 'labscript_devices.FlyCapture2Camera.blacs_workers.FlyCapture2CameraWorker'

FlyCapture2Camera/blacs_workers.py

Lines changed: 155 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,44 @@
2727
PyCapture2 = None
2828

2929
class FlyCapture2_Camera(object):
30+
"""The backend hardware interface class for the FlyCapture2Camera.
31+
32+
This class handles all of the API/hardware implementation details for the
33+
corresponding labscript device. It is used by the BLACS worker to send
34+
appropriate API commands to the camera for the standard BLACS camera operations
35+
(i.e. transition_to_buffered, get_attributes, snap, etc).
36+
37+
Attributes:
38+
camera (PyCapture2.Camera): Handle to connected camera.
39+
get_props (list): This list sets which values of each property object
40+
are returned when queried by :obj:`get_attribute`.
41+
pixel_formats (IntEnum): An IntEnum object that is automatically
42+
populated with the supported pixel types of the connected camera.
43+
width (int): Width of images for most recent acquisition.
44+
Used by :obj:`_decode_image_data` to format images correctly.
45+
height (int): Height of images for most recent acquisition.
46+
Used by :obj:`_decode_image_data` to format images correctly.
47+
pixelFormat (str): Pixel format name for most recent acquisition.
48+
Used by :obj:`_decode_image_data` to format images correctly.
49+
_abort_acquisition (bool): Abort flag that is polled during buffered
50+
acquisitions.
51+
"""
3052
def __init__(self, serial_number):
3153
"""Initialize FlyCapture2 API camera.
3254
33-
Serial number should be of int type."""
55+
Searches all cameras reachable by the host using the provided serial
56+
number. Fails with API error if camera not found.
57+
58+
This function also does a significant amount of default configuration.
59+
60+
* It defaults the grab timeout to 1 s
61+
* Ensures use of the API's HighPerformanceRetrieveBuffer
62+
* Ensures the camera is in Format 7, Mode 0 with full frame readout and MONO8 pixels
63+
* If using a GigE camera, automatically maximizes the packet size and warns if Jumbo packets are not enabled on the NIC
64+
65+
Args:
66+
serial_number (int): serial number of camera to connect to
67+
"""
3468

3569
global PyCapture2
3670
import PyCapture2
@@ -109,12 +143,16 @@ def __init__(self, serial_number):
109143

110144
def set_attributes(self, attr_dict):
111145
"""Sets all attribues in attr_dict.
146+
112147
FlyCapture does not control all settings through same interface,
113148
so we must do them separately.
114-
Interfaces are: {
115-
<Standard PROPERTY_TYPE>,
116-
'TriggerMode',
117-
'ImageMode'}
149+
Interfaces are: <Standard PROPERTY_TYPE>, TriggerMode, ImageMode
150+
151+
Args:
152+
attr_dict (dict): dictionary of property dictionaries to set for the camera.
153+
These property dictionaries assume a specific structure, outlined in
154+
:obj:`set_attribute`, :obj:`set_trigger_mode` and , :obj:`set_image_mode`
155+
methods.
118156
"""
119157

120158
for prop, vals in attr_dict.items():
@@ -128,7 +166,17 @@ def set_attributes(self, attr_dict):
128166
self.set_attribute(prop, vals)
129167

130168
def set_trigger_mode(self,trig_dict):
131-
"""Configures triggering options via Trigger Mode interface."""
169+
"""Configures triggering options via Trigger Mode interface.
170+
171+
Args:
172+
trig_dict (dict): dictionary with trigger mode property settings. Allowed keys:
173+
174+
* 'onOff': bool
175+
* 'polarity': 0,1
176+
* 'source': int
177+
* 'mode': int
178+
179+
"""
132180
trig_mode = self.camera.getTriggerMode()
133181
for k,v in trig_dict.items():
134182
setattr(trig_mode,k,v)
@@ -140,7 +188,17 @@ def set_trigger_mode(self,trig_dict):
140188
raise Exception(msg) from e
141189

142190
def set_image_mode(self,image_settings):
143-
"""Configures ROI and image control via Format 7, Mode 0 interface."""
191+
"""Configures ROI and image control via Format 7, Mode 0 interface.
192+
193+
Args:
194+
image_settings (dict): dictionary of image settings. Allowed keys:
195+
196+
* 'pixelFormat': valid pixel format string, i.e. 'MONO8'
197+
* 'offsetX': int
198+
* 'offsetY': int
199+
* 'width': int
200+
* 'height': int
201+
"""
144202
image_info, supported = self.camera.getFormat7Info(0)
145203
Hstep = image_info.offsetHStepSize
146204
Vstep = image_info.offsetVStepSize
@@ -177,13 +235,23 @@ def set_image_mode(self,image_settings):
177235

178236
def set_attribute(self, name, values):
179237
"""Set the values of the attribute of the given name using the provided
180-
dictionary values. Typical structure is:
181-
values = {'onOff':True,
182-
'autoManualMode':False,
183-
'absControl':True,
184-
'absValue':0.0}
238+
dictionary values.
185239
186-
Note that invalid settings tend to coerce instead of error."""
240+
Generally, absControl should be used to configure settings. Note that
241+
invalid settings tend to coerce instead of presenting an error.
242+
243+
Args:
244+
name (str):
245+
values (dict): Dictionary of settings for the property. Allowed keys are:
246+
247+
* 'onOff': bool
248+
* 'autoManualMode': bool
249+
* 'absControl': bool
250+
* 'absValue': float
251+
* 'valueA': int
252+
* 'valueB': int
253+
* 'onePush': bool
254+
"""
187255
try:
188256
prop = self.camera.getProperty(getattr(PyCapture2.PROPERTY_TYPE,name))
189257

@@ -198,10 +266,12 @@ def set_attribute(self, name, values):
198266
def get_attributes(self, visibility_level, writeable_only=True):
199267
"""Return a nested dict of all readable attributes.
200268
201-
Structure is of form: {
202-
<Standard PROPERTY_TYPE>:{},
203-
'TriggerMode':{},
204-
'ImageMode':{}}
269+
Args:
270+
visibility_level (str): Not used.
271+
writeable_only (:obj:`bool`, optional): Not used
272+
273+
Returns:
274+
dict: Dictionary of property dictionaries
205275
"""
206276
props = {}
207277
prop_names = {prop for prop in dir(PyCapture2.PROPERTY_TYPE)
@@ -233,7 +303,15 @@ def get_attributes(self, visibility_level, writeable_only=True):
233303
return props
234304

235305
def get_attribute(self, name):
236-
"""Return current values dictionary of attribute of the given name"""
306+
"""Return current values dictionary of attribute of the given name.
307+
308+
Args:
309+
name (str): Property name to read
310+
311+
Returns:
312+
dict: Dictionary of property values with structure as defined in
313+
:obj:`set_attribute`.
314+
"""
237315
try:
238316
prop_dict = {}
239317
prop = self.camera.getProperty(getattr(PyCapture2.PROPERTY_TYPE,name))
@@ -245,7 +323,11 @@ def get_attribute(self, name):
245323
raise Exception(f"Failed to get attribute {name}") from e
246324

247325
def snap(self):
248-
"""Acquire a single image and return it"""
326+
"""Acquire a single image and return it
327+
328+
Returns:
329+
numpy.array: Acquired image
330+
"""
249331

250332
self.configure_acquisition(continuous=False,bufferCount=1)
251333
image = self.grab()
@@ -254,9 +336,17 @@ def snap(self):
254336

255337
def configure_acquisition(self, continuous=True, bufferCount=10):
256338
"""Configure acquisition buffer count and grab mode.
257-
Continuous mode only keeps most recent frames. Else, keep all frames.
258339
259-
Also get returned image parameters for formatting purposes.
340+
This method also saves image width, heigh, and pixelFormat to class
341+
attributes for returned image formatting.
342+
343+
Args:
344+
continuous (:obj:`bool`, optional): If True, camera will continuously
345+
acquire and only keep most recent frames in the buffer. If False,
346+
all acquired frames are kept and error occurs if buffer is exceeded.
347+
Default is True.
348+
bufferCount (:obj:`int`, optional): Number of memory buffers to use
349+
in the acquistion. Default is 10.
260350
"""
261351
config = self.camera.getConfiguration()
262352
config.numBuffers = bufferCount
@@ -276,7 +366,11 @@ def configure_acquisition(self, continuous=True, bufferCount=10):
276366
self.camera.startCapture()
277367

278368
def grab(self):
279-
"""Grab and return single image during pre-configured acquisition."""
369+
"""Grab and return single image during pre-configured acquisition.
370+
371+
Returns:
372+
numpy.array: Returns formatted image
373+
"""
280374

281375
result = self.camera.retrieveBuffer()
282376

@@ -286,7 +380,16 @@ def grab(self):
286380
return self._decode_image_data(img)
287381

288382
def grab_multiple(self, n_images, images):
289-
"""Grab n_images into images array during buffered acquistion."""
383+
"""Grab n_images into images array during buffered acquistion.
384+
385+
Grab method involves a continuous loop with fast timeout in order to
386+
poll :obj:`_abort_acquisition` for a signal to abort.
387+
388+
Args:
389+
n_images (int): Number of images to acquire. Should be same number
390+
as the bufferCount in :obj:`configure_acquisition`.
391+
images (list): List that images will be saved to as they are acquired
392+
"""
290393
print(f"Attempting to grab {n_images} images.")
291394
for i in range(n_images):
292395
while True:
@@ -304,9 +407,19 @@ def grab_multiple(self, n_images, images):
304407
print(f"Got {len(images)} of {n_images} images.")
305408

306409
def _decode_image_data(self,img):
307-
"""FlyCapture2 image buffers require significant formatting.
410+
"""Formats returned FlyCapture2 API image buffers.
411+
412+
FlyCapture2 image buffers require significant formatting.
308413
This returns what one would expect from a camera.
309-
configure_acquisition must be called first to set image format parameters."""
414+
:obj:`configure_acquisition` must be called first to set image format parameters.
415+
416+
Args:
417+
img (numpy.array): A 1-D array image buffer of uint8 values to format
418+
419+
Returns:
420+
numpy.array: Formatted array based on :obj:`width`, :obj:`height`,
421+
and :obj:`pixelFormat`.
422+
"""
310423
pix_fmt = self.pixelFormat
311424
if pix_fmt.startswith('MONO'):
312425
if pix_fmt.endswith('8'):
@@ -322,7 +435,12 @@ def _decode_image_data(self,img):
322435
return image.copy()
323436

324437
def _send_format7_config(self,image_config):
325-
"""Validates and sends the Format7 configuration packet."""
438+
"""Validates and sends the Format7 configuration packet.
439+
440+
Args:
441+
image_config (PyCapture2.Format7ImageSettings): Format7ImageSettings
442+
object to validate and send to camera.
443+
"""
326444
try:
327445
fmt7PktInfo, valid = self.camera.validateFormat7Settings(image_config)
328446
if valid:
@@ -331,25 +449,33 @@ def _send_format7_config(self,image_config):
331449
raise RuntimeError('Error configuring image settings') from e
332450

333451
def stop_acquisition(self):
452+
"""Tells camera to stop current acquistion."""
334453
self.camera.stopCapture()
335454

336455
def abort_acquisition(self):
456+
"""Sets :obj:`_abort_acquisition` flag to break buffered acquisition loop."""
337457
self._abort_acquisition = True
338458

339459
def close(self):
460+
"""Closes :obj:`camera` handle to the camera."""
340461
self.camera.disconnect()
341462

342463

343464
class FlyCapture2CameraWorker(IMAQdxCameraWorker):
344465
"""FlyCapture2 API Camera Worker.
345466
346-
Inherits from IMAQdxCameraWorker. Overloads get_attributes_as_dict
347-
to use FlyCapture2Camera.get_attributes() method."""
467+
Inherits from obj:`IMAQdxCameraWorker`. Defines :obj:`interface_class` and overloads
468+
:obj:`get_attributes_as_dict` to use FlyCapture2Camera.get_attributes() method."""
348469
interface_class = FlyCapture2_Camera
349470

350471
def get_attributes_as_dict(self, visibility_level):
351472
"""Return a dict of the attributes of the camera for the given visibility
352-
level"""
473+
level
474+
475+
Args:
476+
visibility_level (str): Normally configures level of attribute detail
477+
to return. Is not used by FlyCapture2_Camera.
478+
"""
353479
if self.mock:
354480
return IMAQdxCameraWorker.get_attributes_as_dict(self,visibility_level)
355481
else:

FlyCapture2Camera/labscript_devices.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@
1414
from labscript_devices.IMAQdxCamera.labscript_devices import IMAQdxCamera
1515

1616
class FlyCapture2Camera(IMAQdxCamera):
17+
"""Thin sub-class of :obj:`IMAQdxCamera`."""
18+
1719
description = 'FlyCapture2 Camera'
1820

0 commit comments

Comments
 (0)