Skip to content

Commit ec68ee3

Browse files
author
rb_chip
committed
AndorCamera: * Correctly reshape arrays returned by GetAcquiredData and GetOldestImage16
* Add uninitialised emccd_gain instance attribute for debug messages * Do not modify defauld acquisition attributes dict, ensure a copy is used * Only print debug info about temperature if cooling is enabled * Missing attribute rename trig_capability -> trig_caps * Move SetImage call to outside an if statement, since it occurs unconditionally * Fix two syntax errors * Simplified reshaping in download_acquisition. * Switch to GetAcquiredData instead of GetOldestImage16 in non-fast-kinetics * Return a 3D array from snap() so that fast kinetics results are displayable in the BLACS tab. * Modify IMAQdxCamera tab to display a 3D array of images with only one image as a 2D array
1 parent f19da46 commit ec68ee3

File tree

4 files changed

+62
-64
lines changed

4 files changed

+62
-64
lines changed

AndorSolis/andor_sdk/andor_solis.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ def GetAcquiredData(shape):
305305
arr = (ctypes.c_int32 * size)()
306306
result = andor_solis.GetAcquiredData(ctypes.pointer(arr), ctypes.c_ulong(size))
307307
check_status(result)
308-
return np.ctypeslib.as_array(arr)
308+
return np.ctypeslib.as_array(arr).reshape(shape)
309309

