Skip to content

Commit ac80033

Browse files
tmporarily added some details for gaussian fitting.
1 parent 8c66617 commit ac80033

4 files changed

Lines changed: 199 additions & 34 deletions

File tree

examples/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
33
ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66"
44
InverseModeling = "ce844058-9528-415d-a63d-06f3dd08b29f"
5+
LsqFit = "2fda8390-95c7-5789-9bda-21331edee243"
56
Noise = "81d43f40-5267-43b7-ae1c-8b967f377efa"
67
Optim = "429524aa-4258-5aef-a3af-852621145aeb"
78
SeparableFunctions = "c8c7ead4-852c-491e-a42d-3d43bc74259e"

examples/gauss_fit.jl

Lines changed: 107 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,66 +6,110 @@ using Noise
66
using CUDA
77

88
# simulate a gaussian blob with Poisson noise and fit it with a Gaussian function
9-
sz = (7,7) # (1600, 1600)
10-
many_fits = true
9+
sz = (9, 9) # (1600, 1600)
10+
many_off = true
11+
many_int = true
12+
many_bg = true
13+
many_sig = true
14+
1115
use_cuda = false
12-
N = 1_000
13-
hyperplanes = many_fits ? rand(Float32, (1, N)) : 0
14-
hp_zeros = many_fits ? zeros(Float32, (1, N)) : 0
16+
DType = Float32
17+
N = 10_000
18+
hp_off = many_off ? 2 .*rand(DType, (1, N)) : 0
19+
hp_sig = many_sig ? zeros(DType, (1, N)) : 0
20+
hp_int = many_int ? 1 .+ rand(DType, (1, N)) : 1
1521

16-
off = [3.2f0, 3.5f0] .+ hyperplanes
17-
sigma = [1.4f0, 1.1f0] .+ hp_zeros
18-
hyperplanes = many_fits ? rand(Float32, (1, N)) : 0
19-
intensity = [50f0] .* (1 .+ hyperplanes)
20-
vec_true = ComponentVector(;bg=10.0f0 .+ hp_zeros, intensity=intensity, off = off, args = sigma)
21-
vec_true = Float64.(vec_true)
22+
off = [5.2, 4.5] .+ hp_off
23+
sigma = [1.4, 1.1] .+ hp_sig
24+
intensity = [50] .* hp_int
25+
bg = many_bg ? 10.2 .+ zeros(DType, (1, N)) : 10.2
26+
vec_true = DType.(ComponentVector(;bg=bg, intensity=intensity, off = off, args = sigma))
2227

28+
# create the perfect spots:
2329
pdat = gaussian_vec(sz, vec_true)
24-
dat = Float32.(poisson(Float64.(pdat)))
30+
dat = DType.(poisson(Float64.(pdat)))
31+
32+
startvals = DType.(ComponentVector(gauss_start(dat, 0.2, length(sz))));
33+
sum(abs2.(collect(startvals.off) .- vec_true.off))
2534

2635
pdat = (use_cuda) ? CuArray(pdat) : pdat
2736
dat = (use_cuda) ? CuArray(dat) : dat
28-
29-
qdat = copy(pdat)
30-
qdat .= qdat[:,:,1]
31-
# now prepare the fitting:
32-
# myfg! = get_fg!(pdat, gaussian_raw, length(sz); loss=loss_anscombe_pos, bg=7f0);
33-
myfg! = get_fg!(qdat, gaussian_raw, length(sz); loss=loss_gaussian);
34-
shyperplanes = many_fits ? zeros(Float32, (1, size(dat)[end])) : 0
35-
soff = [4.0f0, 4.0f0] .+ shyperplanes
36-
bg = [0.5f0] .+ shyperplanes
37-
intensity = [45f0] .+ shyperplanes
38-
sigma = [3.0f0, 2.0f0] .+ shyperplanes
3937
if (use_cuda)
40-
bg = CuArray([bg])
41-
intensity = CuArray(intensity)
42-
soff = CuArray(soff)
43-
sigma = CuArray(sigma)
38+
startvals = ComponentVector(;bg=CuArray(startvals.bg), intensity=CuArray(startvals.intensity), off = CuArray(startvals.off), args = CuArray(startvals.args))
4439
end
45-
startvals = ComponentVector(;bg=bg, intensity=intensity, off = soff, args = sigma)
46-
startvals = Float64.(startvals)
47-
opt = Optim.Options(iterations = 50); #
48-
odo = OnceDifferentiable(Optim.NLSolversBase.only_fg!(myfg!), startvals);
40+
41+
# @vt pdat gaussian_vec(sz, startvals)
42+
# qdat = copy(pdat)
43+
# qdat .= qdat[:,:,1]
44+
# now prepare the fitting:
45+
# myfg! = get_fg!(dat, gaussian_raw, length(sz); loss=loss_anscombe_pos, bg=0.1f0);
46+
# myfg! = get_fg!(dat, gaussian_raw, length(sz); loss=loss_gaussian);
47+
myfg! = get_fg!(dat, gaussian_raw, length(sz); loss=loss_poisson_pos);
48+
# hp_off2 = many_off ? zeros(DType, (1, size(dat)[end])) : 0
49+
# soff = [4.0, 4.0] .+ hp_off2
50+
# bg = [0.5] .+ shyperplanes
51+
# intensity = [45.0] .+ hp_int
52+
# sigma = [3.0, 2.0] .+ hp_sig
53+
# if (use_cuda)
54+
# bg = CuArray([bg])
55+
# intensity = CuArray(intensity)
56+
# soff = CuArray(soff)
57+
# sigma = CuArray(sigma)
58+
# end
59+
# startvals = DType.(ComponentVector(;bg=bg, intensity=intensity, off = soff, args = sigma))
60+
opt = Optim.Options(iterations = 1500); #
4961

