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

duration pseudoglobal trigger type #143

Open carandraug opened 4 years ago

carandraug commented 4 years ago

On the cockpit side of things, there is a TRIGGER_DURATION_PSEUDOGLOBAL which is missing on microscope. On the cockpit side of things, it was added by @juliomateoslangerak in cockpit/87373a86 which documents:

    #   The TRIGGER_DURATION_PSEUDOGLOBAL is for using the rolling shutter and we
    #   only want to excite the sample in the time that all of the pixels are
    #   exposed.

This does not exist in microscope. It is present on @juliomateoslangerak master branch which also changes the andorsdk3 internal trigger mode "external exposure" from TRIGGER_DURATION to the new TRIGGER_DURATION_PSEUDOGLOBAL. Not sure which one is actually correct.

carandraug commented 4 years ago

Extracting the difference between Micron and @juliomateoslangerak master for this pseudoglobal type, the commit is this one.

juliomateoslangerak commented 4 years ago

This is probably wrong and microscope camera should only implement TRIGGER_DURATION. It should be cockpit (experiment or the camera/light handler I guess) that generates the proper timing to achieve a pseudo global shuttering. It is anyway something very important. Cockpit should get if the camera is using rolling or global shutter.

iandobbie commented 4 years ago

I'm not sure I understand why we need the psuedo global shutter option at all. Up until now cameras have been controlling their own exposure times ad then being triggered by a short (100 us typically) ttl pulse at the start of an exposure. I think the psuedo global mode is using light triggering to do the the global effect.

juliomateoslangerak commented 4 years ago

Yes. What I call pseudo global shutter is that the light only triggers when all the pixels are exposed. That is camera_read_time after camera trigger. Then the real exposure time has to be longer than the exposure time requested by the user. Indeed there is no need to implement that as either external (camera controlled exp_time) or external_duration (executor controlled explique_time). But it has to be taken into account

carandraug commented 4 years ago

If I understood correctly, then the aim is to have global exposure mode even when using a rolling shutter. And that can be done by computing the part of a frame exposure where all pixels are being exposed and emit light during that time only. Is that correct? If so, when using rolling shutter, won't there be cases where there's no point in time where all pixels are being exposed?

juliomateoslangerak commented 4 years ago

Yes. That's it. In principle, if exposure_time is shorter than the read_time, yes there is no point in time where all the pixels are being exposed. However, that should never happen cause the exposure time should be that of the light source, to which the read_time has to be added. The executor should trigger that camera read_time before the light source. This is currently working in my set-up when I use exposure mode 'external duration'. I'm not sure with internal trigger (sorry I cannot verify as we are pretty confined here).

This is managed in Experiment.py line 599. Probably I pushed this.

            elif mode == cockpit.handlers.camera.TRIGGER_DURATION_PSEUDOGLOBAL:
                # We added some security time to the readout time that
                # we have to remove now
                cameraExposureStartTime = (exposureStartTime
                                           - self.cameraToReadoutTime[camera]
                                           - decimal.Decimal(0.005))
                table.addAction(cameraExposureStartTime, camera, True)
                table.addAction(exposureEndTime, camera, False)

Probably this should be something like elif shuttering_mode == rolling: ... There are cases where you would like to not use pseudo global. Maybe that should be a configuration option for the camera.

carandraug commented 4 years ago

I see. This indeed does sound like something that a client such as cockpit should handle. So, just to be clear:

  1. we will not change the trigger modes in microscope (no adding TRIGGER_DURATION_PSEUDOGLOBAL mode and no change in andorsdk3)
  2. this issue becomes about figuring out whether camera is using rolling shutter or global exposure (in the case of andor sCMOS, that's the value of "ElectronicShutteringMode").
  3. a new issue open in cockpit to discuss how that will work because it seems that at the moment that won't work without patching the code.

Sounds right @juliomateoslangerak ?

juliomateoslangerak commented 4 years ago

That sounds right to me.

dstoychev commented 3 years ago

I believe all back-illuminated CMOS cameras lack a global shutter and should be treated as if they had a rolling one. As such, it is really important to associate a property with Camera devices that can tell users, e.g. Cockpit, what is the shutter type of the device. What Julio had implemented earlier was a step in the right direction.

Even better if more complex exposure modes can be configured on a per-camera basis. For example, expose only for the first row or expose from the start of the bottom row to the end of the top row (i.e. pseudo-global shutter mode).

juliomateoslangerak commented 3 years ago

Indeed this is an issue for me cause I have rolling shutter Zyla's, and I do not get the right illumination without 'pseudo-global shuttering'. That is I believe the term comming from Andor's manual. So After I merged microscope's code before summer I revisited this issue and opened two issues where I suggest more appropriate changes:

209 Adding a electronic shuttering mode property and functions and corresponding Andor sdk3 implementations. I guess I should have followed this issue here.

https://github.com/MicronOxford/cockpit/issues/651 Changing the code accordingly on cockpit. With those changes, the triggering seems to look good on the oscilloscope and the images. The snap is still not working though. I can look at that.

iandobbie commented 3 years ago

The sCMOS shutter is very complex. Some of the sCMOS cameras do have global shuttering by reading exposure before and afterqards but you effectively double the read noise. A very bad idea. Much better is to know when your sensor is all active and flash you light source in this period with having no light when the sensor is only partially active. However this might make things slow as the rolling shutter period might allow longer exposure, even if some of the light is then thrown away.

juliomateoslangerak commented 3 years ago

Yes, indeed I modified experiment.experiment.expose to do exactly that. Subtract the readout time from the camera trigger start so that all the pixels are active when the laser goes on. Both laser and camera triggers are off simultaneously. I do also add a fraction of time in order to be sure that the camera is ready when the acquisition is too quick (I had issues with that). I believe this time (the time the lights are not on) is not lost as it is considered as 'available' to do other things such as moving z and start reading another channel. I did a fair amount of testing that I would like to repeat and document properly. These are some scenarios that I'm thinking of:

In the case @iandobbie is mentioning, I guess you would want to do something similar from the other side. That is, adding the readout time to the end of the exposure: you trigger on camera and laser at time=0 but you trigger off camera at time=exposure_time - readout_time and laser at time=exposure_time. Otherwise you get non-homogeneous exposure to light. I guess a question is if this is left as an option to the user / configuration.

juliomateoslangerak commented 1 year ago

I suggest we close this issue and continue in #209. It is a duplicate.