SMLMSim is a Julia package for simulating Single Molecule Localization Microscopy (SMLM) data with realistic physical properties. It builds upon SMLMData.jl, reexporting essential types and functions, and utilizes MicroscopePSFs.jl for realistic image generation.
The package provides tools for:
- Static SMLM Simulation: Generating fixed spatial patterns (2D/3D) with realistic fluorophore photophysics (blinking) and localization uncertainty. Ideal for super-resolution studies.
- Diffusion & Interaction Simulation: Modeling dynamic molecule behavior, including Brownian motion, dimerization, and dissociation using Smoluchowski dynamics. Suitable for single-particle tracking (SPT) studies.
- Microscope Image Generation: Creating simulated camera images from emitter data using configurable Point Spread Functions (PSFs).
All simulations use physical units (microns, seconds) and produce data compatible with the broader JuliaSMLM ecosystem.
using Pkg
Pkg.add("SMLMSim")Simulate fixed patterns with blinking and localization noise.
using SMLMSim
# Define a camera and simulation parameters
camera = IdealCamera(128, 128, 0.1) # 128×128 pixels, 100nm pixels
params = StaticSMLMConfig(density=1.0, σ_psf=0.13) # Density 1/μm², PSF 130nm
# Run simulation for an 8-molecule ring pattern
smld_noisy, info = simulate(
params; # Use semicolon to separate positional and keyword arguments
pattern=Nmer2D(n=8, d=0.1), # 100nm diameter ring
camera=camera
)
# smld_noisy contains realistic SMLM coordinates
# info.smld_true and info.smld_model contain intermediate results
println("Generated $(length(smld_noisy.emitters)) localizations.")Output: smld_noisy (kinetics + noise), info (SimInfo with smld_true, smld_model, timing).
Simulate molecules diffusing and interacting (e.g., dimerization).
using SMLMSim
# Set diffusion simulation parameters
params = DiffusionSMLMConfig(
density = 0.5, # molecules per μm²
box_size = 10.0, # μm
diff_monomer = 0.1, # μm²/s
k_off = 0.2, # s⁻¹ dimer dissociation rate
dt = 0.01, # s simulation timestep
t_max = 10.0, # s total simulation time
camera_framerate = 10.0, # 10 fps (100ms per frame)
camera_exposure = 0.1 # 100ms exposure integrates 10 timesteps per frame
)
# Run diffusion simulation
smld, info = simulate(params) # Returns (BasicSMLD, SimInfo)
println("Simulated diffusion for $(params.t_max) seconds.")
# 'smld' can be used for analysis or image generationDefine spatial arrangements (see Pattern types like Nmer2D, Line3D, uniform2D).
# Examples:
nmer = Nmer2D(n=8, d=0.1) # 8 molecules in a 100nm diameter circle
line = Line3D(λ=5.0, endpoints=[(-1.0, 0.0, -0.5), (1.0, 0.0, 0.5)]) # 5 mols/μmControl how many fluorophores attach to each binding site (see AbstractLabeling).
# Default: exactly 1 fluorophore per site
labeling = FixedLabeling()
# Poisson-distributed (avg 1.5 per site)
labeling = PoissonLabeling(1.5)
# Binomial: 4 attachment points, 80% probability each
labeling = BinomialLabeling(4, 0.8)
# With labeling efficiency (90% of sites get labeled)
labeling = PoissonLabeling(1.5; efficiency=0.9)
# Use in simulation
smld_noisy, info = simulate(params; pattern=Nmer2D(), labeling=PoissonLabeling(1.5))Model fluorophore behavior (e.g., GenericFluor with state transitions).
# Example: Two-state blinking model using positional constructor
fluor = GenericFluor(10000.0, [-10.0 10.0; 1e-2 -1e-2]) # γ=1e4, k_off=10, k_on=1e-2Realistic noise based on PSF width (σ_psf) and photon counts is added in static simulations.
Create camera images from simulation results.
using MicroscopePSFs # Needed for PSF types
# Generate images from diffusion simulation output
# Note: Frame timing is controlled by DiffusionSMLMConfig (camera_framerate, camera_exposure)
# Multiple simulation timesteps are automatically integrated during simulate()
psf = GaussianPSF(0.15) # 150nm PSF width
images, img_info = gen_images(smld, psf;
support=1.0, # PSF support radius in μm (faster than default Inf)
poisson_noise=true # Add shot noise
)
println("Generated $(img_info.frames_generated) camera images.")SMLMSim supports realistic sCMOS camera noise modeling with per-pixel calibration.
using SMLMSim
using MicroscopePSFs
# Create an sCMOS camera (128×128 pixels, 100nm pixels, 1.6 e⁻ read noise)
camera_scmos = SCMOSCamera(128, 128, 0.1, 1.6)
# Run static simulation with sCMOS camera
params = StaticSMLMConfig(density=1.0, σ_psf=0.13)
smld_noisy, info = simulate(
params,
pattern=Nmer2D(n=8, d=0.1),
camera=camera_scmos
)
# Generate images with full sCMOS noise model
# (quantum efficiency, Poisson, read noise, gain, offset)
psf = GaussianPSF(0.15)
images_scmos, img_info = gen_images(smld_noisy, psf, bg=10.0, camera_noise=true)
# For diffusion simulations
diff_params = DiffusionSMLMConfig(density=0.5, box_size=10.0)
smld_diff, diff_info = simulate(diff_params; camera=camera_scmos, override_count=10)The sCMOS noise model applies:
- Quantum efficiency: Photon → photoelectron conversion
- Poisson noise: Shot noise on photoelectrons
- Read noise: Gaussian noise per pixel
- Gain: Electron → ADU conversion
- Offset: Dark level addition
using SMLMSim
using CairoMakie # Requires installation: Pkg.add("CairoMakie")
using MicroscopePSFs
# --- Simulation Setup ---
camera = IdealCamera(128, 128, 0.1) # 128×128 pixels, 100nm pixels
params = StaticSMLMConfig(density=1.0, σ_psf=0.13)
smld_noisy, info = simulate(
params,
pattern=Nmer2D(n=6, d=0.2), # Hexamer
camera=camera
)
# --- Visualization ---
emitters = smld_noisy.emitters
x_coords = [e.x for e in emitters]
y_coords = [e.y for e in emitters]
photons = [e.photons for e in emitters]
fig = Figure(size=(600, 500))
ax = Axis(fig[1, 1],
title="Simulated SMLM Localizations (Hexamer)",
xlabel="x (μm)", ylabel="y (μm)",
aspect=DataAspect(), yreversed=true
)
scatter!(ax, x_coords, y_coords, color=photons, colormap=:viridis, markersize=4, alpha=0.7)
Colorbar(fig[1, 2], colormap=:viridis, label="Photons")
display(fig)
# save("smlm_hexamer.png", fig)This example demonstrates a complete workflow for single-particle tracking with realistic camera noise:
using SMLMSim
using MicroscopePSFs
using Statistics
# Create sCMOS camera with realistic noise parameters
camera_scmos = SCMOSCamera(64, 64, 0.1, 1.6) # 64×64 pixels, 100nm/px, 1.6 e⁻ read noise
# Run diffusion simulation
params = DiffusionSMLMConfig(
density = 1.0, # 1 molecule/μm²
box_size = 6.4, # 6.4×6.4 μm field
diff_monomer = 0.1, # 0.1 μm²/s diffusion
t_max = 0.5, # 0.5 second total
camera_framerate = 100.0 # 100 fps
)
smld, info = simulate(params; camera=camera_scmos, photons=200.0)
# Generate images with full sCMOS noise model
# (quantum efficiency, Poisson, read noise, gain, offset)
psf = GaussianPSF(0.13) # 130nm PSF
images_scmos, _ = gen_images(smld, psf, bg=10.0, camera_noise=true)
# For comparison: same data with ideal camera (Poisson noise only)
camera_ideal = IdealCamera(64, 64, 0.1)
smld_ideal = BasicSMLD(smld.emitters, camera_ideal, smld.n_frames, smld.n_datasets)
images_ideal, _ = gen_images(smld_ideal, psf, bg=10.0, poisson_noise=true)
# Compare statistics
println("sCMOS: mean=$(round(mean(images_scmos), digits=1)) ADU, std=$(round(std(images_scmos), digits=1))")
println("Ideal: mean=$(round(mean(images_ideal), digits=1)) photons, std=$(round(std(images_ideal), digits=1))")
# sCMOS includes offset (~100 ADU) and spatially-varying gain/readnoiseFor more detailed examples, API documentation, and explanations of the underlying models, please see the Full Documentation.
The dev/ folder contains demonstration scripts showing sCMOS camera functionality:
dev/scmos_quick_demo.jl- Fast verification that sCMOS works (~5 seconds)dev/scmos_video.jl- Generate MP4 showing sCMOS vs Ideal side-by-sidedev/scmos_demo.jl- Full diffusion simulation with extreme sCMOS artifacts
Run from repository root:
julia --project dev/scmos_quick_demo.jlOutputs are saved to dev/outputs/ (gitignored).
This project is licensed under the MIT License - see the LICENSE file for details.