5062
if (false)
5163
G = copy(startvals)
5264
myfg!(1, G, startvals)
5365

54-
myfg2! = get_fg!(pdat[:,:,1], gaussian_raw, length(sz); loss=loss_anscombe_pos, bg=7f0);
66+
myfg2! = get_fg!(pdat[:,:,1], gaussian_raw, length(sz); loss=loss_anscombe_pos, bg=0.1f0);
5567
sv = ComponentVector{Float32}(bg=startvals.bg[1], intensity=startvals.intensity[1], off = startvals.off[:,1], args = startvals.args[:,1])
5668
G2 = copy(sv)
5769
myfg2!(1, G2, sv)
5870
G2
5971
end
6072

6173
# and perform the fit
62-
@time reso = Optim.optimize(odo, startvals, Optim.LBFGS(), opt);
74+
svb = copy(startvals)
75+
# svb.args = svb.args .* 1.2f0
76+
odo = OnceDifferentiable(Optim.NLSolversBase.only_fg!(myfg!), svb);
77+
@time reso = Optim.optimize(odo, svb, Optim.LBFGS(), opt);
78+
# 14 sec, CUDA: 2.5 sec
6379
# 2 sec, 5k fits/s (44.25 k allocations: 1.546 GiB, 7.35% gc time)
6480
# with intensity variations: 26.833106 seconds (532.47 k allocations: 20.251 GiB, 5.99% gc time)
6581
# in Cuda:
6682
reso.f_calls # 61 # 1766 für 10_000 fits, 155, 2.2 sec for 10_000 fits with all entries being vectors
6783
reso.minimum #
68-
@vt pdat gaussian_vec(sz, startvals) gaussian_vec(sz, reso.minimizer)
84+
@vt pdat dat (gaussian_vec(sz, startvals).-dat) (gaussian_vec(sz, reso.minimizer).-dat)
85+
86+
success = sum(abs.(collect(startvals.off) .- vec_true.off), dims=1) .< 0.5
87+
success = success .&& sum(abs.(collect(reso.minimizer.off) .- vec_true.off), dims=1) .< 0.5
88+
sum(.!success)
89+
# findfirst(.!success)
90+
@vt collect(dat)[:,:,.!success[:]] collect(pdat)[:,:,.!success[:]] collect(gaussian_vec(sz, startvals))[:,:,.!success[:]] collect(gaussian_vec(sz, reso.minimizer))[:,:,.!success[:]]
91+
92+
ff = findfirst(.!success)[2]
93+
sv = ComponentVector(off=startvals.off[:,ff], bg = startvals.bg[:,ff], intensity=startvals.intensity[:,ff], args = startvals.args[:,ff])
94+
sdat = dat[:,:,ff]
95+
afg! = get_fg!(sdat, gaussian_raw, length(sz); loss=loss_gaussian); # loss_poisson_pos
96+
odo = OnceDifferentiable(Optim.NLSolversBase.only_fg!(afg!), sv);
97+
@time reso = Optim.optimize(odo, sv, Optim.LBFGS(), opt);
98+
@vt sdat gaussian_vec(sz, sv) gaussian_vec(sz, reso.minimizer)
99+
100+
trueoff = vec_true.off[:,collect(success[:])]
101+
startoff = collect(startvals.off[:,success[:]])
102+
minoff = collect(reso.minimizer.off[:,success[:]])
103+
sum(abs2.(startoff .- trueoff), dims=2) # 0 failed, 68.29712, 42.612137
104+
sum(abs2.(minoff .- trueoff), dims=2) # 21 failed, 57.92457, 33.990776 # Anscombe: 5 failed, 58.806953, 34.21795
105+
@vt abs.(startoff .- trueoff) abs.(minoff .- trueoff)
106+
107+
sum(abs2.(collect(startvals.intensity) .- vec_true.intensity))
108+
sum(abs2.(collect(reso.minimizer.intensity) .- vec_true.intensity))
109+
sum(abs2.(collect(startvals.bg) .- vec_true.bg))
110+
sum(abs2.(collect(reso.minimizer.bg) .- vec_true.bg))
111+
sum(abs2.(collect(startvals.args) .- vec_true.args), dims=2)
112+
sum(abs2.(collect(reso.minimizer.args) .- vec_true.args), dims=2)
69113

