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.
vision.h
Go to the documentation of this file.
1 /*
2  This file is part of the CVD Library.
3 
4  Copyright (C) 2005 The Authors
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public
8  License as published by the Free Software Foundation; either
9  version 2.1 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library; if not, write to the Free Software
18  Foundation, Inc.,
19  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21 
22 #ifndef CVD_VISION_H_
23 #define CVD_VISION_H_
24 
25 #include <vector>
26 #include <memory>
27 #include <algorithm>
28 
29 #include "vision_exceptions.h"
30 #include "image.h"
31 #include "pixel_operations.h"
32 #include "utility.h"
33 
34 #include "TooN.h"
35 #include "helpers.h"
36 
37 namespace CVD{
44 template<class C> void twoThirdsSample(const SubImage<C>& in, SubImage<C>& out)
45 {
46  typedef typename Pixel::traits<C>::wider_type sum_type;
47  if( (in.size()/3*2) != out.size())
49 
50  for(int yy=0, y=0; y < in.size().y-2; y+=3, yy+=2)
51  for(int xx=0, x=0; x < in.size().x-2; x+=3, xx+=2)
52  {
53  // a b c
54  // d e f
55  // g h i
56 
57  sum_type b = in[y][x+1]*2;
58  sum_type d = in[y+1][x]*2;
59  sum_type f = in[y+1][x+2]*2;
60  sum_type h = in[y+2][x+1]*2;
61  sum_type e = in[y+1][x+1];
62 
63  out[yy][xx] = static_cast<C>((in[ y][ x]*4+b+d+e)/9);
64  out[yy][xx+1] = static_cast<C>((in[ y][x+2]*4+b+f+e)/9);
65  out[yy+1][xx] = static_cast<C>((in[y+2][ x]*4+h+d+e)/9);
66  out[yy+1][xx+1] = static_cast<C>((in[y+2][x+2]*4+h+f+e)/9);
67  }
68 }
69 
73 void twoThirdsSample(const SubImage<byte>& in, SubImage<byte>& out);
74 
75  namespace Internal
76  {
77  template<class C> class twoThirdsSampler{};
78  template<class C> struct ImagePromise<twoThirdsSampler<C> >
79  {
81  :i(im)
82  {}
83 
84  const SubImage<C>& i;
85  template<class D> void execute(Image<D>& j)
86  {
87  j.resize(i.size()/3*2);
88  twoThirdsSample(i, j);
89  }
90  };
91  };
93  {
95  }
96 
102 template <class T>
104 {
105  typedef typename Pixel::traits<T>::wider_type sum_type;
106  if( (in.size()/2) != out.size())
107  throw Exceptions::Vision::IncompatibleImageSizes("halfSample");
108  const T* top = in.data();
109  const T* bottom = top + in.size().x;
110  const T* end = top + in.totalsize();
111  int ow = out.size().x;
112  int skip = in.size().x + (in.size().x % 2);
113  T* p = out.data();
114  while (bottom < end) {
115  for (int j=0; j<ow; j++) {
116  *p = static_cast<T>((sum_type(top[0]) + top[1] + bottom[0] + bottom[1])/4);
117  p++;
118  top += 2;
119  bottom += 2;
120  }
121  top += skip;
122  bottom += skip;
123  }
124 }
125 
126 //void halfSample(const BasicImage<byte>& in, BasicImage<byte>& out);
127 
133 template <class T>
135 {
136  Image<T> out(in.size()/2);
137  halfSample(in, out);
138  return out;
139 }
140 
149 template <class T>
150 inline Image<T> halfSample( Image<T> in, unsigned int octaves){
151  for( ;octaves > 0; --octaves){
152  in = halfSample(in);
153  }
154  return in;
155 }
156 
162 template <class T>
163 void threshold(BasicImage<T>& im, const T& minimum, const T& hi)
164 {
165  typename BasicImage<T>::iterator it = im.begin();
166  typename BasicImage<T>::iterator end = im.end();
167  while (it != end) {
168  if (*it < minimum)
169  *it = T();
170  else
171  *it = hi;
172  ++it;
173  }
174 }
175 
182 template <class T>
183 void stats(const BasicImage<T>& im, T& mean, T& stddev)
184 {
185  const int c = Pixel::Component<T>::count;
186  double v;
187  double sum[c] = {0};
188  double sumSq[c] = {0};
189  const T* p = im.data();
190  const T* end = im.data()+im.totalsize();
191  while (p != end) {
192  for (int k=0; k<c; k++) {
193  v = Pixel::Component<T>::get(*p, k);
194  sum[k] += v;
195  sumSq[k] += v*v;
196  }
197  ++p;
198  }
199  for (int k=0; k<c; k++) {
200  double m = sum[k]/im.totalsize();
202  sumSq[k] /= im.totalsize();
203  Pixel::Component<T>::get(stddev,k) = (typename Pixel::Component<T>::type)sqrt(sumSq[k] - m*m);
204  }
205 }
206 
209 template <class T>
211 {
212  const T& factor;
213  multiplyBy(const T& f) : factor(f) {};
214  template <class S> inline S operator()(const S& s) const {
215  return s * factor;
216  };
217 };
218 
219 template <class S, class T, int Sn=Pixel::Component<S>::count, int Tn=Pixel::Component<T>::count> struct Gradient;
220 template <class S, class T> struct Gradient<S,T,1,2> {
224  static void gradient(const BasicImage<S>& I, BasicImage<T>& grad) {
225  int w = I.size().x;
226  typename BasicImage<S>::const_iterator s = I.begin() + w + 1;
227  typename BasicImage<S>::const_iterator end = I.end() - w - 1;
228  typename BasicImage<T>::iterator t = grad.begin() + w + 1;
229  while (s != end) {
230  Pixel::Component<T>::get(*t, 0) = Pixel::scalar_convert<TComp,SComp,diff_type>(diff_type(*(s+1)) - *(s-1));
231  Pixel::Component<T>::get(*t, 1) = Pixel::scalar_convert<TComp,SComp,diff_type>(diff_type(*(s+w)) - *(s-w));
232  s++;
233  t++;
234  }
235  zeroBorders(grad);
236  }
237 };
238 
245 template <class S, class T> void gradient(const BasicImage<S>& im, BasicImage<T>& out)
246 {
247  if( im.size() != out.size())
249  Gradient<S,T>::gradient(im,out);
250 }
251 
252 
253 void gradient(const BasicImage<byte>& im, BasicImage<short[2]>& out);
254 
255 
256 template <class T, class S> inline void sample(const BasicImage<S>& im, double x, double y, T& result)
257 {
258  typedef typename Pixel::Component<S>::type SComp;
259  typedef typename Pixel::Component<T>::type TComp;
260  int lx = (int)x;
261  int ly = (int)y;
262  x -= lx;
263  y -= ly;
264  for(unsigned int i = 0; i < Pixel::Component<T>::count; i++){
265  Pixel::Component<T>::get(result,i) = Pixel::scalar_convert<TComp,SComp>(
266  (1-y)*((1-x)*Pixel::Component<S>::get(im[ly][lx],i) + x*Pixel::Component<S>::get(im[ly][lx+1], i)) +
267  y * ((1-x)*Pixel::Component<S>::get(im[ly+1][lx],i) + x*Pixel::Component<S>::get(im[ly+1][lx+1],i)));
268  }
269  }
270 
271 template <class T, class S> inline T sample(const BasicImage<S>& im, double x, double y){
272  T result;
273  sample( im, x, y, result);
274  return result;
275 }
276 
277 inline void sample(const BasicImage<float>& im, double x, double y, float& result)
278  {
279  int lx = (int)x;
280  int ly = (int)y;
281  int w = im.size().x;
282  const float* base = im[ly]+lx;
283  float a = base[0];
284  float b = base[1];
285  float c = base[w];
286  float d = base[w+1];
287  float e = a-b;
288  x-=lx;
289  y-=ly;
290  result = (float)(x*(y*(e-c+d)-e)+y*(c-a)+a);
291  }
292 
303 template <class T, class S>
304 int transform(const BasicImage<S>& in, BasicImage<T>& out, const TooN::Matrix<2>& M, const TooN::Vector<2>& inOrig, const TooN::Vector<2>& outOrig, const T defaultValue = T())
305 {
306  const int w = out.size().x, h = out.size().y, iw = in.size().x, ih = in.size().y;
307  const TooN::Vector<2> across = M.T()[0];
308  const TooN::Vector<2> down = M.T()[1];
309 
310  const TooN::Vector<2> p0 = inOrig - M*outOrig;
311  const TooN::Vector<2> p1 = p0 + w*across;
312  const TooN::Vector<2> p2 = p0 + h*down;
313  const TooN::Vector<2> p3 = p0 + w*across + h*down;
314 
315  // ul --> p0
316  // ur --> w*across + p0
317  // ll --> h*down + p0
318  // lr --> w*across + h*down + p0
319  double min_x = p0[0], min_y = p0[1];
320  double max_x = min_x, max_y = min_y;
321 
322  // Minimal comparisons needed to determine bounds
323  if (across[0] < 0)
324  min_x += w*across[0];
325  else
326  max_x += w*across[0];
327  if (down[0] < 0)
328  min_x += h*down[0];
329  else
330  max_x += h*down[0];
331  if (across[1] < 0)
332  min_y += w*across[1];
333  else
334  max_y += w*across[1];
335  if (down[1] < 0)
336  min_y += h*down[1];
337  else
338  max_y += h*down[1];
339 
340  // This gets from the end of one row to the beginning of the next
341  const TooN::Vector<2> carriage_return = down - w*across;
342 
343  //If the patch being extracted is completely in the image then no
344  //check is needed with each point.
345  if (min_x >= 0 && min_y >= 0 && max_x < iw-1 && max_y < ih-1)
346  {
347  TooN::Vector<2> p = p0;
348  for (int i=0; i<h; ++i, p+=carriage_return)
349  for (int j=0; j<w; ++j, p+=across)
350  sample(in,p[0],p[1],out[i][j]);
351  return 0;
352  }
353  else // Check each source location
354  {
355  // Store as doubles to avoid conversion cost for comparison
356  const double x_bound = iw-1;
357  const double y_bound = ih-1;
358  int count = 0;
359  TooN::Vector<2> p = p0;
360  for (int i=0; i<h; ++i, p+=carriage_return) {
361  for (int j=0; j<w; ++j, p+=across) {
362  //Make sure that we are extracting pixels in the image
363  if (0 <= p[0] && 0 <= p[1] && p[0] < x_bound && p[1] < y_bound)
364  sample(in,p[0],p[1],out[i][j]);
365  else {
366  out[i][j] = defaultValue;
367  ++count;
368  }
369  }
370  }
371  return count;
372  }
373 }
374 
375  template <class T> void transform(const BasicImage<T>& in, BasicImage<T>& out, const TooN::Matrix<3>& Minv /* <-- takes points in "out" to points in "in" */)
376  {
377  TooN::Vector<3> base = Minv.T()[2];
378  TooN::Vector<2> offset;
379  offset[0] = in.size().x/2;
380  offset[1] = in.size().y/2;
381  offset -= TooN::project(base);
382  TooN::Vector<3> across = Minv.T()[0];
383  TooN::Vector<3> down = Minv.T()[1];
384  double w = in.size().x-1;
385  double h = in.size().y-1;
386  int ow = out.size().x;
387  int oh = out.size().y;
388  base -= down*(oh/2) + across*(ow/2);
389  for (int row = 0; row < oh; row++, base+=down) {
390  TooN::Vector<3> x = base;
391  for (int col = 0; col < ow; col++, x += across) {
392  TooN::Vector<2> p = project(x) + offset;
393  if (p[0] >= 0 && p[0] <= w-1 && p[1] >=0 && p[1] <= h-1)
394  sample(in,p[0],p[1], out[row][col]);
395  else
396  zeroPixel(out[row][col]);
397  }
398  }
399  }
400 
402 template <class T> void flipVertical( Image<T> & in )
403 {
404  int w = in.size().x;
405  std::auto_ptr<T> buffer_auto(new T[w]);
406  T* buffer = buffer_auto.get();
407  T * top = in.data();
408  T * bottom = top + (in.size().y - 1)*w;
409  while( top < bottom )
410  {
411  std::copy(top, top+w, buffer);
412  std::copy(bottom, bottom+w, top);
413  std::copy(buffer, buffer+w, bottom);
414  top += w;
415  bottom -= w;
416  }
417 }
418 
420 template <class T> void flipHorizontal( Image<T> & in )
421 {
422  int w = in.size().x;
423  int h = in.size().y;
424  std::auto_ptr<T> buffer_auto(new T[w]);
425  T* buffer = buffer_auto.get();
426  T * left = in.data();
427  T * right = left + w;
428  int row = 0;
429  while(row < h)
430  {
431  std::copy(left, right, buffer);
432  std::reverse_copy(buffer, buffer+w-1, left);
433  row++;
434  left += w;
435  right += w;
436  }
437 }
438 
439 
440 namespace median {
441  template <class T> inline T median3(T a, T b, T c) {
442  if (b<a)
443  return std::max(b,std::min(a,c));
444  else
445  return std::max(a,std::min(b,c));
446  }
447 
448  template <class T> inline void sort3(T& a, T& b, T& c) {
449  using std::swap;
450  if (b<a) swap(a,b);
451  if (c<b) swap(b,c);
452  if (b<a) swap(a,b);
453  }
454 
455  template <class T> T median_3x3(const T* p, const int w) {
456  T a = p[-w-1], b = p[-w], c = p[-w+1], d=p[-1], e=p[0], f=p[1], g=p[w-1], h=p[w], i=p[w+1];
457  sort3(a,b,c);
458  sort3(d,e,f);
459  sort3(g,h,i);
460  e = median3(b,e,h);
461  g = std::max(std::max(a,d),g);
462  c = std::min(c,std::min(f,i));
463  return median3(c,e,g);
464  }
465 
466  template <class T> void median_filter_3x3(const T* p, const int w, const int n, T* out)
467  {
468  T a = p[-w-1], b = p[-w], d=p[-1], e=p[0], g=p[w-1], h=p[w];
469  sort3(a,d,g);
470  sort3(b,e,h);
471  for (int j=0; j<n; ++j, ++p, ++out) {
472  T c = p[-w+1], f = p[1], i = p[w+1];
473  sort3(c,f,i);
474  *out = median3(std::min(std::min(g,h),i),
475  median3(d,e,f),
476  std::max(std::max(a,b),c));
477  a=b; b=c; d=e; e=f; g=h; h=i;
478  }
479  }
480 }
481 
482  template <class T> void median_filter_3x3(const SubImage<T>& I, SubImage<T> out)
483  {
484  assert(out.size() == I.size());
485  const int s = I.row_stride();
486  const int n = I.size().x - 2;
487  for (int i=1; i+1<I.size().y; ++i)
488  median::median_filter_3x3(I[i]+1, s, n, out[i]+1);
489  }
490 
491 void median_filter_3x3(const SubImage<byte>& I, SubImage<byte> out);
492 
493 //template<class T>
494 
495 
496 }; // namespace CVD
497 
498 #endif // CVD_VISION_H_
Pixel::traits< SComp >::wider_type diff_type
Definition: vision.h:223
T * iterator
Definition: image.h:503
int totalsize() const
What is the total number of elements in the image (i.e. size().x * size().y), including padding...
Definition: image.h:382
int y
The y co-ordinate.
Definition: image_ref.h:180
int x
The x co-ordinate.
Definition: image_ref.h:179
void halfSample(const BasicImage< T > &in, BasicImage< T > &out)
Definition: vision.h:103
static const P & get(const P &pixel, size_t)
void resize(const ImageRef &size)
Definition: image.h:749
Pixel::Component< S >::type SComp
Definition: vision.h:221
void gradient(const BasicImage< S > &im, BasicImage< T > &out)
Definition: vision.h:245
void median_filter_3x3(const SubImage< T > &I, SubImage< T > out)
Definition: vision.h:482
multiplyBy(const T &f)
Definition: vision.h:213
static void gradient(const BasicImage< S > &I, BasicImage< T > &grad)
Definition: vision.h:224
void sort3(T &a, T &b, T &c)
Definition: vision.h:448
Definition: abs.h:24
void gradient(const BasicImage< byte > &im, BasicImage< short[2]> &out)
void copy(const BasicImage< S > &in, BasicImage< T > &out, ImageRef size=ImageRef(-1,-1), ImageRef begin=ImageRef(), ImageRef dst=ImageRef())
Definition: utility.h:26
int row_stride() const
What is the row stride of the image?
Definition: image.h:376
S operator()(const S &s) const
Definition: vision.h:214
Pixel::Component< T >::type TComp
Definition: vision.h:222
T median3(T a, T b, T c)
Definition: vision.h:441
const T * const_iterator
Definition: image.h:507
int transform(const BasicImage< S > &in, BasicImage< T > &out, const TooN::Matrix< 2 > &M, const TooN::Vector< 2 > &inOrig, const TooN::Vector< 2 > &outOrig, const T defaultValue=T())
Definition: vision.h:304
void zeroPixel(T &pixel)
Definition: utility.h:81
void median_filter_3x3(const T *p, const int w, const int n, T *out)
Definition: vision.h:466
T median_3x3(const T *p, const int w)
Definition: vision.h:455
void threshold(BasicImage< T > &im, const T &minimum, const T &hi)
Definition: vision.h:163
void zeroBorders(BasicImage< T > &I)
Set the one-pixel border (top, bottom, sides) of an image to zero values.
Definition: utility.h:88
ImageRef size() const
What is the size of this image?
Definition: image.h:370
const T & factor
Definition: vision.h:212
const T * data() const
Returns the raw image data.
Definition: image.h:326
void sample(const BasicImage< S > &im, double x, double y, T &result)
Definition: vision.h:256
void flipVertical(Image< T > &in)
flips an image vertically in place.
Definition: vision.h:402
void twoThirdsSample(const SubImage< C > &in, SubImage< C > &out)
Definition: vision.h:44
const_iterator end() const
Definition: image.h:518
const_iterator begin() const
Definition: image.h:511
void stats(const BasicImage< T > &im, T &mean, T &stddev)
Definition: vision.h:183
Vector<(Size==Dynamic?Dynamic:Size-1), Precision > project(const Vector< Size, Precision, Base > &v)
Definition: helpers.h:157
void flipHorizontal(Image< T > &in)
flips an image horizontally in place.
Definition: vision.h:420