Fast 3D + time imaging with hardware triggering

This notebook acquires a fast TZYX data series.

The camera is run at reduced ROI to achieve higher framerate (here 200 frames per second).

Movement of the z stage is “sequenced” to speed up acquisition. The z stage advances to the next position in the sequence when a trigger from the camera is received. This eliminates delays due to software communication.

Here a piezo z stage from ASI Imaging is used. The stage is put in “Fast Sequencing” mode such that the position sequence is repeated if a trigger is received after the last position in the sequence has been reached. In this way the camera is run in “burst mode” for multiple z stack acquisitions without having to resend the sequence buffer.

A custom z position sequence representing a triangle waveform is used. This means that the stage will move from start_end_pos = -2.5 to mid_pos = 2.5 in steps of step_size = 0.25 and then back from mid_pos to start_end_pos in steps of -step_size. This set of stage positions avoids the large jump from the end of the sequence back to the beginning of the sequence which may take more time to complete than a single step and may disturb the sample.

Using pycro-manager this set of acquisition events is encoded as follows:

events = []
z_idx_ = z_idx.copy()
for i in range(num_time_points):
    for j in z_idx_:
        events.append({'axes': {'time':i, 'z': j}})
    z_idx_.reverse()
[ ]:
import numpy as np

from pycromanager import Acquisition, Core, Studio
[ ]:
def upload_piezo_sequence(start_end_pos, mid_pos, step_size, relative=True):
    """
    Upload a triangle waveform of z_stage positions and set the z_stage in UseFastSequence mode

    :param bridge: pycro-manager java bridge
    :type bridge: pycromanager.core.Bridge
    :param start_end_pos: start and end position of triangle waveform
    :type start_end_pos: float
    :param mid_pos: mid position of the triangle waveform
    :type mid_pos: float
    :param step_size: z_stage step size
    :type step_size: float
    :param relative: set to False if given start_end_pos and mid_pos are absolute

    :return: current z position and absolute positions of triangle waveform
    """

    mmc = Core()

    z_stage = mmc.get_focus_device()

    pos_sequence = np.hstack(
        (
            np.arange(start_end_pos, mid_pos + step_size, step_size),
            np.arange(mid_pos, start_end_pos - step_size, -step_size),
        )
    )

    z_pos = 0
    if relative:
        z_pos = mmc.get_position(z_stage)
        pos_sequence += z_pos

    # construct java object
    positionJ = bridge.construct_java_object("mmcorej.DoubleVector")
    for i in pos_sequence:
        positionJ.add(float(i))

    # send sequence to stage
    mmc.set_property(z_stage, "UseSequence", "Yes")
    mmc.set_property(z_stage, "UseFastSequence", "No")
    mmc.load_stage_sequence(z_stage, positionJ)
    mmc.set_property(z_stage, "UseFastSequence", "Armed")

    return z_pos, pos_sequence

Construct java objects

[9]:
mmc = Core()
mmStudio = Studio()

Set acquisition parameters

[10]:
# Data set parameters
path = r"C:\test"
name = "pycromanager test"

# z stack parameters
start_end_pos = -2.5
mid_pos = 2.5
step_size = 0.25
relative = True

# time series parameters
duration = 2  # in seconds
exposure_time = 3  # in milliseconds
framerate = 200

# FOV parameters
ROI = [924, 770, 616, 514]

num_z_positions = int(abs(mid_pos - start_end_pos) / step_size + 1)
z_idx = list(range(num_z_positions))
num_time_points = np.ceil(duration * framerate / num_z_positions).astype(np.int)

Prepare for acquisition

[11]:
# setup cameras
mmc.set_exposure(exposure_time)
mmc.set_roi(*ROI)
mmc.set_property("Camera", "Framerate", framerate)

# setup z stage
z_stage = mmc.get_focus_device()
z_pos, pos_sequence = upload_piezo_sequence(
    bridge, start_end_pos, mid_pos, step_size, relative
)
num_z_positions = len(pos_sequence)

# move to first position
mmc.set_position(z_stage, pos_sequence[0])

Generate events

[12]:
events = []
z_idx_ = z_idx.copy()
for i in range(num_time_points):
    for j in z_idx_:
        events.append({"axes": {"time": i, "z": j}})
    z_idx_.reverse()

Acquire data

[13]:
with Acquisition(directory=path, name=name) as acq:
    acq.acquire(events)

Reset

[14]:
# turn off sequencing
mmc.set_property(z_stage, "UseFastSequence", "No")
mmc.set_property(z_stage, "UseSequence", "No")

# move back to initial position
mmc.set_position(z_stage, z_pos)