3. Confocal Scans

Download this page as a Jupyter notebook

We can download the data needed for this tutorial directly from Zenodo using Pylake. Since we don’t want it in our working folder, we’ll put it in a folder called "test_data":

filenames = lk.download_from_doi("10.5281/zenodo.7729636", "test_data")

The following code uses scans as an example. Kymographs work the same way – just substitute file.scans with file.kymos. To load an HDF5 file and list all of the scans inside of it, run:

import lumicks.pylake as lk

file = lk.File("test_data/scan.h5")
list(file.scans)  # e.g. shows: "['reference', 'bleach', 'imaging']"

.scans is a regular Python dictionary so we can iterate over it:

# Plot all scans in a file
plt.figure()
for name, scan in file.scans.items():
    scan.plot(channel="rgb")
    plt.savefig(name)
plt.show()

Or just pick a single one by providing the name of the scan as scan=file.scans["name"]:

scan = file.scans["41"]
plt.figure()
scan.plot("red")
plt.show()

3.1. Plotting and Exporting

Pylake provides a convenience plot() method to quickly visualize your data. For details and examples see the Plotting Images section.

The scan can also be exported to TIFF format:

scan.export_tiff("image.tiff")

Scans can also be exported to video formats. Exporting the red channel of a multi-scan GIF can be done as follows:

multiframe_file = lk.File("test_data/scan_stack.h5")
multiframe_scan = multiframe_file.scans["46"]

multiframe_scan.export_video(
    "red",
    "test_red.gif",
    adjustment=lk.ColorAdjustment([0], [4])
)

Or if we want to export a subset of frames (the first frame being 2, and the last frame being 15) of all three channels at a frame rate of 2 frames per second, we can do this:

multiframe_scan.export_video(
    "rgb",
    "test_rgb.gif",
    start_frame=2,
    stop_frame=15,
    fps=2,
    adjustment=lk.ColorAdjustment([0, 0, 0], [4, 4, 4])
)

For other video formats such as .mp4 or .avi, ffmpeg must be installed. See installation instructions for more information on this.

You can also export the scan stack as a video including correlated channel data by providing export_video() with a Slice:

multiframe_scan.export_video(
    "rgb",
    "test_rgb.gif",
    start_frame=2,
    stop_frame=15,
    fps=2,
    adjustment=lk.ColorAdjustment(20, 99, mode="percentile"),
    channel_slice=multiframe_file.force1x
)

Note

To export to an mp4 file, you will need to install ffmpeg. See Optional dependencies for more information.

3.2. Photon counts

The images contain pixel data where each pixel represents summed photon counts. The photon count per pixel can be accessed as follows:

photons = scan.red_photon_count
plt.figure()
plt.plot(photons.timestamps, photons.data)
plt.show()

3.3. Scan metadata

There are several properties available for convenient access to the scan metadata:

3.4. Raw data and data selection

You can access the raw image data directly. For a Scan with only a single frame:

rgb = scan.get_image("rgb")  # matrix with `shape == (h, w, 3)`
blue = scan.get_image("blue")  # single color so `shape == (h, w)`

# Plot manually
plt.figure()
plt.imshow(rgb)
plt.show()

For scans with multiple frames:

# returned data has `shape == (n_frames, h, w, 3)`
rgb = multiframe_scan.get_image("rgb")
# returned data has `shape == (n_frames, h, w)`
blue = multiframe_scan.get_image("blue")

# Manually plot the RGB image of the first frame.
plt.figure()
plt.imshow(rgb[0, :, :, :])
plt.show()

We can also slice out a subset of frames from an image stack:

sliced_scan = multiframe_scan[5:10]

This will return a new Scan containing data equivalent to:

multiframe_scan.get_image("rgb")[5:10, :, :, :]

We can also slice the frames by time:

# get frames corresponding to the time range 30 through 90 seconds
sliced_scan = multiframe_scan["30s":"90s"]

Or directly using timestamps:

# get frames that fall between the start and stop of a force channel
multiframe_scan[multiframe_file.force1x.start:multiframe_file.force1x.stop]

3.5. Correlating a multiframe scan with data channels

The frames of a multiframe scan can be correlated to the force or other data channels. Downsample channel data according to the frames in a scan using frame_timestamp_ranges():

frame_timestamp_ranges = multiframe_scan.frame_timestamp_ranges()

You can choose to add the flag include_dead_time = True if you want to include the dead time at the end of each frame (default is False). This returns a list of start and stop timestamps that can be passed directly to downsampled_over(), which will then return a Slice with a datapoint per frame:

downsampled = multiframe_file.force1x.downsampled_over(frame_timestamp_ranges)

The multi-frame confocal scans can also be correlated with a channel Slice using an interactive plot.

multiframe_scan.plot_correlated(multiframe_file.force1x, adjustment=lk.ColorAdjustment([0, 0, 0], [4, 4, 4]))
plt.show()

Note that you need an interactive backend for this plot to work; instead of running %matplotlib inline at the top of the notebook, run %matplotlib notebook. If some cells were already executed, you will need to restart the kernel as well.