310310
def GetAcquiredData16(shape):
311311
""" 16-bit version of the GetAcquiredData function.
@@ -316,7 +316,7 @@ def GetAcquiredData16(shape):
316316
arr = (ctypes.c_uint16 * size)() # not 100% sure this should be unsigned.
317317
result = andor_solis.GetAcquiredData16(ctypes.pointer(arr), ctypes.c_ulong(size))
318318
check_status(result)
319-
return np.ctypeslib.as_array(arr)
319+
return np.ctypeslib.as_array(arr).reshape(shape)
320320

321321
def GetAcquiredFloatData(shape):
322322
""" This function is reserved """

AndorSolis/andor_sdk/andor_utils.py

Lines changed: 54 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def __init__(self, name='andornymous'):
6161
self.cooling = False
6262
self.preamp = False
6363
self.emccd = False
64+
self.emccd_gain = None
6465
self.armed = False
6566
self.initialize_camera()
6667

@@ -240,7 +241,7 @@ def setup_vertical_shift(self, custom_option=1):
240241
self.index_vs_speed = custom_option
241242

242243
# For FastKinetics mode the calls are different
243-
if 'fast_kinetics' in self.acquisition_mode
244+
if 'fast_kinetics' in self.acquisition_mode:
244245
number_fkvs_speeds = GetNumberFKVShiftSpeeds()
245246
if not custom_option in range(number_fkvs_speeds):
246247
raise ValueError("Invalid vertical shift speed custom option value")
@@ -281,15 +282,16 @@ def setup_horizontal_shift(self, custom_option=None):
281282
# Get actual horizontal shifting (i.e. digitization) speed
282283
self.horizontal_shift_speed = GetHSSpeed(ad_number, 0, self.index_hs_speed)
283284

284-
def setup_acquisition(self, added_attributes={}):
285+
def setup_acquisition(self, added_attributes=None):
285286
""" Main acquisition configuration method. Available acquisition modes are
286287
below. The relevant methods are called with the corresponding acquisition
287288
attributes dictionary, then the camera is armed and ready """
289+
if added_attributes is None:
290+
added_attributes = {}
288291

289292
# Override default acquisition attrs with added ones
290-
self.acquisition_attributes = self.default_acquisition_attrs
291-
for attr, val in added_attributes.items():
292-
self.acquisition_attributes[attr] = val
293+
self.acquisition_attributes = self.default_acquisition_attrs.copy()
294+
self.acquisition_attributes.update(added_attributes)
293295

294296
self.acquisition_mode = self.acquisition_attributes['acquisition']
295297

@@ -305,14 +307,14 @@ def setup_acquisition(self, added_attributes={}):
305307
self.acquisition_attributes['water_cooling'],
306308
)
307309

308-
# Get current temperature and temperature status
309-
self.temperature, self.temperature_status = GetTemperatureF()
310-
if self.chatty:
311-
rich_print(
312-
f"""At setup_acquisition the temperature is:
313-
{self.temperature}; with status {self.temperature_status}""",
314-
color='magenta',
315-
)
310+
# Get current temperature and temperature status
311+
self.temperature, self.temperature_status = GetTemperatureF()
312+
if self.chatty:
313+
rich_print(
314+
f"""At setup_acquisition the temperature is:
315+
{self.temperature}; with status {self.temperature_status}""",
316+
color='magenta',
317+
)
316318

317319
# Available modes
318320
modes = {
@@ -447,7 +449,7 @@ def setup_trigger(self, **attrs):
447449
SetTriggerMode(modes[attrs['trigger']])
448450

449451
# Specify edge if invertible trigger capability is present
450-
if 'INVERT' in self.trig_capability:
452+
if 'INVERT' in self.trig_caps:
451453
SetTriggerInvert(edge_modes[attrs['trigger_edge']])
452454

453455
if attrs['trigger'] == 'external':
@@ -499,9 +501,8 @@ def setup_readout(self, **attrs):
499501
attrs['width'] = 1
500502

501503
self.image_shape = (
502-
int(attrs['number_kinetics']),
503-
int(attrs['height']/attrs['ybin']),
504-
int(attrs['width']/attrs['xbin']),
504+
attrs['height'] // attrs['ybin'],
505+
attrs['width'] // attrs['xbin'],
505506
)
506507

507508
# For a full-frame kinetic series, we simply set the frame for readout.
@@ -511,26 +512,18 @@ def setup_readout(self, **attrs):
511512
# self.emgain_caps). For higher ranges use SetEMGainMode(). We also need to
512513
# enable the frame transfer mode.
513514
if self.acquisition_mode == 'kinetic_series':
514-
SetImage(
515-
attrs['xbin'],
516-
attrs['ybin'],
517-
attrs['left_start'],
518-
attrs['width'] + attrs['left_start'] - 1,
519-
attrs['bottom_start'],
520-
attrs['height'] + attrs['bottom_start'] - 1,
521-
)
522515
if self.acquisition_attributes['crop']:
523-
SetOutputAmplifier(0)
524-
SetFrameTransferMode(1)
525-
SetIsolatedCropModeEx(
526-
int(1),
527-
int(attrs['height']),
528-
int(attrs['width']),
529-
attrs['ybin'],
530-
attrs['xbin'],
531-
attrs['left_start'],
532-
attrs['bottom_start'],
533-
)
516+
SetOutputAmplifier(0)
517+
SetFrameTransferMode(1)
518+
SetIsolatedCropModeEx(
519+
int(1),
520+
int(attrs['height']),
521+
int(attrs['width']),
522+
attrs['ybin'],
523+
attrs['xbin'],
524+
attrs['left_start'],
525+
attrs['bottom_start'],
526+
)
534527
else:
535528
SetFrameTransferMode(0)
536529
SetIsolatedCropModeEx(
@@ -542,14 +535,14 @@ def setup_readout(self, **attrs):
542535
attrs['left_start'],
543536
attrs['bottom_start'],
544537
)
545-
SetImage(
546-
attrs['xbin'],
547-
attrs['ybin'],
548-
attrs['left_start'],
549-
attrs['width'] + attrs['left_start'] - 1,
550-
attrs['bottom_start'],
551-
attrs['height'] + attrs['bottom_start'] - 1,
552-
)
538+
SetImage(
539+
attrs['xbin'],
540+
attrs['ybin'],
541+
attrs['left_start'],
542+
attrs['width'] + attrs['left_start'] - 1,
543+
attrs['bottom_start'],
544+
attrs['height'] + attrs['bottom_start'] - 1,
545+
)
553546

554547
def acquire(self):
555548
""" Carries down the acquisition, if the camera is armed and
@@ -606,15 +599,16 @@ def homemade_wait_for_acquisition():
606599
AbortAcquisition()
607600
raise AndorException('Acquisition aborted due to timeout')
608601

