| 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 # using ImageTransformations |
|
| 15 # using Images, CoordinateTransformations, Rotations, OffsetArrays |
|
| 16 # using Interpolations |
|
| 17 |
|
| 18 import Images: center, warp |
|
| 19 import CoordinateTransformations: recenter |
|
| 20 import Rotations: RotMatrix |
|
| 21 import Interpolations: Flat |
|
| 22 |
|
| 23 ########## |
|
| 24 # Exports |
|
| 25 ########## |
|
| 26 |
|
| 27 export flow!, |
|
| 28 pdflow!, |
|
| 29 flow_grad!, |
|
| 30 flow_interp!, |
|
| 31 estimate_Λ², |
|
| 32 estimate_linear_Λ², |
|
| 33 pointwise_gradiprod_2d!, |
|
| 34 pointwise_gradiprod_2dᵀ!, |
|
| 35 horn_schunck_reg_prox!, |
|
| 36 horn_schunck_reg_prox_op!, |
|
| 37 mldivide_step_plus_sym2x2!, |
|
| 38 linearised_optical_flow_error, |
|
| 39 Image, AbstractImage, ImageSize, |
|
| 40 Gradient, Displacement, |
|
| 41 DisplacementFull, DisplacementConstant, |
|
| 42 HornSchunckData, |
|
| 43 filter_hs, |
|
| 44 petpdflow!, |
|
| 45 DualScaling, Greedy, Rotation |
|
| 46 |
|
| 47 ############################################### |
|
| 48 # Types (several imported from ImageTools.Translate) |
|
| 49 ############################################### |
|
| 50 |
|
| 51 Image = Translate.Image |
|
| 52 AbstractImage = AbstractArray{Float64,2} |
|
| 53 Displacement = Translate.Displacement |
|
| 54 DisplacementFull = Translate.DisplacementFull |
|
| 55 DisplacementConstant = Translate.DisplacementConstant |
|
| 56 Gradient = Array{Float64,3} |
|
| 57 ImageSize = Tuple{Int64,Int64} |
|
| 58 |
|
| 59 |
|
| 60 ################################# |
|
| 61 # Struct for flow |
|
| 62 ################################# |
|
| 63 struct DualScaling end |
|
| 64 struct Greedy end |
|
| 65 struct Rotation end |
|
| 66 |
|
| 67 ################################# |
|
| 68 # Displacement field based flow |
|
| 69 ################################# |
|
| 70 |
|
| 71 function flow_interp!(x::AbstractImage, u::Displacement, tmp::AbstractImage; |
|
| 72 threads = false) |
|
| 73 tmp .= x |
|
| 74 Translate.translate_image!(x, tmp, u; threads=threads) |
|
| 75 end |
|
| 76 |
|
| 77 function flow_interp!(x::AbstractImage, u::Displacement; |
|
| 78 threads = false) |
|
| 79 tmp = copy(x) |
|
| 80 Translate.translate_image!(x, tmp, u; threads=threads) |
|
| 81 end |
|
| 82 |
|
| 83 flow! = flow_interp! |
|
| 84 |
|
| 85 function pdflow!(x, Δx, y, Δy, u, dual_flow; threads=:none) |
|
| 86 if dual_flow |
|
| 87 #flow!((x, @view(y[1, :, :]), @view(y[2, :, :])), diffu, |
|
| 88 # (Δx, @view(Δy[1, :, :]), @view(Δy[2, :, :]))) |
|
| 89 @backgroundif (threads==:outer) begin |
|
| 90 flow!(x, u, Δx; threads=(threads==:inner)) |
|
| 91 end begin |
|
| 92 flow!(@view(y[1, :, :]), u, @view(Δy[1, :, :]); threads=(threads==:inner)) |
|
| 93 flow!(@view(y[2, :, :]), u, @view(Δy[2, :, :]); threads=(threads==:inner)) |
|
| 94 end |
|
| 95 else |
|
| 96 flow!(x, u, Δx) |
|
| 97 end |
|
| 98 end |
|
| 99 |
|
| 100 function pdflow!(x, Δx, y, Δy, z, Δz, u, dual_flow; threads=:none) |
|
| 101 if dual_flow |
|
| 102 @backgroundif (threads==:outer) begin |
|
| 103 flow!(x, u, Δx; threads=(threads==:inner)) |
|
| 104 flow!(z, u, Δz; threads=(threads==:inner)) |
|
| 105 end begin |
|
| 106 flow!(@view(y[1, :, :]), u, @view(Δy[1, :, :]); threads=(threads==:inner)) |
|
| 107 flow!(@view(y[2, :, :]), u, @view(Δy[2, :, :]); threads=(threads==:inner)) |
|
| 108 end |
|
| 109 else |
|
| 110 flow!(x, u, Δx; threads=(threads==:inner)) |
|
| 111 flow!(z, u, Δz; threads=(threads==:inner)) |
|
| 112 end |
|
| 113 end |
|
| 114 |
|
| 115 # Additional method for Greedy |
|
| 116 function pdflow!(x, Δx, y, Δy, u, flow :: Greedy; threads=:none) |
|
| 117 @assert(size(u)==(2,)) |
|
| 118 Δx .= x |
|
| 119 Δy .= y |
|
| 120 flow!(x, u; threads=(threads==:inner)) |
|
| 121 Dxx = similar(Δy) |
|
| 122 DΔx = similar(Δy) |
|
| 123 ∇₂!(Dxx, x) |
|
| 124 ∇₂!(DΔx, Δx) |
|
| 125 inds = abs.(Dxx) .≤ 1e-1 |
|
| 126 Dxx[inds] .= 1 |
|
| 127 DΔx[inds] .= 1 |
|
| 128 y .= y.* DΔx ./ Dxx |
|
| 129 end |
|
| 130 |
|
| 131 # Additional method for Rotation |
|
| 132 function pdflow!(x, Δx, y, Δy, u, flow :: Rotation; threads=:none) |
|
| 133 @assert(size(u)==(2,)) |
|
| 134 Δx .= x |
|
| 135 flow!(x, u; threads=(threads==:inner)) |
|
| 136 (m,n) = size(x) |
|
| 137 dx = similar(y) |
|
| 138 dx_banana = similar(y) |
|
| 139 ∇₂!(dx, Δx) |
|
| 140 ∇₂!(dx_banana, x) |
|
| 141 for i=1:m |
|
| 142 for j=1:n |
|
| 143 ndx = @views sum(dx[:, i, j].^2) |
|
| 144 ndx_banana = @views sum(dx_banana[:, i, j].^2) |
|
| 145 if ndx > 1e-4 && ndx_banana > 1e-4 |
|
| 146 A = dx[:, i, j] |
|
| 147 B = dx_banana[:, i, j] |
|
| 148 theta = atan(B[1] * A[2] - B[2] * A[1], B[1] * A[1] + B[2] * A[2]) # Oriented angle from A to B |
|
| 149 cos_theta = cos(theta) |
|
| 150 sin_theta = sin(theta) |
|
| 151 a = cos_theta * y[1, i, j] - sin_theta * y[2, i, j] |
|
| 152 b = sin_theta * y[1, i, j] + cos_theta * y[2, i, j] |
|
| 153 y[1, i, j] = a |
|
| 154 y[2, i, j] = b |
|
| 155 end |
|
| 156 end |
|
| 157 end |
|
| 158 end |
|
| 159 |
|
| 160 # Additional method for Dual Scaling |
|
| 161 function pdflow!(x, Δx, y, Δy, u, flow :: DualScaling; threads=:none) |
|
| 162 @assert(size(u)==(2,)) |
|
| 163 oldx = copy(x) |
|
| 164 flow!(x, u; threads=(threads==:inner)) |
|
| 165 C = similar(y) |
|
| 166 cc = abs.(x-oldx) |
|
| 167 cm = max(1e-12,maximum(cc)) |
|
| 168 c = 1 .* (1 .- cc./ cm) .^(10) |
|
| 169 C[1,:,:] .= c |
|
| 170 C[2,:,:] .= c |
|
| 171 y .= C.*y |
|
| 172 end |
|
| 173 |
|
| 174 |
|
| 175 ########################## |
|
| 176 # PET |
|
| 177 ########################## |
|
| 178 function petflow_interp!(x::AbstractImage, tmp::AbstractImage, u::DisplacementConstant, theta_known::DisplacementConstant; |
|
| 179 threads = false) |
|
| 180 tmp .= x |
|
| 181 center_point = center(x) .+ u |
|
| 182 tform = recenter(RotMatrix(theta_known[1]), center_point) |
|
| 183 tmp = warp(x, tform, axes(x), fillvalue=Flat()) |
|
| 184 x .= tmp |
|
| 185 end |
|
| 186 |
|
| 187 petflow! = petflow_interp! |
|
| 188 |
|
| 189 function petpdflow!(x, Δx, y, Δy, u, theta_known, dual_flow; threads=:none) |
|
| 190 if dual_flow |
|
| 191 @backgroundif (threads==:outer) begin |
|
| 192 petflow!(x, Δx, u, theta_known; threads=(threads==:inner)) |
|
| 193 end begin |
|
| 194 petflow!(@view(y[1, :, :]), @view(Δy[1, :, :]), u, theta_known; threads=(threads==:inner)) |
|
| 195 petflow!(@view(y[2, :, :]), @view(Δy[2, :, :]), u, theta_known; threads=(threads==:inner)) |
|
| 196 end |
|
| 197 else |
|
| 198 petflow!(x, Δx, u, theta_known) |
|
| 199 end |
|
| 200 end |
|
| 201 |
|
| 202 # Method for greedy predictor |
|
| 203 function petpdflow!(x, Δx, y, Δy, u, theta_known, flow :: Greedy; threads=:none) |
|
| 204 oldx = copy(x) |
|
| 205 center_point = center(x) .+ u |
|
| 206 tform = recenter(RotMatrix(theta_known[1]), center_point) |
|
| 207 Δx = warp(x, tform, axes(x), fillvalue=Flat()) |
|
| 208 @. x = Δx |
|
| 209 @. Δy = y |
|
| 210 Dxx = copy(Δy) |
|
| 211 DΔx = copy(Δy) |
|
| 212 ∇₂!(Dxx, x) |
|
| 213 ∇₂!(DΔx, oldx) |
|
| 214 inds = abs.(Dxx) .≤ 1e-2 |
|
| 215 Dxx[inds] .= 1 |
|
| 216 DΔx[inds] .= 1 |
|
| 217 y .= y.* DΔx ./ Dxx |
|
| 218 end |
|
| 219 |
|
| 220 # Method for dual scaling predictor |
|
| 221 function petpdflow!(x, Δx, y, Δy, u, theta_known, flow :: DualScaling; threads=:none) |
|
| 222 oldx = copy(x) |
|
| 223 center_point = center(x) .+ u |
|
| 224 tform = recenter(RotMatrix(theta_known[1]), center_point) |
|
| 225 Δx = warp(x, tform, axes(x), fillvalue=Flat()) |
|
| 226 @. x = Δx |
|
| 227 C = similar(y) |
|
| 228 cc = abs.(x-oldx) |
|
| 229 cm = max(1e-12,maximum(cc)) |
|
| 230 c = 1 .* (1 .- cc./ cm) .^(10) |
|
| 231 C[1,:,:] .= c |
|
| 232 C[2,:,:] .= c |
|
| 233 y .= C.*y |
|
| 234 end |
|
| 235 |
|
| 236 # Method for rotation prediction (exploiting property of inverse rotation) |
|
| 237 function petpdflow!(x, Δx, y, Δy, u, theta_known, flow :: Rotation; threads=:none) |
|
| 238 @backgroundif (threads==:outer) begin |
|
| 239 petflow!(x, Δx, u, theta_known; threads=(threads==:inner)) |
|
| 240 end begin |
|
| 241 petflow!(@view(y[1, :, :]), @view(Δy[1, :, :]), u, -theta_known; threads=(threads==:inner)) |
|
| 242 petflow!(@view(y[2, :, :]), @view(Δy[2, :, :]), u, -theta_known; threads=(threads==:inner)) |
|
| 243 end |
|
| 244 end |
|
| 245 |
|
| 246 ########################## |
|
| 247 # Linearised optical flow |
|
| 248 ########################## |
|
| 249 |
|
| 250 # ⟨⟨u, ∇b⟩⟩ |
|
| 251 function pointwise_gradiprod_2d!(y::Image, vtmp::Gradient, |
|
| 252 u::DisplacementFull, b::Image; |
|
| 253 add = false) |
|
| 254 ∇₂c!(vtmp, b) |
|
| 255 |
|
| 256 u′=reshape(u, (size(u, 1), prod(size(u)[2:end]))) |
|
| 257 vtmp′=reshape(vtmp, (size(vtmp, 1), prod(size(vtmp)[2:end]))) |
|
| 258 y′=reshape(y, prod(size(y))) |
|
| 259 |
|
| 260 if add |
|
| 261 @simd for i = 1:length(y′) |
|
| 262 @inbounds y′[i] += dot(@view(u′[:, i]), @view(vtmp′[:, i])) |
|
| 263 end |
|
| 264 else |
|
| 265 @simd for i = 1:length(y′) |
|
| 266 @inbounds y′[i] = dot(@view(u′[:, i]), @view(vtmp′[:, i])) |
|
| 267 end |
|
| 268 end |
|
| 269 end |
|
| 270 |
|
| 271 function pointwise_gradiprod_2d!(y::Image, vtmp::Gradient, |
|
| 272 u::DisplacementConstant, b::Image; |
|
| 273 add = false) |
|
| 274 ∇₂c!(vtmp, b) |
|
| 275 |
|
| 276 vtmp′=reshape(vtmp, (size(vtmp, 1), prod(size(vtmp)[2:end]))) |
|
| 277 y′=reshape(y, prod(size(y))) |
|
| 278 |
|
| 279 if add |
|
| 280 @simd for i = 1:length(y′) |
|
| 281 @inbounds y′[i] += dot(u, @view(vtmp′[:, i])) |
|
| 282 end |
|
| 283 else |
|
| 284 @simd for i = 1:length(y′) |
|
| 285 @inbounds y′[i] = dot(u, @view(vtmp′[:, i])) |
|
| 286 end |
|
| 287 end |
|
| 288 end |
|
| 289 |
|
| 290 # ∇b ⋅ y |
|
| 291 function pointwise_gradiprod_2dᵀ!(u::DisplacementFull, y::Image, b::Image) |
|
| 292 ∇₂c!(u, b) |
|
| 293 |
|
| 294 u′=reshape(u, (size(u, 1), prod(size(u)[2:end]))) |
|
| 295 y′=reshape(y, prod(size(y))) |
|
| 296 |
|
| 297 @simd for i=1:length(y′) |
|
| 298 @inbounds @. u′[:, i] *= y′[i] |
|
| 299 end |
|
| 300 end |
|
| 301 |
|
| 302 function pointwise_gradiprod_2dᵀ!(u::DisplacementConstant, y::Image, b::Image) |
|
| 303 @assert(size(y)==size(b) && size(u)==(2,)) |
|
| 304 u .= 0 |
|
| 305 ∇₂cfold!(b, nothing) do g, st, (i, j) |
|
| 306 @inbounds u .+= g.*y[i, j] |
|
| 307 return st |
|
| 308 end |
|
| 309 # Reweight to be with respect to 𝟙^*𝟙 inner product. |
|
| 310 u ./= prod(size(b)) |
|
| 311 end |
|
| 312 |
|
| 313 mutable struct ConstantDisplacementHornSchunckData |
|
| 314 M₀::Array{Float64,2} |
|
| 315 z::Array{Float64,1} |
|
| 316 Mv::Array{Float64,2} |
|
| 317 av::Array{Float64,1} |
|
| 318 cv::Float64 |
|
| 319 |
|
| 320 function ConstantDisplacementHornSchunckData() |
|
| 321 return new(zeros(2, 2), zeros(2), zeros(2,2), zeros(2), 0) |
|
| 322 end |
|
| 323 end |
|
| 324 |
|
| 325 # For DisplacementConstant, for the simple prox step |
|
| 326 # |
|
| 327 # (1) argmin_u 1/(2τ)|u-ũ|^2 + (θ/2)|b⁺-b+<<u-ŭ,∇b>>|^2 + (λ/2)|u-ŭ|^2, |
|
| 328 # |
|
| 329 # construct matrix M₀ and vector z such that we can solve u from |
|
| 330 # |
|
| 331 # (2) (I/τ+M₀)u = M₀ŭ + ũ/τ - z |
|
| 332 # |
|
| 333 # Note that the problem |
|
| 334 # |
|
| 335 # argmin_u 1/(2τ)|u-ũ|^2 + (θ/2)|b⁺-b+<<u-ŭ,∇b>>|^2 + (λ/2)|u-ŭ|^2 |
|
| 336 # + (θ/2)|b⁺⁺-b⁺+<<uʹ-u,∇b⁺>>|^2 + (λ/2)|u-uʹ|^2 |
|
| 337 # |
|
| 338 # has with respect to u the system |
|
| 339 # |
|
| 340 # (I/τ+M₀+M₀ʹ)u = M₀ŭ + M₀ʹuʹ + ũ/τ - z + zʹ, |
|
| 341 # |
|
| 342 # where the primed variables correspond to (2) for (1) for uʹ in place of u: |
|
| 343 # |
|
| 344 # argmin_uʹ 1/(2τ)|uʹ-ũʹ|^2 + (θ/2)|b⁺⁺-b⁺+<<uʹ-u,∇b⁺>>|^2 + (λ/2)|uʹ-u|^2 |
|
| 345 # |
|
| 346 function horn_schunck_reg_prox_op!(hs::ConstantDisplacementHornSchunckData, |
|
| 347 bnext::Image, b::Image, θ, λ, T) |
|
| 348 @assert(size(b)==size(bnext)) |
|
| 349 w = prod(size(b)) |
|
| 350 z = hs.z |
|
| 351 cv = 0 |
|
| 352 # Factors of symmetric matrix [a c; c d] |
|
| 353 a, c, d = 0.0, 0.0, 0.0 |
|
| 354 # This used to use ∇₂cfold but it is faster to allocate temporary |
|
| 355 # storage for the full gradient due to probably better memory and SIMD |
|
| 356 # instruction usage. |
|
| 357 g = zeros(2, size(b)...) |
|
| 358 ∇₂c!(g, b) |
|
| 359 @inbounds for i=1:size(b, 1) |
|
| 360 for j=1:size(b, 2) |
|
| 361 δ = bnext[i,j]-b[i,j] |
|
| 362 @. z += g[:,i,j]*δ |
|
| 363 cv += δ*δ |
|
| 364 a += g[1,i,j]*g[1,i,j] |
|
| 365 c += g[1,i,j]*g[2,i,j] |
|
| 366 d += g[2,i,j]*g[2,i,j] |
|
| 367 end |
|
| 368 end |
|
| 369 w₀ = λ |
|
| 370 w₂ = θ/w |
|
| 371 aʹ = w₀ + w₂*a |
|
| 372 cʹ = w₂*c |
|
| 373 dʹ = w₀ + w₂*d |
|
| 374 hs.M₀ .= [aʹ cʹ; cʹ dʹ] |
|
| 375 hs.Mv .= [w*λ+θ*a θ*c; θ*c w*λ+θ*d] |
|
| 376 hs.cv = cv*θ |
|
| 377 hs.av .= hs.z.*θ |
|
| 378 hs.z .*= w₂/T |
|
| 379 end |
|
| 380 |
|
| 381 # Solve the 2D system (I/τ+M₀)u = z |
|
| 382 @inline function mldivide_step_plus_sym2x2!(u, M₀, z, τ) |
|
| 383 a = 1/τ+M₀[1, 1] |
|
| 384 c = M₀[1, 2] |
|
| 385 d = 1/τ+M₀[2, 2] |
|
| 386 u .= ([d -c; -c a]*z)./(a*d-c*c) |
|
| 387 end |
|
| 388 |
|
| 389 function horn_schunck_reg_prox!(u::DisplacementConstant, bnext::Image, b::Image, |
|
| 390 θ, λ, T, τ) |
|
| 391 hs=ConstantDisplacementHornSchunckData() |
|
| 392 horn_schunck_reg_prox_op!(hs, bnext, b, θ, λ, T) |
|
| 393 mldivide_step_plus_sym2x2!(u, hs.M₀, (u./τ)-hs.z, τ) |
|
| 394 end |
|
| 395 |
|
| 396 function flow_grad!(x::Image, vtmp::Gradient, u::Displacement; δ=nothing) |
|
| 397 if !isnothing(δ) |
|
| 398 u = δ.*u |
|
| 399 end |
|
| 400 pointwise_gradiprod_2d!(x, vtmp, u, x; add=true) |
|
| 401 end |
|
| 402 |
|
| 403 # Error b-b_prev+⟨⟨u, ∇b⟩⟩ for Horn–Schunck type penalisation |
|
| 404 function linearised_optical_flow_error(u::Displacement, b::Image, b_prev::Image) |
|
| 405 imdim = size(b) |
|
| 406 vtmp = zeros(2, imdim...) |
|
| 407 tmp = b-b_prev |
|
| 408 pointwise_gradiprod_2d!(tmp, vtmp, u, b_prev; add=true) |
|
| 409 return tmp |
|
| 410 end |
|
| 411 |
|
| 412 ############################################## |
|
| 413 # Helper to smooth data for Horn–Schunck term |
|
| 414 ############################################## |
|
| 415 |
|
| 416 function filter_hs(b, b_next, b_next_filt, kernel) |
|
| 417 if kernel==nothing |
|
| 418 f = x -> x |
|
| 419 else |
|
| 420 f = x -> simple_imfilter(x, kernel; threads=true) |
|
| 421 end |
|
| 422 |
|
| 423 # We already filtered b in the previous step (b_next in that step) |
|
| 424 b_filt = b_next_filt==nothing ? f(b) : b_next_filt |
|
| 425 b_next_filt = f(b_next) |
|
| 426 |
|
| 427 return b_filt, b_next_filt |
|
| 428 end |
|
| 429 |
|
| 430 end # Module |
|