added dual tvl1 optical flow gpu implementation
[profile/ivi/opencv.git] / modules / gpu / src / cuda / tvl1flow.cu
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                           License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 //   * Redistribution's of source code must retain the above copyright notice,
21 //     this list of conditions and the following disclaimer.
22 //
23 //   * Redistribution's in binary form must reproduce the above copyright notice,
24 //     this list of conditions and the following disclaimer in the documentation
25 //     and/or other materials provided with the distribution.
26 //
27 //   * The name of the copyright holders may not be used to endorse or promote products
28 //     derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or bpied warranties, including, but not limited to, the bpied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42
43 #if !defined CUDA_DISABLER
44
45 #include "opencv2/gpu/device/common.hpp"
46 #include "opencv2/gpu/device/border_interpolate.hpp"
47 #include "opencv2/gpu/device/limits.hpp"
48
49 using namespace cv::gpu;
50 using namespace cv::gpu::device;
51
52 ////////////////////////////////////////////////////////////
53 // centeredGradient
54
55 namespace tvl1flow
56 {
57     __global__ void centeredGradientKernel(const PtrStepSzf src, PtrStepf dx, PtrStepf dy)
58     {
59         const int x = blockIdx.x * blockDim.x + threadIdx.x;
60         const int y = blockIdx.y * blockDim.y + threadIdx.y;
61
62         if (x >= src.cols || y >= src.rows)
63             return;
64
65         dx(y, x) = 0.5f * (src(y, ::min(x + 1, src.cols - 1)) - src(y, ::max(x - 1, 0)));
66         dy(y, x) = 0.5f * (src(::min(y + 1, src.rows - 1), x) - src(::max(y - 1, 0), x));
67     }
68
69     void centeredGradient(PtrStepSzf src, PtrStepSzf dx, PtrStepSzf dy)
70     {
71         const dim3 block(32, 8);
72         const dim3 grid(divUp(src.cols, block.x), divUp(src.rows, block.y));
73
74         centeredGradientKernel<<<grid, block>>>(src, dx, dy);
75         cudaSafeCall( cudaGetLastError() );
76
77         cudaSafeCall( cudaDeviceSynchronize() );
78     }
79 }
80
81 ////////////////////////////////////////////////////////////
82 // warpBackward
83
84 namespace tvl1flow
85 {
86     static __device__ __forceinline__ float bicubicCoeff(float x_)
87     {
88         float x = fabsf(x_);
89         if (x <= 1.0f)
90         {
91             return x * x * (1.5f * x - 2.5f) + 1.0f;
92         }
93         else if (x < 2.0f)
94         {
95             return x * (x * (-0.5f * x + 2.5f) - 4.0f) + 2.0f;
96         }
97         else
98         {
99             return 0.0f;
100         }
101     }
102
103     texture<float, cudaTextureType2D, cudaReadModeElementType> tex_I1 (false, cudaFilterModePoint, cudaAddressModeClamp);
104     texture<float, cudaTextureType2D, cudaReadModeElementType> tex_I1x(false, cudaFilterModePoint, cudaAddressModeClamp);
105     texture<float, cudaTextureType2D, cudaReadModeElementType> tex_I1y(false, cudaFilterModePoint, cudaAddressModeClamp);
106
107     __global__ void warpBackwardKernel(const PtrStepSzf I0, const PtrStepf u1, const PtrStepf u2, PtrStepf I1w, PtrStepf I1wx, PtrStepf I1wy, PtrStepf grad, PtrStepf rho)
108     {
109         const int x = blockIdx.x * blockDim.x + threadIdx.x;
110         const int y = blockIdx.y * blockDim.y + threadIdx.y;
111
112         if (x >= I0.cols || y >= I0.rows)
113             return;
114
115         const float u1Val = u1(y, x);
116         const float u2Val = u2(y, x);
117
118         const float wx = x + u1Val;
119         const float wy = y + u2Val;
120
121         const int xmin = ::ceilf(wx - 2.0f);
122         const int xmax = ::floorf(wx + 2.0f);
123
124         const int ymin = ::ceilf(wy - 2.0f);
125         const int ymax = ::floorf(wy + 2.0f);
126
127         float sum  = 0.0f;
128         float sumx = 0.0f;
129         float sumy = 0.0f;
130         float wsum = 0.0f;
131
132         for (int cy = ymin; cy <= ymax; ++cy)
133         {
134             for (int cx = xmin; cx <= xmax; ++cx)
135             {
136                 const float w = bicubicCoeff(wx - cx) * bicubicCoeff(wy - cy);
137
138                 sum  += w * tex2D(tex_I1 , cx, cy);
139                 sumx += w * tex2D(tex_I1x, cx, cy);
140                 sumy += w * tex2D(tex_I1y, cx, cy);
141
142                 wsum += w;
143             }
144         }
145
146         const float coeff = 1.0f / wsum;
147
148         const float I1wVal  = sum  * coeff;
149         const float I1wxVal = sumx * coeff;
150         const float I1wyVal = sumy * coeff;
151
152         I1w(y, x)  = I1wVal;
153         I1wx(y, x) = I1wxVal;
154         I1wy(y, x) = I1wyVal;
155
156         const float Ix2 = I1wxVal * I1wxVal;
157         const float Iy2 = I1wyVal * I1wyVal;
158
159         // store the |Grad(I1)|^2
160         grad(y, x) = Ix2 + Iy2;
161
162         // compute the constant part of the rho function
163         const float I0Val = I0(y, x);
164         rho(y, x) = I1wVal - I1wxVal * u1Val - I1wyVal * u2Val - I0Val;
165     }
166
167     void warpBackward(PtrStepSzf I0, PtrStepSzf I1, PtrStepSzf I1x, PtrStepSzf I1y, PtrStepSzf u1, PtrStepSzf u2, PtrStepSzf I1w, PtrStepSzf I1wx, PtrStepSzf I1wy, PtrStepSzf grad, PtrStepSzf rho)
168     {
169         const dim3 block(32, 8);
170         const dim3 grid(divUp(I0.cols, block.x), divUp(I0.rows, block.y));
171
172         bindTexture(&tex_I1 , I1);
173         bindTexture(&tex_I1x, I1x);
174         bindTexture(&tex_I1y, I1y);
175
176         warpBackwardKernel<<<grid, block>>>(I0, u1, u2, I1w, I1wx, I1wy, grad, rho);
177         cudaSafeCall( cudaGetLastError() );
178
179         cudaSafeCall( cudaDeviceSynchronize() );
180     }
181 }
182
183 ////////////////////////////////////////////////////////////
184 // estimateU
185
186 namespace tvl1flow
187 {
188     __device__ float divergence(const PtrStepf& v1, const PtrStepf& v2, int y, int x)
189     {
190         if (x > 0 && y > 0)
191         {
192             const float v1x = v1(y, x) - v1(y, x - 1);
193             const float v2y = v2(y, x) - v2(y - 1, x);
194             return v1x + v2y;
195         }
196         else
197         {
198             if (y > 0)
199                 return v1(y, 0) + v2(y, 0) - v2(y - 1, 0);
200             else
201             {
202                 if (x > 0)
203                     return v1(0, x) - v1(0, x - 1) + v2(0, x);
204                 else
205                     return v1(0, 0) + v2(0, 0);
206             }
207         }
208     }
209
210     __global__ void estimateUKernel(const PtrStepSzf I1wx, const PtrStepf I1wy,
211                               const PtrStepf grad, const PtrStepf rho_c,
212                               const PtrStepf p11, const PtrStepf p12, const PtrStepf p21, const PtrStepf p22,
213                               PtrStepf u1, PtrStepf u2, PtrStepf error,
214                               const float l_t, const float theta)
215     {
216         const int x = blockIdx.x * blockDim.x + threadIdx.x;
217         const int y = blockIdx.y * blockDim.y + threadIdx.y;
218
219         if (x >= I1wx.cols || y >= I1wx.rows)
220             return;
221
222         const float I1wxVal = I1wx(y, x);
223         const float I1wyVal = I1wy(y, x);
224         const float gradVal = grad(y, x);
225         const float u1OldVal = u1(y, x);
226         const float u2OldVal = u2(y, x);
227
228         const float rho = rho_c(y, x) + (I1wxVal * u1OldVal + I1wyVal * u2OldVal);
229
230         // estimate the values of the variable (v1, v2) (thresholding operator TH)
231
232         float d1 = 0.0f;
233         float d2 = 0.0f;
234
235         if (rho < -l_t * gradVal)
236         {
237             d1 = l_t * I1wxVal;
238             d2 = l_t * I1wyVal;
239         }
240         else if (rho > l_t * gradVal)
241         {
242             d1 = -l_t * I1wxVal;
243             d2 = -l_t * I1wyVal;
244         }
245         else if (gradVal > numeric_limits<float>::epsilon())
246         {
247             const float fi = -rho / gradVal;
248             d1 = fi * I1wxVal;
249             d2 = fi * I1wyVal;
250         }
251
252         const float v1 = u1OldVal + d1;
253         const float v2 = u2OldVal + d2;
254
255         // compute the divergence of the dual variable (p1, p2)
256
257         const float div_p1 = divergence(p11, p12, y, x);
258         const float div_p2 = divergence(p21, p22, y, x);
259
260         // estimate the values of the optical flow (u1, u2)
261
262         const float u1NewVal = v1 + theta * div_p1;
263         const float u2NewVal = v2 + theta * div_p2;
264
265         u1(y, x) = u1NewVal;
266         u2(y, x) = u2NewVal;
267
268         const float n1 = (u1OldVal - u1NewVal) * (u1OldVal - u1NewVal);
269         const float n2 = (u2OldVal - u2NewVal) * (u2OldVal - u2NewVal);
270         error(y, x) = n1 + n2;
271     }
272
273     void estimateU(PtrStepSzf I1wx, PtrStepSzf I1wy,
274                    PtrStepSzf grad, PtrStepSzf rho_c,
275                    PtrStepSzf p11, PtrStepSzf p12, PtrStepSzf p21, PtrStepSzf p22,
276                    PtrStepSzf u1, PtrStepSzf u2, PtrStepSzf error,
277                    float l_t, float theta)
278     {
279         const dim3 block(32, 8);
280         const dim3 grid(divUp(I1wx.cols, block.x), divUp(I1wx.rows, block.y));
281
282         estimateUKernel<<<grid, block>>>(I1wx, I1wy, grad, rho_c, p11, p12, p21, p22, u1, u2, error, l_t, theta);
283         cudaSafeCall( cudaGetLastError() );
284
285         cudaSafeCall( cudaDeviceSynchronize() );
286     }
287 }
288
289 ////////////////////////////////////////////////////////////
290 // estimateDualVariables
291
292 namespace tvl1flow
293 {
294     __global__ void estimateDualVariablesKernel(const PtrStepSzf u1, const PtrStepf u2, PtrStepf p11, PtrStepf p12, PtrStepf p21, PtrStepf p22, const float taut)
295     {
296         const int x = blockIdx.x * blockDim.x + threadIdx.x;
297         const int y = blockIdx.y * blockDim.y + threadIdx.y;
298
299         if (x >= u1.cols || y >= u1.rows)
300             return;
301
302         const float u1x = u1(y, ::min(x + 1, u1.cols - 1)) - u1(y, x);
303         const float u1y = u1(::min(y + 1, u1.rows - 1), x) - u1(y, x);
304
305         const float u2x = u2(y, ::min(x + 1, u1.cols - 1)) - u2(y, x);
306         const float u2y = u2(::min(y + 1, u1.rows - 1), x) - u2(y, x);
307
308         const float g1 = ::hypotf(u1x, u1y);
309         const float g2 = ::hypotf(u2x, u2y);
310
311         const float ng1 = 1.0f + taut * g1;
312         const float ng2 = 1.0f + taut * g2;
313
314         p11(y, x) = (p11(y, x) + taut * u1x) / ng1;
315         p12(y, x) = (p12(y, x) + taut * u1y) / ng1;
316         p21(y, x) = (p21(y, x) + taut * u2x) / ng2;
317         p22(y, x) = (p22(y, x) + taut * u2y) / ng2;
318     }
319
320     void estimateDualVariables(PtrStepSzf u1, PtrStepSzf u2, PtrStepSzf p11, PtrStepSzf p12, PtrStepSzf p21, PtrStepSzf p22, float taut)
321     {
322         const dim3 block(32, 8);
323         const dim3 grid(divUp(u1.cols, block.x), divUp(u1.rows, block.y));
324
325         estimateDualVariablesKernel<<<grid, block>>>(u1, u2, p11, p12, p21, p22, taut);
326         cudaSafeCall( cudaGetLastError() );
327
328         cudaSafeCall( cudaDeviceSynchronize() );
329     }
330 }
331
332 #endif // !defined CUDA_DISABLER