GSLAM  3.0.0
Camera.h
1 // GSLAM - A general SLAM framework and benchmark
2 // Copyright 2018 PILAB Inc. All rights reserved.
3 // https://github.com/zdzhaoyong/GSLAM
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors may be
14 // used to endorse or promote products derived from this software without
15 // specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
28 //
29 // Author: zd5945@126.com (Yong Zhao)
30 //
31 // Camera provide projection of 2D from(to) 3D coordinates
32 
33 #ifndef GSLAM_CAMERA_H
34 #define GSLAM_CAMERA_H
35 
36 #include <vector>
37 #include <sstream>
38 #include <memory>
39 #include "Point.h"
40 
41 #ifdef __SSE3__
42 #include "pmmintrin.h"
43 #endif
44 
45 namespace GSLAM {
46 
47 class CameraImpl//No camera
48 {
49 public:
50  CameraImpl(int _w=-1,int _h=-1):w(_w),h(_h){}
51  ~CameraImpl(){}
52 
53  virtual std::string CameraType()const{return "NoCamera";}
54  virtual bool isValid()const{return false;}
55  virtual Point2d Project(const Point3d& p3d)const{return Point2d(-1,-1);}
56  virtual Point3d UnProject(const Point2d& p2d)const{return Point3d(0,0,0);}
57  virtual std::vector<double> getParameters()const{return std::vector<double>();}
58 
59  virtual bool applyScale(double scale=0.5){return false;}
60  virtual std::string info()const;
61 
62  int32_t w,h;
63  int32_t state,tmp;
64 };
65 
66 
67 /// Identity pinhole camera,fx=fy=1&&cx=cy=0
68 /// (x y)^T=(fx*X/Z+cx,fy*Y/Z+cy)^T
69 class CameraIdeal:public CameraImpl
70 {
71 public:
72  CameraIdeal(int width,int height):CameraImpl(width,height){}
73 
74  virtual std::string CameraType()const{return "Ideal";}
75  virtual bool isValid()const{return true;}
76  virtual Point2d Project(const Point3d& p3d)const{double z_inv=1./p3d.z;return Point2d(p3d.x*z_inv,p3d.y*z_inv);}
77  virtual Point3d UnProject(const Point2d& p2d)const{return Point3d(p2d.x,p2d.y,1.);}
78  virtual std::vector<double> getParameters()const{return {(double)w,(double)h};}
79 };
80 
81 /// Pinhole model
82 /// (x y)^T=(fx*X/Z+cx,fy*Y/Z+cy)^T
84 {
85 public:
86  CameraPinhole():fx(0),fy(0),cx(0),cy(0),fx_inv(0),fy_inv(0){}
87  CameraPinhole(int _w,int _h,double _fx,double _fy,double _cx,double _cy)
88  :CameraImpl(_w,_h),fx(_fx),fy(_fy),cx(_cx),cy(_cy),fx_inv(0),fy_inv(0)
89  {refreshParaments();}
90 
91  virtual std::string CameraType()const{return "PinHole";}
92  virtual bool isValid()const{return w>0&&h>0&&fx!=0&&fy!=0;}
93  virtual Point2d Project(const Point3d& p3d)const;
94  virtual Point3d UnProject(const Point2d& p2d)const;
95  virtual std::vector<double> getParameters()const{return {(double)w,(double)h,fx,fy,cx,cy};}
96 
97  virtual bool applyScale(double scale=0.5);
98 
99  double fx,fy,cx,cy,fx_inv,fy_inv;
100 
101 private:
102  int refreshParaments(){if(isValid()){fx_inv=1./fx;fy_inv=1./fy;
103  return 0;}else return -1;}
104 };
105 
106 /// Camera described in PTAM(alse called ANTA camera), measurement: \f$(x,y)^T\f$, z=1 plane: (X,Y)^T
107 /// Project:
108 /// |x|=|cx| + |fx 0| |X| *r'/r
109 /// |y| |cy| |0 fy| |Y|
110 /// r =sqrt(X*X+Y*Y)
111 /// r'=atan(r * tan2w) / w
112 /// tan2w=2.0 * tan(w / 2.0)
113 ///
114 /// UnProject:
115 /// |X|=(|x|-|cx|) * |1/fx 0|*r/r'
116 /// |Y| |y| |cy| |0 1/fy|
117 /// r' =sqrt(X*X+Y*Y)
118 /// r =(tan(r' * w) / tan2w)
119 class CameraATAN:public CameraImpl
120 {
121 public:
122  CameraATAN();
123  CameraATAN(int _w,int _h,double _fx,double _fy,double _cx,double _cy,double _d);
124 
125  virtual std::string CameraType()const{return "ATAN";}
126  virtual bool isValid()const{return w>0&&h>0&&fx!=0&&fy!=0;}
127  virtual Point2d Project(const Point3d& p3d)const;
128  virtual Point3d UnProject(const Point2d& p2d)const;
129  virtual std::vector<double> getParameters()const{return {(double)w,(double)h,fx,fy,cx,cy,d};}
130 
131  virtual bool applyScale(double scale=0.5);
132 
133  double fx,fy,cx,cy,d,fx_inv,fy_inv;
134  double tan2w,d_inv,tan2w_inv;
135 private:
136  virtual int refreshParaments();
137  bool& UseDistortion(){return useDistortion;}
138  bool useDistortion;
139 };
140 typedef CameraATAN CameraPTAM;
141 typedef CameraATAN CameraANTA;
142 
143 /// Camera model used by OpenCV
144 /// Project:
145 /// r^2= X^2+Y^2;
146 /// X1= X*(1+k1*r^2 + k2*r^4+k3*r^6) + 2*p1*XY + p2*(r^2 + 2*X^2);
147 /// Y1= Y*(1+k1*r^2 + k2*r^4+k3*r^6) + 2*p2*XY + p1*(r^2 + 2*Y^2);
148 /// (x y)^T=(fx*X1+cx,fy*Y1+cy)^T
149 ///
150 /// UnProject:
151 ///
153 {
154 public:
155  CameraOpenCV():fx(0),fy(0),cx(0),cy(0),fx_inv(0),fy_inv(0){}
156  CameraOpenCV(int Width,int Height,
157  double Fx,double Fy,double Cx,double Cy,
158  double K1,double K2,double P1,double P2,double K3)
159  :CameraImpl(Width,Height),fx(Fx),fy(Fy),cx(Cx),cy(Cy),
160  k1(K1),k2(K2),p1(P1),p2(P2),k3(K3){refreshParaments();}
161 
162  virtual std::string CameraType()const{return "OpenCV";}
163  virtual bool isValid()const{return w>0&&h>0&&fx!=0&&fy!=0;}
164  virtual Point2d Project(const Point3d& p3d)const;
165  virtual Point3d UnProject(const Point2d& p2d)const;
166  virtual std::vector<double> getParameters()const{return {(double)w,(double)h,fx,fy,cx,cy,k1,k2,p1,p2,k3};}
167 
168  virtual bool applyScale(double scale=0.5);
169  double fx,fy,cx,cy,fx_inv,fy_inv,
170  k1,k2,p1,p2,k3;
171 private:
172  int refreshParaments();
173 };
174 
175 class Camera
176 {
177 public:
178  Camera(const std::shared_ptr<CameraImpl>& Impl=std::shared_ptr<CameraImpl>(new CameraImpl()));
179  Camera(const std::vector<double>& paras);
180 
181  std::string CameraType()const{return impl->CameraType();}
182  std::string info()const{return impl->info();}
183  bool isValid()const{return impl->isValid();}
184  int width()const{return impl->w;}
185  int height()const{return impl->h;}
186 
187  Point2d Project(const Point3d& p3d)const{return impl->Project(p3d);}
188  Point2d Project(const double x, const double y, const double z)const { return Project(Point3d(x, y, z)); }
189 
190  Point3d UnProject(const Point2d& p2d)const{return impl->UnProject(p2d);}
191  Point3d UnProject(const double x, const double y)const { return UnProject(Point2d(x, y)); }
192 
193  std::vector<double> getParameters()const{return impl->getParameters();}
194 
195  bool applyScale(double scale=0.5){return impl->applyScale(scale);}
196  Camera estimatePinHoleCamera(void)const;
197 
198 private:
199  std::shared_ptr<CameraImpl> impl;
200 };
201 
202 inline std::string CameraImpl::info()const{
203  std::stringstream sst;
204  sst<<CameraType()<<":[";
205  std::vector<double> paras=getParameters();
206  for(size_t i=0;i<paras.size();i++)
207  sst<<paras[i]<<((i+1==paras.size())?"]":",");
208  return sst.str();
209 }
210 
211 inline bool CameraPinhole::applyScale(double scale) { w=(int32_t)(w*scale); h=(int32_t)(h*scale); fx*=scale;fy*=scale;cx*=scale;cy*=scale; refreshParaments(); return true; }
212 
213 inline Point2d CameraPinhole::Project(const Point3d& p3d)const
214 {
215 #ifdef __SSE3__
216  if(p3d.z==1.)
217  {
218  __m128d xy = _mm_setr_pd(p3d.x,p3d.y);
219  xy=_mm_add_pd(_mm_setr_pd(cx,cy),_mm_mul_pd(xy,(__m128d){fx,fy}));
220  return *(Point2d*)&xy;
221  }
222  else if(p3d.z>0)
223  {
224  double z_inv=1./p3d.z;
225  return Point2d(fx*z_inv*p3d.x+cx,fy*z_inv*p3d.y+cy);
226  }
227  else return Point2d(-1,-1);
228 #else
229  if(p3d.z==1.)
230  {
231  return Point2d(fx*p3d.x+cx,fy*p3d.y+cy);
232  }
233  else if(p3d.z>0)
234  {
235  double z_inv=1./p3d.z;
236  return Point2d(fx*z_inv*p3d.x+cx,fy*z_inv*p3d.y+cy);
237  }
238  else return Point2d(-1,-1);
239 #endif
240 }
241 
242 inline Point3d CameraPinhole::UnProject(const Point2d& p2d)const
243 {
244  return Point3d((p2d.x-cx)*fx_inv,(p2d.y-cy)*fy_inv,1.);
245 }
246 
247 inline CameraATAN::CameraATAN():fx(0),fy(0),cx(0),cy(0),d(0),fx_inv(0),fy_inv(0){}
248 
249 inline CameraATAN::CameraATAN(int _w,int _h,double _fx,double _fy,double _cx,double _cy,double _d)
250  :CameraImpl(_w,_h),fx(_fx),fy(_fy),cx(_cx),cy(_cy),d(_d),fx_inv(0),fy_inv(0)
251 {refreshParaments();}
252 
253 inline int CameraATAN::refreshParaments()
254 {
255  if(!isValid()) {
256  std::cout<<"Camera not valid!Info:"<<info()<<std::endl;
257  return -1;}
258  if(fx<1&&fy<1)
259  {
260  fx*=w;fy*=h;cx*=w;cy*=h;
261  }
262  if(fx!=0) fx_inv=1./fx;else return -1;
263  if(fy!=0) fy_inv=1./fy;else return -2;
264  if(d!= 0.0)
265  {
266  tan2w = 2.0 * tan(d / 2.0);
267  tan2w_inv=1.0/tan2w;
268  d_inv = 1.0 / d;
269  useDistortion = 1.0;
270  }
271  else
272  {
273  d_inv = 0.0;
274  tan2w = 0.0;
275  useDistortion = false;
276  }
277  return 0;
278 }
279 
280 inline bool CameraATAN::applyScale(double scale)
281 { w=(int32_t)(w*scale); h=(int32_t)(h*scale); fx*=scale; fy*=scale; cx*=scale; cy*=scale; refreshParaments(); return true;}
282 
283 inline Point2d CameraATAN::Project(const Point3d& p3d)const
284 {
285  if(p3d.z<=0) return Point2d(-1,-1);
286 
287 #ifdef __SSE3__
288  if(useDistortion)
289  {
290  __m128d xy=(__m128d){p3d.x,p3d.y};
291  if(p3d.z!=1.)
292  {
293  xy=_mm_sub_pd(xy,(__m128d){p3d.z,p3d.z});
294  }
295  __m128d xy2=_mm_mul_pd(xy,xy);
296 
297  xy2=_mm_hadd_pd(xy2,xy2);
298  xy2=_mm_sqrt_pd(xy2);
299  double r=((Point2d*)&xy2)->x;
300  if(r < 0.001 || d == 0.0)
301  r=1.0;
302  else
303  r=(d_inv* atan(r * tan2w) / r);
304  xy=_mm_mul_pd((__m128d){fx,fy},xy);
305  xy=_mm_mul_pd(xy,(__m128d){r,r});
306  xy=_mm_add_pd(xy,(__m128d){cx,cy});
307  return *(Point2d*)&xy;
308  }
309  else
310  {
311  if(p3d.z==1.)
312  {
313  __m128d xy = _mm_setr_pd(p3d.x,p3d.y);
314  xy=_mm_add_pd(_mm_setr_pd(cx,cy),_mm_mul_pd(xy,(__m128d){fx,fy}));
315  return *(Point2d*)&xy;
316  }
317  else if(p3d.z>0)
318  {
319  double z_inv=1./p3d.z;
320  return Point2d(fx*z_inv*p3d.x+cx,fy*z_inv*p3d.y+cy);
321  }
322  }
323 #else
324  if(useDistortion)
325  {
326  double X=p3d.x,Y=p3d.y;
327  if(p3d.z!=1.)
328  {
329  double z_inv=1./p3d.z;
330  X*=z_inv;Y*=z_inv;
331  }
332  double r= sqrt(X*X+Y*Y);
333  if(r < 0.001 || d == 0.0)
334  r= 1.0;
335  else
336  r=(d_inv* atan(r * tan2w) / r);
337 
338  return Point2d(cx + fx * r * X,cy + fy * r * Y);
339  }
340  else
341  {
342  if(p3d.z==1.)
343  {
344  return Point2d(fx*p3d.x+cx,fy*p3d.y+cy);
345  }
346  else
347  {
348  double z_inv=1./p3d.z;
349  return Point2d(fx*z_inv*p3d.x+cx,fy*z_inv*p3d.y+cy);
350  }
351  }
352 #endif
353  return Point2d(-1,-1);// let compiler happy
354 }
355 
356 inline Point3d CameraATAN::UnProject(const Point2d& p2d)const
357 {
358  if(useDistortion)
359  {
360  Point3d result((p2d.x-cx)*fx_inv,(p2d.y-cy)*fy_inv,1.);
361  double r = sqrt(result.x*result.x+result.y*result.y);
362 
363  if(d != 0.0&& r >0.01)
364  {
365  r = ( tan(r * d)*tan2w_inv )/r;//;
366  result.x=result.x*r;
367  result.y=result.y*r;
368  }
369  return result;
370  }
371  else return Point3d((p2d.x-cx)*fx_inv,(p2d.y-cy)*fy_inv,1.);
372 }
373 
374 inline bool CameraOpenCV::applyScale(double scale){ w=(int32_t)(w*scale); h=(int32_t)(h*scale); fx*=scale;fy*=scale;cx*=scale;cy*=scale; refreshParaments(); return true;}
375 
376 inline int CameraOpenCV::refreshParaments()
377 {
378  if(!isValid()) {
379  std::cout<<"Camera not valid!Info:"<<info()<<std::endl;
380  return -1;}
381  if(fx!=0) fx_inv=1./fx;else return -1;
382  if(fy!=0) fy_inv=1./fy;else return -2;
383  return 0;
384 }
385 
386 inline Point2d CameraOpenCV::Project(const Point3d& p3d)const
387 {
388  if(p3d.z<=0) return Point2d(-1,-1);
389  double X=p3d.x,Y=p3d.y;
390  if(p3d.z!=1.)
391  {
392  double z_inv=1./p3d.z;
393  X*=z_inv;Y*=z_inv;
394  }
395 
396  double r2,r4,r6,X1,Y1,X2,Y2,xy2;
397  X2=X*X;
398  Y2=Y*Y;
399  r2= X2+Y2;
400  r4=r2*r2;
401  r6=r2*r4;
402  xy2=X*Y*2.0;
403  X1= X*(1.+k1*r2 + k2*r4+k3*r6) + xy2*p1 + p2*(r2 + 2.0*X2);
404  Y1= Y*(1.+k1*r2 + k2*r4+k3*r6) + xy2*p2 + p1*(r2 + 2.0*Y2);
405 
406  return Point2d(cx + fx * X1,cy + fy * Y1);
407 }
408 
409 inline Point3d CameraOpenCV::UnProject(const Point2d& p2d)const
410 {
411  double x = (p2d.x - cx)*fx_inv;
412  double y = (p2d.y - cy)*fy_inv;
413 
414  // compensate tilt distortion
415  double x0 = x ;
416  double y0 = y ;
417  // compensate distortion iteratively
418  for(int j = 0; j < 5; j++ )
419  {
420  double r2 = x*x + y*y;
421  double icdist = (1)/(1 + ((k3*r2 + k2)*r2 + k1)*r2);
422  double deltaX = 2*p1*x*y + p2*(r2 + 2*x*x);
423  double deltaY = p1*(r2 + 2*y*y) + 2*p2*x*y;
424  x = (x0 - deltaX)*icdist;
425  y = (y0 - deltaY)*icdist;
426  }
427  return Point3d(x,y,1);
428 }
429 
430 
431 inline Camera::Camera(const std::shared_ptr<CameraImpl>& Impl):impl(Impl)
432 {
433 }
434 
435 inline Camera::Camera(const std::vector<double> &p)
436 {
437  CameraImpl* cam;
438  if(p.size()==2) cam=new CameraIdeal((int)p[0],(int)p[1]);
439  else if(p.size()==6) cam=new CameraPinhole((int)p[0],(int)p[1],p[2],p[3],p[4],p[5]);
440  else if(p.size()==7) cam=new CameraATAN((int)p[0],(int)p[1],p[2],p[3],p[4],p[5],p[6]);
441  else if(p.size()==11) cam=new CameraOpenCV((int)p[0],(int)p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10]);
442  else cam=new CameraImpl();
443  impl=std::shared_ptr<CameraImpl>(cam);
444 }
445 
446 inline Camera Camera::estimatePinHoleCamera()const
447 {
448  if( CameraType() == "PinHole" || CameraType() == "Ideal" ) {
449  return *this;
450  }
451 
452  int iw, ih;
453  int ix, iy;
454 
455  iw = width();
456  ih = height();
457 
458  double xl_max = -99999, xr_min = 99999, yt_max = -99999, yb_min = 99999;
459 
460  ix = 0;
461  for(iy=0; iy<ih; iy++) {
462  Point3d p = UnProject(ix, iy);
463  if( p.x > xl_max ) xl_max = p.x;
464  }
465 
466  ix = iw-1;
467  for(iy=0; iy<ih; iy++) {
468  Point3d p = UnProject(ix, iy);
469  if( p.x < xr_min ) xr_min = p.x;
470  }
471 
472  iy = 0;
473  for(ix=0; ix<iw; ix++) {
474  Point3d p = UnProject(ix, iy);
475  if( p.y > yt_max ) yt_max = p.y;
476  }
477 
478  iy = ih-1;
479  for(ix=0; ix<iw; ix++) {
480  Point3d p = UnProject(ix, iy);
481  if( p.y < yb_min ) yb_min = p.y;
482  }
483 
484  double xx, yy;
485  double _fx, _fy, _cx, _cy;
486 
487  xx = fabs(xl_max); if( xx > fabs(xr_min) ) xx = fabs(xr_min);
488  yy = fabs(yt_max); if( yy > fabs(yb_min) ) yy = fabs(yb_min);
489 
490  _cx = iw/2.0;
491  _cy = ih/2.0;
492  _fx = iw/2.0 / xx;
493  _fy = ih/2.0 / yy;
494 
495  if( _fx < _fy ) _fx = _fy;//FIXME : why need this?
496  else _fy = _fx;
497 
498  CameraPinhole* camera = new CameraPinhole(iw, ih, _fx, _fy, _cx, _cy);
499  std::shared_ptr<CameraImpl> impl_result = std::shared_ptr<CameraImpl>(camera);
500 
501  return Camera(impl_result);
502 }
503 
504 }
505 #endif // CAMERA_H
Definition: Point.h:48
Definition: Point.h:162
Definition: Camera.h:175
Definition: Camera.h:47
Camera model used by OpenCV Project: r^2= X^2+Y^2; X1= X*(1+k1*r^2 + k2*r^4+k3*r^6) + 2*p1*XY + p2*(r...
Definition: Camera.h:152
Pinhole model (x y)^T=(fx*X/Z+cx,fy*Y/Z+cy)^T.
Definition: Camera.h:83
Definition: Camera.h:45
Identity pinhole camera,fx=fy=1&&cx=cy=0 (x y)^T=(fx*X/Z+cx,fy*Y/Z+cy)^T.
Definition: Camera.h:69
Camera described in PTAM(alse called ANTA camera), measurement: , z=1 plane: (X,Y)^T Project: |x|=|cx...
Definition: Camera.h:119