diff --git a/Project.toml b/Project.toml index dd4a78e..ec39530 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SMLMData" uuid = "5488f106-40b8-4660-84c5-84a168990d1b" authors = ["klidke@unm.edu"] -version = "0.5.1" +version = "0.6.0" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" diff --git a/api_overview.md b/api_overview.md index 2e4b832..74fa60e 100644 --- a/api_overview.md +++ b/api_overview.md @@ -87,6 +87,7 @@ mutable struct Emitter2DFit{T} <: AbstractEmitter bg::T # fitted background in photons/pixel σ_x::T # uncertainty in x position in microns σ_y::T # uncertainty in y position in microns + σ_xy::T # covariance between x and y (microns², 0 = axis-aligned) σ_photons::T # uncertainty in photon count σ_bg::T # uncertainty in background level frame::Int # frame number in acquisition sequence @@ -105,6 +106,9 @@ mutable struct Emitter3DFit{T} <: AbstractEmitter σ_x::T # uncertainty in x position in microns σ_y::T # uncertainty in y position in microns σ_z::T # uncertainty in z position in microns + σ_xy::T # covariance between x and y (microns², 0 = uncorrelated) + σ_xz::T # covariance between x and z (microns², 0 = uncorrelated) + σ_yz::T # covariance between y and z (microns², 0 = uncorrelated) σ_photons::T # uncertainty in photon count σ_bg::T # uncertainty in background level frame::Int # frame number in acquisition sequence @@ -138,6 +142,7 @@ emitter_2d_fit = Emitter2DFit{Float64}( 1000.0, 10.0, # photons detected, background photons/pixel 0.01, 0.01, # σ_x, σ_y: position uncertainties in microns 50.0, 2.0; # σ_photons, σ_bg: photon count uncertainties + σ_xy=0.005, # covariance (optional, default=0 for axis-aligned uncertainty) frame=5, # frame number in acquisition (1-based, default=1) dataset=1, # dataset identifier for multi-acquisition experiments track_id=2, # tracking ID for linked localizations (default=0 = unlinked) diff --git a/docs/src/index.md b/docs/src/index.md index 17925fb..a01e830 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -71,6 +71,7 @@ mutable struct Emitter2DFit{T} <: AbstractEmitter bg::T # fitted background in photons/pixel σ_x::T # uncertainty in x position in microns σ_y::T # uncertainty in y position in microns + σ_xy::T # covariance between x and y (microns², 0 = axis-aligned) σ_photons::T # uncertainty in photon count σ_bg::T # uncertainty in background level frame::Int # frame number in acquisition sequence @@ -88,6 +89,9 @@ mutable struct Emitter3DFit{T} <: AbstractEmitter σ_x::T # uncertainty in x position in microns σ_y::T # uncertainty in y position in microns σ_z::T # uncertainty in z position in microns + σ_xy::T # covariance between x and y (microns², 0 = uncorrelated) + σ_xz::T # covariance between x and z (microns², 0 = uncorrelated) + σ_yz::T # covariance between y and z (microns², 0 = uncorrelated) σ_photons::T # uncertainty in photon count σ_bg::T # uncertainty in background level frame::Int # frame number in acquisition sequence @@ -118,6 +122,7 @@ emitter_2d_fit = Emitter2DFit{Float64}( 1000.0, 10.0, # photons, background 0.01, 0.01, # σ_x, σ_y (uncertainties in μm) 50.0, 2.0; # σ_photons, σ_bg (uncertainties) + σ_xy=0.005, # covariance (optional, 0 = axis-aligned) frame=5, # frame number dataset=1, # dataset identifier track_id=2, # tracking identifier (0 = unlinked) @@ -130,6 +135,7 @@ emitter_3d_fit = Emitter3DFit{Float64}( 1000.0, 10.0, # photons, background 0.01, 0.01, 0.02, # σ_x, σ_y, σ_z (uncertainties in μm) 50.0, 2.0; # σ_photons, σ_bg (uncertainties) + σ_xy=0.005, σ_xz=0.002, σ_yz=0.003, # covariances (optional, 0 = uncorrelated) frame=5, # frame number dataset=1, # dataset identifier track_id=2, # tracking identifier (0 = unlinked) diff --git a/src/types/emitters.jl b/src/types/emitters.jl index c55acbc..5a37e02 100644 --- a/src/types/emitters.jl +++ b/src/types/emitters.jl @@ -52,6 +52,7 @@ Represents fitted 2D localization results with uncertainties and temporal/tracki - `bg::T`: fitted background in photons/pixel - `σ_x::T`: uncertainty in x position in microns - `σ_y::T`: uncertainty in y position in microns +- `σ_xy::T`: covariance between x and y uncertainties (microns², 0 = axis-aligned) - `σ_photons::T`: uncertainty in photon count - `σ_bg::T`: uncertainty in background in photons/pixel - `frame::Int`: frame number in acquisition sequence @@ -66,6 +67,7 @@ mutable struct Emitter2DFit{T} <: AbstractEmitter bg::T σ_x::T σ_y::T + σ_xy::T σ_photons::T σ_bg::T frame::Int @@ -88,6 +90,9 @@ Represents fitted 3D localization results with uncertainties and temporal/tracki - `σ_x::T`: uncertainty in x position in microns - `σ_y::T`: uncertainty in y position in microns - `σ_z::T`: uncertainty in z position in microns +- `σ_xy::T`: covariance between x and y (microns², 0 = uncorrelated) +- `σ_xz::T`: covariance between x and z (microns², 0 = uncorrelated) +- `σ_yz::T`: covariance between y and z (microns², 0 = uncorrelated) - `σ_photons::T`: uncertainty in photon count - `σ_bg::T`: uncertainty in background in photons/pixel - `frame::Int`: frame number in acquisition sequence @@ -104,6 +109,9 @@ mutable struct Emitter3DFit{T} <: AbstractEmitter σ_x::T σ_y::T σ_z::T + σ_xy::T + σ_xz::T + σ_yz::T σ_photons::T σ_bg::T frame::Int @@ -115,7 +123,7 @@ end """ Emitter2DFit{T}(x, y, photons, bg, σ_x, σ_y, σ_photons, σ_bg; - frame=0, dataset=1, track_id=0, id=0) where T + σ_xy=zero(T), frame=1, dataset=1, track_id=0, id=0) where T Convenience constructor for 2D localization fit results with optional identification parameters. @@ -131,6 +139,7 @@ Convenience constructor for 2D localization fit results with optional identifica - `σ_bg::T`: uncertainty in background level ## Optional Keywords +- `σ_xy::T=0`: covariance between x and y uncertainties (microns², 0 = axis-aligned) - `frame::Int=1`: frame number in acquisition sequence - `dataset::Int=1`: identifier for specific acquisition/dataset - `track_id::Int=0`: identifier for linking localizations across frames @@ -146,23 +155,24 @@ emitter = Emitter2DFit{Float64}( 50.0, 2.0 # σ_photons, σ_bg ) -# Create emitter with specific frame and dataset +# Create emitter with covariance for rotated uncertainty ellipse emitter = Emitter2DFit{Float64}( 1.0, 2.0, 1000.0, 10.0, 0.01, 0.01, 50.0, 2.0; - frame=5, dataset=2 + σ_xy=0.005, frame=5, dataset=2 ) ``` """ -function Emitter2DFit{T}(x::T, y::T, photons::T, bg::T, +function Emitter2DFit{T}(x::T, y::T, photons::T, bg::T, σ_x::T, σ_y::T, σ_photons::T, σ_bg::T; - frame::Int=1, dataset::Int=1, track_id::Int=0, id::Int=0) where T - Emitter2DFit{T}(x, y, photons, bg, σ_x, σ_y, σ_photons, σ_bg, + σ_xy::T=zero(T), frame::Int=1, dataset::Int=1, track_id::Int=0, id::Int=0) where T + Emitter2DFit{T}(x, y, photons, bg, σ_x, σ_y, σ_xy, σ_photons, σ_bg, frame, dataset, track_id, id) end """ Emitter3DFit{T}(x, y, z, photons, bg, σ_x, σ_y, σ_z, σ_photons, σ_bg; - frame=0, dataset=1, track_id=0, id=0) where T + σ_xy=zero(T), σ_xz=zero(T), σ_yz=zero(T), + frame=1, dataset=1, track_id=0, id=0) where T Convenience constructor for 3D localization fit results with optional identification parameters. @@ -180,6 +190,9 @@ Convenience constructor for 3D localization fit results with optional identifica - `σ_bg::T`: uncertainty in background level ## Optional Keywords +- `σ_xy::T=0`: covariance between x and y (microns², 0 = uncorrelated) +- `σ_xz::T=0`: covariance between x and z (microns², 0 = uncorrelated) +- `σ_yz::T=0`: covariance between y and z (microns², 0 = uncorrelated) - `frame::Int=1`: frame number in acquisition sequence - `dataset::Int=1`: identifier for specific acquisition/dataset - `track_id::Int=0`: identifier for linking localizations across frames @@ -195,17 +208,18 @@ emitter = Emitter3DFit{Float64}( 50.0, 2.0 # σ_photons, σ_bg ) -# Create emitter with specific frame and tracking +# Create emitter with full 3D covariance emitter = Emitter3DFit{Float64}( 1.0, 2.0, -0.5, 1000.0, 10.0, 0.01, 0.01, 0.02, 50.0, 2.0; - frame=5, track_id=1 + σ_xy=0.005, σ_xz=0.002, σ_yz=0.003, frame=5, track_id=1 ) ``` """ -function Emitter3DFit{T}(x::T, y::T, z::T, photons::T, bg::T, +function Emitter3DFit{T}(x::T, y::T, z::T, photons::T, bg::T, σ_x::T, σ_y::T, σ_z::T, σ_photons::T, σ_bg::T; + σ_xy::T=zero(T), σ_xz::T=zero(T), σ_yz::T=zero(T), frame::Int=1, dataset::Int=1, track_id::Int=0, id::Int=0) where T - Emitter3DFit{T}(x, y, z, photons, bg, σ_x, σ_y, σ_z, σ_photons, σ_bg, + Emitter3DFit{T}(x, y, z, photons, bg, σ_x, σ_y, σ_z, σ_xy, σ_xz, σ_yz, σ_photons, σ_bg, frame, dataset, track_id, id) end @@ -263,6 +277,7 @@ function Base.show(io::IO, ::MIME"text/plain", e::Emitter2DFit{T}) where T println(io, " Uncertainties:") println(io, " σ_x: $(e.σ_x) μm") println(io, " σ_y: $(e.σ_y) μm") + e.σ_xy != 0 && println(io, " σ_xy: $(e.σ_xy) μm²") println(io, " σ_photons: $(e.σ_photons)") println(io, " σ_bg: $(e.σ_bg)") println(io, " Frame: $(e.frame)") @@ -289,6 +304,13 @@ function Base.show(io::IO, ::MIME"text/plain", e::Emitter3DFit{T}) where T println(io, " σ_x: $(e.σ_x) μm") println(io, " σ_y: $(e.σ_y) μm") println(io, " σ_z: $(e.σ_z) μm") + has_cov = e.σ_xy != 0 || e.σ_xz != 0 || e.σ_yz != 0 + if has_cov + println(io, " Covariances:") + e.σ_xy != 0 && println(io, " σ_xy: $(e.σ_xy) μm²") + e.σ_xz != 0 && println(io, " σ_xz: $(e.σ_xz) μm²") + e.σ_yz != 0 && println(io, " σ_yz: $(e.σ_yz) μm²") + end println(io, " σ_photons: $(e.σ_photons)") println(io, " σ_bg: $(e.σ_bg)") println(io, " Frame: $(e.frame)") diff --git a/test/test_emitters.jl b/test/test_emitters.jl index 68a9727..afe081b 100644 --- a/test/test_emitters.jl +++ b/test/test_emitters.jl @@ -27,15 +27,17 @@ end 1.0, 2.0, # x, y 1000.0, 10.0, # photons, bg 0.01, 0.01, # σ_x, σ_y + 0.005, # σ_xy (covariance) 50.0, 2.0, # σ_photons, σ_bg 1, 1, 0, 1 # frame, dataset, track_id, id ) @test e2df.x == 1.0 @test e2df.σ_x == 0.01 + @test e2df.σ_xy == 0.005 @test e2df.frame == 1 @test e2df.track_id == 0 - - # Test convenience constructor + + # Test convenience constructor (σ_xy defaults to 0) e2df_simple = Emitter2DFit{Float64}( 1.0, 2.0, 1000.0, 10.0, 0.01, 0.01, 50.0, 2.0 ) @@ -43,27 +45,52 @@ end @test e2df_simple.dataset == 1 # default value @test e2df_simple.track_id == 0 # default value @test e2df_simple.id == 0 # default value + @test e2df_simple.σ_xy == 0.0 # default covariance + + # Test convenience constructor with σ_xy + e2df_cov = Emitter2DFit{Float64}( + 1.0, 2.0, 1000.0, 10.0, 0.01, 0.01, 50.0, 2.0; + σ_xy=0.003 + ) + @test e2df_cov.σ_xy == 0.003 end @testset "3D Fit" begin # Test full constructor e3df = Emitter3DFit{Float64}( - 1.0, 2.0, 3.0, # x, y, z - 1000.0, 10.0, # photons, bg - 0.01, 0.01, 0.02, # σ_x, σ_y, σ_z - 50.0, 2.0, # σ_photons, σ_bg - 1, 1, 0, 1 # frame, dataset, track_id, id + 1.0, 2.0, 3.0, # x, y, z + 1000.0, 10.0, # photons, bg + 0.01, 0.01, 0.02, # σ_x, σ_y, σ_z + 0.005, 0.002, 0.003, # σ_xy, σ_xz, σ_yz (covariances) + 50.0, 2.0, # σ_photons, σ_bg + 1, 1, 0, 1 # frame, dataset, track_id, id ) @test e3df.z == 3.0 @test e3df.σ_z == 0.02 - - # Test convenience constructor + @test e3df.σ_xy == 0.005 + @test e3df.σ_xz == 0.002 + @test e3df.σ_yz == 0.003 + + # Test convenience constructor (covariances default to 0) e3df_simple = Emitter3DFit{Float64}( - 1.0, 2.0, 3.0, 1000.0, 10.0, + 1.0, 2.0, 3.0, 1000.0, 10.0, 0.01, 0.01, 0.02, 50.0, 2.0 ) @test e3df_simple.frame == 1 @test e3df_simple.dataset == 1 + @test e3df_simple.σ_xy == 0.0 + @test e3df_simple.σ_xz == 0.0 + @test e3df_simple.σ_yz == 0.0 + + # Test convenience constructor with full 3D covariance + e3df_cov = Emitter3DFit{Float64}( + 1.0, 2.0, 3.0, 1000.0, 10.0, + 0.01, 0.01, 0.02, 50.0, 2.0; + σ_xy=0.003, σ_xz=0.001, σ_yz=0.002 + ) + @test e3df_cov.σ_xy == 0.003 + @test e3df_cov.σ_xz == 0.001 + @test e3df_cov.σ_yz == 0.002 end end @@ -71,11 +98,12 @@ end # Test that operations maintain type stability e2d = Emitter2D{Float64}(1.0, 2.0, 1000.0) @test typeof(e2d.x) === Float64 - + e2df = Emitter2DFit{Float32}( 1.0f0, 2.0f0, 1000.0f0, 10.0f0, 0.01f0, 0.01f0, 50.0f0, 2.0f0 ) @test typeof(e2df.x) === Float32 @test typeof(e2df.σ_x) === Float32 + @test typeof(e2df.σ_xy) === Float32 end