70114
odo = OnceDifferentiable(Optim.NLSolversBase.only_fg!(myfg!), startvals);
71115
if isa(dat, CuArray)
@@ -97,3 +141,32 @@ res1
97141
# 5 ms (39192 allocations: 4.35 MiB)
98142

99143
# @btime Optim.optimize($loss, $off_start, $sigma_start, LBFGS(); autodiff = :forward); # 1.000 ms (10001 allocations: 1.53 MiB)
144+
145+
# here is the LsqFit example from https://github.com/JuliaNLSolvers/LsqFit.jl
146+
using LsqFit
147+
148+
x = collect(range(0, stop=200, length=201))
149+
y = collect(range(0, stop=200, length=201))
150+
151+
xy = hcat(x, y)
152+
153+
function twoD_Gaussian(xy, p)
154+
amplitude, xo, yo, sigma_x, sigma_y, theta, offset = p
155+
a = (cos(theta)^2)/(2*sigma_x^2) + (sin(theta)^2)/(2*sigma_y^2)
156+
b = -(sin(2*theta))/(4*sigma_x^2) + (sin(2*theta))/(4*sigma_y^2)
157+
c = (sin(theta)^2)/(2*sigma_x^2) + (cos(theta)^2)/(2*sigma_y^2)
158+
159+
# creating linear meshgrid from xy
160+
x = xy[:, 1]
161+
y = xy[:, 2]
162+
g = offset .+ amplitude .* exp.( - (a.*((x .- xo).^2) + 2 .* b .* (x .- xo) .* (y .- yo) + c * ((y .- yo).^2)))
163+
return g[:]
164+
end
165+
166+
p0 = Float64.([3, 100, 100, 20, 40, 0, 10])
167+
data = twoD_Gaussian(xy, p0)
168+
169+
# Noisy data
170+
data_noisy = data + 0.2 * randn(size(data))
171+
172+
fit = LsqFit.curve_fit(twoD_Gaussian, xy, data_noisy, p0)

src/SeparableFunctions.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ export kwargs_to_args
4747

4848
export loss_anscombe, loss_anscombe_pos, loss_gaussian, loss_poisson, loss_poisson_pos
4949
export get_vec_dim
50+
export gauss_start
5051

5152
DefaultResElType = Float32
5253
DefaultArrType = Array{DefaultResElType}
5354
DefaultComplexArrType = Array{complex(DefaultResElType)}
5455

5556
include("utilities.jl")
5657
include("losses.jl")
58+
include("gauss_params.jl")
5759
include("specific.jl")
5860
include("general.jl")
5961
include("radial.jl")

