├── README.md
├── Results
├── kitti00.png
├── kitti01.png
├── kitti02.png
├── kitti03.png
├── kitti04.png
└── kitti05.png
└── src
├── Frame.cpp
├── Frame.hpp
├── MathOperations.h
├── camera.cpp
├── camera.hpp
└── main.cpp
/README.md:
--------------------------------------------------------------------------------
1 | # SimpleStereoVO
2 | A very simple stereo visual odometer implemented in OpenCV.
3 |
4 | ## Some results:
5 | * For 'VO+IMU', I simply use the orientation provided by the IMU and the translation results from VO.
6 | * Video: http://v.youku.com/v_show/id_XMTgzMzczODI4MA==.html
7 | 
8 | 
9 | 
10 | 
11 | 
12 | 
13 |
14 |
15 | ## Usage
16 | Specifying your dataset parameters (marked as !!!CHANGE!!!) in main.cpp
17 | Note that the stereo images must be rectified.
18 |
19 | ## Dataset
20 | It runs properly on the KITTI benchmark dataset : http://www.cvlibs.net/datasets/kitti/eval_odometry.php
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Results/kitti00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meyiao/SimpleStereoVO/af410243f7c44b7073fcd81116832ea4adeeab88/Results/kitti00.png
--------------------------------------------------------------------------------
/Results/kitti01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meyiao/SimpleStereoVO/af410243f7c44b7073fcd81116832ea4adeeab88/Results/kitti01.png
--------------------------------------------------------------------------------
/Results/kitti02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meyiao/SimpleStereoVO/af410243f7c44b7073fcd81116832ea4adeeab88/Results/kitti02.png
--------------------------------------------------------------------------------
/Results/kitti03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meyiao/SimpleStereoVO/af410243f7c44b7073fcd81116832ea4adeeab88/Results/kitti03.png
--------------------------------------------------------------------------------
/Results/kitti04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meyiao/SimpleStereoVO/af410243f7c44b7073fcd81116832ea4adeeab88/Results/kitti04.png
--------------------------------------------------------------------------------
/Results/kitti05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meyiao/SimpleStereoVO/af410243f7c44b7073fcd81116832ea4adeeab88/Results/kitti05.png
--------------------------------------------------------------------------------
/src/Frame.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Frame.cpp
3 | // dabino
4 | //
5 | // Created by 谭智丹 on 16/3/8.
6 | // Copyright © 2016年 谭智丹. All rights reserved.
7 | //
8 |
9 | #include "Frame.hpp"
10 |
11 | Frame::Frame(Mat &img, int bs, Point2f ppt, float focalt, float baselinet)
12 | {
13 |
14 | im_width = img.cols;
15 | im_height = img.rows;
16 | block_size = bs;
17 | pp = ppt;
18 | focal = focalt;
19 | baseline = baselinet;
20 |
21 | }
22 |
23 |
24 | // Assign corners into grids.
25 | // We devide an image into small square grids. Then we assign the FAST corners into
26 | // these grids. Each element of 'subidx' is a vector, which contains all the indices for
27 | // corners that belong to this grid.
28 | void Frame::GetFeaturesIntoGrid(vector &vPoints, vector > &subidx)
29 | {
30 | subidx.clear();
31 | int n_xSub = im_width/block_size;
32 | int n_ySub = im_height/block_size;
33 | subidx.resize(n_xSub*n_ySub);
34 |
35 | for (int i=0; i &vKeyPoints, vector > &subidx)
48 | {
49 | for (size_t i=0; i sub_response;
51 | vector dsIdx;
52 | if (subidx[i].size()>1) {
53 | for (size_t j=0; j &iPoints, vector &oPoints, vector > &subidx, int N)
70 | {
71 | for (size_t i=0; i &pts1, vector& disp ,cv::Mat &img1, cv::Mat &img2, float ythresh)
90 | {
91 | vector err;
92 | vector status;
93 | vector pts2;
94 | calcOpticalFlowPyrLK(img1, img2, pts1, pts2, status, err, Size(31,31));
95 |
96 | size_t k=0;
97 | for (size_t i=0; i0.5) &&(diffx<150)) {
102 | pts1[k] = pts1[i];
103 | disp.push_back(diffx);
104 | k++;
105 | }
106 | }
107 | }
108 | }
109 |
110 | pts1.resize(k);
111 | disp.resize(k);
112 |
113 | }
114 |
115 |
116 | /*
117 | void Frame::st2rt(vector &stat, cv::Mat &R, cv::Mat &T)
118 | {
119 | float rx=stat[0], ry=stat[1], rz=stat[2];
120 | R = (Mat_(3,3)<<1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
121 | T = (Mat_(3,1)< 0.00001) {
125 | rx /= r; ry /= r; rz /= r;
126 |
127 | // compute some quantity to avoid repetition
128 | float ct = cos(r);
129 | float st = sin(r);
130 | float ict = 1.0f - ct;
131 | float rxy_ict = rx * ry * ict;
132 | float rxz_ict = rx * rz * ict;
133 | float ryz_ict = ry * rz * ict;
134 | float rx_st = rx * st;
135 | float ry_st = ry * st;
136 | float rz_st = rz * st;
137 |
138 | // calculate the elements of R
139 | float r11, r12, r13, r21, r22, r23, r31, r32, r33;
140 | r11 = ct + rx * rx * ict;
141 | r12 = rxy_ict + rz_st;
142 | r13 = rxz_ict - ry_st;
143 | r21 = rxy_ict - rz_st;
144 | r22 = ct + ry * ry * ict;
145 | r23 = ryz_ict + rx_st;
146 | r31 = rxz_ict + ry_st;
147 | r32 = ryz_ict - rx_st;
148 | r33 = ct + rz * rz * ict;
149 |
150 | R = (Mat_(3,3)<
11 | #include
12 |
13 | #include
14 |
15 | using namespace cv;
16 | using namespace std;
17 |
18 | class Frame
19 | {
20 | public:
21 | Frame(Mat& img, int bs, Point2f ppt, float focalt, float baselinet);
22 |
23 | // Assign detected FAST corners into grids
24 | void GetFeaturesIntoGrid(vector& vPoints, vector< vector >& subidx);
25 |
26 | // For all FAST corners belonging to the same grid, we sort them according to their
27 | // FAST-response scores.
28 | void SortSubIdx(vector& vKeyPoints, vector >& subidx);
29 |
30 | // For each grid, only keep the N FAST corners with highest scores.
31 | void SelectTopN(vector& iPoints, vector& oPoints, vector> & subidx, int N);
32 |
33 | // Calculate stereo disparities.
34 | void FindDisparity(vector& pts1, vector& disp, Mat& img1, Mat& img2, float ythresh);
35 |
36 | // Convert a state vector to rotaion matrix R, and translation vector T.
37 | // void st2rt(vector& stat, Mat& R, Mat& T);
38 |
39 |
40 | private:
41 | int im_width; // image width
42 | int im_height; // image height
43 | int block_size; // grid size
44 | Point2f pp; // principle point
45 | float focal; // focal length
46 | float baseline; // stereo base line
47 | };
48 |
49 | #endif /* Frame_hpp */
--------------------------------------------------------------------------------
/src/MathOperations.h:
--------------------------------------------------------------------------------
1 | //
2 | // MathOperations.h
3 | // dabino
4 | //
5 | // Created by 谭智丹 on 16/4/27.
6 | // Copyright © 2016年 谭智丹. All rights reserved.
7 | //
8 |
9 | #ifndef MathOperations_h
10 | #define MathOperations_h
11 |
12 | #include
13 | #include "camera.hpp"
14 |
15 | using namespace cv;
16 | using namespace std;
17 |
18 | /* rvec2mat : convert a rvec into a rotation matrix R (R_Sn) */
19 | void rvec2mat(Mat& rvec, Mat& R)
20 | {
21 | // rotation angle
22 | double rx = rvec.at(0,0);
23 | double ry = rvec.at(1,0);
24 | double rz = rvec.at(2,0);
25 | double theta = sqrt(rx*rx + ry*ry + rz*rz);
26 |
27 | // set rotation matrix to identity
28 | R = (Mat_(3,3)<<1, 0, 0, 0, 1, 0, 0, 0, 1);
29 |
30 | // calculate rotaion matrix
31 | if (theta > 0.0001) {
32 | rx /= theta;
33 | ry /= theta;
34 | rz /= theta;
35 |
36 | // compute some quantity to avoid repetition
37 | double ct = cos(theta);
38 | double st = sin(theta);
39 | double ict = 1 - ct;
40 | double rxy_ict = rx * ry * ict;
41 | double rxz_ict = rx * rz * ict;
42 | double ryz_ict = ry * rz * ict;
43 | double rx_st = rx * st;
44 | double ry_st = ry * st;
45 | double rz_st = rz * st;
46 |
47 | // calculate the elements of R
48 | double R00 = ct + rx * rx * ict;
49 | double R01 = rxy_ict + rz_st;
50 | double R02 = rxz_ict - ry_st;
51 | double R10 = rxy_ict - rz_st;
52 | double R11 = ct + ry * ry * ict;
53 | double R12 = ryz_ict + rx_st;
54 | double R20 = rxz_ict + ry_st;
55 | double R21 = ryz_ict - rx_st;
56 | double R22 = ct + rz * rz * ict;
57 |
58 | R = (Mat_(3,3)<< R00, R10, R20, R01, R11, R21, R02, R12, R22);
59 |
60 | }
61 |
62 | }
63 |
64 |
65 | /* Transform3D : Using R&T to transform a 3D point seen in one camera frame into another camera frame
66 | R : R_Cc0
67 | T : c0->c, described in the c0 frame
68 | */
69 | void Transform3D(vector& oldPts3d, vector& newPts3d, Mat& R, Mat& T)
70 | {
71 | // we need to convert R and T to float type
72 | Mat fR, fT;
73 | R.convertTo(fR, CV_32F);
74 | T.convertTo(fT, CV_32F);
75 |
76 | for (size_t i=0; i(0,0),x.at(1,0),x.at(2,0)));
82 | }
83 | }
84 |
85 |
86 | void projectObj2img(vector& pts3d, vector& pts2d, camera::camera cam, vector& isInImg)
87 | {
88 | float fx = cam.fx, fy = cam.fy, cx = cam.cx, cy = cam.cy;
89 | float minu = 3, maxu = cam.imwidth -3;
90 | float minv = 3, maxv = cam.imheight-3;
91 |
92 | for (size_t i=0; imaxu || vmaxv )
104 | isInImg.push_back(false);
105 | else
106 | isInImg.push_back(true);
107 |
108 | }
109 | }
110 |
111 | }
112 |
113 | #endif /* MathOperations_h */
114 |
--------------------------------------------------------------------------------
/src/camera.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // camera.cpp
3 | // dabino
4 | //
5 | // Created by 谭智丹 on 16/4/27.
6 | // Copyright © 2016年 谭智丹. All rights reserved.
7 | //
8 |
9 | #include "camera.hpp"
10 |
11 | /* we can use
12 | camera cam(.., .., ......);
13 | to set the camera intrinsics
14 | */
15 |
16 | camera::camera(int width, int height, float focalx, float focaly, float ppx, float ppy)
17 | {
18 | imwidth = width;
19 | imheight = height;
20 | fx = focalx;
21 | fy = focaly;
22 | cx = ppx;
23 | cy = ppy;
24 | camMatrix = (Mat_(3,3)<
13 |
14 | #include
15 |
16 | using namespace cv;
17 |
18 | class camera
19 | {
20 | public:
21 | camera(int width, int height, float focalx, float focaly, float ppx, float ppy);
22 | float imwidth; // image width
23 | float imheight; // image height
24 | float fx; // focal length x
25 | float fy; // focal length y
26 | float cx; // principal point's x coordinate
27 | float cy; // principal point's y coordinate
28 | Mat camMatrix; // camera projection matrix
29 | };
30 |
31 |
32 | #endif /* camera_hpp */
33 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // main.cpp
3 | // dabino
4 | //
5 | // Created by 谭智丹 on 16/3/8.
6 | // Copyright © 2016年 谭智丹. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | #include "Frame.hpp"
19 | #include "camera.hpp"
20 | #include "MathOperations.h"
21 |
22 | using namespace cv;
23 | using namespace std;
24 |
25 | //------------Dataset Parameters-----------------------//!!!!!!!!!!!!!CHANGE!!!!!!!!!!!!!!!!!!!
26 | /*----------Kitti Sequence 00, 01, 02--------*/
27 | const float focal = 718.856;
28 | const Point2f pp = Point2f(607.1928, 185.2157);
29 |
30 | /*----------Kitti Sequence 03--------*/
31 | //const float focal = 721.5377;
32 | //const Point2f pp = Point2f(609.5593, 172.854);
33 |
34 | /*----------Kitti Sequence 04, 05, 06--------*/
35 | //const float focal = 707.0912;
36 | //const Point2f pp = Point2f(601.8873, 183.1104);
37 |
38 | const float baseline = 0.53715;
39 |
40 | const int total_frames = 4541; // 00
41 | //const int total_frames = 1101; // 01
42 | //const int total_frames = 4661; // 02
43 | //const int total_frames = 801; // 03
44 | //const int total_frames = 271; // 04
45 | //const int total_frames = 2761; // 05
46 | //const int total_frames = 1101; // 06
47 |
48 | const Mat cam_mat = (Mat_(3,3)< old_pts; // corners' locations
83 | vector old_disp; // disparities
84 |
85 | // Termination criteria used when executing sub-pixel refinement
86 | TermCriteria crit = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01);
87 |
88 | // Pose state vector
89 | Mat oldRvec = (Mat_(3,1)<<0, 0, 0); // rotation
90 | Mat oldTvec = (Mat_(3,1)<<0, 0, 0); // translation
91 |
92 |
93 | // Here we go!
94 | for (int numFrame=start_frame; numFrame keyPts;
113 | FAST(img_l, keyPts, 20, true, 2); // threshold is 20, do non-maximun suppression, 16-9 method
114 | vector pts;
115 | KeyPoint::convert(keyPts, pts);
116 | cout<<"# detected: \t"<> subidx;
120 | frame.GetFeaturesIntoGrid(pts, subidx);
121 |
122 | // Sort the subidx
123 | frame.SortSubIdx(keyPts, subidx);
124 | int occupied=0;
125 | for (size_t i=0; i topPts;
133 | int want_in_each = MIN(1000 / occupied, 7);
134 |
135 | frame.SelectTopN(pts, topPts, subidx, want_in_each);
136 | cout << "Selected:\t" << topPts.size() << endl;
137 |
138 | // Sub-pixel refinement (very optional!)
139 | cornerSubPix(img_l, topPts, Size(5,5), Size(-1,-1), crit);
140 |
141 | // Stereo matching
142 | vector disp;
143 | frame.FindDisparity(topPts, disp, img_l, img_r, y_threshold);
144 |
145 |
146 | ///////
147 | if (numFrame > start_frame) {
148 |
149 | //---------Predict points' locations in current frame------//
150 | vector prevPts3d, prediPts3d;
151 | vector prediPts;
152 | vector canBeSeen;
153 |
154 | // 3D points described in last camera coordinates
155 | for (size_t i=0; i f_status;
180 | vector new_pts;
181 |
182 | // Optical flow tracker
183 | new_pts = prediPts;
184 | calcOpticalFlowPyrLK(old_img_l, img_l, old_pts, new_pts, f_status, err, Size(7,7));
185 |
186 | // Check: If the distance between the tracked and the predicted locations exceeds 15,
187 | // it may be a wrong association, we just discard it.
188 | size_t tracked = 0;
189 | if (numFrame > start_frame+1) {
190 | for (size_t i=0; i pts3d;
222 | for (size_t i=0; i inliers;
233 | solvePnPRansac(pts3d, new_pts, cam_mat, Mat(), rvec, tvec, false, 500, 2.0f, 0.999, inliers, SOLVEPNP_ITERATIVE);
234 | cout<<"# inliers: \t"< start_frame+2) {
258 | double tx = tvec.at(0,0);
259 | double ty = tvec.at(1,0);
260 | double tz = tvec.at(2,0);
261 | double oldtx = oldTvec.at(0,0);
262 | double oldty = oldTvec.at(1,0);
263 | double oldtz = oldTvec.at(2,0);
264 | double difft = sqrt(pow(tx-oldtx, 2) + pow(ty-oldty, 2) + pow(tz-oldtz, 2));
265 | if (fabs(tx)>0.5 || fabs(ty)>0.5 || fabs(tz)>3.5 || difft > 1) {
266 | rvec = oldRvec;
267 | tvec = oldTvec;
268 | }
269 | }
270 |
271 |
272 | // Save the result
273 | fout<(0,0)<<" \t";
275 | fout<(1,0)<<" \t";
276 | fout<(2,0)<<" \t";
277 | fout<(0,0)<<" \t";
278 | fout<(1,0)<<" \t";
279 | fout<(2,0)<<" \t";
280 | fout<