Skip to content

Commit

Permalink
add threading to drain camera buffer and callback mechanism (untested)
Browse files Browse the repository at this point in the history
  • Loading branch information
cjbe committed Apr 9, 2018
1 parent 1660470 commit e6624e3
Showing 1 changed file with 56 additions and 23 deletions.
79 changes: 56 additions & 23 deletions andorEmccd/andorEmccd.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import platform
import os
import numpy as np
import collections
import threading

# Constants returned by Andor SDK
DRV_SUCCESS = 20002
Expand Down Expand Up @@ -36,11 +38,13 @@ class AndorEmccd:
is not acquiring"""
dll = None

def __init__(self, leave_camera_warm=True:
def __init__(self, leave_camera_warm=True, framebuffer_len=1000):
"""Initialise the camera interface.
leave_camera_warm: turn off the cooler when disconnecting from the
camera.
framebuffer_len: maximum number of stored frames before oldest are discarded
"""
self.leave_camera_warm = leave_camera_warm

Expand Down Expand Up @@ -89,6 +93,14 @@ def __init__(self, leave_camera_warm=True:
self.set_trigger_mode(TRIGGER_INTERNAL)
self.dll.SetKineticCycleTime(0)

self.frame_buffer = collections.deque([], framebuffer_len)

self._frame_call_list = []

# Start image acquisition thread
t = threading.Thread(target=self._acquisition_thread)
t.start()

def __del__(self):
if self.dll is not None:
self.close()
Expand Down Expand Up @@ -324,28 +336,9 @@ def wait_for_acquisition(self):
if ret != DRV_SUCCESS:
raise Exception()

def get_image(self):
"""Returns the oldest image in the buffer as a numpy array, or None if
no new images"""
imSize = self.roiWidth*self.roiHeight
buf = (ctypes.c_int * imSize)()
ret = self.dll.GetOldestImage(buf, ctypes.c_ulong(imSize))
if ret == DRV_NO_NEW_DATA:
return None
elif ret == DRV_P2INVALID:
raise Exception("Internal error: Array size is incorrect")
elif ret != DRV_SUCCESS:
raise Exception()

im = np.frombuffer(buf, dtype=np.int32)
im = im.reshape(self.roiHeight, self.roiWidth)

im = np.transpose(im)
return im.copy(order="C")

def get_all_images(self):
"""Returns all of the images in the buffer as an array of numpy arrays,
or None if no new images"""
def _get_all_images(self):
"""Returns all of the images in the camera buffer as an array of numpy
arrays, or None if no new images"""
first = ctypes.c_long()
last = ctypes.c_long()
ret = self.dll.GetNumberNewImages(ctypes.byref(first),
Expand Down Expand Up @@ -378,3 +371,43 @@ def get_all_images(self):
im = np.transpose(im)
im_array.append(im.copy(order="C"))
return im_array

def register_callback(self, f):
"""Register a function to be called from the acquisition thread for each
new image"""
self._frame_call_list.append(f)

def deregister_callback(self, f):
if f in self._frame_call_list:
self._frame_call_list.remove(f)

def _acquisition_thread(self):
while True:
# The GIL is released in the cytes library call, so we get true
# multithreading until wait_for_acquisition() returns
self.wait_for_acquisition()
ims = self._get_all_images()
if ims is None:
continue
for im in ims:
self.frame_buffer.append(im)
for f in self._frame_call_list:
f(im)

def get_image(self):
"""Returns the oldest image in the buffer as a numpy array, or None if
no new images"""
if len(self.frame_buffer) == 0:
return None
return self.frame_buffer.popleft()

def get_all_images(self):
"""Returns all of the images in the buffer as an array of numpy arrays,
or None if no new images"""
if len(self.frame_buffer):
ims = []
while len(self.frame_buffer) > 0:
ims.append(self.frame_buffer.popleft())
else:
ims = None
return ims

0 comments on commit e6624e3

Please sign in to comment.