Skip to content

Commit 55ab1a0

Browse files
authored
Merge pull request #29 from JuliaSMLM/scmos
Release v0.4.0: sCMOS camera support
2 parents ca952e0 + 943c689 commit 55ab1a0

File tree

17 files changed

+1107
-45
lines changed

17 files changed

+1107
-45
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ Manifest.toml
77
examples/Manifest.toml
88
*.mp4
99
*.png
10-
*.tif
10+
*.tif
11+
12+
# Dev outputs folder for examples and demos
13+
dev/outputs/

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "SMLMSim"
22
uuid = "5a0a87e4-02d4-41f5-bf26-b8c94c784a04"
3+
version = "0.4.0"
34
authors = ["klidke@unm.edu"]
4-
version = "0.3.3"
55

66
[deps]
77
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
@@ -14,7 +14,7 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1414
[compat]
1515
Distributions = "0.25"
1616
MicroscopePSFs = "0.5"
17-
SMLMData = "0.2, 0.3"
17+
SMLMData = "0.4, 0.5"
1818
julia = "1.6"
1919

2020
[extras]

README.md

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,14 @@ using SMLMSim
5858

5959
# Set diffusion simulation parameters
6060
params = DiffusionSMLMParams(
61-
density = 0.5, # molecules per μm²
62-
box_size = 10.0, # μm
63-
diff_monomer = 0.1, # μm²/s
64-
k_off = 0.2, # s⁻¹ dimer dissociation rate
65-
dt = 0.01, # s simulation timestep
66-
t_max = 10.0 # s total simulation time
61+
density = 0.5, # molecules per μm²
62+
box_size = 10.0, # μm
63+
diff_monomer = 0.1, # μm²/s
64+
k_off = 0.2, # s⁻¹ dimer dissociation rate
65+
dt = 0.01, # s simulation timestep
66+
t_max = 10.0, # s total simulation time
67+
camera_framerate = 10.0, # 10 fps (100ms per frame)
68+
camera_exposure = 0.1 # 100ms exposure integrates 10 timesteps per frame
6769
)
6870

6971
# Run diffusion simulation
@@ -106,16 +108,53 @@ Create camera images from simulation results.
106108
using MicroscopePSFs # Needed for PSF types
107109

108110
# Generate images from diffusion simulation output
111+
# Note: Frame timing is controlled by DiffusionSMLMParams (camera_framerate, camera_exposure)
112+
# Multiple simulation timesteps are automatically integrated during simulate()
109113
psf = GaussianPSF(0.15) # 150nm PSF width
110-
# Use smld_model to avoid double-counting localization errors
111-
images = gen_images(smld, psf;
112-
frame_integration=10, # 10 simulation time steps for each camera frame
113-
support=1.0 # PSF support range
114-
)
114+
images = gen_images(smld, psf;
115+
support=1.0, # PSF support radius in μm (faster than default Inf)
116+
poisson_noise=true # Add shot noise
117+
)
115118

