Automatic Dark Frame
In this section, the automatic dark frame in the xpdAcq is
introduced.
Dark Frame Preprocessor
The dark frame is taken care by DarkPreprocessor.
It is registered in the xrun.dark_preprocessors list and will be
automatically applied on the users’ plan when the xrun is called.
xrun.dark_preprocessors
[<DarkPreprocessor 0 snapshots cached>,
<DarkPreprocessor 0 snapshots cached>,
<DarkPreprocessor 0 snapshots cached>]
There could be more than one preprocessor, each for a different detector or a different strategy for the same detector.
Logic
Here is the introduction of the logic of the automatic dark frame. When
DarkPreprocessor finds a trigger of the detector and this
trigger is not for a dark frame (labeled by dark_group_prefix),
it will decide what to do based on the situations. The logic is shown in
the peseudocode below.
if (no dark frames have been taken before):
take the first dark frame and save it in the cache
else:
if (they were not taken under the same situation):
take a new dark frame and save it in the cache
else:
if (they expired):
take a new dark frame and update the cache
else:
don't take new dark frame but use the dark frame in the cache
Here, the taken under the same situation means that the values of
the locked_signals are the same. This criterion can be used to take
new dark frame when the frame acquisition time or the number of frames
per image change.
The expired means that the life time of the cached dark frame is
longer than the max_age.
The take a dark frame means a series of operations including:
close shutter if it is not closed
unstage and stage detector to make sure the dark frame is in an individual tiff file
trigger detector
wait for the detector to finish counting
unstage and stage detector to make sure the detector save the future frames in a separate tiff file
move the shutter to the previous state before the dark frame
Tune Configuration
The configuration of the DarkPreprocessors can be tuned by directly
changing the attribute of its class. Below is an example to tune the
max_age to 0., which means that a new dark frame will always be
collected whenever a light frame is going to be taken.
xrun.dark_preprocessors[0].max_age = 0.
If there is no need to use this preprocessor, below is the way to disable it.
xrun.dark_preprocessors[0].disable()
If it needs to be used after disabled, below is the way to enable it again.
xrun.dark_preprocessors[0].enable()
Usage
Usually, users don’t need to create the these preprocessors. The
preprocessors will be created during the start up of the ipython
session. Users just need to run the xrun as usual.
xrun(0, 1)
INFO: requested exposure time = 0.1 - > computed exposure time= 0.1
INFO: Current filter status
INFO: flt1 : In
INFO: flt2 : In
INFO: flt3 : In
INFO: flt4 : In
Transient Scan ID: 1 Time: 2022-04-13 10:55:26
Persistent Unique Scan ID: '6c700510-65c9-4025-8d7e-5352cb9fdf8b'
New stream: 'dark'
New stream: 'primary'
+-----------+------------+
| seq_num | time |
+-----------+------------+
| 1 | 10:55:27.0 |
+-----------+------------+
generator count ['6c700510'] (scan num: 1)
('6c700510-65c9-4025-8d7e-5352cb9fdf8b',)
Dark Frame Data Stream
The dark frames are saved in the dark stream by default while the
light frames are in the primary stream by default.
run = db[-1]
light_image = next(run.data("detector_image", stream_name="primary"))[0]
dark_image = next(run.data("detector_image", stream_name="dark"))[0]
import numpy as np
import matplotlib.pyplot as plt
import xarray as xr
def visualize(light_image: np.ndarray, dark_image: np.ndarray) -> None:
subtracted_image = light_image - dark_image
images = [light_image, dark_image, subtracted_image]
titles = ["Light", "Dark", "Subtracted"]
_, axes = plt.subplots(1, 3, figsize=(12, 4), dpi=72.)
for image, title, ax in zip(images, titles, axes):
ax.imshow(image, vmax=7000.)
ax.set_title(title)
ax.set_xticks([])
ax.set_yticks([])
return
visualize(light_image, dark_image)
Current Limitation
Current version of the DarkPreprocessor has the unexpected behavior
if two detectors are triggered simutaneously and two dark frames are
needed at the same time. This was address in the issue
here.
The suggestion is to use trigger_and_read on the two detector
separately. In the future, the DarkPreprocessor will be enhanced to
take care of multiple detectors in a coherent way.
More Information
DarkPreprocessor is a wrapper class of the DarkFramePreprocessor
in the bluesky-darkframes package. To know more information about
it, please visit the bluesky-darkframes
documentation.