micro-manager / pycro-manager

Python control of micro-manager for customized data acquisition
https://pycro-manager.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
162 stars 49 forks source link

pycromanager acquisition with autofocus #352

Open ieivanov opened 2 years ago

ieivanov commented 2 years ago

Hi Henry,

I'm trying to compose an acquisition where I move between different XYZ positions, run an autofocus routine when I arrive at the new position, and acquire a Z stack relative to the new Z position after autofocus.

What is the best way to generate this sequence of acquisition events? I would like each XY position to be also associated with a Z coordinate to account for tilt in the sample and allow the autofocus device to stay within range. I am currently implementing the autofocus routine as a post_hardware_hook_fn with a call to core.full_focus() similar to your example notebook.

I am starting with code like this:

events = multi_d_acquisition_events(xyz_positions = [[100, 100, 50], [110, 110, 60]],
                                    z_start=-1, z_end=1, z_step=1)

def autofocus_fn(event, bridge, event_queue):
    if 'axes' in event.keys():
        if not hasattr(autofocus_fn, 'pos_index'):  # first position
            autofocus_fn.pos_index = event['axes']['position']
            mmc = bridge.get_core()
            mmc.full_focus()  # may change current z position
        if event['axes']['position'] != autofocus_fn.pos_index:  # different position
            mmc = bridge.get_core()
            mmc.full_focus()  # may change current z position
        autofocus_fn.pos_index = event['axes']['position']
    return event

with Acquisition(directory=r'D:', name='demo_pm_test', 
                 post_hardware_hook_fn=autofocus_fn) as acq:
    acq.acquire(events)

In this acquisition, autofocus_fn is called after the hardware is placed at position [100, 100, 49]. Ideally, I'd call autofocus at the center z position, here z=50, but that's easy to correct in the hook function. The call to mmc.full_focus() may change the current z position by del_z. Will the image at this event be acquired at z=49 or z=49+del_z? What about subsequent images? I'd expect the next image to be acquired at z=49+1+del_z, but I'm worried it may be acquired at z=50 as the event specifies. Will the metadata of the acquired images be updated if I change the hardware in an acquisition hook? Can you think of a better way to accomplish the acquisition I am describing?

In a related issue, I find that more documentation on the acquisition hook functions would be helpful. If I modify an event (e.g. change the hardware) in a post_hardware_hook_fn would those modifications take place? I can see this will happen in a pre_hardware_hook_fn, but I'm guessing the hardware won't change if I modify an event in a post_hardware_hook_fn. I've thought about creating a z stack by adding events to the event queue after the mmc.full_focus() call in the hook function. Do you think this will be a better approach?

henrypinkard commented 2 years ago

The call to mmc.full_focus() may change the current z position by del_z. Will the image at this event be acquired at z=49 or z=49+del_z? What about subsequent images? I'd expect the next image to be acquired at z=49+1+del_z, but I'm worried it may be acquired at z=50 as the event specifies.

It will be acquired at whatever is specified in the event after the pre_hardware_hook_fn, unless you manually change it in the post_hardware_hook. So you probably want to store del_z with hook function like autofocus_fn.offset = del_z and then reuse it on subsequent calls where you dont run the autofocus

If I modify an event (e.g. change the hardware) in a post_hardware_hook_fn would those modifications take place?

if you modify the event, nothing will change since the information in it was already used, but if you call the core and change the hardware, it will.

I've thought about creating a z stack by adding events to the event queue after the mmc.full_focus() call in the hook function. Do you think this will be a better approach?

Yeah I think that would be another reasonable way to do it if it makes more sense to you

henrypinkard commented 2 years ago

I added some more description of hooks to the docs (#357). If you see any areas for further improvement, feel free to send a PR

ieivanov commented 2 years ago

Thanks Henry, your feedback was helpful, sorry I didn't follow up earlier. My question was at what point is the image metadata saved. If I change the hardware in the post_hardware_hook_fn would that be reflected in the saved metadata?

P.S. I'll post here the solution I came up with

henrypinkard commented 2 years ago

Yes I believe it will