Asynchronous acquisition¶
Simple asynchronous acquisition¶
The video recording may be used as part of a larger experimental program. In particular, you may want to keep monitoring the experiment conditions while recording the video, and possibly save the experiment parameters next to the video frames. The simplest way to achieve that is to implement the monitoring task and the video recording task as asynchronous functions.
The very simple acquire_to_files_async()
method
is sufficient for very basic cases. The usage is strictly similar to
the synchronous acquire_to_files()
method described
in the previous section. In fact, the synchronous method is only a wrapper
around the asynchronous method.
The simple example of the previous section can be rewritten like this:
import asyncio
import numpy as np
from pymanip.video.avt import AVT_Camera as Camera
async def some_monitoring():
# ... do some monitoring of the experiment conditions ...
async def video_recording():
acquisition_name = "essai_1"
nframes = 3000
with Camera() as cam:
cam.set_trigger_mode(True)
count, dt = await cam.acquire_to_files_async(
nframes,
f"{acquisition_name:}/img",
dryrun=False,
file_format="png",
compression_level=9,
delay_save=True,
)
dt_avg = np.mean(t[1:] - t[:-1])
print("Average:", 1.0 / dt_avg, "fps")
async def main():
await asyncio.gather(some_monitoring(),
video_recording(),
)
asyncio.run(main())
Multi-camera acquisition¶
One application of this simple method is to extent to simultaneous acquisition on several cameras (possibly of different brands). To ensure simultaneous frame grabbing, it is necessary to use an external function generator for external triggering. In the following example, we use an Agilent 33220a function generator which we configure for a burst with a software trigger. In our case, we use USB to communicate with the generator. Once the two cameras are ready for frame grabbing, the software trigger is sent, and the frames from both cameras are acquired.
Some manual tickering may be necessary in real cases. For example, in our example we use two Firewire AVT cameras connected on the same FireWire board. So we must adjust the packet size.
To know when both cameras are ready to grab frames, we use the
initialising_cams
parameter. Each camera object removes itself from
this set when it is ready to grab frames. All we need is therefore to implement
a task which will send the software trigger to the function generator, once the
set is empty.
Because the function generator is programmed for a burst of N peaks, the cameras will only be triggered N times. Therefore, it is a way to make sure that the N obtained frames were indeed simultaneous. If one camera skips one frame, the total number of frame will no longer be N.
The code is as follow:
import asyncio
from datetime import datetime
from pymanip.instruments import Agilent33220a
from pymanip.video.avt import AVT_Camera
basename = "multi"
fps = 10.0
nframes = 100
with Agilent33220a("USB0::2391::1031::MY44052515::INSTR") as gbf:
gbf.configure_burst(fps, nframes)
async def start_clock(cams):
"""This asynchronous function sends the software trigger to
the gbf when all cams are ready.
:param cams: initialising cams
:type cams: set
:return: timestamp of the time when the software trigger was sent
:rtype: float
"""
while len(cams) > 0:
await asyncio.sleep(1e-3)
gbf.trigger()
return datetime.now().timestamp()
with AVT_Camera(0) as cam0, \
AVT_Camera(1) as cam1:
cam0.set_trigger_mode(True) # external trigger
cam1.set_trigger_mode(True)
cam0.set_exposure_time(10e-3)
cam1.set_exposure_time(10e-3)
cam0.camera.IIDCPacketSizeAuto = "Off"
cam0.camera.IIDCPacketSize = 5720
cam1.camera.IIDCPacketSizeAuto = "Off"
cam1.camera.IIDCPacketSize = 8192 // 2
initialing_cams = {cam0, cam1}
task0 = cam0.acquire_to_files_async(
nframes,
basename + "/cam0",
zerofill=4,
file_format="png",
delay_save=True,
progressbar=True,
initialising_cams=initialing_cams,
)
task1 = cam1.acquire_to_files_async(
nframes,
basename + "/cam1",
zerofill=4,
file_format="png",
delay_save=True,
progressbar=False,
initialising_cams=initialing_cams,
)
task2 = start_clock(initialing_cams)
tasks = asyncio.gather(task0, task1, task2)
loop = asyncio.get_event_loop()
(countA, dtA), (countB, dtB), t0 = loop.run_until_complete(tasks)
Note that we use the progressbar
parameter to avoid printing two progress bars. The acquire_to_files_async()
methods are passed the number of expected frames. If one frame is skipped, a Timeout exception will be raised.