Skip to content

Commit

Permalink
lowlevel: add low level api to create objects
Browse files Browse the repository at this point in the history
Adds tested functions to directly create confocal objects and slices
  • Loading branch information
JoepVanlier committed Sep 26, 2024
1 parent 2b9dbef commit e5861ef
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 122 deletions.
14 changes: 8 additions & 6 deletions lumicks/pylake/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import timeit
import tempfile
import contextlib
from copy import copy

import numpy as np

Expand Down Expand Up @@ -41,14 +42,15 @@ def _generate_kymo_for_tracking(duration, line_count, samples_per_pixel=1):
def _generate_blank_kymo_data(samples=1000000):
"""This is a different function since generating a junk data kymo is significantly faster than
generating one with sensible image content."""
from lumicks.pylake.tests.data.mock_confocal import MockConfocalFile
from lumicks.pylake.tests.data.mock_confocal import mock_confocal_from_arrays

counts = np.ones(samples)
infowave = np.ones(samples)
infowave[::2] = 0
infowave[::10] = 2

return MockConfocalFile.from_streams(
return mock_confocal_from_arrays(
"kymo",
start=0,
dt=int(1e9),
axes=[0],
Expand Down Expand Up @@ -86,17 +88,17 @@ class _KymoImage(_Benchmark):
loops = 60

def context(self):
confocal_file, metadata, stop = _generate_blank_kymo_data()
yield lambda: lk.kymo.Kymo("big_kymo", confocal_file, 0, stop, metadata).get_image("red")
kymo = _generate_blank_kymo_data()
yield lambda: copy(kymo).get_image("red") # Copy to not use the cache!


class _KymoTimestamps(_Benchmark):
name = "Reconstructing kymo timestamps"
loops = 45

def context(self):
confocal_file, metadata, stop = _generate_blank_kymo_data()
yield lambda: lk.kymo.Kymo("big_kymo", confocal_file, 0, stop, metadata).timestamps
kymo = _generate_blank_kymo_data()
yield lambda: copy(kymo).timestamps # Copy to not use the cache!


class _Tracking(_Benchmark):
Expand Down
30 changes: 28 additions & 2 deletions lumicks/pylake/detail/confocal.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import numpy as np
from numpy import typing as npt

from lumicks.pylake.channel import Slice, empty_slice

from .image import reconstruct_image, reconstruct_image_sum
from .mixin import PhotonCounts, ExcitationLaserPower
from .plotting import parse_color_channel
Expand Down Expand Up @@ -157,7 +159,9 @@ def scan_order(self):

@classmethod
def from_json(cls, json_string):
json_dict = json.loads(json_string)["value0"]
json_dict = json.loads(json_string)
if "value0" in json_dict:
json_dict = json_dict["value0"]

axes = [
ScanAxis(ax["axis"], ax["num of pixels"], ax["pixel size (nm)"] / 1000)
Expand All @@ -167,14 +171,36 @@ def from_json(cls, json_string):
return cls(axes, json_dict["scan volume"]["center point (um)"], json_dict["scan count"])


class ConfocalFileProxy:
"""Class with the minimal requirements to reconstruct confocal scans from photon streams"""

def __init__(
self,
infowave: Slice,
red_channel: Slice = empty_slice,
green_channel: Slice = empty_slice,
blue_channel: Slice = empty_slice,
):
self.infowave = infowave
self.red_photon_count = red_channel
self.green_photon_count = green_channel
self.blue_photon_count = blue_channel

def __getitem__(self, key):
if key == "Info wave":
return {"Info wave": self.infowave}
else:
raise KeyError(f"Key {key} not found.")


class BaseScan(PhotonCounts, ExcitationLaserPower):
"""Base class for confocal scans
Parameters
----------
name : str
confocal scan name
file : lumicks.pylake.File
file : lumicks.pylake.File | lumicks.pylake.ConfocalFile
Parent file. Contains the channel data.
start : int
Start point in the relevant info wave.
Expand Down
2 changes: 1 addition & 1 deletion lumicks/pylake/detail/imaging_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .timeindex import to_timestamp
from ..adjustments import no_adjustment

_FIRST_TIMESTAMP = 1388534400
_FIRST_TIMESTAMP = 1388534400000000000


class TiffExport:
Expand Down
2 changes: 1 addition & 1 deletion lumicks/pylake/kymo.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class Kymo(ConfocalImage):
----------
name : str
Kymograph name
file : lumicks.pylake.File
file : lumicks.pylake.File | lumicks.pylake.detail.confocal.ConfocalFile
Parent file. Contains the channel data.
start : int
Start point in the relevant info wave.
Expand Down
61 changes: 61 additions & 0 deletions lumicks/pylake/lowlevel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import numpy as np

from lumicks.pylake.kymo import Kymo
from lumicks.pylake.scan import Scan
from lumicks.pylake.channel import Slice, Continuous, empty_slice
from lumicks.pylake.point_scan import PointScan
from lumicks.pylake.detail.confocal import ScanMetaData, ConfocalFileProxy
from lumicks.pylake.detail.imaging_mixins import _FIRST_TIMESTAMP


def create_confocal_object(
name,
infowave,
json_metadata,
red_channel=empty_slice,
green_channel=empty_slice,
blue_channel=empty_slice,
) -> Kymo | Scan | PointScan:
"""Create a confocal object from slices and json metadata
Parameters
----------
name : str
Name of this object
infowave : lumicks.pylake.Slice
Info wave that encodes how the photon counts should be assembled into an image.
json_metadata : str
json metadata generated by Bluelake.
red_channel, green_channel, blue_channel : lumicks.pylake.Slice
Photon counts
"""
metadata = ScanMetaData.from_json(json_metadata)
file = ConfocalFileProxy(infowave, red_channel, green_channel, blue_channel)
confocal_cls = {0: PointScan, 1: Kymo, 2: Scan}
return confocal_cls[metadata.num_axes](name, file, infowave.start, infowave.stop, metadata)


def make_continuous_slice(data, start, dt, y_label="y", name="") -> Slice:
"""Make a continuous slice of data
Converts a raw `array_like` to a pylake `Slice`.
Parameters
----------
data : array_like
Source of data
start : int
Start timestamp in nanoseconds since epoch
dt : int
Timestep in nanoseconds
y_label : str
Label to show on the y-axis.
name : str
Name of the slice (used on plot titles).
"""
if start < _FIRST_TIMESTAMP:
raise ValueError(
f"Starting timestamp must be larger than {_FIRST_TIMESTAMP}. You provided: {start}."
)

return Slice(Continuous(np.asarray(data), start, dt), labels={"title": name, "y": y_label})
2 changes: 1 addition & 1 deletion lumicks/pylake/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Scan(ConfocalImage, VideoExport, FrameIndex):
----------
name : str
Scan name
file : lumicks.pylake.File
file : lumicks.pylake.File | lumicks.pylake.detail.confocal.ConfocalFile
Parent file. Contains the channel data.
start : int
Start point in the relevant info wave.
Expand Down
Loading

0 comments on commit e5861ef

Please sign in to comment.