SLAMflex SE  0.1.0
SLAMflex provides detection and tracking of dominant planes for smartphone devices. This plane can then be used to show AR content relative to the plane orientation. The detection of plane is performed in the field of view of the smartphone camera. In subsequent frames it is tracked. The interface returns the plane position and orientation.
convolution.cpp
Go to the documentation of this file.
1 #include "abs.h"
2 #include "convolution.h"
3 using namespace std;
4 
5 namespace CVD {
6 
7 namespace Internal {
8 
9 void convolveSeparableGray(unsigned char* I, unsigned int width, unsigned int height, const int kernel[], unsigned int size, int divisor)
10 {
11  std::vector<unsigned char> buffer(width>height ? width : height);
12  unsigned char* p = I;
13  unsigned int i,j,m;
14  if (size%2 == 0) {
15  throw Exceptions::Convolution::OddSizedKernelRequired("convolveSeparable");
16  }
17  for (i=height; i>0; i--) {
18  for (j=0;j<width-size+1;j++) {
19  int sum = 0;
20  for (m=0; m<size; m++)
21  sum += p[j+m] * kernel[m];
22  buffer[j] = (unsigned char)(sum/divisor);
23  }
24  memcpy(p+size/2, &buffer.front(), width-size+1);
25  p += width;
26  }
27  for (j=0;j<width-size+1;j++) {
28  p = I+j+(size/2)*width;
29  for (i=0; i<height;i++)
30  buffer[i] = I[i*width+j];
31  for (i=0;i<height-size+1;i++) {
32  int sum = 0;
33  for (m=0; m<size; m++)
34  sum += buffer[i+m] * kernel[m];
35  *p = (unsigned char)(sum/divisor);
36  p += width;
37  }
38  }
39 }
40 
41 };
42 
44 {
45  int w = I.size().x;
46  int h = I.size().y;
47  int i,j;
48  for (j=0;j<w;j++) {
49  byte* src = I.data()+j;
50  byte* end = src + w*(h-4);
51  while (src != end) {
52  int sum= (3571*(src[0]+src[4*w])
53  + 16004*(src[w]+src[3*w])
54  + 26386*src[2*w]);
55  *(src) = sum >> 16;
56  src += w;
57  }
58  }
59  for (i=h-5;i>=0;i--) {
60  byte* src = I.data()+i*w;
61  byte* end = src + w-4;
62  while (src != end) {
63  int sum= (3571*(src[0]+src[4])
64  + 16004*(src[1]+src[3])
65  + 26386*src[2]);
66  *(src+2*w+2) = sum >> 16;
67  ++src;
68  }
69  }
70 }
71 
72 double compute_van_vliet_variance(const double d[])
73 {
74  const double a = d[0];
75  const double b = d[1];
76  const double num = 2*(a*(a*(a-2) + 1 + b*b) - 2*b*b);
77  const double den1 = a*a - b*b -2*a + 1;
78  const double den2 = 2*(a-1)*b;
79  const double inv_den = 1.0/(den1*den1 + den2*den2);
80 
81  const double inv_den3 = 1.0/((d[2]-1)*(d[2]-1));
82 
83  return 2*(num * inv_den + d[2] * inv_den3);
84 }
85 
86 double compute_van_vliet_variance(const double d[], double J[3])
87 {
88  const double a = d[0];
89  const double b = d[1];
90  const double num = 2*(a*(a*(a-2) + 1 + b*b) - 2*b*b);
91  const double den1 = a*a - b*b -2*a + 1;
92  const double den2 = 2*(a-1)*b;
93  const double inv_den = 1.0/(den1*den1 + den2*den2);
94  const double inv_den3 = 1.0/((d[2]-1)*(d[2]-1));
95 
96  double N_D = num*inv_den;
97  J[0] = 2*inv_den * (2*(a*(3*a - 4) + (1+b*b)) - N_D * 4*(den1*(a-1) + den2*b));
98  J[1] = 2*inv_den * (4*b*(a - 2) - N_D * 4*(den2*(a-1) - den1*b));
99  J[2] = 2*inv_den3*(1 - d[2]*inv_den3*2*(d[2]-1));
100 
101  return 2*(N_D + d[2] * inv_den3);
102 }
103 
104 void compute_scaling_jacobian(const double d[], double J[])
105 {
106  double rr = d[0]*d[0] + d[1]*d[1];
107  double lnr = 0.5 * log(rr);
108  double theta = atan2(d[1], d[0]);
109  J[0] = d[0]*lnr - d[1]*theta;
110  J[1] = d[0]*theta + d[1]*lnr;
111  J[2] = d[2]*log(d[2]);
112 }
113 
114 void scale_d(double d[], double p)
115 {
116  double theta = atan2(d[1], d[0]);
117  double rr = d[0]*d[0] + d[1]*d[1];
118  double new_theta = theta*p;
119  double new_r = pow(rr, p*0.5);
120  d[0] = new_r * cos(new_theta);
121  d[1] = new_r * sin(new_theta);
122  d[2] = pow(d[2], p);
123 }
124 
125 void compute_van_vliet_scaled_d(double sigma, double d[])
126 {
127  // Approximately sigma = 2
128  d[0] = 1.41650;
129  d[1] = 1.00829;
130  d[2] = 1.86543;
131 
132  // Cubic fitted to log(p) s.t. poles->poles^p gives sigma
133  const double A = 3.69892409013e-03;
134  const double B =-4.28967830150e-02;
135  const double C =-3.38065667167e-01;
136  const double D = 5.44264202732e-01;
137 
138  double log_var = 2*log(sigma);
139  double log_p = D + log_var*(C + log_var*(B + log_var*A));
140  double predicted_p = exp(log_p);
141 
142  scale_d(d, predicted_p);
143 
144  double total_p = 1;
145  const double target_var = sigma*sigma;
146 
147  // Newton-Rapheson on scale of poles
148 
149  for (int i=0; i<5; ++i)
150  {
151  double sj[3], vj[3];
153  double v = target_var - compute_van_vliet_variance(d, vj);
154  double step = v / (vj[0]*sj[0] + vj[1]*sj[1] + vj[2]*sj[2]);
155  if (CVD::abs<double>(step) < 1e-6)
156  break;
157  double exp_step = exp(std::min(std::max(step, -1.0), 1.0));
158  scale_d(d, exp_step);
159  total_p *= exp_step;
160  }
161 }
162 
163 void build_van_vliet_b(const double d[], double b[])
164 {
165  double B = 1.0/(d[2]*(d[0]*d[0] + d[1]*d[1]));
166  b[2] = -B;
167  b[1] = B*(2*d[0] + d[2]);
168  b[0] = -B*(d[0]*d[0] + d[1]*d[1] + 2*(d[0] * d[2]));
169 }
170 
171  void compute_van_vliet_b(double sigma, double b[])
172  {
173  double d[3];
174  compute_van_vliet_scaled_d(sigma, d);
175  build_van_vliet_b(d, b);
176  }
177 
178 void compute_triggs_M(const double b[], double M[][3])
179 {
180  const double a1= -b[0];
181  const double a2= -b[1];
182  const double a3= -b[2];
183 
184  const double factor = 1.0/((1+a1-a2+a3)*
185  (1-a1-a2-a3)*
186  (1+a2+(a1-a3)*a3));
187  M[0][0] = factor*(1-a2-a1*a3-a3*a3);
188  M[0][1] = factor*(a3+a1)*(a2+a1*a3);
189  M[0][2] = factor*a3*(a1 + a2*a3);
190  M[1][0] = a1*M[0][0] + M[0][1];
191  M[1][1] = a2*M[0][0] + M[0][2];
192  M[1][2] = a3*M[0][0];
193  M[2][0] = a1*M[1][0] + M[1][1];
194  M[2][1] = a2*M[1][0] + M[1][2];
195  M[2][2] = a3*M[1][0];
196 }
197 
198 inline void forward_to_backward(const double M[][3], const double i_plus, const double inv_alpha, double& y1, double& y2, double& y3)
199 {
200  const double u_plus = i_plus*inv_alpha;
201  const double v_plus = u_plus*inv_alpha;
202  double x1=y1-u_plus, x2=y2-u_plus, x3=y3-u_plus;
203  y1 = M[0][0]*x1 + M[0][1]*x2 + M[0][2]*x3 + v_plus;
204  y2 = M[1][0]*x1 + M[1][1]*x2 + M[1][2]*x3 + v_plus;
205  y3 = M[2][0]*x1 + M[2][1]*x2 + M[2][2]*x3 + v_plus;
206 }
207 
208 template <class T> inline T clamp01(T x) { return x < 0 ? 0 : (x > 1 ? 1 : x); }
209 
210 
211 // Implementation of Young-van Vliet third-order recursive gaussian filter.
212 // See "Recursive Gaussian Derivative Filters", by van Vliet, Young and Verbeck, 1998
213 // and "Boundary Conditions for Young - van Vliet Recursive Filtering", by Triggs and Sdika, 2005
214 // Can result in values just outside of the input range
215 void van_vliet_blur(const double b[], const CVD::SubImage<float> in, CVD::SubImage<float> out)
216 {
217  assert(in.size() == out.size());
218  const int w = in.size().x;
219  const int h = in.size().y;
220 
221  double M[3][3];
222  compute_triggs_M(b, M);
223 
224  vector<double> tmp(w);
225 
226  const double b0 = b[0];
227  const double b1 = b[1];
228  const double b2 = b[2];
229 
230  const int rw = w%4;
231  const double alpha = 1 + b0 + b1 + b2;
232  const double inv_alpha = 1.0/alpha;
233 
234  for (int i=0; i<h; ++i) {
235  const float* p = in[i]+w-1;
236 
237  double y3, y2, y1;
238  y3 = y2 = y1 = inv_alpha* *p;
239 
240 
241  for (int j=w-1; j-3>=0; j-=4, p-=4) {
242  double y0 = p[0] - (b0*y1 + b1*y2 + b2 * y3);
243  y3 = p[-1] - (b0*y0 + b1*y1 + b2 * y2);
244  y2 = p[-2] - (b0*y3 + b1*y0 + b2 * y1);
245  y1 = p[-3] - (b0*y2 + b1*y3 + b2 * y0);
246  tmp[j] = y0;
247  tmp[j-1] = y3;
248  tmp[j-2] = y2;
249  tmp[j-3] = y1;
250  }
251 
252  for (int j=rw-1; j>=0; --j, --p) {
253  double y0 = p[0] - (b0*y1 + b1*y2 + b2 * y3);
254  tmp[j] = y0;
255  y3 = y2; y2 = y1; y1 = y0;
256  }
257 
258  {
259  const double i_plus = p[1];
260  double y0 = i_plus - (b0*y1 + b1*y2 + b2*y3);
261  y3=y2; y2=y1; y1=y0;
262  forward_to_backward(M, i_plus, inv_alpha, y1, y2, y3);
263  }
264 
265  float* o = out[i];
266  for (int j=0; j+3<w; j+=4, o+=4) {
267  double y0 = tmp[j] - (b0*y1 + b1*y2 + b2 * y3);
268  y3 = tmp[j+1] - (b0*y0 + b1*y1 + b2 * y2);
269  y2 = tmp[j+2] - (b0*y3 + b1*y0 + b2 * y1);
270  y1 = tmp[j+3] - (b0*y2 + b1*y3 + b2 * y0);
271  o[0] = (float)y0;
272  o[1] = (float)y3;
273  o[2] = (float)y2;
274  o[3] = (float)y1;
275  }
276 
277  for (int j=w-rw; j<w; ++j, ++o) {
278  double y0 = tmp[j] - (b0*y1 + b1*y2 + b2 * y3);
279  o[0] = (float)y0;
280  y3 = y2; y2 = y1; y1 = y0;
281  }
282 
283  }
284 
285  tmp.resize(h);
286 
287  const double alpha_fourth = alpha*alpha*alpha*alpha;
288 
289  const int rh = h%4;
290  const int stride = out.row_stride();
291  for (int i=0; i<w; ++i) {
292  double y3, y2, y1;
293 
294  const float* in = out[h-1] + i;
295  y3 = y2 = y1 = inv_alpha* *in;
296 
297  for (int j=h-1; j-3>=0; j-=4, in -= stride) {
298  double y0 = in[0] - (b0*y1 + b1*y2 + b2 * y3);
299  in -= stride;
300  y3 = in[0] - (b0*y0 + b1*y1 + b2 * y2);
301  in -= stride;
302  y2 = in[0] - (b0*y3 + b1*y0 + b2 * y1);
303  in -= stride;
304  y1 = in[0] - (b0*y2 + b1*y3 + b2 * y0);
305  tmp[j] = y0;
306  tmp[j-1] = y3;
307  tmp[j-2] = y2;
308  tmp[j-3] = y1;
309  }
310 
311  for (int j=rh-1; j>=0; --j, in-=stride) {
312  double y0 = in[0] - (b0*y1 + b1*y2 + b2 * y3);
313  tmp[j] = y0;
314  y3 = y2; y2 = y1; y1 = y0;
315  }
316 
317  {
318  const double i_plus = in[stride];
319  double y0 = i_plus - (b0*y1 + b1*y2 + b2*y3);
320  y3=y2; y2=y1; y1=y0;
321  forward_to_backward(M, i_plus, inv_alpha, y1, y2, y3);
322  }
323 
324  float* o = out[0]+i;
325  for (int j=0; j+3<h; j+=4) {
326  double y0 = tmp[j] - (b0*y1 + b1*y2 + b2 * y3);
327  y3 = tmp[j+1] - (b0*y0 + b1*y1 + b2 * y2);
328  y2 = tmp[j+2] - (b0*y3 + b1*y0 + b2 * y1);
329  y1 = tmp[j+3] - (b0*y2 + b1*y3 + b2 * y0);
330  o[0] = (float)(alpha_fourth*y0);//clamp01(alpha_fourth*y0);
331  o+=stride;
332  o[0] = (float)(alpha_fourth*y3);//clamp01(alpha_fourth*y3);
333  o+=stride;
334  o[0] = (float)(alpha_fourth*y2);//clamp01(alpha_fourth*y2);
335  o+=stride;
336  o[0] = (float)(alpha_fourth*y1);//clamp01(alpha_fourth*y1);
337  o+=stride;
338  }
339 
340  for (int j=h-rh; j<h; ++j, o+=stride) {
341  double y0 = tmp[j] - (b0*y1 + b1*y2 + b2 * y3);
342  o[0] = (float)(alpha_fourth*y0);//clamp01(alpha_fourth*y0);
343  y3 = y2; y2 = y1; y1 = y0;
344  }
345  }
346 }
347 
348 
349 };
350 
int y
The y co-ordinate.
Definition: image_ref.h:180
int x
The x co-ordinate.
Definition: image_ref.h:179
void compute_van_vliet_scaled_d(double sigma, double d[])
Definition: abs.h:24
int row_stride() const
What is the row stride of the image?
Definition: image.h:376
void compute_scaling_jacobian(const double d[], double J[])
void convolveGaussian5_1(Image< byte > &I)
Definition: convolution.cpp:43
Matrix< R, C, P > exp(const Matrix< R, C, P, B > &m)
Definition: helpers.h:284
ImageRef size() const
What is the size of this image?
Definition: image.h:370
double compute_van_vliet_variance(const double d[], double J[3])
Definition: convolution.cpp:86
const T * data() const
Returns the raw image data.
Definition: image.h:326
void convolveSeparableGray(unsigned char *I, unsigned int width, unsigned int height, const int kernel[], unsigned int size, int divisor)
Definition: convolution.cpp:9
unsigned char byte
Definition: byte.h:28
void scale_d(double d[], double p)
void compute_triggs_M(const double b[], double M[][3])
void build_van_vliet_b(const double d[], double b[])
void compute_van_vliet_b(double sigma, double b[])
void van_vliet_blur(const double b[], const CVD::SubImage< float > in, CVD::SubImage< float > out)
T clamp01(T x)
void forward_to_backward(const double M[][3], const double i_plus, const double inv_alpha, double &y1, double &y2, double &y3)