Source code for decode.simulation.simulator

import torch
from typing import Tuple, Union

from ..generic import EmitterSet
from . import psf_kernel


[docs]class Simulation: """ A simulation class that holds the necessary modules, i.e. an emitter source (either a static EmitterSet or a function from which we can sample emitters), a psf background and noise. You may also specify the desired frame range, i.e. the indices of the frames you want to have as output. If they are not specified, they are automatically determined but may vary with new sampled emittersets. Attributes: em (EmitterSet): Static EmitterSet em_sampler: instance with 'sample()' method to sample EmitterSets from frame_range: frame indices between which to compute the frames. If None they will be auto-determined by the psf implementation. psf: psf model with forward method background (Background): background implementation noise (Noise): noise implementation """ def __init__(self, psf: psf_kernel.PSF, em_sampler=None, background=None, noise=None, frame_range: Tuple[int, int] = None): """ Init Simulation. Args: psf: point spread function instance em_sampler: callable that returns an EmitterSet upon call background: background instance noise: noise instance frame_range: limit frames to static range """ self.em_sampler = em_sampler self.frame_range = frame_range if frame_range is not None else (None, None) self.psf = psf self.background = background self.noise = noise
[docs] def sample(self): """ Sample a new set of emitters and forward them through the simulation pipeline. Returns: EmitterSet: sampled emitters torch.Tensor: simulated frames torch.Tensor: background frames """ emitter = self.em_sampler() frames, bg = self.forward(emitter) return emitter, frames, bg
[docs] def forward(self, em: EmitterSet, ix_low: Union[None, int] = None, ix_high: Union[None, int] = None) -> Tuple[ torch.Tensor, torch.Tensor]: """ Forward an EmitterSet through the simulation pipeline. Setting ix_low or ix_high overwrites the frame range specified in the init. Args: em (EmitterSet): Emitter Set ix_low: lower frame index ix_high: upper frame index (inclusive) Returns: torch.Tensor: simulated frames torch.Tensor: background frames (e.g. to predict the bg seperately) """ if ix_low is None: ix_low = self.frame_range[0] if ix_high is None: ix_high = self.frame_range[1] frames = self.psf.forward(em.xyz_px, em.phot, em.frame_ix, ix_low=ix_low, ix_high=ix_high) """ Add background. This needs to happen here and not on a single frame, since background may be correlated. The difference between background and noise is, that background is assumed to be independent of the emitter position / signal. """ if self.background is not None: frames, bg_frames = self.background.forward(frames) else: bg_frames = None if self.noise is not None: frames = self.noise.forward(frames) return frames, bg_frames