116119
println("Generated $(size(images,3)) camera images.")
117120
```
118121

122+
### sCMOS Camera with Realistic Noise
123+
124+
SMLMSim supports realistic sCMOS camera noise modeling with per-pixel calibration.
125+
126+
```julia
127+
using SMLMSim
128+
using MicroscopePSFs
129+
130+
# Create an sCMOS camera (128×128 pixels, 100nm pixels, 1.6 e⁻ read noise)
131+
camera_scmos = SCMOSCamera(128, 128, 0.1, 1.6)
132+
133+
# Run static simulation with sCMOS camera
134+
params = StaticSMLMParams(density=1.0, σ_psf=0.13)
135+
smld_true, smld_model, smld_noisy = simulate(
136+
params,
137+
pattern=Nmer2D(n=8, d=0.1),
138+
camera=camera_scmos
139+
)
140+
141+
# Generate images with full sCMOS noise model
142+
# (quantum efficiency, Poisson, read noise, gain, offset)
143+
psf = GaussianPSF(0.15)
144+
images_scmos = gen_images(smld_noisy, psf, bg=10.0, camera_noise=true)
145+
146+
# For diffusion simulations
147+
diff_params = DiffusionSMLMParams(density=0.5, box_size=10.0)
148+
smld_diff = simulate(diff_params; camera=camera_scmos, override_count=10)
149+
```
150+
151+
The sCMOS noise model applies:
152+
1. **Quantum efficiency**: Photon → photoelectron conversion
153+
2. **Poisson noise**: Shot noise on photoelectrons
154+
3. **Read noise**: Gaussian noise per pixel
155+
4. **Gain**: Electron → ADU conversion
156+
5. **Offset**: Dark level addition
157+
119158
## Example Workflow: Static Simulation & Visualization
120159

121160
```julia
@@ -150,10 +189,63 @@ display(fig)
150189
# save("smlm_hexamer.png", fig)
151190
```
152191

192+
## Example Workflow: Diffusion with Realistic sCMOS Noise
193+
194+
This example demonstrates a complete workflow for single-particle tracking with realistic camera noise:
195+
196+
```julia
197+
using SMLMSim
198+
using MicroscopePSFs
199+
using Statistics
200+
201+
# Create sCMOS camera with realistic noise parameters
202+
camera_scmos = SCMOSCamera(64, 64, 0.1, 1.6) # 64×64 pixels, 100nm/px, 1.6 e⁻ read noise
203+
204+
# Run diffusion simulation
205+
params = DiffusionSMLMParams(
206+
density = 1.0, # 1 molecule/μm²
207+
box_size = 6.4, # 6.4×6.4 μm field
208+
diff_monomer = 0.1, # 0.1 μm²/s diffusion
209+
t_max = 0.5, # 0.5 second total
210+
camera_framerate = 100.0 # 100 fps
211+
)
212+
smld = simulate(params; camera=camera_scmos, photons=200.0)
213+
214+
# Generate images with full sCMOS noise model
215+
# (quantum efficiency, Poisson, read noise, gain, offset)
216+
psf = GaussianPSF(0.13) # 130nm PSF
217+
images_scmos = gen_images(smld, psf, bg=10.0, camera_noise=true)
218+
219+
# For comparison: same data with ideal camera (Poisson noise only)
220+
camera_ideal = IdealCamera(64, 64, 0.1)
221+
smld_ideal = BasicSMLD(smld.emitters, camera_ideal, smld.n_frames, smld.n_datasets)
222+
images_ideal = gen_images(smld_ideal, psf, bg=10.0, poisson_noise=true)
223+
224+
# Compare statistics
225+
println("sCMOS: mean=$(round(mean(images_scmos), digits=1)) ADU, std=$(round(std(images_scmos), digits=1))")
226+
println("Ideal: mean=$(round(mean(images_ideal), digits=1)) photons, std=$(round(std(images_ideal), digits=1))")
227+
# sCMOS includes offset (~100 ADU) and spatially-varying gain/readnoise
228+
```
229+
153230
## Further Information
154231

155232
For more detailed examples, API documentation, and explanations of the underlying models, please see the [Full Documentation](https://JuliaSMLM.github.io/SMLMSim.jl/dev).
156233

234+
### Demo Scripts
235+
236+
The `dev/` folder contains demonstration scripts showing sCMOS camera functionality:
237+
238+
- **`dev/scmos_quick_demo.jl`** - Fast verification that sCMOS works (~5 seconds)
239+
- **`dev/scmos_video.jl`** - Generate MP4 showing sCMOS vs Ideal side-by-side
240+
- **`dev/scmos_demo.jl`** - Full diffusion simulation with extreme sCMOS artifacts
241+
242+
Run from repository root:
243+
```bash
244+
julia --project dev/scmos_quick_demo.jl
245+
```
246+
247+
Outputs are saved to `dev/outputs/` (gitignored).
248+
157249
## Contributors
158250

159251
- [JuliaSMLM Team](https://github.com/JuliaSMLM)

api_overview.md

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,27 @@ fluor = GenericFluor(photons=1e5, k_off=50.0, k_on=1e-2)
189189
### Creating a Camera
190190

191191
```julia
192-
# Create a camera with 100nm pixels (128x128 pixels)
192+
# IdealCamera: Poisson noise only
193193
camera = IdealCamera(128, 128, 0.1) # 128×128 pixels, 100nm pixels
194194

