micro-manager/pycro-manager

Delay in post hardware hook function

ckolluru opened this issue · 14 comments

Is it ok to setup a while loop inside the post hardware hook function that waits for a particular signal from the hardware before returning?

I'm implementing a time-lapse acquisition. My idea was to set time_interval_s to zero, but wait in the post hardware hook function for several seconds each time until a particular signal goes high before taking another picture from the camera.

That would probably work, but why not just implement it in the normal way?

Sorry @henrypinkard, but what would be the normal way to do this?

Adding an interval into the acquisition events, as in your recent PR #603

I'm misunderstanding something about what you're trying to do, I think

With the custom time intervals, I would need to know the timepoints for each event beforehand. Instead, I want to check whether a particular signal from the hardware is set to high before proceeding with the capture at an event.

For example, I might need to wait 15 seconds in the first event before the signal goes from low to high, but it might be 25 seconds in the next event.

@ckolluru there is a better way of using the min_start_time (set by time_interval_s) and the hook functions here.

Instead of modifying the min_start_time for each events, you can use time_interval_s = 0 for all events such that they execute right away and then add a sleep call in a pre- or post-hardware hook function which will gate the data acquisition based on a hardware signal.

To ensure that hardware sequencing is not engaged (in which case the hook functions will run only once for the entire sequence) you may need to make time_interval_s = 0.01 or other small value.

Does that make sense?

@ieivanov thanks for the explanation, that makes sense. I wasn’t aware about hardware sequencing. I can set it to 0.01 seconds and give it a try.

Happy to help. To keep your code conceptually cleaner, you could also set time_interval_s = 0 for all events and disable hardware sequencing like this:

for event in events:
    acq.acquire(event)

Thanks @ieivanov, will use this workflow.

Hi @ieivanov, @henrypinkard

I ran into an issue after refactoring some of my code. I'm on pycromanager v0.27.5 from pip install.

The way I have things set up right now is below:

self.counter = 0

with Acquisition(directory = dir, name='acq', image_process_fn=self.image_process_fn, post_hardware_hook_fn = self.post_hardware_hook_fn) as acq:

    events = multi_d_acquisition_events(xyz_positions=self.xyz_positions, num_time_points = self.num_images, time_interval_s=0)

    for event in events:
        acq.acquire(event)

        self.counter = self.counter + 1
        
       # Some non-pycromanager related code to do a few things I need

I have a while loop in my post_hardware_hook_fn to wait on some hardware to be ready before capturing images. What I see now is that the acq.acquire(event) call is not blocked in that while loop, and runs continuously without any waiting and finishes out.

If I use self.counter in the post hardware hook function, I only see that it takes intermediate values (say 4, 6, 10 etc.). If I print it in the for event in events: loop, it increments continuously up from 0 in steps of 1.

Can you give me some insight on what might be the issue here?

Thanks,
Chaitanya

The way to do this for now is in a hook function. If its counting up by more than 1 at a time, then probably you are running a sequence of multiple events. Are you submitting the events one at a time in that case (i.e. calling acquire seperately for each one)?

Hmm, isn't the way I have it right now submitting events one at a time? I was thinking that repeated calls to acquire run sequentially and do not allow optimizations/hardware triggering.

for event in events:
    acq.acquire(event)

I think I found the issue. I was assuming image_process_fn was a blocking call, stopping the acquisition from continuing on to the next call of the post_hardware_hook_fn. It seems like that is not the case, only the hook functions are blocking.

It is also best to just use the metadata['axes']['time'] value to get the current image index and not rely on a counter that increments in for event in events: loop, since the acq.acquire() call itself is non-blocking.

Could there be a use for a blocking image processing function? Let's say changing stage positions based on the location of an object centroid in the image?

That makes sense. Glad you figured it out.

Could there be a use for a blocking image processing function?

Yes, this is something I'd like to add in the near future, as it would make writing this type of multithreaded code easier. Also see #567

Thanks, will follow the other thread.