| |
1 ################################ |
| |
2 # Code relevant to optical flow |
| |
3 ################################ |
| |
4 |
| |
5 __precompile__() |
| |
6 |
| |
7 module OpticalFlow |
| |
8 |
| |
9 using AlgTools.Util |
| |
10 using ImageTools.Gradient |
| |
11 import ImageTools.Translate |
| |
12 using ImageTools.ImFilter |
| |
13 |
| |
14 ########## |
| |
15 # Exports |
| |
16 ########## |
| |
17 |
| |
18 export flow!, |
| |
19 pdflow!, |
| |
20 flow_grad!, |
| |
21 flow_interp!, |
| |
22 estimate_Λ², |
| |
23 estimate_linear_Λ², |
| |
24 pointwise_gradiprod_2d!, |
| |
25 pointwise_gradiprod_2dᵀ!, |
| |
26 horn_schunck_reg_prox!, |
| |
27 horn_schunck_reg_prox_op!, |
| |
28 mldivide_step_plus_sym2x2!, |
| |
29 linearised_optical_flow_error, |
| |
30 Image, AbstractImage, ImageSize, |
| |
31 Gradient, Displacement, |
| |
32 DisplacementFull, DisplacementConstant, |
| |
33 HornSchunckData, |
| |
34 filter_hs |
| |
35 |
| |
36 ############################################### |
| |
37 # Types (several imported from ImageTools.Translate) |
| |
38 ############################################### |
| |
39 |
| |
40 Image = Translate.Image |
| |
41 AbstractImage = AbstractArray{Float64,2} |
| |
42 Displacement = Translate.Displacement |
| |
43 DisplacementFull = Translate.DisplacementFull |
| |
44 DisplacementConstant = Translate.DisplacementConstant |
| |
45 Gradient = Array{Float64,3} |
| |
46 ImageSize = Tuple{Int64,Int64} |
| |
47 |
| |
48 ################################# |
| |
49 # Displacement field based flow |
| |
50 ################################# |
| |
51 |
| |
52 function flow_interp!(x::AbstractImage, u::Displacement, tmp::AbstractImage; |
| |
53 threads = false) |
| |
54 tmp .= x |
| |
55 Translate.translate_image!(x, tmp, u; threads=threads) |
| |
56 end |
| |
57 |
| |
58 function flow_interp!(x::AbstractImage, u::Displacement; |
| |
59 threads = false) |
| |
60 tmp = copy(x) |
| |
61 Translate.translate_image!(x, tmp, u; threads=threads) |
| |
62 end |
| |
63 |
| |
64 flow! = flow_interp! |
| |
65 |
| |
66 function pdflow!(x, Δx, y, Δy, u, dual_flow; threads=:none) |
| |
67 if dual_flow |
| |
68 #flow!((x, @view(y[1, :, :]), @view(y[2, :, :])), diffu, |
| |
69 # (Δx, @view(Δy[1, :, :]), @view(Δy[2, :, :]))) |
| |
70 @backgroundif (threads==:outer) begin |
| |
71 flow!(x, u, Δx; threads=(threads==:inner)) |
| |
72 end begin |
| |
73 flow!(@view(y[1, :, :]), u, @view(Δy[1, :, :]); threads=(threads==:inner)) |
| |
74 flow!(@view(y[2, :, :]), u, @view(Δy[2, :, :]); threads=(threads==:inner)) |
| |
75 end |
| |
76 else |
| |
77 flow!(x, u, Δx) |
| |
78 end |
| |
79 end |
| |
80 |
| |
81 function pdflow!(x, Δx, y, Δy, z, Δz, u, dual_flow; threads=:none) |
| |
82 if dual_flow |
| |
83 @backgroundif (threads==:outer) begin |
| |
84 flow!(x, u, Δx; threads=(threads==:inner)) |
| |
85 flow!(z, u, Δz; threads=(threads==:inner)) |
| |
86 end begin |
| |
87 flow!(@view(y[1, :, :]), u, @view(Δy[1, :, :]); threads=(threads==:inner)) |
| |
88 flow!(@view(y[2, :, :]), u, @view(Δy[2, :, :]); threads=(threads==:inner)) |
| |
89 end |
| |
90 else |
| |
91 flow!(x, u, Δx; threads=(threads==:inner)) |
| |
92 flow!(z, u, Δz; threads=(threads==:inner)) |
| |
93 end |
| |
94 end |
| |
95 |
| |
96 ########################## |
| |
97 # Linearised optical flow |
| |
98 ########################## |
| |
99 |
| |
100 # ⟨⟨u, ∇b⟩⟩ |
| |
101 function pointwise_gradiprod_2d!(y::Image, vtmp::Gradient, |
| |
102 u::DisplacementFull, b::Image; |
| |
103 add = false) |
| |
104 ∇₂c!(vtmp, b) |
| |
105 |
| |
106 u′=reshape(u, (size(u, 1), prod(size(u)[2:end]))) |
| |
107 vtmp′=reshape(vtmp, (size(vtmp, 1), prod(size(vtmp)[2:end]))) |
| |
108 y′=reshape(y, prod(size(y))) |
| |
109 |
| |
110 if add |
| |
111 @simd for i = 1:length(y′) |
| |
112 @inbounds y′[i] += dot(@view(u′[:, i]), @view(vtmp′[:, i])) |
| |
113 end |
| |
114 else |
| |
115 @simd for i = 1:length(y′) |
| |
116 @inbounds y′[i] = dot(@view(u′[:, i]), @view(vtmp′[:, i])) |
| |
117 end |
| |
118 end |
| |
119 end |
| |
120 |
| |
121 function pointwise_gradiprod_2d!(y::Image, vtmp::Gradient, |
| |
122 u::DisplacementConstant, b::Image; |
| |
123 add = false) |
| |
124 ∇₂c!(vtmp, b) |
| |
125 |
| |
126 vtmp′=reshape(vtmp, (size(vtmp, 1), prod(size(vtmp)[2:end]))) |
| |
127 y′=reshape(y, prod(size(y))) |
| |
128 |
| |
129 if add |
| |
130 @simd for i = 1:length(y′) |
| |
131 @inbounds y′[i] += dot(u, @view(vtmp′[:, i])) |
| |
132 end |
| |
133 else |
| |
134 @simd for i = 1:length(y′) |
| |
135 @inbounds y′[i] = dot(u, @view(vtmp′[:, i])) |
| |
136 end |
| |
137 end |
| |
138 end |
| |
139 |
| |
140 # ∇b ⋅ y |
| |
141 function pointwise_gradiprod_2dᵀ!(u::DisplacementFull, y::Image, b::Image) |
| |
142 ∇₂c!(u, b) |
| |
143 |
| |
144 u′=reshape(u, (size(u, 1), prod(size(u)[2:end]))) |
| |
145 y′=reshape(y, prod(size(y))) |
| |
146 |
| |
147 @simd for i=1:length(y′) |
| |
148 @inbounds @. u′[:, i] *= y′[i] |
| |
149 end |
| |
150 end |
| |
151 |
| |
152 function pointwise_gradiprod_2dᵀ!(u::DisplacementConstant, y::Image, b::Image) |
| |
153 @assert(size(y)==size(b) && size(u)==(2,)) |
| |
154 u .= 0 |
| |
155 ∇₂cfold!(b, nothing) do g, st, (i, j) |
| |
156 @inbounds u .+= g.*y[i, j] |
| |
157 return st |
| |
158 end |
| |
159 # Reweight to be with respect to 𝟙^*𝟙 inner product. |
| |
160 u ./= prod(size(b)) |
| |
161 end |
| |
162 |
| |
163 mutable struct ConstantDisplacementHornSchunckData |
| |
164 M₀::Array{Float64,2} |
| |
165 z::Array{Float64,1} |
| |
166 Mv::Array{Float64,2} |
| |
167 av::Array{Float64,1} |
| |
168 cv::Float64 |
| |
169 |
| |
170 function ConstantDisplacementHornSchunckData() |
| |
171 return new(zeros(2, 2), zeros(2), zeros(2,2), zeros(2), 0) |
| |
172 end |
| |
173 end |
| |
174 |
| |
175 # For DisplacementConstant, for the simple prox step |
| |
176 # |
| |
177 # (1) argmin_u 1/(2τ)|u-ũ|^2 + (θ/2)|b⁺-b+<<u-ŭ,∇b>>|^2 + (λ/2)|u-ŭ|^2, |
| |
178 # |
| |
179 # construct matrix M₀ and vector z such that we can solve u from |
| |
180 # |
| |
181 # (2) (I/τ+M₀)u = M₀ŭ + ũ/τ - z |
| |
182 # |
| |
183 # Note that the problem |
| |
184 # |
| |
185 # argmin_u 1/(2τ)|u-ũ|^2 + (θ/2)|b⁺-b+<<u-ŭ,∇b>>|^2 + (λ/2)|u-ŭ|^2 |
| |
186 # + (θ/2)|b⁺⁺-b⁺+<<uʹ-u,∇b⁺>>|^2 + (λ/2)|u-uʹ|^2 |
| |
187 # |
| |
188 # has with respect to u the system |
| |
189 # |
| |
190 # (I/τ+M₀+M₀ʹ)u = M₀ŭ + M₀ʹuʹ + ũ/τ - z + zʹ, |
| |
191 # |
| |
192 # where the primed variables correspond to (2) for (1) for uʹ in place of u: |
| |
193 # |
| |
194 # argmin_uʹ 1/(2τ)|uʹ-ũʹ|^2 + (θ/2)|b⁺⁺-b⁺+<<uʹ-u,∇b⁺>>|^2 + (λ/2)|uʹ-u|^2 |
| |
195 # |
| |
196 function horn_schunck_reg_prox_op!(hs::ConstantDisplacementHornSchunckData, |
| |
197 bnext::Image, b::Image, θ, λ, T) |
| |
198 @assert(size(b)==size(bnext)) |
| |
199 w = prod(size(b)) |
| |
200 z = hs.z |
| |
201 cv = 0 |
| |
202 # Factors of symmetric matrix [a c; c d] |
| |
203 a, c, d = 0.0, 0.0, 0.0 |
| |
204 # This used to use ∇₂cfold but it is faster to allocate temporary |
| |
205 # storage for the full gradient due to probably better memory and SIMD |
| |
206 # instruction usage. |
| |
207 g = zeros(2, size(b)...) |
| |
208 ∇₂c!(g, b) |
| |
209 @inbounds for i=1:size(b, 1) |
| |
210 for j=1:size(b, 2) |
| |
211 δ = bnext[i,j]-b[i,j] |
| |
212 @. z += g[:,i,j]*δ |
| |
213 cv += δ*δ |
| |
214 a += g[1,i,j]*g[1,i,j] |
| |
215 c += g[1,i,j]*g[2,i,j] |
| |
216 d += g[2,i,j]*g[2,i,j] |
| |
217 end |
| |
218 end |
| |
219 w₀ = λ |
| |
220 w₂ = θ/w |
| |
221 aʹ = w₀ + w₂*a |
| |
222 cʹ = w₂*c |
| |
223 dʹ = w₀ + w₂*d |
| |
224 hs.M₀ .= [aʹ cʹ; cʹ dʹ] |
| |
225 hs.Mv .= [w*λ+θ*a θ*c; θ*c w*λ+θ*d] |
| |
226 hs.cv = cv*θ |
| |
227 hs.av .= hs.z.*θ |
| |
228 hs.z .*= w₂/T |
| |
229 end |
| |
230 |
| |
231 # Solve the 2D system (I/τ+M₀)u = z |
| |
232 @inline function mldivide_step_plus_sym2x2!(u, M₀, z, τ) |
| |
233 a = 1/τ+M₀[1, 1] |
| |
234 c = M₀[1, 2] |
| |
235 d = 1/τ+M₀[2, 2] |
| |
236 u .= ([d -c; -c a]*z)./(a*d-c*c) |
| |
237 end |
| |
238 |
| |
239 function horn_schunck_reg_prox!(u::DisplacementConstant, bnext::Image, b::Image, |
| |
240 θ, λ, T, τ) |
| |
241 hs=ConstantDisplacementHornSchunckData() |
| |
242 horn_schunck_reg_prox_op!(hs, bnext, b, θ, λ, T) |
| |
243 mldivide_step_plus_sym2x2!(u, hs.M₀, (u./τ)-hs.z, τ) |
| |
244 end |
| |
245 |
| |
246 function flow_grad!(x::Image, vtmp::Gradient, u::Displacement; δ=nothing) |
| |
247 if !isnothing(δ) |
| |
248 u = δ.*u |
| |
249 end |
| |
250 pointwise_gradiprod_2d!(x, vtmp, u, x; add=true) |
| |
251 end |
| |
252 |
| |
253 # Error b-b_prev+⟨⟨u, ∇b⟩⟩ for Horn–Schunck type penalisation |
| |
254 function linearised_optical_flow_error(u::Displacement, b::Image, b_prev::Image) |
| |
255 imdim = size(b) |
| |
256 vtmp = zeros(2, imdim...) |
| |
257 tmp = b-b_prev |
| |
258 pointwise_gradiprod_2d!(tmp, vtmp, u, b_prev; add=true) |
| |
259 return tmp |
| |
260 end |
| |
261 |
| |
262 ############################################## |
| |
263 # Helper to smooth data for Horn–Schunck term |
| |
264 ############################################## |
| |
265 |
| |
266 function filter_hs(b, b_next, b_next_filt, kernel) |
| |
267 if kernel==nothing |
| |
268 f = x -> x |
| |
269 else |
| |
270 f = x -> simple_imfilter(x, kernel; threads=true) |
| |
271 end |
| |
272 |
| |
273 # We already filtered b in the previous step (b_next in that step) |
| |
274 b_filt = b_next_filt==nothing ? f(b) : b_next_filt |
| |
275 b_next_filt = f(b_next) |
| |
276 |
| |
277 return b_filt, b_next_filt |
| |
278 end |
| |
279 |
| |
280 end # Module |