195195
# Specify field of view with an array of pixel edges
196196
pixel_edges_x = 0.0:0.1:12.8 # 0 to 12.8μm in 0.1μm steps
197197
pixel_edges_y = 0.0:0.1:12.8
198198
camera = IdealCamera(pixel_edges_x, pixel_edges_y)
199+
200+
# SCMOSCamera: Realistic noise model with per-pixel calibration (SMLMData 0.4+)
201+
# Parameters: width, height, pixel_size, readnoise
202+
camera_scmos = SCMOSCamera(128, 128, 0.1, 1.6) # 1.6 e⁻ RMS read noise
203+
204+
# Advanced: Specify all calibration parameters
205+
# offset (ADU), gain (e⁻/ADU), readnoise (e⁻ RMS), quantum efficiency (0-1)
206+
camera_scmos = SCMOSCamera(
207+
128, 128, 0.1;
208+
offset=100.0, # 100 ADU dark level
209+
gain=0.5, # 0.5 e⁻/ADU
210+
readnoise=1.6, # 1.6 e⁻ RMS
211+
qe=0.95 # 95% quantum efficiency
212+
)
199213
```
200214

201215
## Core Functions
@@ -287,8 +301,10 @@ images = gen_images(
287301
sampling=2, # Supersampling factor for PSF integration
288302
threaded=true, # Enable multithreading
289303
bg=0.0, # Background signal level (photons per pixel)
290-
poisson_noise=false, # Apply Poisson noise
291-
camera_noise=false # Apply camera read noise
304+
poisson_noise=false, # Apply Poisson noise only (simple shot noise)
305+
camera_noise=false # Apply full camera noise model (requires SCMOSCamera)
306+
# - For SCMOSCamera: QE, Poisson, read noise, gain, offset
307+
# - For IdealCamera: ignored (use poisson_noise instead)
292308
)
293309

294310
# The support parameter controls PSF computation region:
@@ -301,6 +317,17 @@ support=1.0 # 1.0 µm radius around each emitter
301317

302318
# 3. Tuple (xmin, xmax, ymin, ymax): Explicit region in microns
303319
support=(4.0, 6.0, 4.0, 6.0) # Only compute PSF within this region
320+
321+
# Example: sCMOS camera with realistic noise
322+
camera_scmos = SCMOSCamera(128, 128, 0.1, 1.6)
323+
smld = BasicSMLD(emitters, camera_scmos, n_frames, n_datasets)
324+
images_scmos = gen_images(smld, psf, bg=10.0, camera_noise=true)
325+
# Applies: QE → Poisson → read noise → gain → offset
326+
327+
# Example: IdealCamera with Poisson noise only
328+
camera_ideal = IdealCamera(128, 128, 0.1)
329+
smld = BasicSMLD(emitters, camera_ideal, n_frames, n_datasets)
330+
images_poisson = gen_images(smld, psf, bg=10.0, poisson_noise=true)
304331
```
305332

306333
#### gen_image
@@ -471,12 +498,17 @@ smld = simulate(params)
471498
dimer_smld = get_dimers(smld)
472499
frames, fractions = analyze_dimer_fraction(smld)
473500

474-
# 4. Generate microscope images with finite PSF support
501+
# 4. Generate microscope images with realistic sCMOS noise
502+
# Create sCMOS camera matching simulation box
503+
n_pixels = Int(ceil(params.box_size / 0.1)) # 0.1 μm pixels
504+
camera_scmos = SCMOSCamera(n_pixels, n_pixels, 0.1, 1.6)
505+
smld_cam = BasicSMLD(smld.emitters, camera_scmos, smld.n_frames, 1)
506+
475507
psf = GaussianPSF(0.15) # 150nm PSF width
476-
images = gen_images(smld, psf;
508+
images = gen_images(smld_cam, psf;
477509
support=1.0, # 1.0 μm PSF support radius (faster)
478510
bg=5.0, # Background photons per pixel
479-
poisson_noise=true # Add realistic photon counting noise
511+
camera_noise=true # Full sCMOS noise model (QE, Poisson, read noise, gain, offset)
480512
)
481513
```
482514

dev/Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
[deps]
2+
ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
3+
FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
24
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
35
MicroscopePSFs = "de9ac726-48a4-48ab-84b0-1c03c7c18929"
46
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
57
SMLMSim = "5a0a87e4-02d4-41f5-bf26-b8c94c784a04"
68
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
9+
VideoIO = "d6d074c3-1acf-5d4c-9a43-ef38773959a2"

dev/outputs/.gitkeep

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Outputs from dev demos are saved here (gitignored)

0 commit comments

Comments
 (0)