src/OpticalFlow.jl

changeset 0
a55e35d20336
child 5
843e7611b068
equal deleted inserted replaced
-1:000000000000 0:a55e35d20336
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

mercurial