python-microscope / microscope

Python library for control of microscope devices, supporting hardware triggers and distribution of devices over the network for performance and flexibility.
https://www.python-microscope.org
GNU General Public License v3.0
69 stars 41 forks source link

Adding `TriggerType.OFF` #243

Open jacopoabramo opened 2 years ago

jacopoabramo commented 2 years ago

Greetings,

I already mentioned in an image.sc thread that I wanted to use microscope to control an industrial-grade Ximea camera with a continuous frame capture. After some thought, the only way to actually manage to do it was to add a new value to the TriggerType enumerator.

I wanted to know if this is a feasible change to add in the main fork or if its outside the scope of how microscope should be used. You can check the commit here. Not a big change but it served my objective quite nice.

carandraug commented 2 years ago

I already replied on the image.sc thread but I'll add some more detail here. There's also some assumptions from me on what it is that you're trying to achieve, so correct me if I'm wrong.

  1. The goal here is to have enable start acquiring images of a given exposure time and have disable stop.
  2. This behaviour should be mapped to (TriggerType.SOFTWARE, TriggerMode.STROBE) which is currently not implemented in XimeaCamera.
  3. On the Ximea side things, the wanted behaviour is (XI_TRG_SOURCE.XI_TRG_OFF, XI_TRG_SELECTOR.XI_TRG_SEL_FRAME_START).
  4. The way XimeaCamera handles trigger configuration is by mapping Ximea's trigger_source to Microscope's TriggerType, and Ximea's trigger_selector to Microscope's TriggerMode.
  5. We already map Microscope's TriggerType.SOFTWARE to XI_TRG_SOURCE.XI_TRG_SOFTWARE so we can't also map it XI_TRG_SOURCE.XI_TRG_OFF
  6. The solution being proposed is to add a new TriggerType.OFF so that things just work automatically without much changes to the XimeaCamera internals.

I really don't think this is the right approach. The issue is in our simple mapping of Microcope's trigger type/mode to Ximea's trigger source/selectors are just not fit for purpose anymore. So the solution is to change those internals and not to extend the public interface.

carandraug commented 2 years ago

Putting it a on slightly different manner. What would TriggerType.OFF add to the public interface that is not already possible with the existing interface?

iandobbie commented 2 years ago

Overall this is showing there is a fundamental issue of incompatible and somewhat arbitrary triggering control mechanisms in different camera APIs.

What I believe is being attempted is to run the camera in a free run mode, which microscope has never really supported. If we are going to add a trigger type I think it should be something along the lines of TriggerType.FREE_RUN as many cameras APIs implement a free run mode but in different manners. Then it is up to the microscope device to set the relevant device specific parameters to achieve this aim.

This still adds to the existing API but in a much clearer manner and will not generate a weird special case where you have to set trigger to OFF on Ximea cameras but some other gymnastics on other cameras.

carandraug commented 2 years ago

What I believe is being attempted is to run the camera in a free run mode, which microscope has never really supported.

While I don't think none of the cameras actually implements it, the current API already provisions for it (it's TriggerMode.STROBE in the sense that once you trigger it it starts acquiring images until the trigger stops and with TriggerType.SOFTWARE it is triggered while the camera is enabled) so there shouldn't be a need to add more types or modes Maybe a table of `Trigger. I feel we keep coming back to this so maybe we should create a table listing all TriggerMode and TriggerType combinations and the expected behaviour in the context of camera devices. This is my interpretation of them:

TriggerType TriggerMode Description
SOFTWARE ONCE enable prepares the camera, trigger acquires one image.
SOFTWARE BULB enable starts exposing, disable stops exposing.
SOFTWARE STROBE enable starts acquiring, disable stops acquiring. Each acquired image is exposed by get_exposure_time.
SOFTWARE START enable prepares the camera, trigger starts acquiring a pre-defined sequence of images.
RISING_EDGE ONCE enable prepares the camera. Hardware trigger acquires one image.
RISING_EDGE BULB enable prepares the camera. Hardware trigger starts exposing and stops exposing when line is low.
RISING_EDGE STROBE enable prepares the camera. Hardware trigger starts acquiring images (exposure time defined by `set_epxosure_time) and stops acquiring when line is low.
RISING_EDGE START enable prepares the camera. Harwarde trigger starts acquiring one pre-define sequence of images.
FALLING_EDGE ... Same as RISING_EDGE but trigger is on falling edge.
PULSE ... I don't quite remember this one and it's not documented and used by no-one. I think it meant a trigger is a pulse in the line (of which duration?). If so, I think it makes no sense to use with modes BULB and STROBE.
iandobbie commented 2 years ago

The TriggerMode.STROBE is from the Andor cameras that keeps exposing a single frame while the hardware trigger is high, eg provides external control of the individual frame length, this is a different mode from FREE_RUN where the camera determines the individual frame length internally (presumably from the exposure time setting) but it keps taking new images until told to stop.

carandraug commented 2 years ago

The TriggerMode.STROBE is from the Andor cameras [...]

This does not look right. TriggerMode.STROBE is not handled by AndorAtmcd or AndorSDK3. Maybe one of Andor SDK's does have a mode named strobe but it's not handled in Microscope.

PVCam also has a mode named "strobe" which is when an external trigger starts each exposure in a sequence. This is very different from your description of Andor's "strobe" mode. Also, PVCam "strobe" mode seems to map to AndorSDK2 "external" mode. My point with this is that we can't try to use the vendor name in the Python Microscope API or we'll just end up with clashes of modes. We need to use our own trigger modes/types and then on each camera class map it to whatever each vendor calls it.

I have made a table above with what I had in mind for each trigger type/mode. Without trying to reuse names from the many many different and incompatible vendors, can you fix the table to add the new configurations that are not supported?

jacopoabramo commented 2 years ago

Hi both, thank you for the replies!

I'll just address the point raised from @carandraug:

Putting it a on slightly different manner. What would TriggerType.OFF add to the public interface that is not already possible with the existing interface?

First of all, I agree that my proposal is a bit far fetched and only limited to the current use case scenario I've encountered so far (having a camera in free run condition), and even while opening this issue I was aware that even if it was a single line of code it would have made a bigger change than it looks like. But by only relying on the microscope documentation (and, of course, I might have missed something too in reading it) and source code, and in order to avoid having to drastically change the Ximea interface for my needs, I thought this was the most "easy" approach. This was my thought as I was under the assumption that I could not change the current trigger interface of the Ximea. But if I have a green light I'll definetely look into it.

I also think that the table you proposed though is a great indicator on how to correctly use the TriggerType and TriggerMode, and adding it to the documentation would be of great help, at least for me. I just have a question regarding the combination SOFTWARE - BULB. From my understanding after reading both yours and @iandobbie comments, I'm guessing that:

I'm not familiar with the Andor cameras so maybe you could clarify this for me (just in case in the future I may need to use this approach).

Still, if I may be somewhat bold, I find the usage of the STROBE keyword a little confusing, especially when in relation to the table presented above. Maybe taking into account Ian's suggestion, you could consider using FREE_RUN as a substitute for STROBE in the supported TriggerMode rather than adding it as a TriggerType. From the table it seems that it fits better in the description of how the camera should behave in case of a trigger event. Of course it's just food for thought.

iandobbie commented 2 years ago

Sorry I am obviously totally miss remember the facts of this. I will make sure I know what I'm talking about next time. I agree about the names, every manufacture calls them something different. We just need to define the behavior for each state and then the relevant device needs to map that onto the specific settings for that camera or range of cameras.

carandraug commented 2 years ago

I just have a question regarding the combination SOFTWARE - BULB. From my understanding after reading both yours and @iandobbie comments, I'm guessing that:

  • the camera starts an exposure cycle
  • a call to trigger fetches an image from the camera

Kind of. In the combination SOFTWARE - BULB, when you call enable it starts exposing and when you call disable it stops exposing. Afterwards, the image will be available in the client/queue, but there's no need to call trigger.

The trigger method sends one trigger and so only makes sense for actions that happen in response to one trigger: START (start a predefined sequence) and ONCE (acquire one image). But sending one trigger makes no sense for actions that happen while the device is being triggered: BULB (expose while being triggered) and STROBE (acquire while being triggered) because there's no method to start/stop a trigger. Maybe we could introduce such methods? I thought that we could get away with using enable / disable but maybe that's confusing and slow (some cameras take a while to enable/disable). Opinions?

[...] I find the usage of the STROBE keyword a little confusing, [...] consider using FREE_RUN as a substitute for STROBE in the supported TriggerMode rather than adding it as a TriggerType.

Sure. We are using an enum and we can use alias (we already do this, TriggerType.HIGH is an alias for TriggerType.RISING_EDGE). See:

>>> import enum
>>> class TriggerMode(enum.Enum):
...     ONCE = 1
...     BULB = 2
...     STROBE = 3
...     FREE = 3
... 
>>> TriggerMode.STROBE == TriggerMode.FREE  # equality works
True
>>> tmap = {TriggerMode.STROBE: "some method"}
>>> tmap[TriggerMode.FREE]  # also works as keys
'some method'

The reason why this mode is named STROBE is that it is the mode that intermittently triggers an action in the device. In the context of a camera, that action is exposing the sensor to acquire one image. So strobe means that intermittently acquiring images. These trigger modes and types names are not only for cameras. They are shared between all devices, so deformable mirrors and light sources also use them.

iandobbie commented 2 years ago

I think there is a misunderstanding here. From David's post a couple above:

I'm guessing that:

the camera starts an exposure cycle a call to trigger fetches an image from the camera

In a free run mode the camera is configured with an exposure time etc... then once a single trigger is sent the camera keeps collecting data and microscope should stream that data to the remote as fast as it comes in. Microscope should not wait for another trigger to send each image.

jacopoabramo commented 2 years ago

Hi both,

apologies for the late reply - I was on vacation until yesterday. I think that @carandraug has clarified what I wanted to implement with using the current interface. I'll follow your suggestions and make the changes on the Ximea interface rather than the common one. I'll close the issue.

jacopoabramo commented 2 years ago

Hi everyone,

as requested by @carandraug , I'm reopening this issue just to notify the changes I did for the Ximea interface in relation to the trigger management. You can check the changes in this commit on my microscope for.