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
165 stars 52 forks source link

add `return_to_original_state` option to acquisition #144

Open henrypinkard opened 4 years ago

henrypinkard commented 4 years ago

to return all stages, cameras, etc to their pre-acqusition state, if desired

ieivanov commented 4 years ago

Since acquire can be called multiple times, I think it'll be good to keep return_to_original_state outside of the Acquisition class, but maybe still within the acquire module. What I'm imagining is something like this:

microscope_state_1 = capture_microscope_state()

acquire(events1)
acquire(events2)
acquire(events3)

change_microscope_state(microscope_state_1)

Having something like change_microscope_state would allow you to change the microscope hardware state independent of acquiring images. This can still be accomplished in bulk by passing a dictionary (very similar to the structure of events), which is an advantage over using calls to the core directly.

What do you think about that?

henrypinkard commented 4 years ago

Since acquire can be called multiple times, I think it'll be good to keep return_to_original_state outside of the Acquisition class, but maybe still within the acquire module. What I'm imagining is something like this:

Agree

microscope_state_1 = capture_microscope_state()

acquire(events1)
acquire(events2)
acquire(events3)

change_microscope_state(microscope_state_1)

Having something like change_microscope_state would allow you to change the microscope hardware state independent of acquiring images. This can still be accomplished in bulk by passing a dictionary (very similar to the structure of events), which is an advantage over using calls to the core directly.

What do you think about that?

Yes, I like the interface you suggest. I think this can be done purely in the acquire module, as you suggest. The only question is what to include in that dictionary. The core has a mix of both an imperative (i.e. the property system) and a functional (e.g. calls like setExposure) API. The latter definitely needs to be included, because this is how the acquisition engine interacts with hardware. Basically all the "default" devices listed here (camera, focus, xystage, etc)

Screen Shot 2020-10-01 at 10 37 50 AM

correspond to functions in the core api (like core.set_exposure, core.set_position) that work all devices of that type generically.

This I think needs to be included, because much of the functionality is not duplicated with properties in devices. For example, some, but not all, cameras have a property for exposure that does the same thing as the set_exposure function.

But then there's also obviously a lot that this won't capture that's only in device properties. And then the are other weird cases where the API might break down like multi-camera. Does calling set_exposure set it for all cameras?

In light of that, it might be good to include all properties for every device.

I think the functions in the core that mess around with "systemState" do something similar to this, but I've never tried them. For example: https://valelab4.ucsf.edu/~MM/doc-2.0.0-gamma/mmcorej/mmcorej/CMMCore.html#setSystemState-mmcorej.Configuration-

ieivanov commented 4 years ago

Yeah, this might be tricky, but I think what you're suggesting makes sense.

I was thinking of generating a list of all device properties and then changing them back if they are different and writable. This will not capture functional calls, however. Maybe the systemState in the API can do it all?

henrypinkard commented 4 years ago

No, I think its the reverse: System state is a list of all properties and their values. You can also get use get_system_state_cache, which unlike get_system_state will not communicate with the hardware when getting these values. The advantage is this is guaranteed to be fast, this disadvantage is it might be wrong. For example, if you move a stage with a joystick, it will only update the property if the device adapter has an internal thread that is polling for position and updating. If you call the non-cached version, it will go into every hardware component and in many cases query the hardware directly to update property values. This could be slow, depending on the hardware.

So it might be nice to have different levels of thoroughness for such a function. At minimum, I think it makes sense to start with running through the API calls, since this is exclusively what the acquisition uses, so anything that changes through acquisition should be reset-able here. Next, you could add in the config groups, since these properties that are more likely to be used. Next the full set of properties as described by the system state cache. Finally the full set of properties as defined by the system state.

You want to give it a shot and I'll look over what you come up with?

ieivanov commented 4 years ago

Ah, I see. We should provide the option to use cached or fresh states as you suggested.

I don't think I have bandwidth to write code for this now (we're trying to push a paper out), but I could try in a couple of months if you or someone else hadn't gotten to it first. Happy to help with testing or discussing ideas in the meantime.