609-
def download_acquisition(self,):
610-
""" Download buffered acquisition """
611-
shape = self.image_shape
612-
613-
# Various useful FK image shapes
602+
def download_acquisition(self):
603+
""" Download buffered acquisition. For fast kinetics, returns a 3D
604+
array of shape (N_fast_kinetics, Ny//N, Nx). Otherwise, returns array
605+
of shape (N_exposures, Ny, Nx)."""
606+
607+
N = self.acquisition_attributes['number_kinetics']
614608
if 'fast_kinetics' in self.acquisition_mode:
615-
fk_read_shape = (1, *shape[1::])
616-
fk_single_shape = (shape[1] // shape [0], shape[2])
617-
fk_save_shape = (shape[0], shape[1] // shape[0], shape[2])
609+
shape = (N, self.image_shape[0] // N, self.image_shape[1])
610+
else:
611+
shape = (N, self.image_shape[0], self.image_shape[1])
618612

619613
# self.abort_acquisition # This seems like a bad thing to do here...
620614

@@ -625,23 +619,24 @@ def download_acquisition(self,):
625619
f"Number of available images in the circular buffer is {available_images}."
626620
)
627621

628-
if (available_images[1] - available_images[0]) + 1 == shape[0]:
622+
if (available_images[1] - available_images[0]) + 1 == N:
629623
# Special save format for FK frames
630624
if 'fast_kinetics' in self.acquisition_mode:
631-
fk_data = GetAcquiredData(fk_read_shape)
632-
data = np.array(fk_data).reshape(fk_save_shape)
625+
data = GetAcquiredData(shape)
633626
if self.chatty:
634627
print(
635-
f"Shape of the downloaded images in FK mode are {fk_save_shape}"
628+
f"Shape of the downloaded images in FK mode are {data.shape}"
636629
)
637630
# Regular save format for other acquisition modes
638631
else:
639-
# data = GetAcquiredData(shape)
640-
data = GetOldestImage16(*shape[1::])
632+
print("Shape passed to GetAcquiredData is", shape)
633+
data = GetAcquiredData(shape)
634+
# data = GetOldestImage16(shape)
635+
print("Data shape and dtype is:", data.shape, data.dtype)
641636
else:
642637
print(
643638
f"""------> Incorrect number of images to download:
644-
{available_images}, expecting: {shape[0]}."""
639+
{available_images}, expecting: {N}."""
645640
)
646641
data = np.zeros(shape)
647642

AndorSolis/blacs_workers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def snap(self):
3939
self.camera.acquire()
4040
images = self.camera.download_acquisition()
4141
print(f'Exposure time was {self.camera.exposure_time}')
42-
return images[-1] # return last element
42+
return images # This may be a 3D array of several images
4343

4444
def configure_acquisition(self, continuous=False, bufferCount=None):
4545
self.camera.setup_acquisition(self.attributes)
@@ -68,7 +68,7 @@ def grab_multiple(self, n_images, images, waitForNextBuffer=True):
6868
print(f"FK mode Kinetics Number is {self.camera.number_fast_kinetics}.")
6969
print(f" ---> Attempting to grab {n_images} acquisition(s).")
7070

71-
if 'single' in self.camera.acquisition_mode:
71+
if 'single' in self.camera.acquisition_mode:
7272
for image_number in range(n_images):
7373
self.camera.acquire()
7474
print(f" {image_number}: Acquire complete")
@@ -79,7 +79,7 @@ def grab_multiple(self, n_images, images, waitForNextBuffer=True):
7979
self.camera.armed = False
8080
print(f"Got {len(images)} of {n_images} acquisition(s).")
8181
elif 'fast_kinetics' in self.camera.acquisition_mode:
82-
nacquisitions = n_images // self.camera.number_fast_kinetics
82+
nacquisitions = n_images // self.camera.number_fast_kinetics
8383
for image_number in range(nacquisitions):
8484
self.camera.acquire()
8585
print(f" {image_number}: Acquire complete")

IMAQdxCamera/blacs_tabs.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ def handler(self, data):
6868
md = json.loads(data[0])
6969
image = np.frombuffer(memoryview(data[1]), dtype=md['dtype'])
7070
image = image.reshape(md['shape'])
71+
if len(image.shape) == 3 and image.shape[0] == 1:
72+
# If only one image given as a 3D array, convert to 2D array:
73+
image = image.reshape(image.shape[1:])
7174
this_frame_time = perf_counter()
7275
if self.last_frame_time is not None:
7376
dt = this_frame_time - self.last_frame_time

0 commit comments

Comments
 (0)