src/gauss_params.jl

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""
2+
get_com(dat::AbstractArray{T, N}, mask, prod_dims=N) where {T, N}
3+
4+
returns the center of mass of a data array `dat` with a mask `mask`.
5+
"""
6+
function get_com!(dst::AbstractMatrix{T}, dat::AbstractArray{T, N}, mask, prod_dims=N) where {T, N}
7+
ax = axes(dat)[1:prod_dims]
8+
ax = ntuple((d) -> reorient(ax[d], Val(d), Val(prod_dims)), Val(prod_dims))
9+
sum_dat_mask = sum(dat.*mask, dims=1:prod_dims)
10+
for d in 1:prod_dims
11+
dst[d,:] .= (sum(ax[d].*dat.*mask, dims=1:prod_dims) ./ sum_dat_mask)[:]
12+
end
13+
end
14+
function get_com(dat::AbstractArray{T, N}, mask, prod_dims=N) where {T, N}
15+
dst = similar(dat, (prod_dims, size(dat)[prod_dims+1:end]...))
16+
get_com!(dst, dat, mask, prod_dims)
17+
return dst
18+
end
19+
20+
"""
21+
get_std(dat::AbstractArray{T, N}, t_ctr, mask) where {T, N}
22+
23+
returns the center of mass of a data array `dat` with a mask `mask`.
24+
"""
25+
function get_std!(dst::AbstractMatrix{T}, dat::AbstractArray{T, N}, mask, t_ctr, prod_dims=N) where {T, N}
26+
sum_dat_mask = sum(dat.*mask, dims=1:prod_dims)
27+
for d in 1:prod_dims
28+
ax = reorient(axes(dat)[d], Val(d), Val(prod_dims))
29+
ctrs = reshape(t_ctr[d,:], (ones(Integer, prod_dims)..., size(t_ctr)[2:end]...))
30+
dst[d,:] .= (sqrt.(sum(dat.*mask.*(ax .- ctrs).^2, dims=1:prod_dims) ./ sum_dat_mask))[:]
31+
end
32+
end
33+
function get_std(dat::AbstractArray{T, N}, mask, t_ctr, prod_dims=N) where {T, N}
34+
dst = similar(dat, (prod_dims, size(dat)[prod_dims+1:end]...))
35+
get_std!(dst, dat, mask, t_ctr, prod_dims)
36+
return dst
37+
end
38+
39+
function get_bg_sum(dat::AbstractArray{T, N}, prod_dims=N) where {T, N}
40+
sz = size(dat)[1:prod_dims]
41+
isz = max.(1, sz.-2)
42+
inner = select_region_view(dat, isz)
43+
sdat = sum(dat, dims=1:prod_dims)
44+
sinner = sum(inner, dims=1:prod_dims)
45+
ninner = prod(isz)
46+
nedge = prod(sz) - ninner
47+
bg = (sdat .- sinner)/nedge
48+
return bg, (sinner .- ninner.*bg) # minimum(dat)
49+
end
50+
51+
function get_intensity(dat::AbstractArray{T, N}, prod_dims=N) where {T, N}
52+
sz = size(dat)[1:prod_dims]
53+
# select a 3x3 pixel region and calculate the average intensity in it
54+
psz = min.(sz, 3)
55+
peak = select_region_view(dat, psz)
56+
npeak = prod(psz)
57+
return sum(peak, dims=1:prod_dims) / npeak # maximum(meas) - offset
58+
end
59+
60+
function gauss_start(meas::AbstractArray{T, N}, rel_thresh=0.2, prod_dims=N; has_covariance=false) where {T, N}
61+
sz = size(meas)[1:prod_dims]
62+
bg, sinner = get_bg_sum(meas, prod_dims)
63+
# just a fletch factor 1.25 for typical spotsize
64+
i0 = get_intensity(meas, prod_dims) * 1.25
65+
meas = meas .- bg .- i0 .* rel_thresh
66+
mymask = meas .> 0
67+
68+
t_ctr = get_com(meas, mymask, prod_dims)
69+
σ = get_std(meas, mymask, t_ctr, prod_dims) .* 1.22 ./ (1-rel_thresh)
70+
μ = t_ctr # .- (sz.÷2 .+1)
71+
# @show μ
72+
# @show σ
73+
# sumpix = sum(meas .* mymask)
74+
# tosum(apos, dat) = apos .* dat
75+
# pos = idx(sz)
76+
# μ = tuple_sum(tosum.(pos, meas.*mymask)) ./ sumpix
77+
# mysqr(apos, dat) = abs2.(apos) .* dat
78+
# pos = idx(size(meas), offset=size(pos).÷2 .+1 .+ μ)
79+
# σ = 1.0 .* max.(1.0, sqrt.(tuple_sum(mysqr.(pos, meas.*mymask )) ./ sumpix))
80+
# if has_covariance
81+
# σ = [σ..., zeros(((length(μ))*(length(μ)-1))÷2)...]
82+
# @show σ
83+
# end
84+
maxint = reshape(0.178 .* sinner[:] ./ prod(σ, dims=1)[:], (1, prod(size(sinner))))
85+
bg = reshape(bg[:], size(maxint))
86+
start_params = (bg=bg, intensity = maxint, off=μ, args=σ)
87+
# @show start_params
88+
return start_params # Fixed(), Positive
89+
end

0 commit comments

Comments
 (0)