# HG changeset patch # User Tuomo Valkonen # Date 1714072838 18000 # Node ID b180a4f3b9bd3529cf9c17cbf534198ca4e68492 # Parent bba159cf1636e0db11c9165cd0c2ae340c3bdefd# Parent 75116ad1d2e640452f1d88f1c2ba44194801522d merge diff -r 75116ad1d2e6 -r b180a4f3b9bd Manifest.toml --- a/Manifest.toml Thu Apr 25 19:16:17 2024 +0300 +++ b/Manifest.toml Thu Apr 25 14:20:38 2024 -0500 @@ -25,7 +25,7 @@ deps = ["DelimitedFiles", "Printf"] path = "../AlgTools" uuid = "c46e2e78-5339-41fd-a966-983ff60ab8e7" -version = "0.1.0" +version = "0.1.1" [[ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" @@ -40,12 +40,6 @@ [[Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -[[AssetRegistry]] -deps = ["Distributed", "JSON", "Pidfile", "SHA", "Test"] -git-tree-sha1 = "b25e88db7944f98789130d7b503276bc34bc098e" -uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893" -version = "0.1.0" - [[AxisAlgorithms]] deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"] git-tree-sha1 = "66771c8d21c8ff5e3a93379480a2307ac36863f7" @@ -66,12 +60,6 @@ uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" version = "0.1.8" -[[Blink]] -deps = ["Base64", "Distributed", "HTTP", "JSExpr", "JSON", "Lazy", "Logging", "MacroTools", "Mustache", "Mux", "Pkg", "Reexport", "Sockets", "WebIO"] -git-tree-sha1 = "bc93511973d1f949d45b0ea17878e6cb0ad484a1" -uuid = "ad839575-38b3-5650-b840-f874b8c74a25" -version = "0.12.9" - [[BufferedStreams]] git-tree-sha1 = "4ae47f9a4b1dc19897d3743ff13685925c5202ec" uuid = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" @@ -287,12 +275,6 @@ uuid = "2e619515-83b5-522b-bb60-26c02a35a201" version = "2.5.0+0" -[[EzXML]] -deps = ["Printf", "XML2_jll"] -git-tree-sha1 = "380053d61bb9064d6aa4a9777413b40429c79901" -uuid = "8f5d6c58-4d21-5cfd-889c-e3ad7ee6a615" -version = "1.2.0" - [[FFMPEG_jll]] deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] git-tree-sha1 = "ab3f7e1819dba9434a3a5126510c8fda3a4e7000" @@ -356,12 +338,6 @@ uuid = "559328eb-81f9-559d-9380-de523a88c83c" version = "1.0.10+0" -[[FunctionalCollections]] -deps = ["Test"] -git-tree-sha1 = "04cb9cfaa6ba5311973994fe3496ddec19b6292a" -uuid = "de31a74c-ac4f-5751-b3fd-e18cd04993ca" -version = "0.5.0" - [[Future]] deps = ["Random"] uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" @@ -450,12 +426,6 @@ uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" version = "2.8.1+1" -[[Hiccup]] -deps = ["MacroTools", "Test"] -git-tree-sha1 = "6187bb2d5fcbb2007c39e7ac53308b0d371124bd" -uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7" -version = "0.2.2" - [[Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "ca0f6bf568b4bfc807e7537f081c81e35ceca114" @@ -547,10 +517,10 @@ version = "0.3.8" [[ImageTools]] -deps = ["AlgTools", "ColorTypes", "FileIO", "GR", "OffsetArrays", "Printf", "Setfield"] +deps = ["AlgTools", "ColorTypes", "ColorVectorSpace", "FileIO", "GR", "OffsetArrays", "Printf", "Setfield", "TestImages"] path = "../ImageTools" uuid = "b548cc0d-4ade-417e-bf62-0e39f9d2eee9" -version = "0.1.0" +version = "0.1.1" [[ImageTransformations]] deps = ["AxisAlgorithms", "ColorVectorSpace", "CoordinateTransformations", "ImageBase", "ImageCore", "Interpolations", "OffsetArrays", "Rotations", "StaticArrays"] @@ -655,12 +625,6 @@ uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" version = "1.5.0" -[[JSExpr]] -deps = ["JSON", "MacroTools", "Observables", "WebIO"] -git-tree-sha1 = "b413a73785b98474d8af24fd4c8a975e31df3658" -uuid = "97c1335a-c9c5-57fe-bc5d-ec35cebe8660" -version = "0.5.4" - [[JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" @@ -679,12 +643,6 @@ uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" version = "3.0.2+0" -[[Kaleido_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "43032da5832754f58d14a91ffbe86d5f176acda9" -uuid = "f7e6163d-2fa5-5f23-b69c-1db539e41963" -version = "0.2.1+0" - [[LAME_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c" @@ -714,12 +672,6 @@ uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" version = "1.3.1" -[[Lazy]] -deps = ["MacroTools"] -git-tree-sha1 = "1370f8202dac30758f3c345f9909b97f53d87d3f" -uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" -version = "0.15.1" - [[LazyArtifacts]] deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" @@ -732,12 +684,12 @@ [[LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.4" +version = "0.6.3" [[LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.4.0+0" +version = "7.84.0+0" [[LibGit2]] deps = ["Base64", "NetworkOptions", "Printf", "SHA"] @@ -746,7 +698,7 @@ [[LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.11.0+1" +version = "1.10.2+0" [[Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" @@ -921,18 +873,6 @@ uuid = "14a3606d-f60d-562e-9121-12d972cd8159" version = "2022.10.11" -[[Mustache]] -deps = ["Printf", "Tables"] -git-tree-sha1 = "a7cefa21a2ff993bff0456bf7521f46fc077ddf1" -uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" -version = "1.0.19" - -[[Mux]] -deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "MbedTLS", "Pkg", "Sockets"] -git-tree-sha1 = "7295d849103ac4fcbe3b2e439f229c5cc77b9b69" -uuid = "a975b10e-0019-58db-a62f-e48ff68538c9" -version = "1.0.2" - [[NaNMath]] deps = ["OpenLibm_jll"] git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" @@ -955,11 +895,6 @@ uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" -[[Observables]] -git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" -uuid = "510215fc-4207-5dde-b226-833fc4488ee2" -version = "0.5.5" - [[OffsetArrays]] git-tree-sha1 = "e64b4f5ea6b7389f6f046d13d4896a8f9c1ba71e" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" @@ -1073,12 +1008,6 @@ uuid = "54e51dfa-9dd7-5231-aa84-a4037b83483a" version = "0.3.6" -[[Pidfile]] -deps = ["FileWatching", "Test"] -git-tree-sha1 = "2d8aaf8ee10df53d0dfb9b8ee44ae7c04ced2b03" -uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307" -version = "1.3.0" - [[Pixman_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] git-tree-sha1 = "64779bc4c9784fee475689a1752ef4d5747c5e87" @@ -1096,36 +1025,6 @@ uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688" version = "0.3.3" -[[PlotlyBase]] -deps = ["ColorSchemes", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"] -git-tree-sha1 = "56baf69781fc5e61607c3e46227ab17f7040ffa2" -uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5" -version = "0.8.19" - -[[PlotlyJS]] -deps = ["Base64", "Blink", "DelimitedFiles", "JSExpr", "JSON", "Kaleido_jll", "Markdown", "Pkg", "PlotlyBase", "PlotlyKaleido", "REPL", "Reexport", "Requires", "WebIO"] -git-tree-sha1 = "e62d886d33b81c371c9d4e2f70663c0637f19459" -uuid = "f0f68f2c-4968-5e81-91da-67840de0976a" -version = "0.18.13" - - [PlotlyJS.extensions] - CSVExt = "CSV" - DataFramesExt = ["DataFrames", "CSV"] - IJuliaExt = "IJulia" - JSON3Ext = "JSON3" - - [PlotlyJS.weakdeps] - CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" - JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" - -[[PlotlyKaleido]] -deps = ["Base64", "JSON", "Kaleido_jll"] -git-tree-sha1 = "2650cd8fb83f73394996d507b3411a7316f6f184" -uuid = "f2990250-8cf9-495f-b13a-cce12b45703c" -version = "2.2.4" - [[PoissonRandom]] deps = ["Random"] git-tree-sha1 = "a0f1159c33f846aa77c3f30ebbc69795e5327152" @@ -1496,24 +1395,6 @@ uuid = "ea10d353-3f73-51f8-a26c-33c1cb351aa5" version = "1.4.2" -[[WebIO]] -deps = ["AssetRegistry", "Base64", "Distributed", "FunctionalCollections", "JSON", "Logging", "Observables", "Pkg", "Random", "Requires", "Sockets", "UUIDs", "WebSockets", "Widgets"] -git-tree-sha1 = "0eef0765186f7452e52236fa42ca8c9b3c11c6e3" -uuid = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" -version = "0.8.21" - -[[WebSockets]] -deps = ["Base64", "Dates", "HTTP", "Logging", "Sockets"] -git-tree-sha1 = "4162e95e05e79922e44b9952ccbc262832e4ad07" -uuid = "104b5d7c-a370-577a-8038-80a2059c5097" -version = "1.6.0" - -[[Widgets]] -deps = ["Colors", "Dates", "Observables", "OrderedCollections"] -git-tree-sha1 = "fcdae142c1cfc7d89de2d11e08721d0f2f86c98a" -uuid = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62" -version = "0.6.6" - [[WoodburyMatrices]] deps = ["LinearAlgebra", "SparseArrays"] git-tree-sha1 = "5f24e158cf4cee437052371455fe361f526da062" @@ -1525,12 +1406,6 @@ uuid = "76eceee3-57b5-4d4a-8e66-0e911cebbf60" version = "1.6.1" -[[XLSX]] -deps = ["Artifacts", "Dates", "EzXML", "Printf", "Tables", "ZipFile"] -git-tree-sha1 = "319b05e790046f18f12b8eae542546518ef1a88f" -uuid = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0" -version = "0.10.1" - [[XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] git-tree-sha1 = "532e22cf7be8462035d092ff21fada7527e2c488" @@ -1693,12 +1568,6 @@ uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" version = "1.5.0+0" -[[ZipFile]] -deps = ["Libdl", "Printf", "Zlib_jll"] -git-tree-sha1 = "f492b7fe1698e623024e873244f10d89c95c340a" -uuid = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea" -version = "0.10.1" - [[Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" @@ -1790,7 +1659,7 @@ [[nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.52.0+1" +version = "1.48.0+0" [[oneTBB_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] diff -r 75116ad1d2e6 -r b180a4f3b9bd Project.toml --- a/Project.toml Thu Apr 25 19:16:17 2024 +0300 +++ b/Project.toml Thu Apr 25 14:20:38 2024 -0500 @@ -22,7 +22,6 @@ MAT = "23992714-dd62-5051-b70f-ba57cb901cac" OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" PerceptualColourMaps = "54e51dfa-9dd7-5231-aa84-a4037b83483a" -PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a" PoissonRandom = "e409e4f3-bfea-5376-8464-e040bb5c01ab" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" QuartzImageIO = "dca85d43-d64c-5e67-8c65-017450d5d020" @@ -32,4 +31,3 @@ StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990" -XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0" diff -r 75116ad1d2e6 -r b180a4f3b9bd src/Algorithm.jl --- a/src/Algorithm.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,148 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module Algorithm - -identifier = "pdps_known" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient - -using ..OpticalFlow: ImageSize, - Image, - pdflow! - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - - return x, y, Δx, Δy, x̄ -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -############ -# Algorithm -############ - -function step_lengths(params, γ, R_K²) - ρ̃₀, τ₀, σ₀, σ̃₀ = params.ρ̃₀, params.τ₀, params.σ₀, params.σ̃₀ - δ = params.δ - ρ = isdefined(params, :phantom_ρ) ? params.phantom_ρ : params.ρ - Λ = params.Λ - Θ = params.dual_flow ? Λ : 1 - - τ = τ₀/γ - @assert(1+γ*τ ≥ Λ) - σ = σ₀*min(1/(τ*R_K²), 1/max(0, τ*R_K²/((1+γ*τ-Λ)*(1-δ))-ρ)) - q = δ*(1+σ*ρ)/Θ - if 1 ≥ q - σ̃ = σ̃₀*σ/q - #ρ̃ = ρ̃₀*max(0, ((Θ*σ)/(2*δ*σ̃^2*(1+σ*ρ))+1/(2σ)-1/σ̃)) - ρ̃ = max(0, (1-q)/(2*σ)) - else - σ̃ = σ̃₀*σ/(q*(1-√(1-1/q))) - ρ̃ = 0 - end - - println("Step length parameters: τ=$(τ), σ=$(σ), σ̃=$(σ̃), ρ̃=$(ρ̃)") - - return τ, σ, σ̃, ρ̃ -end - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1 - τ, σ, σ̃, ρ̃ = step_lengths(params, γ, R_K²) - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄ = init_iterates(dim) - init_data = (params.init == :data) - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, - v_known :: DisplacementT, - 🚫unused_b_next :: Image - - ################## - # Prediction step - ################## - if init_data - x .= b - init_data = false - end - - pdflow!(x, Δx, y, Δy, v_known, params.dual_flow) - - if params.prox_predict - ∇₂!(Δy, x) - @. y = (y + σ̃*Δy)/(1 + σ̃*(ρ̃+ρ/α)) - proj_norm₂₁ball!(y, α) - end - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - @. x = (x-τ*(Δx-b))/(1+τ) # | prox - @. x̄ = 2x - x̄ # over-relax - ∇₂!(Δy, x̄) # dual step: y - @. y = (y + σ*Δy)/(1 + σ*ρ/α) # | - proj_norm₂₁ball!(y, α) # | prox - - ################################ - # Give function value if needed - ################################ - v = verbose() do - ∇₂!(Δy, x) - value = norm₂²(b-x)/2 + params.α*γnorm₂₁(Δy, params.ρ) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmBoth.jl --- a/src/AlgorithmBoth.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,239 +0,0 @@ -###################################################################### -# Predictive online PDPS for optical flow with unknown velocity field -###################################################################### - -__precompile__() - -module AlgorithmBoth - -identifier = "pdps_unknown_basic" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient - -using ..OpticalFlow: ImageSize, - Image, - Gradient, - DisplacementConstant, - DisplacementFull, - pdflow!, - pointwise_gradiprod_2d!, - pointwise_gradiprod_2dᵀ!, - filter_hs - -using ..Algorithm: step_lengths - -############# -# Data types -############# - -struct Primal{DisplacementT} - x :: Image - u :: DisplacementT -end - -function Base.similar(x::Primal{DisplacementT}) where DisplacementT - return Primal{DisplacementT}(Base.similar(x.x), Base.similar(x.u)) -end - -function Base.copy(x::Primal{DisplacementT}) where DisplacementT - return Primal{DisplacementT}(Base.copy(x.x), Base.copy(x.u)) - end - -struct Dual - tv :: Gradient - flow :: Image -end - -function Base.similar(y::Dual) - return Dual(Base.similar(y.tv), Base.similar(y.flow)) -end - -function Base.copy(y::Dual) - return Dual(Base.copy(y.tv), Base.copy(y.flow)) - end - -######################### -# Iterate initialisation -######################### - -function init_primal(xinit::Image, ::Type{DisplacementConstant}) - return Primal{DisplacementConstant}(xinit, zeros(2)) -end - -function init_primal(xinit::Image, ::Type{DisplacementFull}) - return Primal{DisplacementFull}(xinit, zeros(2, size(xinit)...)) -end - -function init_rest(x::Primal{DisplacementT}) where DisplacementT - imdim=size(x.x) - - y = Dual(zeros(2, imdim...), zeros(imdim)) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - - return x, y, Δx, Δy, x̄ -end - -function init_iterates( :: Type{DisplacementT}, - xinit::Primal{DisplacementT}) where DisplacementT - return init_rest(copy(xinit)) -end - -function init_iterates( :: Type{DisplacementT}, xinit::Image) where DisplacementT - return init_rest(init_primal(copy(xinit), DisplacementT)) -end - -function init_iterates( :: Type{DisplacementT}, dim::ImageSize) where DisplacementT - return init_rest(init_primal(zeros(dim...), DisplacementT)) -end - -############################################## -# Weighting for different displacements types -############################################## - -norm²weight( :: Type{DisplacementConstant}, sz ) = prod(sz) -norm²weight( :: Type{DisplacementFull}, sz ) = 1 - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄ = init_iterates(DisplacementT, dim) - init_data = (params.init == :data) - - # … for tracking cumulative movement - if DisplacementT == DisplacementConstant - ucumul = [0.0, 0.0] - else - ucumul = [NaN, NaN] - end - - ############################################# - # Extract parameters and set up step lengths - ############################################# - - α, ρ, λ, θ, T = params.α, params.ρ, params.λ, params.θ, params.timestep - R_K² = max(∇₂_norm₂₂_est², ∇₂_norm₂∞_est²*params.dynrange^2) - γ = min(1, λ*norm²weight(DisplacementT, size(x.x))) - τ, σ, σ̃, ρ̃ = step_lengths(params, γ, R_K²) - - kernel = params.kernel - - #################### - # Run the algorithm - #################### - - b_next_filt=nothing - - v = iterate(params) do verbose :: Function, - b :: Image, - 🚫unused_v_known :: DisplacementT, - b_next :: Image - - #################################### - # Smooth data for Horn–Schunck term - #################################### - - b_filt, b_next_filt = filter_hs(b, b_next, b_next_filt, kernel) - - ############################ - # Construct K for this step - ############################ - - K! = (yʹ, xʹ) -> begin - # Optical flow part: ⟨⟨u, ∇b_k⟩⟩. - # Use y.tv as temporary gradient storage. - pointwise_gradiprod_2d!(yʹ.flow, yʹ.tv, xʹ.u, b_filt) - #@. yʹ.flow = -yʹ.flow - # TV part - ∇₂!(yʹ.tv, xʹ.x) - end - Kᵀ! = (xʹ, yʹ) -> begin - # Optical flow part: ∇b_k ⋅ y - pointwise_gradiprod_2dᵀ!(xʹ.u, yʹ.flow, b_filt) - #@. xʹ.u = -xʹ.u - # TV part - ∇₂ᵀ!(xʹ.x, yʹ.tv) - end - - ################## - # Prediction step - ################## - - if init_data - x .= b - init_data = false - end - - pdflow!(x.x, Δx.x, y.tv, Δy.tv, y.flow, Δy.flow, x.u, params.dual_flow) - - # Predict zero displacement - x.u .= 0 - if params.prox_predict - K!(Δy, x) - @. y.tv = (y.tv + σ̃*Δy.tv)/(1 + σ̃*(ρ̃+ρ/α)) - proj_norm₂₁ball!(y.tv, α) - @. y.flow = (y.flow+σ̃*((b_next_filt-b_filt)/T+Δy.flow))/(1+σ̃*(ρ̃+1/θ)) - end - - ############ - # PDPS step - # - # NOTE: For DisplacementConstant, the x.u update is supposed to be with - # respect to the 𝟙^*𝟙 norm/inner product that makes the norm equivalent - # to full-space norm when restricted to constant displacements. Since - # `OpticalFlow.pointwise_gradiprod_2dᵀ!` already uses this inner product, - # and the λ-weighted term in the problem is with respect to this norm, - # all the norm weights disappear in this update. - ############ - - Kᵀ!(Δx, y) # primal step: - @. x̄.x = x.x # | save old x for over-relax - @. x̄.u = x.u # | - @. x.x = (x.x-τ*(Δx.x-b))/(1+τ) # | prox - @. x.u = (x.u-τ*Δx.u)/(1+τ*λ) # | - @. x̄.x = 2x.x - x̄.x # over-relax - @. x̄.u = 2x.u - x̄.u # | - K!(Δy, x̄) # dual step: y - @. y.tv = (y.tv + σ*Δy.tv)/(1 + σ*ρ/α) # | - proj_norm₂₁ball!(y.tv, α) # | prox - @. y.flow = (y.flow+σ*((b_next_filt-b_filt)/T+Δy.flow))/(1+σ/θ) - - if DisplacementT == DisplacementConstant - ucumul .+= x.u - end - - ######################################################## - # Give function value and cumulative movement if needed - ######################################################## - v = verbose() do - K!(Δy, x) - value = (norm₂²(b-x.x)/2 + θ*norm₂²((b_next_filt-b_filt)./T+Δy.flow) - + λ*norm₂²(x.u)/2 + α*γnorm₂₁(Δy.tv, ρ)) - - value, x.x, ucumul, nothing - end - - return v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmBothCumul.jl --- a/src/AlgorithmBothCumul.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,139 +0,0 @@ -###################################################################### -# Predictive online PDPS for optical flow with unknown velocity field -###################################################################### - -__precompile__() - -module AlgorithmBothCumul - -identifier = "pdps_unknown_cumul" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient -using ImageTools.ImFilter - -using ..OpticalFlow: Image, - ImageSize, - DisplacementConstant, - pdflow!, - horn_schunck_reg_prox!, - pointwise_gradiprod_2d! - -using ..AlgorithmBothGreedyV: init_iterates -using ..Algorithm: step_lengths - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄, u = init_iterates(DisplacementT, dim) - init_data = (params.init == :data) - - # … for tracking cumulative movement - if DisplacementT == DisplacementConstant - ucumul = zeros(size(u)...) - end - - ############################################# - # Extract parameters and set up step lengths - ############################################# - - α, ρ, λ, θ, T = params.α, params.ρ, params.λ, params.θ, params.timestep - R_K² = ∇₂_norm₂₂_est² - γ = 1 - τ, σ, σ̃, ρ̃ = step_lengths(params, γ, R_K²) - - kernel = params.kernel - - #################### - # Run the algorithm - #################### - - b₀=nothing - b₀_filt=nothing - u_prev=zeros(size(u)) - - v = iterate(params) do verbose :: Function, - b :: Image, - 🚫unused_v_known :: DisplacementT, - 🚫unused_b_next :: Image - - ######################################################### - # Smoothen data for Horn–Schunck term; zero initial data - ######################################################### - - b_filt = (kernel==nothing ? b : simple_imfilter(b, kernel)) - - if b₀ == nothing - b₀ = b - b₀_filt = b_filt - end - - ################################################ - # Prediction step - # We leave u as-is in this cumulative version - ################################################ - - if init_data - x .= b - init_data = false - end - - pdflow!(x, Δx, y, Δy, u-u_prev, params.dual_flow) - - if params.prox_predict - ∇₂!(Δy, x) - @. y = (y + σ̃*Δy)/(1 + σ̃*(ρ̃+ρ/α)) - proj_norm₂₁ball!(y, α) - end - - # Store current cumulative displacement before updating in next step. - u_prev .= u - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - @. x = (x-τ*(Δx-b))/(1+τ) # | prox - horn_schunck_reg_prox!(u, b_filt, b₀_filt, τ, θ, λ, T) - @. x̄ = 2x - x̄ # over-relax - ∇₂!(Δy, x̄) # dual step: y - @. y = (y + σ*Δy)/(1 + σ*ρ/α) # | - proj_norm₂₁ball!(y, α) # | prox - - ######################################################## - # Give function value and cumulative movement if needed - ######################################################## - v = verbose() do - ∇₂!(Δy, x) - tmp = zeros(size(b_filt)) - pointwise_gradiprod_2d!(tmp, Δy, u, b₀_filt) - value = (norm₂²(b-x)/2 + θ*norm₂²((b_filt-b₀_filt)./T+tmp) - + λ*norm₂²(u)/2 + α*γnorm₂₁(Δy, ρ)) - - value, x, u, nothing - end - - return v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmBothGreedyV.jl --- a/src/AlgorithmBothGreedyV.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +0,0 @@ -###################################################################### -# Predictive online PDPS for optical flow with unknown velocity field -###################################################################### - -__precompile__() - -module AlgorithmBothGreedyV - -identifier = "pdps_unknown_greedyv" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient - -using ..OpticalFlow: Image, - ImageSize, - DisplacementConstant, - DisplacementFull, - pdflow!, - horn_schunck_reg_prox!, - pointwise_gradiprod_2d!, - filter_hs - -using ..Algorithm: step_lengths - -######################### -# Iterate initialisation -######################### - -function init_displ(xinit::Image, ::Type{DisplacementConstant}) - return xinit, zeros(2) -end - -function init_displ(xinit::Image, ::Type{DisplacementFull}) - return xinit, zeros(2, size(xinit)...) -end - -function init_rest(x::Image, u::DisplacementT) where DisplacementT - imdim=size(x) - - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - - return x, y, Δx, Δy, x̄, u -end - -function init_iterates( :: Type{DisplacementT}, xinit::Image) where DisplacementT - return init_rest(init_displ(copy(xinit), DisplacementT)...) -end - -function init_iterates( :: Type{DisplacementT}, dim::ImageSize) where DisplacementT - return init_rest(init_displ(zeros(dim...), DisplacementT)...) -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄, u = init_iterates(DisplacementT, dim) - init_data = (params.init == :data) - - # … for tracking cumulative movement - if DisplacementT == DisplacementConstant - ucumul = [0.0, 0.0] - else - ucumul = [NaN, NaN] - end - - ############################################# - # Extract parameters and set up step lengths - ############################################# - - α, ρ, λ, θ, T = params.α, params.ρ, params.λ, params.θ, params.timestep - R_K² = ∇₂_norm₂₂_est² - γ = 1 - τ, σ, σ̃, ρ̃ = step_lengths(params, γ, R_K²) - - kernel = params.kernel - - #################### - # Run the algorithm - #################### - - b_next_filt=nothing - - v = iterate(params) do verbose :: Function, - b :: Image, - 🚫unused_v_known :: DisplacementT, - b_next :: Image - - #################################### - # Smooth data for Horn–Schunck term - #################################### - - b_filt, b_next_filt = filter_hs(b, b_next, b_next_filt, kernel) - - ################## - # Prediction step - ################## - - if init_data - x .= b - init_data = false - end - - pdflow!(x, Δx, y, Δy, u, params.dual_flow) - - # Predict zero displacement - u .= 0 - if params.prox_predict - ∇₂!(y, x) - @. y = (y + σ̃*Δy)/(1 + σ̃*(ρ̃+ρ/α)) - proj_norm₂₁ball!(y, α) - end - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - @. x = (x-τ*(Δx-b))/(1+τ) # | prox - horn_schunck_reg_prox!(u, b_next_filt, b_filt, θ, λ, T, τ) - @. x̄ = 2x - x̄ # over-relax - ∇₂!(y, x̄) # dual step: y - @. y = (y + σ*Δy)/(1 + σ*ρ/α) # | - proj_norm₂₁ball!(y, α) # | prox - - if DisplacementT == DisplacementConstant - ucumul .+= u - end - - ######################################################## - # Give function value and cumulative movement if needed - ######################################################## - v = verbose() do - ∇₂!(Δy, x) - tmp = zeros(size(b_filt)) - pointwise_gradiprod_2d!(tmp, Δy, u, b_filt) - value = (norm₂²(b-x)/2 + θ*norm₂²((b_next_filt-b_filt)./T+tmp) - + λ*norm₂²(u)/2 + α*γnorm₂₁(Δy, ρ)) - - value, x, ucumul, nothing - end - - return v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmBothMulti.jl --- a/src/AlgorithmBothMulti.jl Thu Apr 25 19:16:17 2024 +0300 +++ b/src/AlgorithmBothMulti.jl Thu Apr 25 14:20:38 2024 -0500 @@ -26,7 +26,7 @@ ConstantDisplacementHornSchunckData, filter_hs -using ..Algorithm: step_lengths +using ..AlgorithmProximal: step_lengths ######################### # Iterate initialisation diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmBothMulti.jl.orig --- a/src/AlgorithmBothMulti.jl.orig Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,277 +0,0 @@ -###################################################################### -# Predictive online PDPS for optical flow with unknown velocity field -###################################################################### - -__precompile__() - -module AlgorithmBothMulti - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient -using ImageTools.ImFilter - -using ..OpticalFlow: Image, - ImageSize, - DisplacementConstant, - pdflow!, - horn_schunck_reg_prox!, - pointwise_gradiprod_2d!, - horn_schunck_reg_prox_op!, - mldivide_step_plus_sym2x2!, - ConstantDisplacementHornSchunckData, - filter_hs - -using ..Algorithm: step_lengths - -######################### -# Iterate initialisation -######################### - -function init_displ(xinit::Image, ::Type{DisplacementConstant}, n::Integer) - return xinit, zeros(n, 2) -end - -# function init_displ(xinit::Image, ::Type{DisplacementFull}, n::Integer) -# return xinit, zeros(n, 2, size(xinit)...) -# end - -function init_rest(x::Image, u::DisplacementT) where DisplacementT - imdim=size(x) - - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - - return x, y, Δx, Δy, x̄, u -end - -function init_iterates( :: Type{DisplacementT}, - xinit::Image, - n::Integer) where DisplacementT - return init_rest(init_displ(copy(xinit), DisplacementT, n)...) -end - -function init_iterates( :: Type{DisplacementT}, - dim::ImageSize, - n::Integer) where DisplacementT - return init_rest(init_displ(zeros(dim...), DisplacementT, n)...) -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ###################### - # Initialise iterates - ###################### - - n = params.displacement_count - k = 0 # number of displacements we have already - - x, y, Δx, Δy, x̄, u = init_iterates(DisplacementT, dim, n) - init_data = (params.init == :data) - hs = [ConstantDisplacementHornSchunckData() for i=1:n] - #hs = Array{ConstantDisplacementHornSchunckData}(undef, n) - A = Array{Float64,3}(undef, n, 2, 2) - d = Array{Float64,2}(undef, n, 2) - - # … for tracking cumulative movement - ucumulbase = [0.0, 0.0] - - ############################################# - # Extract parameters and set up step lengths - ############################################# - - α, ρ, λ, θ = params.α, params.ρ, params.λ, params.θ - R_K² = ∇₂_norm₂₂_est² - γ = 1 - τ, σ, σ̃, ρ̃ = step_lengths(params, γ, R_K²) - - kernel = params.kernel - T = params.timestep - - #################### - # Run the algorithm - #################### - - b_next_filt = nothing - diffu = similar(u[1, :]) - - v = iterate(params) do verbose :: Function, - b :: Image, - 🚫unused_v_known :: DisplacementT, - b_next :: Image - - #################################### - # Smooth data for Horn–Schunck term - #################################### - - b_filt, b_next_filt = filter_hs(b, b_next, b_next_filt, kernel) - - ################################################ - # Prediction step - ################################################ - - # Predict x and y - if k==0 - if init_data - x .= b - init_data = false - end - else - # Displacement from previous to this image is estimated as - # the difference of their displacements from beginning of window. - if k>1 - @. @views diffu = u[k, :] - u[k-1, :] - else - @. @views diffu = u[k, :] - end - - pdflow!(x, Δx, y, Δy, diffu, params.dual_flow) - end - - # Shift stored prox matrices - if k==n - tmp = copy(u[1, :]) - ucumulbase .+= tmp - for j=1:(n-1) - @. @views u[j, :] = u[j+1, :] - tmp - hs[j] = hs[j+1] - end - # Create new struct as original contains references to objects that - # have been moved to index n-1. - hs[n]=ConstantDisplacementHornSchunckData() - else - k += 1 - end - - # Predict u: zero displacement from current to next image, i.e., - # same displacement to beginning of window. - if k==1 - @. @views u[k, :] = 0.0 - else - @. @views u[k, :] = u[k-1, :] - end - - # Predictor proximals tep - if params.prox_predict - ∇₂!(Δy, x) - @. y = (y + σ̃*Δy)/(1 + σ̃*(ρ̃+ρ/α)) - proj_norm₂₁ball!(y, α) - end - - ################################################################################# - # PDPS step - # - # For the displacements, with τ̃=τ/k, we need to solve for 2≤j begin - # Optical flow part - @. yʹ.flow = b_filt - flow!(yʹ.flow, Δx.x, xʹ.u) - @. yʹ.flow = yʹ.flow - b_next_filt - # TV part - ∇₂!(yʹ.tv, xʹ.x) - end - Kᵀ! = (xʹ, yʹ) -> begin - # Optical flow part: ∇b_k ⋅ y - # - # TODO: This really should depend x.u, but x.u is zero. - # - pointwise_gradiprod_2dᵀ!(xʹ.u, yʹ.flow, b_filt) - # TV part - ∇₂ᵀ!(xʹ.x, yʹ.tv) - end - - ################## - # Prediction step - ################## - - if init_data - x .= b - init_data = false - end - - pdflow!(x.x, Δx.x, y.tv, Δy.tv, y.flow, Δy.flow, x.u, params.dual_flow) - - # Predict zero displacement - x.u .= 0 - if params.prox_predict - K!(Δy, x) - @. y.tv = (y.tv + σ̃*Δy.tv)/(1 + σ̃*(ρ̃+ρ/α)) - proj_norm₂₁ball!(y.tv, α) - @. y.flow = (y.flow+σ̃*Δy.flow)/(1+σ̃*(ρ̃+1/θ)) - end - - ############ - # PDPS step - # - # NOTE: For DisplacementConstant, the x.u update is supposed to be with - # respect to the 𝟙^*𝟙 norm/inner product that makes the norm equivalent - # to full-space norm when restricted to constant displacements. Since - # `OpticalFlow.pointwise_gradiprod_2dᵀ!` already uses this inner product, - # and the λ-weighted term in the problem is with respect to this norm, - # all the norm weights disappear in this update. - ############ - - Kᵀ!(Δx, y) # primal step: - @. x̄.x = x.x # | save old x for over-relax - @. x̄.u = x.u # | - @. x.x = (x.x-τ*(Δx.x-b))/(1+τ) # | prox - @. x.u = (x.u-τ*Δx.u)/(1+τ*λ) # | - @. x̄.x = 2x.x - x̄.x # over-relax - @. x̄.u = 2x.u - x̄.u # | - K!(Δy, x̄) # dual step: y - @. y.tv = (y.tv + σ*Δy.tv)/(1 + σ*ρ/α) # | - proj_norm₂₁ball!(y.tv, α) # | prox - @. y.flow = (y.flow+σ*Δy.flow)/(1+σ/θ) - - if DisplacementT == DisplacementConstant - ucumul .+= x.u - end - - ######################################################## - # Give function value and cumulative movement if needed - ######################################################## - v = verbose() do - K!(Δy, x) - value = (norm₂²(b-x.x)/2 + θ*norm₂²(Δy.flow) - + λ*norm₂²(x.u)/2 + α*γnorm₂₁(Δy.tv, ρ)) - - value, x.x, ucumul, nothing - end - - return v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmDualScaling.jl --- a/src/AlgorithmDualScaling.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,125 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmDualScaling - -identifier = "pdps_known_dualscaling" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient - -using ..OpticalFlow: ImageSize, - Image, - pdflow!, - DualScaling - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - - return x, y, Δx, Δy, x̄ -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1.0 - Λ = params.Λ - τ₀, σ₀ = params.τ₀, params.σ₀ - - τ = τ₀/γ - @assert(1+γ*τ ≥ Λ) - σ = σ₀*1/(τ*R_K²) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄ = init_iterates(dim) - init_data = (params.init == :data) - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, - v_known :: DisplacementT, - 🚫unused_b_next :: Image - - ################## - # Prediction step - ################## - if init_data - x .= b - init_data = false - end - - pdflow!(x, Δx, y, Δy, v_known, DualScaling()) - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - @. x = (x-τ*(Δx-b))/(1+τ) # | prox - @. x̄ = 2x - x̄ # over-relax - ∇₂!(Δy, x̄) # dual step: y - @. y = (y + σ*Δy)/(1 + σ*ρ/α) # | - proj_norm₂₁ball!(y, α) # | prox - - ################################ - # Give function value if needed - ################################ - v = verbose() do - ∇₂!(Δy, x) - value = norm₂²(b-x)/2 + params.α*γnorm₂₁(Δy, params.ρ) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmGreedy.jl --- a/src/AlgorithmGreedy.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,125 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmGreedy - -identifier = "pdps_known_greedy" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient - -using ..OpticalFlow: ImageSize, - Image, - pdflow!, - Greedy - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - - return x, y, Δx, Δy, x̄ -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1.0 - Λ = params.Λ - τ₀, σ₀ = params.τ₀, params.σ₀ - - τ = τ₀/γ - @assert(1+γ*τ ≥ Λ) - σ = σ₀*1/(τ*R_K²) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄ = init_iterates(dim) - init_data = (params.init == :data) - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, - v_known :: DisplacementT, - 🚫unused_b_next :: Image - - ################## - # Prediction step - ################## - if init_data - x .= b - init_data = false - end - - pdflow!(x, Δx, y, Δy, v_known, Greedy()) - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - @. x = (x-τ*(Δx-b))/(1+τ) # | prox - @. x̄ = 2x - x̄ # over-relax - ∇₂!(Δy, x̄) # dual step: y - @. y = (y + σ*Δy)/(1 + σ*ρ/α) # | - proj_norm₂₁ball!(y, α) # | prox - - ################################ - # Give function value if needed - ################################ - v = verbose() do - ∇₂!(Δy, x) - value = norm₂²(b-x)/2 + params.α*γnorm₂₁(Δy, params.ρ) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmNew.jl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/AlgorithmNew.jl Thu Apr 25 14:20:38 2024 -0500 @@ -0,0 +1,127 @@ +#################################################################### +# Predictive online PDPS for optical flow with known velocity field +#################################################################### + +__precompile__() + +module AlgorithmNew + +# Default identifier for when none is given by predictor +identifier = "pdps_known_noprediction" + +using Printf + +using AlgTools.Util +import AlgTools.Iterate +using ImageTools.Gradient + +using ..OpticalFlow: ImageSize, + Image, + pdflow! + +######################### +# Iterate initialisation +######################### + +function init_rest(x::Image) + imdim=size(x) + + y = zeros(2, imdim...) + Δx = copy(x) + Δy = copy(y) + x̄ = copy(x) + + return x, y, Δx, Δy, x̄ +end + +function init_iterates(xinit::Image) + return init_rest(copy(xinit)) +end + +function init_iterates(dim::ImageSize) + return init_rest(zeros(dim...)) +end + +############ +# Algorithm +############ + +function solve( :: Type{DisplacementT}; + dim :: ImageSize, + iterate = AlgTools.simple_iterate, + params::NamedTuple) where DisplacementT + + ################################ + # Extract and set up parameters + ################################ + + α, ρ = params.α, params.ρ + R_K² = ∇₂_norm₂₂_est² + γ = 1.0 + Λ = params.Λ + τ₀, σ₀ = params.τ₀, params.σ₀ + + τ = τ₀/γ + @assert(1+γ*τ ≥ Λ) + σ = σ₀*1/(τ*R_K²) + + println("Step length parameters: τ=$(τ), σ=$(σ)") + + ###################### + # Initialise iterates + ###################### + + x, y, Δx, Δy, x̄ = init_iterates(dim) + init_data = (params.init == :data) + + #################### + # Run the algorithm + #################### + + v = iterate(params) do verbose :: Function, + b :: Image, + v_known :: DisplacementT, + 🚫unused_b_next :: Image + + ################## + # Prediction step + ################## + if init_data + x .= b + init_data = false + end + + if haskey(params, :predictor) && ~isnothing(params.predictor) + pdflow!(x, Δx, y, Δy, v_known, params.predictor) + end + + ############ + # PDPS step + ############ + + ∇₂ᵀ!(Δx, y) # primal step: + @. x̄ = x # | save old x for over-relax + @. x = (x-τ*(Δx-b))/(1+τ) # | prox + @. x̄ = 2x - x̄ # over-relax + ∇₂!(Δy, x̄) # dual step: y + @. y = (y + σ*Δy)/(1 + σ*ρ/α) # | + proj_norm₂₁ball!(y, α) # | prox + + ################################ + # Give function value if needed + ################################ + v = verbose() do + ∇₂!(Δy, x) + value = norm₂²(b-x)/2 + params.α*γnorm₂₁(Δy, params.ρ) + value, x, [NaN, NaN], nothing + end + + v + end + + return x, y, v +end + +end # Module + + diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmNoPrediction.jl --- a/src/AlgorithmNoPrediction.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,124 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmNoPrediction - -identifier = "pdps_known_noprediction" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient - -using ..OpticalFlow: ImageSize, - Image, - pdflow! - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - - return x, y, Δx, Δy, x̄ -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1.0 - Λ = params.Λ - τ₀, σ₀ = params.τ₀, params.σ₀ - - τ = τ₀/γ - @assert(1+γ*τ ≥ Λ) - σ = σ₀*1/(τ*R_K²) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄ = init_iterates(dim) - init_data = (params.init == :data) - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, - v_known :: DisplacementT, - 🚫unused_b_next :: Image - - ################## - # Prediction step - ################## - if init_data - x .= b - init_data = false - end - - # No prediction - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - @. x = (x-τ*(Δx-b))/(1+τ) # | prox - @. x̄ = 2x - x̄ # over-relax - ∇₂!(Δy, x̄) # dual step: y - @. y = (y + σ*Δy)/(1 + σ*ρ/α) # | - proj_norm₂₁ball!(y, α) # | prox - - ################################ - # Give function value if needed - ################################ - v = verbose() do - ∇₂!(Δy, x) - value = norm₂²(b-x)/2 + params.α*γnorm₂₁(Δy, params.ρ) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmPrimalOnly.jl --- a/src/AlgorithmPrimalOnly.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,124 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmPrimalOnly - -identifier = "pdps_known_primalonly" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient - -using ..OpticalFlow: ImageSize, - Image, - pdflow! - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - - return x, y, Δx, Δy, x̄ -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1.0 - Λ = params.Λ - τ₀, σ₀ = params.τ₀, params.σ₀ - - τ = τ₀/γ - @assert(1+γ*τ ≥ Λ) - σ = σ₀*1/(τ*R_K²) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄ = init_iterates(dim) - init_data = (params.init == :data) - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, - v_known :: DisplacementT, - 🚫unused_b_next :: Image - - ################## - # Prediction step - ################## - if init_data - x .= b - init_data = false - end - - pdflow!(x, Δx, y, Δy, v_known, false) - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - @. x = (x-τ*(Δx-b))/(1+τ) # | prox - @. x̄ = 2x - x̄ # over-relax - ∇₂!(Δy, x̄) # dual step: y - @. y = (y + σ*Δy)/(1 + σ*ρ/α) # | - proj_norm₂₁ball!(y, α) # | prox - - ################################ - # Give function value if needed - ################################ - v = verbose() do - ∇₂!(Δy, x) - value = norm₂²(b-x)/2 + params.α*γnorm₂₁(Δy, params.ρ) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmRotation.jl --- a/src/AlgorithmRotation.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,125 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmRotation - -identifier = "pdps_known_rotation" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient - -using ..OpticalFlow: ImageSize, - Image, - pdflow!, - Rotation - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - - return x, y, Δx, Δy, x̄ -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1.0 - Λ = params.Λ - τ₀, σ₀ = params.τ₀, params.σ₀ - - τ = τ₀/γ - @assert(1+γ*τ ≥ Λ) - σ = σ₀*1/(τ*R_K²) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄ = init_iterates(dim) - init_data = (params.init == :data) - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, - v_known :: DisplacementT, - 🚫unused_b_next :: Image - - ################## - # Prediction step - ################## - if init_data - x .= b - init_data = false - end - - pdflow!(x, Δx, y, Δy, v_known, Rotation()) - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - @. x = (x-τ*(Δx-b))/(1+τ) # | prox - @. x̄ = 2x - x̄ # over-relax - ∇₂!(Δy, x̄) # dual step: y - @. y = (y + σ*Δy)/(1 + σ*ρ/α) # | - proj_norm₂₁ball!(y, α) # | prox - - ################################ - # Give function value if needed - ################################ - v = verbose() do - ∇₂!(Δy, x) - value = norm₂²(b-x)/2 + params.α*γnorm₂₁(Δy, params.ρ) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/AlgorithmZeroDual.jl --- a/src/AlgorithmZeroDual.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,125 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmZeroDual - -identifier = "pdps_known_zerodual" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient - -using ..OpticalFlow: ImageSize, - Image, - pdflow! - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - - return x, y, Δx, Δy, x̄ -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1.0 - Λ = params.Λ - τ₀, σ₀ = params.τ₀, params.σ₀ - - τ = τ₀/γ - @assert(1+γ*τ ≥ Λ) - σ = σ₀*1/(τ*R_K²) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄ = init_iterates(dim) - init_data = (params.init == :data) - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, - v_known :: DisplacementT, - 🚫unused_b_next :: Image - - ################## - # Prediction step - ################## - if init_data - x .= b - init_data = false - end - - pdflow!(x, Δx, y, Δy, v_known, false) - y .= zeros(size(y)...) - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - @. x = (x-τ*(Δx-b))/(1+τ) # | prox - @. x̄ = 2x - x̄ # over-relax - ∇₂!(Δy, x̄) # dual step: y - @. y = (y + σ*Δy)/(1 + σ*ρ/α) # | - proj_norm₂₁ball!(y, α) # | prox - - ################################ - # Give function value if needed - ################################ - v = verbose() do - ∇₂!(Δy, x) - value = norm₂²(b-x)/2 + params.α*γnorm₂₁(Δy, params.ρ) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/ImGenerate.jl --- a/src/ImGenerate.jl Thu Apr 25 19:16:17 2024 +0300 +++ b/src/ImGenerate.jl Thu Apr 25 14:20:38 2024 -0500 @@ -100,10 +100,10 @@ θ = 2π*rand(rng,Float64) r = params.shake return [r*cos(θ), r*sin(θ)] - end + end else error("Unknown shaketype $(params.shaketype)") - end + end end pixelwise = (shakefn, sz) -> () -> make_const_u(shakefn(), sz) @@ -181,15 +181,14 @@ :: Type{DisplacementConstant}, datachannel :: Channel{OnlineData{DisplacementConstant}}, params :: NamedTuple) - # Set up counter and zero factor for stabilisation @assert(params.maxiter ≥ maximum(params.stable_interval)) indx = 1 - zero_factor = indx in params.stable_interval ? 0.0 : 1.0 + zero_factor = indx in params.stable_interval ? 0.0 : 1.0 - # Restart the seed to enable comparison across predictors - Random.seed!(rng,951508) # algreadygood + # Restart the seed to enable comparison across predictors + Random.seed!(rng, if haskey(params, :seed) params.seed else 951508 end) nextv = shake(params) v_true = zero_factor.*nextv() @@ -228,18 +227,17 @@ # PETscan ######################################################################## function generate_sinogram(im, sz, - :: Type{DisplacementConstant}, - datachannel :: Channel{PetOnlineData{DisplacementConstant}}, - params :: NamedTuple) + :: Type{DisplacementConstant}, + datachannel :: Channel{PetOnlineData{DisplacementConstant}}, + params :: NamedTuple) # Set up counter and zero factor for stabilisation @assert(params.maxiter ≥ maximum(params.stable_interval)) indx = 1 zero_factor = indx in params.stable_interval ? 0.0 : 1.0 - # Restart the seed to enable comparison across predictors - # Random.seed!(rng,314159) # for shepp - Random.seed!(rng,9182737465) # for brain + # Restart the seed to enable comparison across predictors + Random.seed!(rng, if haskey(params, :seed) params.seed else 314159 end) nextv = shake(params) v_true = zero_factor.*nextv() @@ -283,7 +281,7 @@ # Next theta theta_true = zero_factor*rotatebytheta(params) theta_cumul += theta_true - # Next sinogram mask + # Next sinogram mask S_true = generate_radonmask(params) # Update indx and zero factor indx += 1 diff -r 75116ad1d2e6 -r b180a4f3b9bd src/OpticalFlow.jl --- a/src/OpticalFlow.jl Thu Apr 25 19:16:17 2024 +0300 +++ b/src/OpticalFlow.jl Thu Apr 25 14:20:38 2024 -0500 @@ -42,7 +42,8 @@ HornSchunckData, filter_hs, petpdflow!, - DualScaling, Greedy, Rotation + DualScaling, Greedy, Rotation, ZeroDual, PrimalOnly, + identifier ############################################### # Types (several imported from ImageTools.Translate) @@ -60,9 +61,36 @@ ################################# # Struct for flow ################################# -struct DualScaling end +struct DualScaling + exponent :: Integer + threshold :: Real + DualScaling(e = 10, t = 1e-1) = new(e, t) +end + struct Greedy end struct Rotation end +struct ZeroDual end +struct PrimalOnly end + +function identifier(::DualScaling) + "pdps_known_dualscaling" +end + +function identifier(::Rotation) + "pdps_known_rotation" +end + +function identifier(::Greedy) + "pdps_known_greedy" +end + +function identifier(::ZeroDual) + "pdps_known_zerodual" +end + +function identifier(::PrimalOnly) + "pdps_known_primalonly" +end ################################# # Displacement field based flow @@ -97,7 +125,7 @@ end end -function pdflow!(x, Δx, y, Δy, z, Δz, u, dual_flow; threads=:none) +function pdflow!(x, Δx, y, Δy, z, Δz, u, dual_flow :: Bool; threads=:none) if dual_flow @backgroundif (threads==:outer) begin flow!(x, u, Δx; threads=(threads==:inner)) @@ -114,7 +142,7 @@ # Additional method for Greedy function pdflow!(x, Δx, y, Δy, u, flow :: Greedy; threads=:none) - @assert(size(u)==(2,)) + @assert(size(u)==(2,)) Δx .= x Δy .= y flow!(x, u; threads=(threads==:inner)) @@ -125,11 +153,11 @@ inds = abs.(Dxx) .≤ 1e-1 Dxx[inds] .= 1 DΔx[inds] .= 1 - y .= y.* DΔx ./ Dxx + y .= y.* DΔx ./ Dxx end # Additional method for Rotation -function pdflow!(x, Δx, y, Δy, u, flow :: Rotation; threads=:none) +function pdflow!(x, Δx, y, Δy, u, flow :: Rotation; threads=:none) @assert(size(u)==(2,)) Δx .= x flow!(x, u; threads=(threads==:inner)) @@ -164,17 +192,28 @@ flow!(x, u; threads=(threads==:inner)) C = similar(y) cc = abs.(x-oldx) - cm = max(1e-12,maximum(cc)) - c = 1 .* (1 .- cc./ cm) .^(10) + cm = max(flow.threshold,maximum(cc)) + c = 1 .* (1 .- cc./ cm) .^flow.exponent C[1,:,:] .= c C[2,:,:] .= c - y .= C.*y + y .= C.*y end +function pdflow!(x, Δx, y, Δy, u, :: ZeroDual; threads=:none) + @assert(size(u)==(2,)) + flow!(x, u; threads=(threads==:inner)) + y .= 0.0 +end + +function pdflow!(x, Δx, y, Δy, u, :: PrimalOnly; threads=:none) + @assert(size(u)==(2,)) + flow!(x, u; threads=(threads==:inner)) +end ########################## # PET ########################## + function petflow_interp!(x::AbstractImage, tmp::AbstractImage, u::DisplacementConstant, theta_known::DisplacementConstant; threads = false) tmp .= x @@ -186,7 +225,7 @@ petflow! = petflow_interp! -function petpdflow!(x, Δx, y, Δy, u, theta_known, dual_flow; threads=:none) +function petpdflow!(x, Δx, y, Δy, u, theta_known, dual_flow :: Bool; threads=:none) if dual_flow @backgroundif (threads==:outer) begin petflow!(x, Δx, u, theta_known; threads=(threads==:inner)) @@ -214,7 +253,7 @@ inds = abs.(Dxx) .≤ 1e-2 Dxx[inds] .= 1 DΔx[inds] .= 1 - y .= y.* DΔx ./ Dxx + y .= y.* DΔx ./ Dxx end # Method for dual scaling predictor @@ -243,6 +282,15 @@ end end +function petpdflow!(x, Δx, y, Δy, u, theta_known, :: ZeroDual; threads=:none) + petflow!(x, Δx, u, theta_known, threads=(threads==:inner)) + y .= 0.0 +end + +function petpdflow!(x, Δx, y, Δy, u, theta_known, :: PrimalOnly; threads=:none) + petflow!(x, Δx, u, theta_known, threads=(threads==:inner)) +end + ########################## # Linearised optical flow ########################## @@ -353,7 +401,7 @@ a, c, d = 0.0, 0.0, 0.0 # This used to use ∇₂cfold but it is faster to allocate temporary # storage for the full gradient due to probably better memory and SIMD - # instruction usage. + # instruction usage. g = zeros(2, size(b)...) ∇₂c!(g, b) @inbounds for i=1:size(b, 1) @@ -420,7 +468,7 @@ f = x -> simple_imfilter(x, kernel; threads=true) end - # We already filtered b in the previous step (b_next in that step) + # We already filtered b in the previous step (b_next in that step) b_filt = b_next_filt==nothing ? f(b) : b_next_filt b_next_filt = f(b_next) diff -r 75116ad1d2e6 -r b180a4f3b9bd src/OpticalFlow.jl.orig --- a/src/OpticalFlow.jl.orig Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,280 +0,0 @@ -################################ -# Code relevant to optical flow -################################ - -__precompile__() - -module OpticalFlow - -using AlgTools.Util -using ImageTools.Gradient -import ImageTools.Translate -using ImageTools.ImFilter - -########## -# Exports -########## - -export flow!, - pdflow!, - flow_grad!, - flow_interp!, - estimate_Λ², - estimate_linear_Λ², - pointwise_gradiprod_2d!, - pointwise_gradiprod_2dᵀ!, - horn_schunck_reg_prox!, - horn_schunck_reg_prox_op!, - mldivide_step_plus_sym2x2!, - linearised_optical_flow_error, - Image, AbstractImage, ImageSize, - Gradient, Displacement, - DisplacementFull, DisplacementConstant, - HornSchunckData, - filter_hs - -############################################### -# Types (several imported from ImageTools.Translate) -############################################### - -Image = Translate.Image -AbstractImage = AbstractArray{Float64,2} -Displacement = Translate.Displacement -DisplacementFull = Translate.DisplacementFull -DisplacementConstant = Translate.DisplacementConstant -Gradient = Array{Float64,3} -ImageSize = Tuple{Int64,Int64} - -################################# -# Displacement field based flow -################################# - -function flow_interp!(x::AbstractImage, u::Displacement, tmp::AbstractImage; - threads = false) - tmp .= x - Translate.translate_image!(x, tmp, u; threads=threads) -end - -function flow_interp!(x::AbstractImage, u::Displacement; - threads = false) - tmp = copy(x) - Translate.translate_image!(x, tmp, u; threads=threads) -end - -flow! = flow_interp! - -function pdflow!(x, Δx, y, Δy, u, dual_flow; threads=:none) - if dual_flow - #flow!((x, @view(y[1, :, :]), @view(y[2, :, :])), diffu, - # (Δx, @view(Δy[1, :, :]), @view(Δy[2, :, :]))) - @backgroundif (threads==:outer) begin - flow!(x, u, Δx; threads=(threads==:inner)) - end begin - flow!(@view(y[1, :, :]), u, @view(Δy[1, :, :]); threads=(threads==:inner)) - flow!(@view(y[2, :, :]), u, @view(Δy[2, :, :]); threads=(threads==:inner)) - end - else - flow!(x, u, Δx) - end -end - -function pdflow!(x, Δx, y, Δy, z, Δz, u, dual_flow; threads=:none) - if dual_flow - @backgroundif (threads==:outer) begin - flow!(x, u, Δx; threads=(threads==:inner)) - flow!(z, u, Δz; threads=(threads==:inner)) - end begin - flow!(@view(y[1, :, :]), u, @view(Δy[1, :, :]); threads=(threads==:inner)) - flow!(@view(y[2, :, :]), u, @view(Δy[2, :, :]); threads=(threads==:inner)) - end - else - flow!(x, u, Δx; threads=(threads==:inner)) - flow!(z, u, Δz; threads=(threads==:inner)) - end -end - -########################## -# Linearised optical flow -########################## - -# ⟨⟨u, ∇b⟩⟩ -function pointwise_gradiprod_2d!(y::Image, vtmp::Gradient, - u::DisplacementFull, b::Image; - add = false) - ∇₂c!(vtmp, b) - - u′=reshape(u, (size(u, 1), prod(size(u)[2:end]))) - vtmp′=reshape(vtmp, (size(vtmp, 1), prod(size(vtmp)[2:end]))) - y′=reshape(y, prod(size(y))) - - if add - @simd for i = 1:length(y′) - @inbounds y′[i] += dot(@view(u′[:, i]), @view(vtmp′[:, i])) - end - else - @simd for i = 1:length(y′) - @inbounds y′[i] = dot(@view(u′[:, i]), @view(vtmp′[:, i])) - end - end -end - -function pointwise_gradiprod_2d!(y::Image, vtmp::Gradient, - u::DisplacementConstant, b::Image; - add = false) - ∇₂c!(vtmp, b) - - vtmp′=reshape(vtmp, (size(vtmp, 1), prod(size(vtmp)[2:end]))) - y′=reshape(y, prod(size(y))) - - if add - @simd for i = 1:length(y′) - @inbounds y′[i] += dot(u, @view(vtmp′[:, i])) - end - else - @simd for i = 1:length(y′) - @inbounds y′[i] = dot(u, @view(vtmp′[:, i])) - end - end -end - -# ∇b ⋅ y -function pointwise_gradiprod_2dᵀ!(u::DisplacementFull, y::Image, b::Image) - ∇₂c!(u, b) - - u′=reshape(u, (size(u, 1), prod(size(u)[2:end]))) - y′=reshape(y, prod(size(y))) - - @simd for i=1:length(y′) - @inbounds @. u′[:, i] *= y′[i] - end -end - -function pointwise_gradiprod_2dᵀ!(u::DisplacementConstant, y::Image, b::Image) - @assert(size(y)==size(b) && size(u)==(2,)) - u .= 0 - ∇₂cfold!(b, nothing) do g, st, (i, j) - @inbounds u .+= g.*y[i, j] - return st - end - # Reweight to be with respect to 𝟙^*𝟙 inner product. - u ./= prod(size(b)) -end - -mutable struct ConstantDisplacementHornSchunckData - M₀::Array{Float64,2} - z::Array{Float64,1} - Mv::Array{Float64,2} - av::Array{Float64,1} - cv::Float64 - - function ConstantDisplacementHornSchunckData() - return new(zeros(2, 2), zeros(2), zeros(2,2), zeros(2), 0) - end -end - -# For DisplacementConstant, for the simple prox step -# -# (1) argmin_u 1/(2τ)|u-ũ|^2 + (θ/2)|b⁺-b+<>|^2 + (λ/2)|u-ŭ|^2, -# -# construct matrix M₀ and vector z such that we can solve u from -# -# (2) (I/τ+M₀)u = M₀ŭ + ũ/τ - z -# -# Note that the problem -# -# argmin_u 1/(2τ)|u-ũ|^2 + (θ/2)|b⁺-b+<>|^2 + (λ/2)|u-ŭ|^2 -# + (θ/2)|b⁺⁺-b⁺+<>|^2 + (λ/2)|u-uʹ|^2 -# -# has with respect to u the system -# -# (I/τ+M₀+M₀ʹ)u = M₀ŭ + M₀ʹuʹ + ũ/τ - z + zʹ, -# -# where the primed variables correspond to (2) for (1) for uʹ in place of u: -# -# argmin_uʹ 1/(2τ)|uʹ-ũʹ|^2 + (θ/2)|b⁺⁺-b⁺+<>|^2 + (λ/2)|uʹ-u|^2 -# -function horn_schunck_reg_prox_op!(hs::ConstantDisplacementHornSchunckData, - bnext::Image, b::Image, θ, λ, T) - @assert(size(b)==size(bnext)) - w = prod(size(b)) - z = hs.z - cv = 0 - # Factors of symmetric matrix [a c; c d] - a, c, d = 0.0, 0.0, 0.0 - # This used to use ∇₂cfold but it is faster to allocate temporary - # storage for the full gradient due to probably better memory and SIMD - # instruction usage. - g = zeros(2, size(b)...) - ∇₂c!(g, b) - @inbounds for i=1:size(b, 1) - for j=1:size(b, 2) - δ = bnext[i,j]-b[i,j] - @. z += g[:,i,j]*δ - cv += δ*δ - a += g[1,i,j]*g[1,i,j] - c += g[1,i,j]*g[2,i,j] - d += g[2,i,j]*g[2,i,j] - end - end - w₀ = λ - w₂ = θ/w - aʹ = w₀ + w₂*a - cʹ = w₂*c - dʹ = w₀ + w₂*d - hs.M₀ .= [aʹ cʹ; cʹ dʹ] - hs.Mv .= [w*λ+θ*a θ*c; θ*c w*λ+θ*d] - hs.cv = cv*θ - hs.av .= hs.z.*θ - hs.z .*= w₂/T -end - -# Solve the 2D system (I/τ+M₀)u = z -@inline function mldivide_step_plus_sym2x2!(u, M₀, z, τ) - a = 1/τ+M₀[1, 1] - c = M₀[1, 2] - d = 1/τ+M₀[2, 2] - u .= ([d -c; -c a]*z)./(a*d-c*c) -end - -function horn_schunck_reg_prox!(u::DisplacementConstant, bnext::Image, b::Image, - θ, λ, T, τ) - hs=ConstantDisplacementHornSchunckData() - horn_schunck_reg_prox_op!(hs, bnext, b, θ, λ, T) - mldivide_step_plus_sym2x2!(u, hs.M₀, (u./τ)-hs.z, τ) -end - -function flow_grad!(x::Image, vtmp::Gradient, u::Displacement; δ=nothing) - if !isnothing(δ) - u = δ.*u - end - pointwise_gradiprod_2d!(x, vtmp, u, x; add=true) -end - -# Error b-b_prev+⟨⟨u, ∇b⟩⟩ for Horn–Schunck type penalisation -function linearised_optical_flow_error(u::Displacement, b::Image, b_prev::Image) - imdim = size(b) - vtmp = zeros(2, imdim...) - tmp = b-b_prev - pointwise_gradiprod_2d!(tmp, vtmp, u, b_prev; add=true) - return tmp -end - -############################################## -# Helper to smooth data for Horn–Schunck term -############################################## - -function filter_hs(b, b_next, b_next_filt, kernel) - if kernel==nothing - f = x -> x - else - f = x -> simple_imfilter(x, kernel; threads=false) - end - - # We already filtered b in the previous step (b_next in that step) - b_filt = b_next_filt==nothing ? f(b) : b_next_filt - b_next_filt = f(b_next) - - return b_filt, b_next_filt -end - -end # Module diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/AlgorithmDualScaling.jl --- a/src/PET/AlgorithmDualScaling.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmDualScaling - -identifier = "pdps_known_dualscaling" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient -using ImageTools.Translate - -using ..Radon -using ImageTransformations -using Images, CoordinateTransformations, Rotations, OffsetArrays -using ImageCore, Interpolations - -using ..OpticalFlow: ImageSize, - Image, - petpdflow!, - DualScaling - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - radonx = copy(x) - return x, y, Δx, Δy, x̄, radonx -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -######################### -# PETscan related -######################### -function petvalue(x, b, c) - tmp = similar(b) - radon!(tmp, x) - return sum(@. tmp - b*log(tmp+c)) -end - -function petgrad!(res, x, b, c, S) - tmp = similar(b) - radon!(tmp, x) - @. tmp = S .- b/(tmp+c) - backproject!(res, S.*tmp) -end - -function proj_nonneg!(y) - @inbounds @simd for i=1:length(y) - if y[i] < 0 - y[i] = 0 - end - end - return y -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1 - L = params.L - τ₀, σ₀ = params.τ₀, params.σ₀ - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - λ = params.λ - c = params.c*ones(params.radondims...) - - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄, r∇ = init_iterates(dim) - - if params.L_experiment - oldpetgradx = zeros(size(x)...) - petgradx = zeros(size(x)) - oldx = ones(size(x)) - end - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, # noisy_sinogram - v_known :: DisplacementT, - theta_known :: DisplacementT, - b_true :: Image, - S :: Image - - ################### - # Prediction steps - ################### - - petpdflow!(x, Δx, y, Δy, v_known, theta_known, DualScaling()) - - if params.L_experiment - @. oldx = x - end - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - petgrad!(r∇, x, b, c, S) # | Calculate gradient of fidelity term - - @. x = x-(τ*λ)*r∇-τ*Δx # | - proj_nonneg!(x) # | non-negativity constaint prox - @. x̄ = 2x - x̄ # over-relax: x̄ = 2x-x_old - ∇₂!(Δy, x̄) # dual step: - @. y = y + σ*Δy # | - proj_norm₂₁ball!(y, α) # | prox - - ##################### - # L update if needed - ##################### - if params.L_experiment - petgrad!(petgradx, x, b, c, S) - petgrad!(oldpetgradx, oldx, b, c, S) - if norm₂(x-oldx)>1e-12 - L = max(0.9*norm₂(petgradx - oldpetgradx)/norm₂(x-oldx),L) - println("Step length parameters: L=$(L)") - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - end - end - - ################################ - # Give function value if needed - ################################ - - v = verbose() do - ∇₂!(Δy, x) - value = λ*petvalue(x, b, c) + params.α*norm₂₁(Δy) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/AlgorithmGreedy.jl --- a/src/PET/AlgorithmGreedy.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmGreedy - -identifier = "pdps_known_greedy" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient -using ImageTools.Translate - -using ..Radon -using ImageTransformations -using Images, CoordinateTransformations, Rotations, OffsetArrays -using ImageCore, Interpolations - -using ..OpticalFlow: ImageSize, - Image, - petpdflow!, - Greedy - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - radonx = copy(x) - return x, y, Δx, Δy, x̄, radonx -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -######################### -# PETscan related -######################### -function petvalue(x, b, c) - tmp = similar(b) - radon!(tmp, x) - return sum(@. tmp - b*log(tmp+c)) -end - -function petgrad!(res, x, b, c, S) - tmp = similar(b) - radon!(tmp, x) - @. tmp = S .- b/(tmp+c) - backproject!(res, S.*tmp) -end - -function proj_nonneg!(y) - @inbounds @simd for i=1:length(y) - if y[i] < 0 - y[i] = 0 - end - end - return y -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1 - L = params.L - τ₀, σ₀ = params.τ₀, params.σ₀ - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - λ = params.λ - c = params.c*ones(params.radondims...) - - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄, r∇ = init_iterates(dim) - - if params.L_experiment - oldpetgradx = zeros(size(x)...) - petgradx = zeros(size(x)) - oldx = ones(size(x)) - end - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, # noisy_sinogram - v_known :: DisplacementT, - theta_known :: DisplacementT, - b_true :: Image, - S :: Image - - ################### - # Prediction steps - ################### - - petpdflow!(x, Δx, y, Δy, v_known, theta_known, Greedy()) - - if params.L_experiment - @. oldx = x - end - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - petgrad!(r∇, x, b, c, S) # | Calculate gradient of fidelity term - - @. x = x-(τ*λ)*r∇-τ*Δx # | - proj_nonneg!(x) # | non-negativity constaint prox - @. x̄ = 2x - x̄ # over-relax: x̄ = 2x-x_old - ∇₂!(Δy, x̄) # dual step: - @. y = y + σ*Δy # | - proj_norm₂₁ball!(y, α) # | prox - - ##################### - # L update if needed - ##################### - if params.L_experiment - petgrad!(petgradx, x, b, c, S) - petgrad!(oldpetgradx, oldx, b, c, S) - if norm₂(x-oldx)>1e-12 - L = max(0.9*norm₂(petgradx - oldpetgradx)/norm₂(x-oldx),L) - println("Step length parameters: L=$(L)") - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - end - end - - ################################ - # Give function value if needed - ################################ - - v = verbose() do - ∇₂!(Δy, x) - value = λ*petvalue(x, b, c) + params.α*norm₂₁(Δy) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/AlgorithmNew.jl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/PET/AlgorithmNew.jl Thu Apr 25 14:20:38 2024 -0500 @@ -0,0 +1,183 @@ +#################################################################### +# Predictive online PDPS for optical flow with known velocity field +#################################################################### + +__precompile__() + +module AlgorithmNew + +# Default identifier for when none is given by predictor +identifier = "pdps_known_noprediction" + +using Printf + +using AlgTools.Util +import AlgTools.Iterate +using ImageTools.Gradient +using ImageTools.Translate + +using ImageTransformations +using Images, CoordinateTransformations, Rotations, OffsetArrays +using ImageCore, Interpolations + +using ...Radon +using ...OpticalFlow: ImageSize, + Image, + petpdflow! + +######################### +# Iterate initialisation +######################### + +function init_rest(x::Image) + imdim=size(x) + y = zeros(2, imdim...) + Δx = copy(x) + Δy = copy(y) + x̄ = copy(x) + radonx = copy(x) + return x, y, Δx, Δy, x̄, radonx +end + +function init_iterates(xinit::Image) + return init_rest(copy(xinit)) +end + +function init_iterates(dim::ImageSize) + return init_rest(zeros(dim...)) +end + +######################### +# PETscan related +######################### +function petvalue(x, b, c) + tmp = similar(b) + radon!(tmp, x) + return sum(@. tmp - b*log(tmp+c)) +end + +function petgrad!(res, x, b, c, S) + tmp = similar(b) + radon!(tmp, x) + @. tmp = S .- b/(tmp+c) + backproject!(res, S.*tmp) +end + +function proj_nonneg!(y) + @inbounds @simd for i=1:length(y) + if y[i] < 0 + y[i] = 0 + end + end + return y +end + +############ +# Algorithm +############ + +function solve( :: Type{DisplacementT}; + dim :: ImageSize, + iterate = AlgTools.simple_iterate, + params::NamedTuple) where DisplacementT + + ################################ + # Extract and set up parameters + ################################ + α, ρ = params.α, params.ρ + R_K² = ∇₂_norm₂₂_est² + γ = 1 + L = params.L + τ₀, σ₀ = params.τ₀, params.σ₀ + τ = τ₀/L + σ = σ₀*(1-τ₀)/(R_K²*τ) + + println("Step length parameters: τ=$(τ), σ=$(σ)") + + λ = params.λ + c = params.c*ones(params.radondims...) + + + ###################### + # Initialise iterates + ###################### + + x, y, Δx, Δy, x̄, r∇ = init_iterates(dim) + + if params.L_experiment + oldpetgradx = zeros(size(x)...) + petgradx = zeros(size(x)) + oldx = ones(size(x)) + end + + #################### + # Run the algorithm + #################### + + v = iterate(params) do verbose :: Function, + b :: Image, # noisy_sinogram + v_known :: DisplacementT, + theta_known :: DisplacementT, + b_true :: Image, + S :: Image + + ################### + # Prediction steps + ################### + + if haskey(params, :predictor) && ~isnothing(params.predictor) + petpdflow!(x, Δx, y, Δy, v_known, theta_known, params.predictor) + end + + if params.L_experiment + @. oldx = x + end + + ############ + # PDPS step + ############ + + ∇₂ᵀ!(Δx, y) # primal step: + @. x̄ = x # | save old x for over-relax + petgrad!(r∇, x, b, c, S) # | Calculate gradient of fidelity term + + @. x = x-(τ*λ)*r∇-τ*Δx # | + proj_nonneg!(x) # | non-negativity constaint prox + @. x̄ = 2x - x̄ # over-relax: x̄ = 2x-x_old + ∇₂!(Δy, x̄) # dual step: + @. y = y + σ*Δy # | + proj_norm₂₁ball!(y, α) # | prox + + ##################### + # L update if needed + ##################### + if params.L_experiment + petgrad!(petgradx, x, b, c, S) + petgrad!(oldpetgradx, oldx, b, c, S) + if norm₂(x-oldx)>1e-12 + L = max(0.9*norm₂(petgradx - oldpetgradx)/norm₂(x-oldx),L) + println("Step length parameters: L=$(L)") + τ = τ₀/L + σ = σ₀*(1-τ₀)/(R_K²*τ) + end + end + + ################################ + # Give function value if needed + ################################ + + v = verbose() do + ∇₂!(Δy, x) + value = λ*petvalue(x, b, c) + params.α*norm₂₁(Δy) + value, x, [NaN, NaN], nothing + end + + v + end + + return x, y, v +end + +end # Module + + diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/AlgorithmNoPrediction.jl --- a/src/PET/AlgorithmNoPrediction.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,180 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmNoPrediction - -identifier = "pdps_known_noprediction" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient -using ImageTools.Translate - -using ..Radon -using ImageTransformations -using Images, CoordinateTransformations, Rotations, OffsetArrays -using ImageCore, Interpolations - -using ..OpticalFlow: ImageSize, - Image, - petpdflow! - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - radonx = copy(x) - return x, y, Δx, Δy, x̄, radonx -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -######################### -# PETscan related -######################### -function petvalue(x, b, c) - tmp = similar(b) - radon!(tmp, x) - return sum(@. tmp - b*log(tmp+c)) -end - -function petgrad!(res, x, b, c, S) - tmp = similar(b) - radon!(tmp, x) - @. tmp = S .- b/(tmp+c) - backproject!(res, S.*tmp) -end - -function proj_nonneg!(y) - @inbounds @simd for i=1:length(y) - if y[i] < 0 - y[i] = 0 - end - end - return y -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1 - L = params.L - τ₀, σ₀ = params.τ₀, params.σ₀ - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - λ = params.λ - c = params.c*ones(params.radondims...) - - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄, r∇ = init_iterates(dim) - - if params.L_experiment - oldpetgradx = zeros(size(x)...) - petgradx = zeros(size(x)) - oldx = ones(size(x)) - end - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, # noisy_sinogram - v_known :: DisplacementT, - theta_known :: DisplacementT, - b_true :: Image, - S :: Image - - ################### - # Prediction steps - ################### - - # No prediction - - if params.L_experiment - @. oldx = x - end - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - petgrad!(r∇, x, b, c, S) # | Calculate gradient of fidelity term - - @. x = x-(τ*λ)*r∇-τ*Δx # | - proj_nonneg!(x) # | non-negativity constaint prox - @. x̄ = 2x - x̄ # over-relax: x̄ = 2x-x_old - ∇₂!(Δy, x̄) # dual step: - @. y = y + σ*Δy # | - proj_norm₂₁ball!(y, α) # | prox - - ##################### - # L update if needed - ##################### - if params.L_experiment - petgrad!(petgradx, x, b, c, S) - petgrad!(oldpetgradx, oldx, b, c, S) - if norm₂(x-oldx)>1e-12 - L = max(0.9*norm₂(petgradx - oldpetgradx)/norm₂(x-oldx),L) - println("Step length parameters: L=$(L)") - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - end - end - - ################################ - # Give function value if needed - ################################ - - v = verbose() do - ∇₂!(Δy, x) - value = λ*petvalue(x, b, c) + params.α*norm₂₁(Δy) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/AlgorithmPrimalOnly.jl --- a/src/PET/AlgorithmPrimalOnly.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,180 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmPrimalOnly - -identifier = "pdps_known_primalonly" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient -using ImageTools.Translate - -using ..Radon -using ImageTransformations -using Images, CoordinateTransformations, Rotations, OffsetArrays -using ImageCore, Interpolations - -using ..OpticalFlow: ImageSize, - Image, - petpdflow! - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - radonx = copy(x) - return x, y, Δx, Δy, x̄, radonx -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -######################### -# PETscan related -######################### -function petvalue(x, b, c) - tmp = similar(b) - radon!(tmp, x) - return sum(@. tmp - b*log(tmp+c)) -end - -function petgrad!(res, x, b, c, S) - tmp = similar(b) - radon!(tmp, x) - @. tmp = S .- b/(tmp+c) - backproject!(res, S.*tmp) -end - -function proj_nonneg!(y) - @inbounds @simd for i=1:length(y) - if y[i] < 0 - y[i] = 0 - end - end - return y -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1 - L = params.L - τ₀, σ₀ = params.τ₀, params.σ₀ - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - λ = params.λ - c = params.c*ones(params.radondims...) - - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄, r∇ = init_iterates(dim) - - if params.L_experiment - oldpetgradx = zeros(size(x)...) - petgradx = zeros(size(x)) - oldx = ones(size(x)) - end - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, # noisy_sinogram - v_known :: DisplacementT, - theta_known :: DisplacementT, - b_true :: Image, - S :: Image - - ################### - # Prediction steps - ################### - - petpdflow!(x, Δx, y, Δy, v_known, theta_known, false) - - if params.L_experiment - @. oldx = x - end - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - petgrad!(r∇, x, b, c, S) # | Calculate gradient of fidelity term - - @. x = x-(τ*λ)*r∇-τ*Δx # | - proj_nonneg!(x) # | non-negativity constaint prox - @. x̄ = 2x - x̄ # over-relax: x̄ = 2x-x_old - ∇₂!(Δy, x̄) # dual step: - @. y = y + σ*Δy # | - proj_norm₂₁ball!(y, α) # | prox - - ##################### - # L update if needed - ##################### - if params.L_experiment - petgrad!(petgradx, x, b, c, S) - petgrad!(oldpetgradx, oldx, b, c, S) - if norm₂(x-oldx)>1e-12 - L = max(0.9*norm₂(petgradx - oldpetgradx)/norm₂(x-oldx),L) - println("Step length parameters: L=$(L)") - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - end - end - - ################################ - # Give function value if needed - ################################ - - v = verbose() do - ∇₂!(Δy, x) - value = λ*petvalue(x, b, c) + params.α*norm₂₁(Δy) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/AlgorithmProximal.jl --- a/src/PET/AlgorithmProximal.jl Thu Apr 25 19:16:17 2024 +0300 +++ b/src/PET/AlgorithmProximal.jl Thu Apr 25 14:20:38 2024 -0500 @@ -15,14 +15,14 @@ using ImageTools.Gradient using ImageTools.Translate -using ..Radon using ImageTransformations using Images, CoordinateTransformations, Rotations, OffsetArrays using ImageCore, Interpolations -using ..OpticalFlow: ImageSize, - Image, - petpdflow! +using ...Radon +using ...OpticalFlow: ImageSize, + Image, + petpdflow! ######################### # Iterate initialisation @@ -103,9 +103,9 @@ iterate = AlgTools.simple_iterate, params::NamedTuple) where DisplacementT - ################################ + ################################ # Extract and set up parameters - ################################ + ################################ α, ρ = params.α, params.ρ R_K² = ∇₂_norm₂₂_est² γ = 1 @@ -139,9 +139,9 @@ v_known :: DisplacementT, theta_known :: DisplacementT, b_true :: Image, - S :: Image + S :: Image - ################### + ################### # Prediction steps ################### @@ -151,14 +151,14 @@ @. oldx = x end - ############################## + ############################## # Proximal step of prediction ############################## ∇₂!(Δy, x) @. y = (y + σ̃*Δy)/(1 + σ̃*(ρ̃+ρ/α)) - #@. cc = y + 1000000*σ̃*Δy + #@. cc = y + 1000000*σ̃*Δy #@. y = (y + σ̃*Δy)/(1 + σ̃*(ρ̃+ρ/α)) + (1 - 1/(1 + ρ̃*σ̃))*cc - proj_norm₂₁ball!(y, α) + proj_norm₂₁ball!(y, α) ############ # PDPS step @@ -177,7 +177,7 @@ ##################### # L update if needed - ##################### + ##################### if params.L_experiment petgrad!(petgradx, x, b, c, S) petgrad!(oldpetgradx, oldx, b, c, S) @@ -186,18 +186,18 @@ println("Step length parameters: L=$(L)") τ = τ₀/L σ = σ₀*(1-τ₀)/(R_K²*τ) - end - end + end + end ################################ # Give function value if needed ################################ - v = verbose() do + v = verbose() do ∇₂!(Δy, x) value = λ*petvalue(x, b, c) + params.α*norm₂₁(Δy) value, x, [NaN, NaN], nothing - end + end v end diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/AlgorithmRotation.jl --- a/src/PET/AlgorithmRotation.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmRotation - -identifier = "pdps_known_rotation" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient -using ImageTools.Translate - -using ..Radon -using ImageTransformations -using Images, CoordinateTransformations, Rotations, OffsetArrays -using ImageCore, Interpolations - -using ..OpticalFlow: ImageSize, - Image, - petpdflow!, - Rotation - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - radonx = copy(x) - return x, y, Δx, Δy, x̄, radonx -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -######################### -# PETscan related -######################### -function petvalue(x, b, c) - tmp = similar(b) - radon!(tmp, x) - return sum(@. tmp - b*log(tmp+c)) -end - -function petgrad!(res, x, b, c, S) - tmp = similar(b) - radon!(tmp, x) - @. tmp = S .- b/(tmp+c) - backproject!(res, S.*tmp) -end - -function proj_nonneg!(y) - @inbounds @simd for i=1:length(y) - if y[i] < 0 - y[i] = 0 - end - end - return y -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1 - L = params.L - τ₀, σ₀ = params.τ₀, params.σ₀ - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - λ = params.λ - c = params.c*ones(params.radondims...) - - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄, r∇ = init_iterates(dim) - - if params.L_experiment - oldpetgradx = zeros(size(x)...) - petgradx = zeros(size(x)) - oldx = ones(size(x)) - end - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, # noisy_sinogram - v_known :: DisplacementT, - theta_known :: DisplacementT, - b_true :: Image, - S :: Image - - ################### - # Prediction steps - ################### - - petpdflow!(x, Δx, y, Δy, v_known, theta_known, Rotation()) - - if params.L_experiment - @. oldx = x - end - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - petgrad!(r∇, x, b, c, S) # | Calculate gradient of fidelity term - - @. x = x-(τ*λ)*r∇-τ*Δx # | - proj_nonneg!(x) # | non-negativity constaint prox - @. x̄ = 2x - x̄ # over-relax: x̄ = 2x-x_old - ∇₂!(Δy, x̄) # dual step: - @. y = y + σ*Δy # | - proj_norm₂₁ball!(y, α) # | prox - - ##################### - # L update if needed - ##################### - if params.L_experiment - petgrad!(petgradx, x, b, c, S) - petgrad!(oldpetgradx, oldx, b, c, S) - if norm₂(x-oldx)>1e-12 - L = max(0.9*norm₂(petgradx - oldpetgradx)/norm₂(x-oldx),L) - println("Step length parameters: L=$(L)") - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - end - end - - ################################ - # Give function value if needed - ################################ - - v = verbose() do - ∇₂!(Δy, x) - value = λ*petvalue(x, b, c) + params.α*norm₂₁(Δy) - value, x, [NaN, NaN], nothing - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/AlgorithmZeroDual.jl --- a/src/PET/AlgorithmZeroDual.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -#################################################################### -# Predictive online PDPS for optical flow with known velocity field -#################################################################### - -__precompile__() - -module AlgorithmZeroDual - -identifier = "pdps_known_zerodual" - -using Printf - -using AlgTools.Util -import AlgTools.Iterate -using ImageTools.Gradient -using ImageTools.Translate - -using ..Radon -using ImageTransformations -using Images, CoordinateTransformations, Rotations, OffsetArrays -using ImageCore, Interpolations - -using ..OpticalFlow: ImageSize, - Image, - petpdflow! - -######################### -# Iterate initialisation -######################### - -function init_rest(x::Image) - imdim=size(x) - y = zeros(2, imdim...) - Δx = copy(x) - Δy = copy(y) - x̄ = copy(x) - radonx = copy(x) - return x, y, Δx, Δy, x̄, radonx -end - -function init_iterates(xinit::Image) - return init_rest(copy(xinit)) -end - -function init_iterates(dim::ImageSize) - return init_rest(zeros(dim...)) -end - -######################### -# PETscan related -######################### -function petvalue(x, b, c) - tmp = similar(b) - radon!(tmp, x) - return sum(@. tmp - b*log(tmp+c)) -end - -function petgrad!(res, x, b, c, S) - tmp = similar(b) - radon!(tmp, x) - @. tmp = S .- b/(tmp+c) - backproject!(res, S.*tmp) -end - -function proj_nonneg!(y) - @inbounds @simd for i=1:length(y) - if y[i] < 0 - y[i] = 0 - end - end - return y -end - -############ -# Algorithm -############ - -function solve( :: Type{DisplacementT}; - dim :: ImageSize, - iterate = AlgTools.simple_iterate, - params::NamedTuple) where DisplacementT - - ################################ - # Extract and set up parameters - ################################ - α, ρ = params.α, params.ρ - R_K² = ∇₂_norm₂₂_est² - γ = 1 - L = params.L - τ₀, σ₀ = params.τ₀, params.σ₀ - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - - println("Step length parameters: τ=$(τ), σ=$(σ)") - - λ = params.λ - c = params.c*ones(params.radondims...) - - - ###################### - # Initialise iterates - ###################### - - x, y, Δx, Δy, x̄, r∇ = init_iterates(dim) - - if params.L_experiment - oldpetgradx = zeros(size(x)...) - petgradx = zeros(size(x)) - oldx = ones(size(x)) - end - - #################### - # Run the algorithm - #################### - - v = iterate(params) do verbose :: Function, - b :: Image, # noisy_sinogram - v_known :: DisplacementT, - theta_known :: DisplacementT, - b_true :: Image, - S :: Image - - ################### - # Prediction steps - ################### - - petpdflow!(x, Δx, y, Δy, v_known, theta_known, false) - y .= zeros(size(y)...) - - if params.L_experiment - @. oldx = x - end - - ############ - # PDPS step - ############ - - ∇₂ᵀ!(Δx, y) # primal step: - @. x̄ = x # | save old x for over-relax - petgrad!(r∇, x, b, c, S) # | Calculate gradient of fidelity term - - @. x = x-(τ*λ)*r∇-τ*Δx # | - proj_nonneg!(x) # | non-negativity constaint prox - @. x̄ = 2x - x̄ # over-relax: x̄ = 2x-x_old - ∇₂!(Δy, x̄) # dual step: - @. y = y + σ*Δy # | - proj_norm₂₁ball!(y, α) # | prox - - ##################### - # L update if needed - ##################### - if params.L_experiment - petgrad!(petgradx, x, b, c, S) - petgrad!(oldpetgradx, oldx, b, c, S) - if norm₂(x-oldx)>1e-12 - L = max(0.9*norm₂(petgradx - oldpetgradx)/norm₂(x-oldx),L) - println("Step length parameters: L=$(L)") - τ = τ₀/L - σ = σ₀*(1-τ₀)/(R_K²*τ) - end - end - - ################################ - # Give function value if needed - ################################ - - v = verbose() do - ∇₂!(Δy, x) - value = λ*petvalue(x, b, c) + params.α*norm₂₁(Δy) - value, x, [NaN, NaN], nothing, τ, σ - end - - v - end - - return x, y, v -end - -end # Module - - diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/ImGenerate.jl --- a/src/PET/ImGenerate.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,309 +0,0 @@ -################### -# Image generation -################### - -module ImGenerate - -using ColorTypes: Gray -import TestImages -# We don't really *directly* depend on QuartzImageIO. The import here is -# merely a workaround to suppress warnings when loading TestImages. -# Something is broken in those packages. -import QuartzImageIO - -using AlgTools.Util -using AlgTools.Comms -using ImageTools.Translate - -using ..OpticalFlow: Image, DisplacementConstant, DisplacementFull -using ..Radon - -# Added for reproducibility -import StableRNGs: StableRNG, Random -const rng = StableRNG(314159) - -# Added for PET -import PoissonRandom: pois_rand -import Random: shuffle -import Images: center, warp, imresize -import CoordinateTransformations: recenter -import Rotations: RotMatrix -import Interpolations: Flat -import MAT: matread - - -############## -# Our exports -############## - -export ImGen, - OnlineData, - imgen_square, - imgen_shake, - PetOnlineData, - imgen_shepplogan_radon, - imgen_brainphantom_radon - -################## -# Data structures -################## - -struct ImGen - f :: Function - dim :: Tuple{Int64,Int64} - Λ :: Float64 - dynrange :: Float64 - name :: String -end - -struct OnlineData{DisplacementT} - b_true :: Image - b_noisy :: Image - v :: DisplacementT - v_true :: DisplacementT - v_cumul_true :: DisplacementT -end - -struct PetOnlineData{DisplacementT} - b_true :: Image - sinogram_true :: Image - sinogram_noisy :: Image - v :: DisplacementT - v_true :: DisplacementT - v_cumul_true :: DisplacementT - theta :: DisplacementT # theta = thetaknown, theta_cumul - S :: Image -end - -################### -# Shake generation -################### - -function make_const_v(displ, sz) - v = zeros(2, sz...) - v[1, :, :] .= displ[1] - v[2, :, :] .= displ[2] - return v -end - -function shake(params) - if !haskey(params, :shaketype) || params.shaketype == :gaussian - return () -> params.shake.*randn(rng,2) - elseif params.shaketype == :disk - return () -> begin - θ = 2π*rand(rng,Float64) - r = params.shake*√(rand(rng,Float64)) - return [r*cos(θ), r*sin(θ)] - end - elseif params.shaketype == :circle - return () -> begin - θ = 2π*rand(rng,Float64) - r = params.shake - return [r*cos(θ), r*sin(θ)] - end - else - error("Unknown shaketype $(params.shaketype)") - end -end - -pixelwise = (shakefn, sz) -> () -> make_const_u(shakefn(), sz) - - -function rotatebytheta(params) - r = params.rotation_factor*randn(rng) - return r -end - -function generate_radonmask(params) - imdim = params.radondims - sino_sparse = params.sino_sparsity - numzero = Int64(round(sino_sparse*imdim[1]*imdim[2])) - numone = imdim[1]*imdim[2]-numzero - A = shuffle(rng,reshape([ones(numone); zeros(numzero)],(imdim[1],imdim[2]))) - return A -end - -################ -# Moving square -################ - -function generate_square(sz, - :: Type{DisplacementT}, - datachannel :: Channel{OnlineData{DisplacementT}}, - params) where DisplacementT - - if false - v₀ = make_const_v(0.1.*(-1, 1), sz) - nextv = () -> v₀ - elseif DisplacementT == DisplacementFull - nextv = pixelwise(shake(params), sz) - elseif DisplacementT == DisplacementConstant - nextv = shake(params) - else - @error "Invalid DisplacementT" - end - - # Constant linear displacement everywhere has Jacobian determinant one - # (modulo the boundaries which we ignore here) - m = round(Int, sz[1]/5) - b_orig = zeros(sz...) - b_orig[sz[1].-(2*m:3*m), 2*m:3*m] .= 1 - - v_true = nextv() - v_cumul = copy(v_true) - - while true - # Flow original data and add noise - b_true = zeros(sz...) - translate_image!(b_true, b_orig, v_cumul; threads=true) - b = b_true .+ params.noise_level.*randn(rng,sz...) - v = v_true.*(1.0 .+ params.shake_noise_level.*randn(rng,size(v_true)...)) - # Pass true data to iteration routine - data = OnlineData{DisplacementT}(b_true, b, v, v_true, v_cumul) - if !put_unless_closed!(datachannel, data) - return - end - # Next step shake - v_true = nextv() - v_cumul .+= v_true - end -end - -function imgen_square(sz) - return ImGen(curry(generate_square, sz), sz, 1, 1, "square$(sz[1])x$(sz[2])") -end - -################ -# Shake a photo -################ - -function generate_shake_image(im, sz, - :: Type{DisplacementConstant}, - datachannel :: Channel{OnlineData{DisplacementConstant}}, - params :: NamedTuple) - - - # Set up counter and zero factor for stabilisation - @assert(params.maxiter ≥ maximum(params.stable_interval)) - indx = 1 - zero_factor = indx in params.stable_interval ? 0.0 : 1.0 - - # Restart the seed to enable comparison across predictors - Random.seed!(rng,951508) # alreadygood - - - nextv = shake(params) - v_true = zero_factor.*nextv() - v_cumul = copy(v_true) - - while true - # Extract subwindow of original image and add noise - b_true = zeros(sz...) - extract_subimage!(b_true, im, v_cumul; threads=true) - b = b_true .+ params.noise_level.*randn(rng,sz...) - v = v_true.*(1.0 .+ params.shake_noise_level.*randn(rng,size(v_true)...)) - # Pass data to iteration routine - data = OnlineData{DisplacementConstant}(b_true, b, v, v_true, v_cumul) - if !put_unless_closed!(datachannel, data) - return - end - # Next step shake - v_true = zero_factor.*nextv() - v_cumul .+= v_true - # Update indx and zero factor - indx += 1 - zero_factor = indx in params.stable_interval ? 0.0 : 1.0 - end -end - -function imgen_shake(imname, sz) - im = Float64.(Gray.(TestImages.testimage(imname))) - dynrange = maximum(im) - return ImGen(curry(generate_shake_image, im, sz), sz, 1, dynrange, - "$(imname)$(sz[1])x$(sz[2])") -end - - - -######################################################################## -# PETscan -######################################################################## -function generate_sinogram(im, sz, - :: Type{DisplacementConstant}, - datachannel :: Channel{PetOnlineData{DisplacementConstant}}, - params :: NamedTuple) - - # Set up counter and zero factor for stabilisation - @assert(params.maxiter ≥ maximum(params.stable_interval)) - indx = 1 - zero_factor = indx in params.stable_interval ? 0.0 : 1.0 - - # Restart the seed to enable comparison across predictors - Random.seed!(rng,314159) # for shepp - # Random.seed!(rng,9182737465) # for brain - - nextv = shake(params) - v_true = zero_factor.*nextv() - v_cumul = copy(v_true) - - S_true = generate_radonmask(params) - - theta_true = zero_factor*rotatebytheta(params) - theta_cumul = copy(theta_true) - - while true - # Define the transformation matrix - center_point = (sz[1]/2 + v_true[1], sz[2]/2 + v_true[2]) - tform = recenter(RotMatrix(theta_cumul), center_point) - - # Apply the transformation to the image using warp - b_true = copy(warp(im, tform, axes(im), fillvalue=Flat())) - - v = v_true.*(1.0 .+ params.shake_noise_level.*randn(rng,size(v_true)...)) - theta = theta_true*(1.0 + params.rotation_noise_level.*randn(rng)) - - # Generate the true and noisy sinograms - sinogram_true = zeros(params.radondims...) - sinogram_true .*= params.scale - radon!(sinogram_true, b_true) - sinogram_noisy = copy(sinogram_true) - - for i=1:params.radondims[1], j=1:params.radondims[2] - sinogram_noisy[i, j] += pois_rand(rng,params.noise_level) - end - - # Pass data to iteration routine - data = PetOnlineData{DisplacementConstant}(b_true, sinogram_true, sinogram_noisy, v, v_true, v_cumul, [theta, theta_cumul], S_true) - if !put_unless_closed!(datachannel, data) - return - end - - # Next step shake - v_true = zero_factor.*nextv() - v_cumul .+= v_true - # Next theta - theta_true = zero_factor*rotatebytheta(params) - theta_cumul += theta_true - # Next sinogram mask - S_true = generate_radonmask(params) - # Update indx and zero factor - indx += 1 - zero_factor = indx in params.stable_interval ? 0.0 : 1.0 - end -end - -function imgen_shepplogan_radon(sz) - im = convert(Array{Float64},TestImages.shepp_logan(sz[1], highContrast=true)) - dynrange = maximum(im) - return ImGen(curry(generate_sinogram, im, sz), sz, 1, dynrange, "shepplogan$(sz[1])x$(sz[2])") -end - -function imgen_brainphantom_radon(sz) - data = matread("src/PET/phantom_slice.mat") - im = normalise(imresize(convert(Array{Float64},data["square_data"]),sz)) - dynrange = maximum(im) - return ImGen(curry(generate_sinogram, im, sz), sz, 1, dynrange, "brainphantom$(sz[1])x$(sz[2])") -end - -normalise = (data) -> data./maximum(data) -end # Module diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/OpticalFlow.jl --- a/src/PET/OpticalFlow.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,430 +0,0 @@ -################################ -# Code relevant to optical flow -################################ - -__precompile__() - -module OpticalFlow - -using AlgTools.Util -using ImageTools.Gradient -import ImageTools.Translate -using ImageTools.ImFilter - -# using ImageTransformations -# using Images, CoordinateTransformations, Rotations, OffsetArrays -# using Interpolations - -import Images: center, warp -import CoordinateTransformations: recenter -import Rotations: RotMatrix -import Interpolations: Flat - -########## -# Exports -########## - -export flow!, - pdflow!, - flow_grad!, - flow_interp!, - estimate_Λ², - estimate_linear_Λ², - pointwise_gradiprod_2d!, - pointwise_gradiprod_2dᵀ!, - horn_schunck_reg_prox!, - horn_schunck_reg_prox_op!, - mldivide_step_plus_sym2x2!, - linearised_optical_flow_error, - Image, AbstractImage, ImageSize, - Gradient, Displacement, - DisplacementFull, DisplacementConstant, - HornSchunckData, - filter_hs, - petpdflow!, - DualScaling, Greedy, Rotation - -############################################### -# Types (several imported from ImageTools.Translate) -############################################### - -Image = Translate.Image -AbstractImage = AbstractArray{Float64,2} -Displacement = Translate.Displacement -DisplacementFull = Translate.DisplacementFull -DisplacementConstant = Translate.DisplacementConstant -Gradient = Array{Float64,3} -ImageSize = Tuple{Int64,Int64} - - -################################# -# Struct for flow -################################# -struct DualScaling end -struct Greedy end -struct Rotation end - -################################# -# Displacement field based flow -################################# - -function flow_interp!(x::AbstractImage, u::Displacement, tmp::AbstractImage; - threads = false) - tmp .= x - Translate.translate_image!(x, tmp, u; threads=threads) -end - -function flow_interp!(x::AbstractImage, u::Displacement; - threads = false) - tmp = copy(x) - Translate.translate_image!(x, tmp, u; threads=threads) -end - -flow! = flow_interp! - -function pdflow!(x, Δx, y, Δy, u, dual_flow; threads=:none) - if dual_flow - #flow!((x, @view(y[1, :, :]), @view(y[2, :, :])), diffu, - # (Δx, @view(Δy[1, :, :]), @view(Δy[2, :, :]))) - @backgroundif (threads==:outer) begin - flow!(x, u, Δx; threads=(threads==:inner)) - end begin - flow!(@view(y[1, :, :]), u, @view(Δy[1, :, :]); threads=(threads==:inner)) - flow!(@view(y[2, :, :]), u, @view(Δy[2, :, :]); threads=(threads==:inner)) - end - else - flow!(x, u, Δx) - end -end - -function pdflow!(x, Δx, y, Δy, z, Δz, u, dual_flow; threads=:none) - if dual_flow - @backgroundif (threads==:outer) begin - flow!(x, u, Δx; threads=(threads==:inner)) - flow!(z, u, Δz; threads=(threads==:inner)) - end begin - flow!(@view(y[1, :, :]), u, @view(Δy[1, :, :]); threads=(threads==:inner)) - flow!(@view(y[2, :, :]), u, @view(Δy[2, :, :]); threads=(threads==:inner)) - end - else - flow!(x, u, Δx; threads=(threads==:inner)) - flow!(z, u, Δz; threads=(threads==:inner)) - end -end - -# Additional method for Greedy -function pdflow!(x, Δx, y, Δy, u, flow :: Greedy; threads=:none) - @assert(size(u)==(2,)) - Δx .= x - Δy .= y - flow!(x, u; threads=(threads==:inner)) - Dxx = similar(Δy) - DΔx = similar(Δy) - ∇₂!(Dxx, x) - ∇₂!(DΔx, Δx) - inds = abs.(Dxx) .≤ 1e-1 - Dxx[inds] .= 1 - DΔx[inds] .= 1 - y .= y.* DΔx ./ Dxx -end - -# Additional method for Rotation -function pdflow!(x, Δx, y, Δy, u, flow :: Rotation; threads=:none) - @assert(size(u)==(2,)) - Δx .= x - flow!(x, u; threads=(threads==:inner)) - (m,n) = size(x) - dx = similar(y) - dx_banana = similar(y) - ∇₂!(dx, Δx) - ∇₂!(dx_banana, x) - for i=1:m - for j=1:n - ndx = @views sum(dx[:, i, j].^2) - ndx_banana = @views sum(dx_banana[:, i, j].^2) - if ndx > 1e-4 && ndx_banana > 1e-4 - A = dx[:, i, j] - B = dx_banana[:, i, j] - theta = atan(B[1] * A[2] - B[2] * A[1], B[1] * A[1] + B[2] * A[2]) # Oriented angle from A to B - cos_theta = cos(theta) - sin_theta = sin(theta) - a = cos_theta * y[1, i, j] - sin_theta * y[2, i, j] - b = sin_theta * y[1, i, j] + cos_theta * y[2, i, j] - y[1, i, j] = a - y[2, i, j] = b - end - end - end -end - -# Additional method for Dual Scaling -function pdflow!(x, Δx, y, Δy, u, flow :: DualScaling; threads=:none) - @assert(size(u)==(2,)) - oldx = copy(x) - flow!(x, u; threads=(threads==:inner)) - C = similar(y) - cc = abs.(x-oldx) - cm = max(1e-12,maximum(cc)) - c = 1 .* (1 .- cc./ cm) .^(10) - C[1,:,:] .= c - C[2,:,:] .= c - y .= C.*y -end - - -########################## -# PET -########################## -function petflow_interp!(x::AbstractImage, tmp::AbstractImage, u::DisplacementConstant, theta_known::DisplacementConstant; - threads = false) - tmp .= x - center_point = center(x) .+ u - tform = recenter(RotMatrix(theta_known[1]), center_point) - tmp = warp(x, tform, axes(x), fillvalue=Flat()) - x .= tmp -end - -petflow! = petflow_interp! - -function petpdflow!(x, Δx, y, Δy, u, theta_known, dual_flow; threads=:none) - if dual_flow - @backgroundif (threads==:outer) begin - petflow!(x, Δx, u, theta_known; threads=(threads==:inner)) - end begin - petflow!(@view(y[1, :, :]), @view(Δy[1, :, :]), u, theta_known; threads=(threads==:inner)) - petflow!(@view(y[2, :, :]), @view(Δy[2, :, :]), u, theta_known; threads=(threads==:inner)) - end - else - petflow!(x, Δx, u, theta_known) - end -end - -# Method for greedy predictor -function petpdflow!(x, Δx, y, Δy, u, theta_known, flow :: Greedy; threads=:none) - oldx = copy(x) - center_point = center(x) .+ u - tform = recenter(RotMatrix(theta_known[1]), center_point) - Δx = warp(x, tform, axes(x), fillvalue=Flat()) - @. x = Δx - @. Δy = y - Dxx = copy(Δy) - DΔx = copy(Δy) - ∇₂!(Dxx, x) - ∇₂!(DΔx, oldx) - inds = abs.(Dxx) .≤ 1e-2 - Dxx[inds] .= 1 - DΔx[inds] .= 1 - y .= y.* DΔx ./ Dxx -end - -# Method for dual scaling predictor -function petpdflow!(x, Δx, y, Δy, u, theta_known, flow :: DualScaling; threads=:none) - oldx = copy(x) - center_point = center(x) .+ u - tform = recenter(RotMatrix(theta_known[1]), center_point) - Δx = warp(x, tform, axes(x), fillvalue=Flat()) - @. x = Δx - C = similar(y) - cc = abs.(x-oldx) - cm = max(1e-12,maximum(cc)) - c = 1 .* (1 .- cc./ cm) .^(10) - C[1,:,:] .= c - C[2,:,:] .= c - y .= C.*y -end - -# Method for rotation prediction (exploiting property of inverse rotation) -function petpdflow!(x, Δx, y, Δy, u, theta_known, flow :: Rotation; threads=:none) - @backgroundif (threads==:outer) begin - petflow!(x, Δx, u, theta_known; threads=(threads==:inner)) - end begin - petflow!(@view(y[1, :, :]), @view(Δy[1, :, :]), u, -theta_known; threads=(threads==:inner)) - petflow!(@view(y[2, :, :]), @view(Δy[2, :, :]), u, -theta_known; threads=(threads==:inner)) - end -end - -########################## -# Linearised optical flow -########################## - -# ⟨⟨u, ∇b⟩⟩ -function pointwise_gradiprod_2d!(y::Image, vtmp::Gradient, - u::DisplacementFull, b::Image; - add = false) - ∇₂c!(vtmp, b) - - u′=reshape(u, (size(u, 1), prod(size(u)[2:end]))) - vtmp′=reshape(vtmp, (size(vtmp, 1), prod(size(vtmp)[2:end]))) - y′=reshape(y, prod(size(y))) - - if add - @simd for i = 1:length(y′) - @inbounds y′[i] += dot(@view(u′[:, i]), @view(vtmp′[:, i])) - end - else - @simd for i = 1:length(y′) - @inbounds y′[i] = dot(@view(u′[:, i]), @view(vtmp′[:, i])) - end - end -end - -function pointwise_gradiprod_2d!(y::Image, vtmp::Gradient, - u::DisplacementConstant, b::Image; - add = false) - ∇₂c!(vtmp, b) - - vtmp′=reshape(vtmp, (size(vtmp, 1), prod(size(vtmp)[2:end]))) - y′=reshape(y, prod(size(y))) - - if add - @simd for i = 1:length(y′) - @inbounds y′[i] += dot(u, @view(vtmp′[:, i])) - end - else - @simd for i = 1:length(y′) - @inbounds y′[i] = dot(u, @view(vtmp′[:, i])) - end - end -end - -# ∇b ⋅ y -function pointwise_gradiprod_2dᵀ!(u::DisplacementFull, y::Image, b::Image) - ∇₂c!(u, b) - - u′=reshape(u, (size(u, 1), prod(size(u)[2:end]))) - y′=reshape(y, prod(size(y))) - - @simd for i=1:length(y′) - @inbounds @. u′[:, i] *= y′[i] - end -end - -function pointwise_gradiprod_2dᵀ!(u::DisplacementConstant, y::Image, b::Image) - @assert(size(y)==size(b) && size(u)==(2,)) - u .= 0 - ∇₂cfold!(b, nothing) do g, st, (i, j) - @inbounds u .+= g.*y[i, j] - return st - end - # Reweight to be with respect to 𝟙^*𝟙 inner product. - u ./= prod(size(b)) -end - -mutable struct ConstantDisplacementHornSchunckData - M₀::Array{Float64,2} - z::Array{Float64,1} - Mv::Array{Float64,2} - av::Array{Float64,1} - cv::Float64 - - function ConstantDisplacementHornSchunckData() - return new(zeros(2, 2), zeros(2), zeros(2,2), zeros(2), 0) - end -end - -# For DisplacementConstant, for the simple prox step -# -# (1) argmin_u 1/(2τ)|u-ũ|^2 + (θ/2)|b⁺-b+<>|^2 + (λ/2)|u-ŭ|^2, -# -# construct matrix M₀ and vector z such that we can solve u from -# -# (2) (I/τ+M₀)u = M₀ŭ + ũ/τ - z -# -# Note that the problem -# -# argmin_u 1/(2τ)|u-ũ|^2 + (θ/2)|b⁺-b+<>|^2 + (λ/2)|u-ŭ|^2 -# + (θ/2)|b⁺⁺-b⁺+<>|^2 + (λ/2)|u-uʹ|^2 -# -# has with respect to u the system -# -# (I/τ+M₀+M₀ʹ)u = M₀ŭ + M₀ʹuʹ + ũ/τ - z + zʹ, -# -# where the primed variables correspond to (2) for (1) for uʹ in place of u: -# -# argmin_uʹ 1/(2τ)|uʹ-ũʹ|^2 + (θ/2)|b⁺⁺-b⁺+<>|^2 + (λ/2)|uʹ-u|^2 -# -function horn_schunck_reg_prox_op!(hs::ConstantDisplacementHornSchunckData, - bnext::Image, b::Image, θ, λ, T) - @assert(size(b)==size(bnext)) - w = prod(size(b)) - z = hs.z - cv = 0 - # Factors of symmetric matrix [a c; c d] - a, c, d = 0.0, 0.0, 0.0 - # This used to use ∇₂cfold but it is faster to allocate temporary - # storage for the full gradient due to probably better memory and SIMD - # instruction usage. - g = zeros(2, size(b)...) - ∇₂c!(g, b) - @inbounds for i=1:size(b, 1) - for j=1:size(b, 2) - δ = bnext[i,j]-b[i,j] - @. z += g[:,i,j]*δ - cv += δ*δ - a += g[1,i,j]*g[1,i,j] - c += g[1,i,j]*g[2,i,j] - d += g[2,i,j]*g[2,i,j] - end - end - w₀ = λ - w₂ = θ/w - aʹ = w₀ + w₂*a - cʹ = w₂*c - dʹ = w₀ + w₂*d - hs.M₀ .= [aʹ cʹ; cʹ dʹ] - hs.Mv .= [w*λ+θ*a θ*c; θ*c w*λ+θ*d] - hs.cv = cv*θ - hs.av .= hs.z.*θ - hs.z .*= w₂/T -end - -# Solve the 2D system (I/τ+M₀)u = z -@inline function mldivide_step_plus_sym2x2!(u, M₀, z, τ) - a = 1/τ+M₀[1, 1] - c = M₀[1, 2] - d = 1/τ+M₀[2, 2] - u .= ([d -c; -c a]*z)./(a*d-c*c) -end - -function horn_schunck_reg_prox!(u::DisplacementConstant, bnext::Image, b::Image, - θ, λ, T, τ) - hs=ConstantDisplacementHornSchunckData() - horn_schunck_reg_prox_op!(hs, bnext, b, θ, λ, T) - mldivide_step_plus_sym2x2!(u, hs.M₀, (u./τ)-hs.z, τ) -end - -function flow_grad!(x::Image, vtmp::Gradient, u::Displacement; δ=nothing) - if !isnothing(δ) - u = δ.*u - end - pointwise_gradiprod_2d!(x, vtmp, u, x; add=true) -end - -# Error b-b_prev+⟨⟨u, ∇b⟩⟩ for Horn–Schunck type penalisation -function linearised_optical_flow_error(u::Displacement, b::Image, b_prev::Image) - imdim = size(b) - vtmp = zeros(2, imdim...) - tmp = b-b_prev - pointwise_gradiprod_2d!(tmp, vtmp, u, b_prev; add=true) - return tmp -end - -############################################## -# Helper to smooth data for Horn–Schunck term -############################################## - -function filter_hs(b, b_next, b_next_filt, kernel) - if kernel==nothing - f = x -> x - else - f = x -> simple_imfilter(x, kernel; threads=true) - end - - # We already filtered b in the previous step (b_next in that step) - b_filt = b_next_filt==nothing ? f(b) : b_next_filt - b_next_filt = f(b_next) - - return b_filt, b_next_filt -end - -end # Module diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/PET.jl --- a/src/PET/PET.jl Thu Apr 25 19:16:17 2024 +0300 +++ b/src/PET/PET.jl Thu Apr 25 14:20:38 2024 -0500 @@ -18,7 +18,7 @@ using AlgTools.StructTools using AlgTools.LinkedLists using AlgTools.Comms -using ImageTools.Visualise: secs_ns, grayimg, do_visualise +using ImageTools.Visualise: secs_ns, grayimg, do_visualise using ImageTools.ImFilter: gaussian # For PET @@ -26,66 +26,36 @@ ##################### # Load local modules -##################### -include("OpticalFlow.jl") -include("Radon.jl") -include("ImGenerate.jl") -include("AlgorithmDualScaling.jl") -include("AlgorithmGreedy.jl") -include("AlgorithmNoPrediction.jl") -include("AlgorithmPrimalOnly.jl") +#####################a +include("AlgorithmNew.jl") include("AlgorithmProximal.jl") -include("AlgorithmRotation.jl") -include("AlgorithmZeroDual.jl") -include("PlotResults.jl") +#include("PlotResults.jl") -import .AlgorithmDualScaling -import .AlgorithmGreedy -import .AlgorithmNoPrediction -import .AlgorithmPrimalOnly +import .AlgorithmNew import .AlgorithmProximal -import .AlgorithmRotation -import .AlgorithmZeroDual -using .Radon: backproject! -using .ImGenerate -using .OpticalFlow: DisplacementFull, DisplacementConstant -using .PlotResults +using ..Radon: backproject! +using ..ImGenerate +using ..OpticalFlow +using ..Run +#using .PlotResults ############## # Our exports ############## -export demo_petS1, demo_petS2, demo_petS3, +export demo_petS1, demo_petS2, demo_petS3, demo_petS4, demo_petS5, demo_petS6, demo_petS7, - demo_petB1, demo_petB2, demo_petB3, + demo_petB1, demo_petB2, demo_petB3, demo_petB4, demo_petB5, demo_petB6, demo_petB7, - batchrun_shepplogan, batchrun_brainphantom, batchrun_pet, - plot_pet + batchrun_shepplogan, batchrun_brainphantom, batchrun_pet + #plot_pet ################################### # Parameterisation and experiments ################################### -struct Experiment - mod :: Module - DisplacementT :: Type - imgen :: ImGen - params :: NamedTuple -end - -function Base.show(io::IO, e::Experiment) - displacementname(::Type{DisplacementFull}) = "DisplacementFull" - displacementname(::Type{DisplacementConstant}) = "DisplacementConstant" - print(io, " - mod: $(e.mod) - DisplacementT: $(displacementname(e.DisplacementT)) - imgen: $(e.imgen.name) $(e.imgen.dim[1])×$(e.imgen.dim[2]) - params: $(e.params ⬿ (kernel = "(not shown)",)) - ") -end - const default_save_prefix="img/" const default_params = ( @@ -107,24 +77,24 @@ stable_interval = Set(0), ) -const p_known₀_pet = ( +const p_known₀_pet = default_params ⬿ ( noise_level = 0.5, shake_noise_level = 0.25, - shake = 1.0, - rotation_factor = 0.15, - rotation_noise_level = 0.035, + shake = 1.0, + rotation_factor = 0.15, + rotation_noise_level = 0.035, α = 0.15, ρ̃₀ = 1.0, σ̃₀ = 1.0, δ = 0.9, σ₀ = 1.0, τ₀ = 0.9, - λ = 1, + λ = 1, radondims = [128,64], sz = (256,256), scale = 1, c = 1.0, - sino_sparsity = 0.5, + sino_sparsity = 0.5, L = 300.0, L_experiment = false, #stable_interval = Set(0), @@ -132,41 +102,43 @@ ) const shepplogan = imgen_shepplogan_radon(p_known₀_pet.sz) +const p_known₀_pets = p_known₀_pet ⬿ ( seed = 314159, ) +const p_known₀_petb = p_known₀_pet ⬿ ( seed = 9182737465, ) const brainphantom = imgen_brainphantom_radon(p_known₀_pet.sz) const shepplogan_experiments_pdps_known = ( - Experiment(AlgorithmDualScaling, DisplacementConstant, shepplogan, - p_known₀_pet), - Experiment(AlgorithmGreedy, DisplacementConstant, shepplogan, - p_known₀_pet), - Experiment(AlgorithmNoPrediction, DisplacementConstant, shepplogan, - p_known₀_pet), - Experiment(AlgorithmPrimalOnly, DisplacementConstant, shepplogan, - p_known₀_pet), + Experiment(AlgorithmNew, DisplacementConstant, shepplogan, + p_known₀_pets ⬿ (predictor=DualScaling(),)), + Experiment(AlgorithmNew, DisplacementConstant, shepplogan, + p_known₀_pets ⬿ (predictor=Greedy(),)), + Experiment(AlgorithmNew, DisplacementConstant, shepplogan, + p_known₀_pets ⬿ (predictor=nothing,),), + Experiment(AlgorithmNew, DisplacementConstant, shepplogan, + p_known₀_pets ⬿ (predictor=PrimalOnly(),)), Experiment(AlgorithmProximal, DisplacementConstant, shepplogan, - p_known₀_pet ⬿ (phantom_ρ = 100,)), - Experiment(AlgorithmRotation, DisplacementConstant, shepplogan, - p_known₀_pet), - Experiment(AlgorithmZeroDual, DisplacementConstant, shepplogan, - p_known₀_pet), + p_known₀_pets ⬿ (phantom_ρ = 100,)), + Experiment(AlgorithmNew, DisplacementConstant, shepplogan, + p_known₀_pets ⬿ (predictor=Rotation(),)), + Experiment(AlgorithmNew, DisplacementConstant, shepplogan, + p_known₀_pets ⬿ (predictor=ZeroDual(),)), ) const brainphantom_experiments_pdps_known = ( - Experiment(AlgorithmDualScaling, DisplacementConstant, brainphantom, - p_known₀_pet), - Experiment(AlgorithmGreedy, DisplacementConstant, brainphantom, - p_known₀_pet), - Experiment(AlgorithmNoPrediction, DisplacementConstant, brainphantom, - p_known₀_pet), - Experiment(AlgorithmPrimalOnly, DisplacementConstant, brainphantom, - p_known₀_pet), + Experiment(AlgorithmNew, DisplacementConstant, brainphantom, + p_known₀_petb ⬿ (predictor=DualScaling(),)), + Experiment(AlgorithmNew, DisplacementConstant, brainphantom, + p_known₀_petb ⬿ (predictor=Greedy(),)), + Experiment(AlgorithmNew, DisplacementConstant, brainphantom, + p_known₀_petb ⬿ (predictor=nothing,),), + Experiment(AlgorithmNew, DisplacementConstant, brainphantom, + p_known₀_petb ⬿ (predictor=PrimalOnly(),)), Experiment(AlgorithmProximal, DisplacementConstant, brainphantom, - p_known₀_pet ⬿ (phantom_ρ = 100,)), - Experiment(AlgorithmRotation, DisplacementConstant, brainphantom, - p_known₀_pet), - Experiment(AlgorithmZeroDual, DisplacementConstant, brainphantom, - p_known₀_pet), + p_known₀_petb ⬿ (phantom_ρ = 100,)), + Experiment(AlgorithmNew, DisplacementConstant, brainphantom, + p_known₀_petb ⬿ (predictor=Rotation(),)), + Experiment(AlgorithmNew, DisplacementConstant, brainphantom, + p_known₀_petb ⬿ (predictor=ZeroDual(),)), ) @@ -178,167 +150,15 @@ brainphantom_experiments_pdps_known, )) -################ -# Log -################ - -struct LogEntry <: IterableStruct - iter :: Int - time :: Float64 - function_value :: Float64 - #v_cumul_true_y :: Float64 - #v_cumul_true_x :: Float64 - #v_cumul_est_y :: Float64 - #v_cumul_est_x :: Float64 - psnr :: Float64 - ssim :: Float64 - #psnr_data :: Float64 - #ssim_data :: Float64 -end - -struct LogEntryHiFi <: IterableStruct - iter :: Int - v_cumul_true_y :: Float64 - v_cumul_true_x :: Float64 -end - -############### -# Main routine -############### - -struct State - vis :: Union{Channel,Bool,Nothing} - start_time :: Union{Real,Nothing} - wasted_time :: Real - log :: LinkedList{LogEntry} - log_hifi :: LinkedList{LogEntryHiFi} - aborted :: Bool -end - -function name(e::Experiment, p) - ig = e.imgen - # return "$(ig.name)_$(e.mod.identifier)_$(@sprintf "%x" hash(p))" - return "$(ig.name)_$(e.mod.identifier)_$(Int64(100*p.α))_$(Int64(10000*p.σ₀))_$(Int64(10000*p.τ₀))" -end - -function write_tex(texfile, e_params) - open(texfile, "w") do io - wp = (n, v) -> println(io, "\\def\\EXPPARAM$(n){$(v)}") - wf = (n, s) -> if isdefined(e_params, s) - wp(n, getfield(e_params, s)) - end - wf("alpha", :α) - wf("sigmazero", :σ₀) - wf("tauzero", :τ₀) - wf("tildetauzero", :τ̃₀) - wf("delta", :δ) - wf("lambda", :λ) - wf("theta", :θ) - wf("maxiter", :maxiter) - wf("noiselevel", :noise_level) - wf("shakenoiselevel", :shake_noise_level) - wf("shake", :shake) - wf("timestep", :timestep) - wf("displacementcount", :displacementcount) - wf("phantomrho", :phantom_ρ) - if isdefined(e_params, :σ₀) - wp("sigma", (e_params.σ₀ == 1 ? "" : "$(e_params.σ₀)") * "\\sigma_{\\max}") - end - end -end - -function run_experiments(;visualise=true, - recalculate=true, - experiments, - save_prefix=default_save_prefix, - fullscreen=false, - kwargs...) - - # Create visualisation - if visualise - rc = Channel(1) - visproc = Threads.@spawn bg_visualise_enhanced(rc, fullscreen=fullscreen) - bind(rc, visproc) - vis = rc - else - vis = false - end - - # Run all experiments - for e ∈ experiments - - # Parameters for this experiment - e_params = default_params ⬿ e.params ⬿ kwargs - ename = name(e, e_params) - e_params = e_params ⬿ (save_prefix = save_prefix * ename, - dynrange = e.imgen.dynrange, - Λ = e.imgen.Λ) - - if recalculate || !isfile(e_params.save_prefix * ".txt") - println("Running experiment \"$(ename)\"") - - # Start data generation task - datachannel = Channel{PetOnlineData{e.DisplacementT}}(2) - gentask = Threads.@spawn e.imgen.f(e.DisplacementT, datachannel, e_params) - bind(datachannel, gentask) - - # Run algorithm - iterate = curry(iterate_visualise, datachannel, - State(vis, nothing, 0.0, nothing, nothing, false)) - - x, y, st = e.mod.solve(e.DisplacementT; - dim=e.imgen.dim, - iterate=iterate, - params=e_params) - - # Clear non-saveable things - st = @set st.vis = nothing - - println("Wasted_time: $(st.wasted_time)s") - - if e_params.save_results - println("Saving " * e_params.save_prefix * "(.txt,_hifi.txt,_params.tex)") - - perffile = e_params.save_prefix * ".txt" - hififile = e_params.save_prefix * "_hifi.txt" - texfile = e_params.save_prefix * "_params.tex" - # datafile = e_params.save_prefix * ".jld2" - - write_log(perffile, st.log, "# params = $(e_params)\n") - #write_log(hififile, st.log_hifi, "# params = $(e_params)\n") - #write_tex(texfile, e_params) - # @save datafile x y st params - end - - close(datachannel) - wait(gentask) - - if st.aborted - break - end - else - println("Skipping already computed experiment \"$(ename)\"") - # texfile = e_params.save_prefix * "_params.tex" - # write_tex(texfile, e_params) - end - end - - if visualise - # Tell subprocess to finish, and wait - put!(rc, nothing) - close(rc) - wait(visproc) - end - - return -end - ####################### # Demos and batch runs ####################### function demo(experiment; kwargs...) run_experiments(;experiments=(experiment,), + save_prefix=default_save_prefix, + visfn=iterate_visualise_pet, + datatype=PetOnlineData, save_results=false, save_images=false, visualise=true, @@ -367,6 +187,9 @@ function batchrun_shepplogan(;kwargs...) run_experiments(;experiments=shepplogan_experiments_all, + visfn=iterate_visualise_pet, + datatype=PetOnlineData, + save_prefix=default_save_prefix, save_results=true, save_images=true, visualise=false, @@ -376,6 +199,9 @@ function batchrun_brainphantom(;kwargs...) run_experiments(;experiments=brainphantom_experiments_all, + visfn=iterate_visualise_pet, + datatype=PetOnlineData, + save_prefix=default_save_prefix, save_results=true, save_images=true, visualise=false, @@ -395,21 +221,21 @@ function rescale(arr, new_range) old_min = minimum(arr) old_max = maximum(arr) - scale_factor = (new_range[2] - new_range[1]) / (old_max - old_min) + scale_factor = (new_range[2] - new_range[1]) / (old_max - old_min) scaled_arr = new_range[1] .+ (arr .- old_min) * scale_factor return scaled_arr end -function iterate_visualise(datachannel::Channel{PetOnlineData{DisplacementT}}, - st :: State, - step :: Function, - params :: NamedTuple) where DisplacementT +function iterate_visualise_pet(datachannel::Channel{PetOnlineData{DisplacementT}}, + st :: State, + step :: Function, + params :: NamedTuple) where DisplacementT try sc = nothing d = take!(datachannel) - for iter=1:params.maxiter + for iter=1:params.maxiter dnext = take!(datachannel) st = step(d.sinogram_noisy, d.v, d.theta, d.b_true, d.S) do calc_objective stn = st @@ -435,7 +261,7 @@ entry = LogEntry(iter, tm, value, #sc*d.v_cumul_true[1], #sc*d.v_cumul_true[2], - #sc*v[1], sc*v[2], + #sc*v[1], sc*v[2], assess_psnr(x, d.b_true), assess_ssim(x, d.b_true), #assess_psnr(d.b_noisy, d.b_true), @@ -509,39 +335,6 @@ return st end -function bg_visualise_enhanced(rc; fullscreen=false) - process_channel(rc) do d - imgs, plot_movement, log, vhist = d - do_visualise(imgs, refresh=false, fullscreen=fullscreen) - # Overlay movement - GR.settextcolorind(5) - GR.setcharheight(0.015) - GR.settextpath(GR.TEXT_PATH_RIGHT) - tx, ty = GR.wctondc(0, 1) - GR.text(tx, ty, @sprintf "FPS %.1f, SSIM %.2f, PSNR %.1f" (log.value.iter/log.value.time) log.value.ssim log.value.psnr) - if plot_movement - sc=1.0 - p=unfold_linked_list(log) - x=map(e -> 1.5+sc*e.v_cumul_true_x, p) - y=map(e -> 0.5+sc*e.v_cumul_true_y, p) - GR.setlinewidth(2) - GR.setlinecolorind(2) - GR.polyline(x, y) - x=map(e -> 1.5+sc*e.v_cumul_est_x, p) - y=map(e -> 0.5+sc*e.v_cumul_est_y, p) - GR.setlinecolorind(3) - GR.polyline(x, y) - if vhist != nothing - GR.setlinecolorind(4) - x=map(v -> 1.5+sc*v, vhist[:,2]) - y=map(v -> 0.5+sc*v, vhist[:,1]) - GR.polyline(x, y) - end - end - GR.updatews() - end -end - # Clip image values to allowed range clip = x -> min(max(x, 0.0), 1.0) @@ -557,22 +350,13 @@ # Plotting SSIM and PSNR ######################### -function plot_pet(kwargs...) - ssim_plot("shepplogan") - psnr_plot("shepplogan") - fv_plot("shepplogan") - ssim_plot("brainphantom") - psnr_plot("brainphantom") - fv_plot("brainphantom") -end - - -############### -# Precompiling -############### - -# precompile(Tuple{typeof(GR.drawimage), Float64, Float64, Float64, Float64, Int64, Int64, Array{UInt32, 2}}) -# precompile(Tuple{Type{Plots.Plot{T} where T<:RecipesBase.AbstractBackend}, Plots.GRBackend, Int64, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Array{Plots.Series, 1}, Nothing, Array{Plots.Subplot{T} where T<:RecipesBase.AbstractBackend, 1}, Base.Dict{Any, Plots.Subplot{T} where T<:RecipesBase.AbstractBackend}, Plots.EmptyLayout, Array{Plots.Subplot{T} where T<:RecipesBase.AbstractBackend, 1}, Bool}) -# precompile(Tuple{typeof(Plots._plot!), Plots.Plot{Plots.GRBackend}, Base.Dict{Symbol, Any}, Tuple{Array{ColorTypes.Gray{Float64}, 2}}}) +#function plot_pet(kwargs...) +# ssim_plot("shepplogan") +# psnr_plot("shepplogan") +# fv_plot("shepplogan") +# ssim_plot("brainphantom") +# psnr_plot("brainphantom") +# fv_plot("brainphantom") +#end end # Module diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PET/Radon.jl --- a/src/PET/Radon.jl Thu Apr 25 19:16:17 2024 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -# From https://github.com/LMescheder/Radon.jl/blob/master/src/Radon.jl -# Updated to modern Julia. Fixed radon! to zero out `radonim`. - -__precompile__() - - -module Radon -export radon!, backproject! -# package code goes here - -function radon(im::AbstractArray{T, 2}) where T<:AbstractFloat - Nx, Ny = size(im) - radonim = similar(im, min(Nx, Ny), max(Nx, Ny)) - radon!(radonim, im) - radonim -end - -function radon!(radonim::AbstractArray{T, 2}, im::AbstractArray{T, 2}) where T<:AbstractFloat - Nφ, Ns = size(radonim) - Nx, Ny = size(im) - Ldiag = hypot(Nx, Ny) - K = round(Int, Ldiag) - - dLz = Ldiag/(K-1) - dLs = Ldiag/(Ns-1) - dφ = π/Nφ - - @inbounds for is = 1:Ns, iφ = 1:Nφ - φ = (iφ-1)*dφ - s = -Ldiag/2 + (is-1)*dLs - tx = cos(φ) - ty = sin(φ) - - radonim[iφ, is] = zero(T) - for k=1:K - z = -Ldiag/2 + (k-1)*dLz - x = round(Int, Nx/2 + z*ty + s*tx) - y = round(Int, Ny/2 - z*tx + s*ty) - - if 1 <= x <= Nx && 1 <= y <= Ny - radonim[iφ, is] += im[x, y]*dLz - end - end - end - radonim -end - -function backproject(radonim::AbstractArray{T, 2}) where T<:AbstractFloat - Nφ, Ns = size(radonim) - im = similar(radonim, Ns, Ns) - backproject!(im, radonim) - im -end - -function backproject!(im::AbstractArray{T, 2}, radonim::AbstractArray{T, 2}) where T<:AbstractFloat - Nφ, Ns = size(radonim) - Nx, Ny = size(im) - Ldiag = hypot(Nx, Ny) - K = round(Int, Ldiag) - - dLz = Ldiag/(K-1) - dLs = Ldiag/(Ns-1) - dφ = π/Nφ - - im .= 0. - @inbounds for is = 1:Ns, iφ = 1:Nφ - φ = (iφ-1)*dφ - s = -Ldiag/2 + (is-1)*dLs - tx = cos(φ) - ty = sin(φ) - - for k=1:K - z = -Ldiag/2 + (k-1)*dLz - x = round(Int, Nx/2 + z*ty + s*tx) - y = round(Int, Ny/2 - z*tx + s*ty) - - if 1 <= x <= Nx && 1 <= y <= Ny - im[x, y] += radonim[iφ, is]*dLz - end - end - end - im -end - -end # module diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PlotResults.jl --- a/src/PlotResults.jl Thu Apr 25 19:16:17 2024 +0300 +++ b/src/PlotResults.jl Thu Apr 25 14:20:38 2024 -0500 @@ -1,5 +1,3 @@ -__precompile__() - module PlotResults @@ -7,13 +5,12 @@ # Load external modules ######################## -using DelimitedFiles, CSV, DataFrames +using CSV, DataFrames using PlotlyJS using Colors -using XLSX: writetable using Statistics -export fv_plot, ssim_plot, psnr_plot, calculate_statistics +export fv_plot, ssim_plot, psnr_plot global mystart = 38 global myend = 135 @@ -656,66 +653,4 @@ end -function calculate_statistics() - ImName = ("lighthouse200x300", "shepplogan256x256", "brainphantom256x256") - AlgName = ("dualscaling", "greedy","noprediction","primalonly","proximal","rotation","zerodual") - mystart = 41 # Corresponds to the 500th iterate - - # Define an array to store results - results = DataFrame(experiment = String[], α = Float64[], algorithm = String[], psnr_mean1 = Float64[], psnr_mean500 = Float64[], psnr_ci = String[], ssim_mean1 = Float64[], ssim_mean500 = Float64[], ssim_ci = String[]) - - for imname in ImName - for algname in AlgName - directory_path = "./img/" - files = readdir(directory_path) - filtered_files = filter(file -> startswith(file, "$(imname)_pdps_known_$(algname)") && endswith(file, ".txt"), files) - - for file in filtered_files - filename = directory_path * file - data = CSV.File(filename, delim='\t', header=2) |> DataFrame - - # Extract α from filename - α, _, _ = extract_parameters(filename) - - - # Extract SSIM and PSNR columns starting from 1st iteration - ssim_values1 = Float64.(data[:, :ssim]) - psnr_values1 = Float64.(data[:, :psnr]) - - # Extract SSIM and PSNR columns starting from 500th iteration - ssim_values500 = Float64.(data[mystart:end, :ssim]) - psnr_values500 = Float64.(data[mystart:end, :psnr]) - - # Calculate mean and confidence intervals - ssim_mean1 = round(mean(ssim_values1), digits=4) - psnr_mean1 = round(mean(psnr_values1), digits=4) - - ssim_mean500 = round(mean(ssim_values500), digits=4) - psnr_mean500 = round(mean(psnr_values500), digits=4) - ssim_std500 = round(std(ssim_values500), digits=4) - psnr_std500 = round(std(psnr_values500), digits=4) - n = length(ssim_values500) - - ssim_ci_lower = round(ssim_mean500 - 1.96 * ssim_std500 / sqrt(n), digits=4) - ssim_ci_upper = round(ssim_mean500 + 1.96 * ssim_std500 / sqrt(n), digits=4) - psnr_ci_lower = round(psnr_mean500 - 1.96 * psnr_std500 / sqrt(n), digits=4) - psnr_ci_upper = round(psnr_mean500 + 1.96 * psnr_std500 / sqrt(n), digits=4) - - ssim_ci = "$(ssim_ci_lower) - $(ssim_ci_upper)" - psnr_ci = "$(psnr_ci_lower) - $(psnr_ci_upper)" - experiment = "$(imname)" - algorithm = "$(algname)" - - # Append results to DataFrame - push!(results, (experiment, α, algorithm, psnr_mean1, psnr_mean500, psnr_ci, ssim_mean1, ssim_mean500, ssim_ci)) - end - end - end - sort!(results, [:experiment, :α]) - excel_path = "./img/summarystats.xlsx" - if isfile(excel_path) - rm(excel_path) - end - writetable(excel_path, sheetname="Experiments", results) -end end # Module \ No newline at end of file diff -r 75116ad1d2e6 -r b180a4f3b9bd src/PredictPDPS.jl --- a/src/PredictPDPS.jl Thu Apr 25 19:16:17 2024 +0300 +++ b/src/PredictPDPS.jl Thu Apr 25 14:20:38 2024 -0500 @@ -10,20 +10,8 @@ # Load external modules ######################## -using Printf -using FileIO -#using JLD2 -using Setfield -using ImageQualityIndexes: assess_psnr, assess_ssim -using DelimitedFiles -import GR - +using ImageTools.ImFilter: gaussian using AlgTools.Util -using AlgTools.StructTools -using AlgTools.LinkedLists -using AlgTools.Comms -using ImageTools.Visualise: secs_ns, grayimg, do_visualise -using ImageTools.ImFilter: gaussian ##################### # Load local modules @@ -32,48 +20,29 @@ include("OpticalFlow.jl") include("Radon.jl") include("ImGenerate.jl") -include("Algorithm.jl") -include("AlgorithmBoth.jl") -include("AlgorithmBothGreedyV.jl") -include("AlgorithmBothCumul.jl") +include("Run.jl") +include("AlgorithmProximal.jl") include("AlgorithmBothMulti.jl") -include("AlgorithmBothNL.jl") include("AlgorithmFB.jl") include("AlgorithmFBDual.jl") -include("PlotResults.jl") - - -# Additional -include("AlgorithmProximal.jl") -include("AlgorithmGreedy.jl") -include("AlgorithmRotation.jl") -include("AlgorithmNoPrediction.jl") -include("AlgorithmPrimalOnly.jl") -include("AlgorithmDualScaling.jl") -include("AlgorithmZeroDual.jl") +include("AlgorithmNew.jl") +include("Stats.jl") +#include("PlotResults.jl") include("PET/PET.jl") -import .Algorithm, - .AlgorithmBoth, - .AlgorithmBothGreedyV, - .AlgorithmBothCumul, - .AlgorithmBothMulti, - .AlgorithmBothNL, +import .AlgorithmBothMulti, .AlgorithmFB, .AlgorithmFBDual, .AlgorithmProximal, - .AlgorithmGreedy, - .AlgorithmRotation, - .AlgorithmNoPrediction, - .AlgorithmPrimalOnly, - .AlgorithmDualScaling, - .AlgorithmZeroDual + .AlgorithmNew using .ImGenerate -using .OpticalFlow: DisplacementFull, DisplacementConstant -using .PlotResults +using .OpticalFlow +using .Stats +#using .PlotResults using .PET +using .Run ############## # Our exports @@ -85,37 +54,20 @@ demo_unknown1,demo_unknown2,demo_unknown3, batchrun_denoising, batchrun_predictors, - demo_denoising1, demo_denoising2, demo_denoising3, + demo_denoising1, demo_denoising2, demo_denoising3, demo_denoising4, demo_denoising5, demo_denoising6, demo_denoising7, - demo_petS1, demo_petS2, demo_petS3, + demo_petS1, demo_petS2, demo_petS3, demo_petS4, demo_petS5, demo_petS6, demo_petS7, - demo_petB1, demo_petB2, demo_petB3, + demo_petB1, demo_petB2, demo_petB3, demo_petB4, demo_petB5, demo_petB6, demo_petB7, batchrun_shepplogan, batchrun_brainphantom, batchrun_pet, - plot_denoising, plot_pet, calculate_statistics + calculate_statistics + #plot_denoising, plot_pet, ################################### # Parameterisation and experiments ################################### -struct Experiment - mod :: Module - DisplacementT :: Type - imgen :: ImGen - params :: NamedTuple -end - -function Base.show(io::IO, e::Experiment) - displacementname(::Type{DisplacementFull}) = "DisplacementFull" - displacementname(::Type{DisplacementConstant}) = "DisplacementConstant" - print(io, " - mod: $(e.mod) - DisplacementT: $(displacementname(e.DisplacementT)) - imgen: $(e.imgen.name) $(e.imgen.dim[1])×$(e.imgen.dim[2]) - params: $(e.params ⬿ (kernel = "(not shown)",)) - ") -end - const default_save_prefix="img/" const default_params = ( @@ -130,8 +82,7 @@ 1000, 2000, 2500, 3000, 4000, 5000, 6000, 7000, 7500, 8000, 9000, 10000, 8700]), pixelwise_displacement=false, - dual_flow = true, - prox_predict = true, + dual_flow = true, # For AlgorithmProximalfrom 2019 paper handle_interrupt = true, init = :zero, plot_movement = false, @@ -141,7 +92,7 @@ const square = imgen_square((200, 300)) const lighthouse = imgen_shake("lighthouse", (200, 300)) -const p_known₀_denoising = ( +const p_known₀_denoising = default_params ⬿ ( noise_level = 0.5, shake_noise_level = 0.025, shake = 2.0, @@ -155,7 +106,7 @@ stable_interval = union(Set(2500:5000),Set(8700:10000)), ) -const p_known₀ = ( +const p_known₀ = default_params ⬿ ( noise_level = 0.5, shake_noise_level = 0.05, shake = 2, @@ -167,7 +118,7 @@ τ₀ = 0.01, ) -const p_unknown₀ = ( +const p_unknown₀ = default_params ⬿ ( noise_level = 0.3, shake_noise_level = 0.05, shake = 2, @@ -185,12 +136,14 @@ ) +# Experiments for 2019 paper + const experiments_pdps_known = ( - Experiment(Algorithm, DisplacementConstant, lighthouse, + Experiment(AlgorithmProximal, DisplacementConstant, lighthouse, p_known₀_denoising ⬿ (phantom_ρ = 0,)), - Experiment(Algorithm, DisplacementConstant, lighthouse, + Experiment(AlgorithmProximal, DisplacementConstant, lighthouse, p_known₀ ⬿ (phantom_ρ = 100,)), - Experiment(Algorithm, DisplacementConstant, square, + Experiment(AlgorithmProximal, DisplacementConstant, square, p_known₀ ⬿ (phantom_ρ = 0,)) ) @@ -214,182 +167,29 @@ experiments_fb_known )) +# Image stabilisation experiments for 2024 paper. PET experiments are in PET/PET.jl + const denoising_experiments_pdps_known = ( - Experiment(AlgorithmDualScaling, DisplacementConstant, lighthouse, - p_known₀_denoising), - Experiment(AlgorithmGreedy, DisplacementConstant, lighthouse, - p_known₀_denoising), - Experiment(AlgorithmNoPrediction, DisplacementConstant, lighthouse, - p_known₀_denoising), - Experiment(AlgorithmPrimalOnly, DisplacementConstant, lighthouse, - p_known₀_denoising), + Experiment(AlgorithmNew, DisplacementConstant, lighthouse, + p_known₀_denoising ⬿ (predictor=DualScaling(),)), + Experiment(AlgorithmNew, DisplacementConstant, lighthouse, + p_known₀_denoising ⬿ (predictor=Greedy(),)), + Experiment(AlgorithmNew, DisplacementConstant, lighthouse, + p_known₀_denoising ⬿ (predictor=nothing,),), + Experiment(AlgorithmNew, DisplacementConstant, lighthouse, + p_known₀_denoising ⬿ (predictor=PrimalOnly(),)), Experiment(AlgorithmProximal, DisplacementConstant, lighthouse, p_known₀_denoising ⬿ (phantom_ρ = 100,)), - Experiment(AlgorithmRotation, DisplacementConstant, lighthouse, - p_known₀_denoising), - Experiment(AlgorithmZeroDual, DisplacementConstant, lighthouse, - p_known₀_denoising), + Experiment(AlgorithmNew, DisplacementConstant, lighthouse, + p_known₀_denoising ⬿ (predictor=Rotation(),)), + Experiment(AlgorithmNew, DisplacementConstant, lighthouse, + p_known₀_denoising ⬿ (predictor=ZeroDual(),)), ) const denoising_experiments_all = Iterators.flatten(( denoising_experiments_pdps_known, )) -################ -# Log -################ - -struct LogEntry <: IterableStruct - iter :: Int - time :: Float64 - function_value :: Float64 - #v_cumul_true_y :: Float64 - #v_cumul_true_x :: Float64 - #v_cumul_est_y :: Float64 - #v_cumul_est_x :: Float64 - psnr :: Float64 - ssim :: Float64 - #psnr_data :: Float64 - #ssim_data :: Float64 -end - -struct LogEntryHiFi <: IterableStruct - iter :: Int - v_cumul_true_y :: Float64 - v_cumul_true_x :: Float64 -end - -############### -# Main routine -############### - -struct State - vis :: Union{Channel,Bool,Nothing} - start_time :: Union{Real,Nothing} - wasted_time :: Real - log :: LinkedList{LogEntry} - log_hifi :: LinkedList{LogEntryHiFi} - aborted :: Bool -end - -function name(e::Experiment, p) - ig = e.imgen - # return "$(ig.name)_$(e.mod.identifier)_$(@sprintf "%x" hash(p))" - return "$(ig.name)_$(e.mod.identifier)_$(Int64(100*p.α))_$(Int64(10000*p.σ₀))_$(Int64(10000*p.τ₀))" -end - -function write_tex(texfile, e_params) - open(texfile, "w") do io - wp = (n, v) -> println(io, "\\def\\EXPPARAM$(n){$(v)}") - wf = (n, s) -> if isdefined(e_params, s) - wp(n, getfield(e_params, s)) - end - wf("alpha", :α) - wf("sigmazero", :σ₀) - wf("tauzero", :τ₀) - wf("tildetauzero", :τ̃₀) - wf("delta", :δ) - wf("lambda", :λ) - wf("theta", :θ) - wf("maxiter", :maxiter) - wf("noiselevel", :noise_level) - wf("shakenoiselevel", :shake_noise_level) - wf("shake", :shake) - wf("timestep", :timestep) - wf("displacementcount", :displacementcount) - wf("phantomrho", :phantom_ρ) - if isdefined(e_params, :σ₀) - wp("sigma", (e_params.σ₀ == 1 ? "" : "$(e_params.σ₀)") * "\\sigma_{\\max}") - end - end -end - -function run_experiments(;visualise=true, - recalculate=true, - experiments, - save_prefix=default_save_prefix, - fullscreen=false, - kwargs...) - - # Create visualisation - if visualise - rc = Channel(1) - visproc = Threads.@spawn bg_visualise_enhanced(rc, fullscreen=fullscreen) - bind(rc, visproc) - vis = rc - else - vis = false - end - - # Run all experiments - for e ∈ experiments - - # Parameters for this experiment - e_params = default_params ⬿ e.params ⬿ kwargs - ename = name(e, e_params) - e_params = e_params ⬿ (save_prefix = save_prefix * ename, - dynrange = e.imgen.dynrange, - Λ = e.imgen.Λ) - - if recalculate || !isfile(e_params.save_prefix * ".txt") - println("Running experiment \"$(ename)\"") - - # Start data generation task - datachannel = Channel{OnlineData{e.DisplacementT}}(2) - gentask = Threads.@spawn e.imgen.f(e.DisplacementT, datachannel, e_params) - bind(datachannel, gentask) - - # Run algorithm - iterate = curry(iterate_visualise, datachannel, - State(vis, nothing, 0.0, nothing, nothing, false)) - - x, y, st = e.mod.solve(e.DisplacementT; - dim=e.imgen.dim, - iterate=iterate, - params=e_params) - - # Clear non-saveable things - st = @set st.vis = nothing - - println("Wasted_time: $(st.wasted_time)s") - - if e_params.save_results - println("Saving " * e_params.save_prefix * "(.txt,_hifi.txt,_params.tex)") - - perffile = e_params.save_prefix * ".txt" - hififile = e_params.save_prefix * "_hifi.txt" - texfile = e_params.save_prefix * "_params.tex" - # datafile = e_params.save_prefix * ".jld2" - - write_log(perffile, st.log, "# params = $(e_params)\n") - #write_log(hififile, st.log_hifi, "# params = $(e_params)\n") - #write_tex(texfile, e_params) - # @save datafile x y st params - end - - close(datachannel) - wait(gentask) - - if st.aborted - break - end - else - println("Skipping already computed experiment \"$(ename)\"") - # texfile = e_params.save_prefix * "_params.tex" - # write_tex(texfile, e_params) - end - end - - if visualise - # Tell subprocess to finish, and wait - put!(rc, nothing) - close(rc) - wait(visproc) - end - - return -end - ####################### # Demos and batch runs ####################### @@ -398,6 +198,7 @@ run_experiments(;experiments=(experiment,), save_results=false, save_images=false, + save_prefix=default_save_prefix, visualise=true, recalculate=true, verbose_iter=50, @@ -423,6 +224,7 @@ function batchrun_article(kwargs...) run_experiments(;experiments=experiments_all, + save_prefix=default_save_prefix, save_results=true, save_images=true, visualise=false, @@ -432,6 +234,7 @@ function batchrun_denoising(;kwargs...) run_experiments(;experiments=denoising_experiments_all, + save_prefix=default_save_prefix, save_results=true, save_images=true, visualise=false, @@ -445,167 +248,14 @@ batchrun_pet(;kwargs...) end -###################################################### -# Iterator that does visualisation and log collection -###################################################### - -function iterate_visualise(datachannel::Channel{OnlineData{DisplacementT}}, - st :: State, - step :: Function, - params :: NamedTuple) where DisplacementT - try - sc = nothing - - d = take!(datachannel) - - for iter=1:params.maxiter - dnext = take!(datachannel) - st = step(d.b_noisy, d.v, dnext.b_noisy) do calc_objective - stn = st - - if isnothing(stn.start_time) - # The Julia precompiler is a miserable joke, apparently not crossing module - # boundaries, so only start timing after the first iteration. - stn = @set stn.start_time=secs_ns() - end - - verb = params.verbose_iter!=0 && mod(iter, params.verbose_iter) == 0 - - # Normalise movement to image dimensions so - # our TikZ plotting code doesn't need to know - # the image pixel size. - sc = 1.0./maximum(size(d.b_true)) - - if verb || iter ≤ 20 || (iter ≤ 200 && mod(iter, 10) == 0) - verb_start = secs_ns() - tm = verb_start - stn.start_time - stn.wasted_time - value, x, v, vhist = calc_objective() - - entry = LogEntry(iter, tm, value, - #sc*d.v_cumul_true[1], - #sc*d.v_cumul_true[2], - #sc*v[1], sc*v[2], - assess_psnr(x, d.b_true), - assess_ssim(x, d.b_true), - #assess_psnr(d.b_noisy, d.b_true), - #assess_ssim(d.b_noisy, d.b_true) - ) - - # (**) Collect a singly-linked list of log to avoid array resizing - # while iterating - stn = @set stn.log=LinkedListEntry(entry, stn.log) - - if !isnothing(vhist) - vhist=vhist.*sc - end - - if verb - @printf("%d/%d J=%f, PSNR=%f, SSIM=%f, avg. FPS=%f\n", - iter, params.maxiter, value, entry.psnr, - entry.ssim, entry.iter/entry.time) - if isa(stn.vis, Channel) - put_onlylatest!(stn.vis, ((d.b_noisy, x), - params.plot_movement, - stn.log, vhist)) - - end - end - - if params.save_images && (!haskey(params, :save_images_iters) - || iter ∈ params.save_images_iters) - fn = (t, ext) -> "$(params.save_prefix)_$(t)_frame$(iter).$(ext)" - # save(File(format"PNG", fn("true", "png")), grayimg(d.b_true)) - # save(File(format"PNG", fn("data", "png")), grayimg(d.b_noisy)) - save(File(format"PNG", fn("reco", "png")), grayimg(x)) - if !isnothing(vhist) - open(fn("movement", "txt"), "w") do io - writedlm(io, ["est_y" "est_x"]) - writedlm(io, vhist) - end - end - end - - stn = @set stn.wasted_time += (secs_ns() - verb_start) - - return stn - end - - hifientry = LogEntryHiFi(iter, sc*d.v_cumul_true[1], sc*d.v_cumul_true[2]) - st = @set st.log_hifi=LinkedListEntry(hifientry, st.log_hifi) - - return st - end - d=dnext - end - catch ex - if params.handle_interrupt && isa(ex, InterruptException) - # If SIGINT is received (user pressed ^C), terminate computations, - # returning current status. Effectively, we do not call `step()` again, - # ending the iterations, but letting the algorithm finish up. - # Assuming (**) above occurs atomically, `st.log` should be valid, but - # any results returned by the algorithm itself may be partial, as for - # reasons of efficiency we do *not* store results of an iteration until - # the next iteration is finished. - printstyled("\rUser interrupt—finishing up.\n", bold=true, color=202) - st = @set st.aborted = true - else - rethrow(ex) - end - end - - return st -end - -function bg_visualise_enhanced(rc; fullscreen=false) - process_channel(rc) do d - imgs, plot_movement, log, vhist = d - do_visualise(imgs, refresh=false, fullscreen=fullscreen) - # Overlay movement - GR.settextcolorind(5) - GR.setcharheight(0.015) - GR.settextpath(GR.TEXT_PATH_RIGHT) - tx, ty = GR.wctondc(0, 1) - GR.text(tx, ty, @sprintf "FPS %.1f, SSIM %.2f, PSNR %.1f" (log.value.iter/log.value.time) log.value.ssim log.value.psnr) - if plot_movement - sc=1.0 - p=unfold_linked_list(log) - x=map(e -> 1.5+sc*e.v_cumul_true_x, p) - y=map(e -> 0.5+sc*e.v_cumul_true_y, p) - GR.setlinewidth(2) - GR.setlinecolorind(2) - GR.polyline(x, y) - x=map(e -> 1.5+sc*e.v_cumul_est_x, p) - y=map(e -> 0.5+sc*e.v_cumul_est_y, p) - GR.setlinecolorind(3) - GR.polyline(x, y) - if vhist != nothing - GR.setlinecolorind(4) - x=map(v -> 1.5+sc*v, vhist[:,2]) - y=map(v -> 0.5+sc*v, vhist[:,1]) - GR.polyline(x, y) - end - end - GR.updatews() - end -end - ######################### # Plotting SSIM and PSNR ######################### -function plot_denoising(kwargs...) - ssim_plot("lighthouse") - psnr_plot("lighthouse") - fv_plot("lighthouse") -end - - -############### -# Precompiling -############### - -# precompile(Tuple{typeof(GR.drawimage), Float64, Float64, Float64, Float64, Int64, Int64, Array{UInt32, 2}}) -# precompile(Tuple{Type{Plots.Plot{T} where T<:RecipesBase.AbstractBackend}, Plots.GRBackend, Int64, Base.Dict{Symbol, Any}, Base.Dict{Symbol, Any}, Array{Plots.Series, 1}, Nothing, Array{Plots.Subplot{T} where T<:RecipesBase.AbstractBackend, 1}, Base.Dict{Any, Plots.Subplot{T} where T<:RecipesBase.AbstractBackend}, Plots.EmptyLayout, Array{Plots.Subplot{T} where T<:RecipesBase.AbstractBackend, 1}, Bool}) -# precompile(Tuple{typeof(Plots._plot!), Plots.Plot{Plots.GRBackend}, Base.Dict{Symbol, Any}, Tuple{Array{ColorTypes.Gray{Float64}, 2}}}) +#function plot_denoising(kwargs...) +# ssim_plot("lighthouse") +# psnr_plot("lighthouse") +# fv_plot("lighthouse") +#end end # Module diff -r 75116ad1d2e6 -r b180a4f3b9bd src/Run.jl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Run.jl Thu Apr 25 14:20:38 2024 -0500 @@ -0,0 +1,360 @@ +################## +# Experiment running and interactive visualisation +################## + +module Run + +import GR +using Setfield +using Printf +using ImageQualityIndexes: assess_psnr, assess_ssim +using FileIO +#using JLD2 +using DelimitedFiles + +using AlgTools.Util +using AlgTools.StructTools +using AlgTools.LinkedLists +using AlgTools.Comms + +using ImageTools.Visualise: secs_ns, grayimg, do_visualise + +using ..ImGenerate +using ..OpticalFlow: identifier, DisplacementFull, DisplacementConstant + +export run_experiments, + Experiment, + State, + LogEntry, + LogEntryHiFi + +################ +# Experiment +################ + +struct Experiment + mod :: Module + DisplacementT :: Type + imgen :: ImGen + params :: NamedTuple +end + +function Base.show(io::IO, e::Experiment) + displacementname(::Type{DisplacementFull}) = "DisplacementFull" + displacementname(::Type{DisplacementConstant}) = "DisplacementConstant" + print(io, " + mod: $(e.mod) + DisplacementT: $(displacementname(e.DisplacementT)) + imgen: $(e.imgen.name) $(e.imgen.dim[1])×$(e.imgen.dim[2]) + params: $(e.params ⬿ (kernel = "(not shown)",)) + ") +end + +################ +# Log +################ + +struct LogEntry <: IterableStruct + iter :: Int + time :: Float64 + function_value :: Float64 + #v_cumul_true_y :: Float64 + #v_cumul_true_x :: Float64 + #v_cumul_est_y :: Float64 + #v_cumul_est_x :: Float64 + psnr :: Float64 + ssim :: Float64 + #psnr_data :: Float64 + #ssim_data :: Float64 +end + +struct LogEntryHiFi <: IterableStruct + iter :: Int + v_cumul_true_y :: Float64 + v_cumul_true_x :: Float64 +end + +############### +# Main routine +############### + +struct State + vis :: Union{Channel,Bool,Nothing} + start_time :: Union{Real,Nothing} + wasted_time :: Real + log :: LinkedList{LogEntry} + log_hifi :: LinkedList{LogEntryHiFi} + aborted :: Bool +end + +function name(e::Experiment, p) + ig = e.imgen + id = if haskey(p, :predictor) && ~isnothing(p.predictor) + identifier(p.predictor) + else + e.mod.identifier + end + # return "$(ig.name)_$(e.mod.identifier)_$(@sprintf "%x" hash(p))" + return "$(ig.name)_$(id)_$(Int64(100*p.α))_$(Int64(10000*p.σ₀))_$(Int64(10000*p.τ₀))" +end + +function write_tex(texfile, e_params) + open(texfile, "w") do io + wp = (n, v) -> println(io, "\\def\\EXPPARAM$(n){$(v)}") + wf = (n, s) -> if isdefined(e_params, s) + wp(n, getfield(e_params, s)) + end + wf("alpha", :α) + wf("sigmazero", :σ₀) + wf("tauzero", :τ₀) + wf("tildetauzero", :τ̃₀) + wf("delta", :δ) + wf("lambda", :λ) + wf("theta", :θ) + wf("maxiter", :maxiter) + wf("noiselevel", :noise_level) + wf("shakenoiselevel", :shake_noise_level) + wf("shake", :shake) + wf("timestep", :timestep) + wf("displacementcount", :displacementcount) + wf("phantomrho", :phantom_ρ) + if isdefined(e_params, :σ₀) + wp("sigma", (e_params.σ₀ == 1 ? "" : "$(e_params.σ₀)") * "\\sigma_{\\max}") + end + end +end + +function run_experiments(;visualise=true, + visfn = iterate_visualise, + datatype = OnlineData, + recalculate=true, + experiments, + save_prefix="", + fullscreen=false, + kwargs...) + + # Create visualisation + if visualise + rc = Channel(1) + visproc = Threads.@spawn bg_visualise_enhanced(rc, fullscreen=fullscreen) + bind(rc, visproc) + vis = rc + else + vis = false + end + + # Run all experiments + for e ∈ experiments + + # Parameters for this experiment + e_params = e.params ⬿ kwargs + ename = name(e, e_params) + e_params = e_params ⬿ (save_prefix = save_prefix * ename, + dynrange = e.imgen.dynrange, + Λ = e.imgen.Λ) + + if recalculate || !isfile(e_params.save_prefix * ".txt") + println("Running experiment \"$(ename)\"") + + # Start data generation task + datachannel = Channel{datatype{e.DisplacementT}}(2) + gentask = Threads.@spawn e.imgen.f(e.DisplacementT, datachannel, e_params) + bind(datachannel, gentask) + + # Run algorithm + iterate = curry(visfn, datachannel, + State(vis, nothing, 0.0, nothing, nothing, false)) + + x, y, st = e.mod.solve(e.DisplacementT; + dim=e.imgen.dim, + iterate=iterate, + params=e_params) + + # Clear non-saveable things + st = @set st.vis = nothing + + println("Wasted_time: $(st.wasted_time)s") + + if e_params.save_results + println("Saving " * e_params.save_prefix * "(.txt,_hifi.txt,_params.tex)") + + perffile = e_params.save_prefix * ".txt" + hififile = e_params.save_prefix * "_hifi.txt" + texfile = e_params.save_prefix * "_params.tex" + # datafile = e_params.save_prefix * ".jld2" + + write_log(perffile, st.log, "# params = $(e_params)\n") + #write_log(hififile, st.log_hifi, "# params = $(e_params)\n") + #write_tex(texfile, e_params) + # @save datafile x y st params + end + + close(datachannel) + wait(gentask) + + if st.aborted + break + end + else + println("Skipping already computed experiment \"$(ename)\"") + # texfile = e_params.save_prefix * "_params.tex" + # write_tex(texfile, e_params) + end + end + + if visualise + # Tell subprocess to finish, and wait + put!(rc, nothing) + close(rc) + wait(visproc) + end + + return +end + + +###################################################### +# Iterator that does visualisation and log collection +###################################################### + +function iterate_visualise(datachannel::Channel{OnlineData{DisplacementT}}, + st :: State, + step :: Function, + params :: NamedTuple) where DisplacementT + try + sc = nothing + + d = take!(datachannel) + + for iter=1:params.maxiter + dnext = take!(datachannel) + st = step(d.b_noisy, d.v, dnext.b_noisy) do calc_objective + stn = st + + if isnothing(stn.start_time) + # The Julia precompiler is a miserable joke, apparently not crossing module + # boundaries, so only start timing after the first iteration. + stn = @set stn.start_time=secs_ns() + end + + verb = params.verbose_iter!=0 && mod(iter, params.verbose_iter) == 0 + + # Normalise movement to image dimensions so + # our TikZ plotting code doesn't need to know + # the image pixel size. + sc = 1.0./maximum(size(d.b_true)) + + if verb || iter ≤ 20 || (iter ≤ 200 && mod(iter, 10) == 0) + verb_start = secs_ns() + tm = verb_start - stn.start_time - stn.wasted_time + value, x, v, vhist = calc_objective() + + entry = LogEntry(iter, tm, value, + #sc*d.v_cumul_true[1], + #sc*d.v_cumul_true[2], + #sc*v[1], sc*v[2], + assess_psnr(x, d.b_true), + assess_ssim(x, d.b_true), + #assess_psnr(d.b_noisy, d.b_true), + #assess_ssim(d.b_noisy, d.b_true) + ) + + # (**) Collect a singly-linked list of log to avoid array resizing + # while iterating + stn = @set stn.log=LinkedListEntry(entry, stn.log) + + if !isnothing(vhist) + vhist=vhist.*sc + end + + if verb + @printf("%d/%d J=%f, PSNR=%f, SSIM=%f, avg. FPS=%f\n", + iter, params.maxiter, value, entry.psnr, + entry.ssim, entry.iter/entry.time) + if isa(stn.vis, Channel) + put_onlylatest!(stn.vis, ((d.b_noisy, x), + params.plot_movement, + stn.log, vhist)) + + end + end + + if params.save_images && (!haskey(params, :save_images_iters) + || iter ∈ params.save_images_iters) + fn = (t, ext) -> "$(params.save_prefix)_$(t)_frame$(iter).$(ext)" + # save(File(format"PNG", fn("true", "png")), grayimg(d.b_true)) + # save(File(format"PNG", fn("data", "png")), grayimg(d.b_noisy)) + save(File(format"PNG", fn("reco", "png")), grayimg(x)) + if !isnothing(vhist) + open(fn("movement", "txt"), "w") do io + writedlm(io, ["est_y" "est_x"]) + writedlm(io, vhist) + end + end + end + + stn = @set stn.wasted_time += (secs_ns() - verb_start) + + return stn + end + + hifientry = LogEntryHiFi(iter, sc*d.v_cumul_true[1], sc*d.v_cumul_true[2]) + st = @set st.log_hifi=LinkedListEntry(hifientry, st.log_hifi) + + return st + end + d=dnext + end + catch ex + if params.handle_interrupt && isa(ex, InterruptException) + # If SIGINT is received (user pressed ^C), terminate computations, + # returning current status. Effectively, we do not call `step()` again, + # ending the iterations, but letting the algorithm finish up. + # Assuming (**) above occurs atomically, `st.log` should be valid, but + # any results returned by the algorithm itself may be partial, as for + # reasons of efficiency we do *not* store results of an iteration until + # the next iteration is finished. + printstyled("\rUser interrupt—finishing up.\n", bold=true, color=202) + st = @set st.aborted = true + else + rethrow(ex) + end + end + + return st +end + +function bg_visualise_enhanced(rc; fullscreen=false) + process_channel(rc) do d + imgs, plot_movement, log, vhist = d + do_visualise(imgs, refresh=false, fullscreen=fullscreen) + # Overlay movement + GR.settextcolorind(5) + GR.setcharheight(0.015) + GR.settextpath(GR.TEXT_PATH_RIGHT) + tx, ty = GR.wctondc(0, 1) + GR.text(tx, ty, @sprintf "FPS %.1f, SSIM %.2f, PSNR %.1f" (log.value.iter/log.value.time) log.value.ssim log.value.psnr) + if plot_movement + sc=1.0 + p=unfold_linked_list(log) + x=map(e -> 1.5+sc*e.v_cumul_true_x, p) + y=map(e -> 0.5+sc*e.v_cumul_true_y, p) + GR.setlinewidth(2) + GR.setlinecolorind(2) + GR.polyline(x, y) + x=map(e -> 1.5+sc*e.v_cumul_est_x, p) + y=map(e -> 0.5+sc*e.v_cumul_est_y, p) + GR.setlinecolorind(3) + GR.polyline(x, y) + if vhist != nothing + GR.setlinecolorind(4) + x=map(v -> 1.5+sc*v, vhist[:,2]) + y=map(v -> 0.5+sc*v, vhist[:,1]) + GR.polyline(x, y) + end + end + GR.updatews() + end +end + +end # module diff -r 75116ad1d2e6 -r b180a4f3b9bd src/Stats.jl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Stats.jl Thu Apr 25 14:20:38 2024 -0500 @@ -0,0 +1,78 @@ +__precompile__() + +module Stats + + +######################## +# Load external modules +######################## + +using CSV, DataFrames +using Statistics + +export calculate_statistics + +function calculate_statistics() + ImName = ("lighthouse200x300", "shepplogan256x256", "brainphantom256x256") + AlgName = ("dualscaling", "greedy","noprediction","primalonly","proximal","rotation","zerodual") + mystart = 41 # Corresponds to the 500th iterate + + # Define an array to store results + results = DataFrame(experiment = String[], α = Float64[], algorithm = String[], psnr_mean1 = Float64[], psnr_mean500 = Float64[], psnr_ci = String[], ssim_mean1 = Float64[], ssim_mean500 = Float64[], ssim_ci = String[]) + + for imname in ImName + for algname in AlgName + directory_path = "./img/" + files = readdir(directory_path) + filtered_files = filter(file -> startswith(file, "$(imname)_pdps_known_$(algname)") && endswith(file, ".txt"), files) + + for file in filtered_files + filename = directory_path * file + data = CSV.File(filename, delim='\t', header=2) |> DataFrame + + # Extract α from filename + α, _, _ = extract_parameters(filename) + + + # Extract SSIM and PSNR columns starting from 1st iteration + ssim_values1 = Float64.(data[:, :ssim]) + psnr_values1 = Float64.(data[:, :psnr]) + + # Extract SSIM and PSNR columns starting from 500th iteration + ssim_values500 = Float64.(data[mystart:end, :ssim]) + psnr_values500 = Float64.(data[mystart:end, :psnr]) + + # Calculate mean and confidence intervals + ssim_mean1 = round(mean(ssim_values1), digits=4) + psnr_mean1 = round(mean(psnr_values1), digits=4) + + ssim_mean500 = round(mean(ssim_values500), digits=4) + psnr_mean500 = round(mean(psnr_values500), digits=4) + ssim_std500 = round(std(ssim_values500), digits=4) + psnr_std500 = round(std(psnr_values500), digits=4) + n = length(ssim_values500) + + ssim_ci_lower = round(ssim_mean500 - 1.96 * ssim_std500 / sqrt(n), digits=4) + ssim_ci_upper = round(ssim_mean500 + 1.96 * ssim_std500 / sqrt(n), digits=4) + psnr_ci_lower = round(psnr_mean500 - 1.96 * psnr_std500 / sqrt(n), digits=4) + psnr_ci_upper = round(psnr_mean500 + 1.96 * psnr_std500 / sqrt(n), digits=4) + + ssim_ci = "$(ssim_ci_lower) - $(ssim_ci_upper)" + psnr_ci = "$(psnr_ci_lower) - $(psnr_ci_upper)" + experiment = "$(imname)" + algorithm = "$(algname)" + + # Append results to DataFrame + push!(results, (experiment, α, algorithm, psnr_mean1, psnr_mean500, psnr_ci, ssim_mean1, ssim_mean500, ssim_ci)) + end + end + end + sort!(results, [:experiment, :α]) + csv_path = "./img/summarystats.csv" + if isfile(csv_path) + rm(csv_path) + end + CSV.write(csv_path, results) +end + +end