Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3 | PointedEar | 1 | /*------------------------------------------------------------------------------------------*\ |
2 | This file contains material supporting chapter 9 of the cookbook: |
||
3 | Computer Vision Programming using the OpenCV Library. |
||
4 | by Robert Laganiere, Packt Publishing, 2011. |
||
5 | |||
6 | This program is free software; permission is hereby granted to use, copy, modify, |
||
7 | and distribute this source code, or portions thereof, for any purpose, without fee, |
||
8 | subject to the restriction that the copyright notice may not be removed |
||
9 | or altered from any source or altered source distribution. |
||
10 | The software is released on an as-is basis and without any warranties of any kind. |
||
11 | In particular, the software is not guaranteed to be fault-tolerant or free from failure. |
||
12 | The author disclaims all warranties with regard to this software, any use, |
||
13 | and any consequent failure, is purely the responsibility of the user. |
||
14 | |||
15 | Copyright (C) 2010-2011 Robert Laganiere, www.laganiere.name |
||
16 | \*------------------------------------------------------------------------------------------*/ |
||
17 | |||
18 | #include <iostream> |
||
19 | #include <vector> |
||
20 | #include <opencv2/core/core.hpp> |
||
21 | #include <opencv2/imgproc/imgproc.hpp> |
||
22 | #include <opencv2/highgui/highgui.hpp> |
||
23 | #include <opencv2/features2d/features2d.hpp> |
||
24 | #include <opencv2/calib3d/calib3d.hpp> |
||
25 | |||
26 | int main() |
||
27 | { |
||
28 | // Read input images |
||
29 | cv::Mat image1= cv::imread("../church01.jpg",0); |
||
30 | cv::Mat image2= cv::imread("../church03.jpg",0); |
||
31 | if (!image1.data || !image2.data) |
||
32 | return 0; |
||
33 | |||
34 | // Display the images |
||
35 | cv::namedWindow("Right Image"); |
||
36 | cv::imshow("Right Image",image1); |
||
37 | cv::namedWindow("Left Image"); |
||
38 | cv::imshow("Left Image",image2); |
||
39 | |||
40 | // vector of keypoints |
||
41 | std::vector<cv::KeyPoint> keypoints1; |
||
42 | std::vector<cv::KeyPoint> keypoints2; |
||
43 | |||
44 | // Construction of the SURF feature detector |
||
45 | cv::SurfFeatureDetector surf(3000); |
||
46 | |||
47 | // Detection of the SURF features |
||
48 | surf.detect(image1,keypoints1); |
||
49 | surf.detect(image2,keypoints2); |
||
50 | |||
51 | std::cout << "Number of SURF points (1): " << keypoints1.size() << std::endl; |
||
52 | std::cout << "Number of SURF points (2): " << keypoints2.size() << std::endl; |
||
53 | |||
54 | // Draw the kepoints |
||
55 | cv::Mat imageKP; |
||
56 | cv::drawKeypoints(image1,keypoints1,imageKP,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); |
||
57 | cv::namedWindow("Right SURF Features"); |
||
58 | cv::imshow("Right SURF Features",imageKP); |
||
59 | cv::drawKeypoints(image2,keypoints2,imageKP,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); |
||
60 | cv::namedWindow("Left SURF Features"); |
||
61 | cv::imshow("Left SURF Features",imageKP); |
||
62 | |||
63 | // Construction of the SURF descriptor extractor |
||
64 | cv::SurfDescriptorExtractor surfDesc; |
||
65 | |||
66 | // Extraction of the SURF descriptors |
||
67 | cv::Mat descriptors1, descriptors2; |
||
68 | surfDesc.compute(image1,keypoints1,descriptors1); |
||
69 | surfDesc.compute(image2,keypoints2,descriptors2); |
||
70 | |||
71 | std::cout << "descriptor matrix size: " << descriptors1.rows << " by " << descriptors1.cols << std::endl; |
||
72 | |||
73 | // Construction of the matcher |
||
74 | cv::BruteForceMatcher<cv::L2<float>> matcher; |
||
75 | |||
76 | // Match the two image descriptors |
||
77 | std::vector<cv::DMatch> matches; |
||
78 | matcher.match(descriptors1,descriptors2, matches); |
||
79 | |||
80 | std::cout << "Number of matched points: " << matches.size() << std::endl; |
||
81 | |||
82 | // Select few Matches |
||
83 | std::vector<cv::DMatch> selMatches; |
||
84 | /* |
||
85 | keypoints1.push_back(cv::KeyPoint(342.,615.,2)); |
||
86 | keypoints2.push_back(cv::KeyPoint(410.,600.,2)); |
||
87 | selMatches.push_back(cv::DMatch(keypoints1.size()-1,keypoints2.size()-1,0)); // street light bulb |
||
88 | selMatches.push_back(matches[6]); // right tower |
||
89 | selMatches.push_back(matches[60]); // left bottom window |
||
90 | selMatches.push_back(matches[139]); |
||
91 | selMatches.push_back(matches[141]); // middle window |
||
92 | selMatches.push_back(matches[213]); |
||
93 | selMatches.push_back(matches[273]); |
||
94 | |||
95 | int kk=0; |
||
96 | while (kk<matches.size()) { |
||
97 | std::cout<<kk<<std::endl; |
||
98 | selMatches.push_back(matches[kk++]); |
||
99 | selMatches.pop_back(); |
||
100 | cv::waitKey(); |
||
101 | } |
||
102 | */ |
||
103 | |||
104 | /* between church01 and church03 */ |
||
105 | selMatches.push_back(matches[14]); |
||
106 | selMatches.push_back(matches[16]); |
||
107 | selMatches.push_back(matches[141]); |
||
108 | selMatches.push_back(matches[146]); |
||
109 | selMatches.push_back(matches[235]); |
||
110 | selMatches.push_back(matches[238]); |
||
111 | selMatches.push_back(matches[274]); |
||
112 | |||
113 | // Draw the selected matches |
||
114 | cv::Mat imageMatches; |
||
115 | cv::drawMatches(image1,keypoints1, // 1st image and its keypoints |
||
116 | image2,keypoints2, // 2nd image and its keypoints |
||
117 | // selMatches, // the matches |
||
118 | matches, // the matches |
||
119 | imageMatches, // the image produced |
||
120 | cv::Scalar(255,255,255)); // color of the lines |
||
121 | cv::namedWindow("Matches"); |
||
122 | cv::imshow("Matches",imageMatches); |
||
123 | |||
124 | // Convert 1 vector of keypoints into |
||
125 | // 2 vectors of Point2f |
||
126 | std::vector<int> pointIndexes1; |
||
127 | std::vector<int> pointIndexes2; |
||
128 | for (std::vector<cv::DMatch>::const_iterator it= selMatches.begin(); |
||
129 | it!= selMatches.end(); ++it) { |
||
130 | |||
131 | // Get the indexes of the selected matched keypoints |
||
132 | pointIndexes1.push_back(it->queryIdx); |
||
133 | pointIndexes2.push_back(it->trainIdx); |
||
134 | } |
||
135 | |||
136 | // Convert keypoints into Point2f |
||
137 | std::vector<cv::Point2f> selPoints1, selPoints2; |
||
138 | cv::KeyPoint::convert(keypoints1,selPoints1,pointIndexes1); |
||
139 | cv::KeyPoint::convert(keypoints2,selPoints2,pointIndexes2); |
||
140 | |||
141 | // check by drawing the points |
||
142 | std::vector<cv::Point2f>::const_iterator it= selPoints1.begin(); |
||
143 | while (it!=selPoints1.end()) { |
||
144 | |||
145 | // draw a circle at each corner location |
||
146 | cv::circle(image1,*it,3,cv::Scalar(255,255,255),2); |
||
147 | ++it; |
||
148 | } |
||
149 | |||
150 | it= selPoints2.begin(); |
||
151 | while (it!=selPoints2.end()) { |
||
152 | |||
153 | // draw a circle at each corner location |
||
154 | cv::circle(image2,*it,3,cv::Scalar(255,255,255),2); |
||
155 | ++it; |
||
156 | } |
||
157 | |||
158 | // Compute F matrix from 7 matches |
||
159 | cv::Mat fundemental= cv::findFundamentalMat( |
||
160 | cv::Mat(selPoints1), // points in first image |
||
161 | cv::Mat(selPoints2), // points in second image |
||
162 | CV_FM_7POINT); // 7-point method |
||
163 | |||
164 | std::cout << "F-Matrix size= " << fundemental.rows << "," << fundemental.cols << std::endl; |
||
165 | |||
166 | // draw the left points corresponding epipolar lines in right image |
||
167 | std::vector<cv::Vec3f> lines1; |
||
168 | cv::computeCorrespondEpilines( |
||
169 | cv::Mat(selPoints1), // image points |
||
170 | 1, // in image 1 (can also be 2) |
||
171 | fundemental, // F matrix |
||
172 | lines1); // vector of epipolar lines |
||
173 | |||
174 | // for all epipolar lines |
||
175 | for (vector<cv::Vec3f>::const_iterator it= lines1.begin(); |
||
176 | it!=lines1.end(); ++it) { |
||
177 | |||
178 | // draw the epipolar line between first and last column |
||
179 | cv::line(image2,cv::Point(0,-(*it)[2]/(*it)[1]), |
||
180 | cv::Point(image2.cols,-((*it)[2]+(*it)[0]*image2.cols)/(*it)[1]), |
||
181 | cv::Scalar(255,255,255)); |
||
182 | } |
||
183 | |||
184 | // draw the left points corresponding epipolar lines in left image |
||
185 | std::vector<cv::Vec3f> lines2; |
||
186 | cv::computeCorrespondEpilines(cv::Mat(selPoints2),2,fundemental,lines2); |
||
187 | for (vector<cv::Vec3f>::const_iterator it= lines2.begin(); |
||
188 | it!=lines2.end(); ++it) { |
||
189 | |||
190 | // draw the epipolar line between first and last column |
||
191 | cv::line(image1,cv::Point(0,-(*it)[2]/(*it)[1]), |
||
192 | cv::Point(image1.cols,-((*it)[2]+(*it)[0]*image1.cols)/(*it)[1]), |
||
193 | cv::Scalar(255,255,255)); |
||
194 | } |
||
195 | |||
196 | // Display the images with points and epipolar lines |
||
197 | cv::namedWindow("Right Image Epilines"); |
||
198 | cv::imshow("Right Image Epilines",image1); |
||
199 | cv::namedWindow("Left Image Epilines"); |
||
200 | cv::imshow("Left Image Epilines",image2); |
||
201 | |||
202 | /* |
||
203 | std::nth_element(matches.begin(), // initial position |
||
204 | matches.begin()+matches.size()/2, // 50% |
||
205 | matches.end()); // end position |
||
206 | // remove all elements after the |
||
207 | matches.erase(matches.begin()+matches.size()/2, matches.end()); |
||
208 | */ |
||
209 | // Convert keypoints into Point2f |
||
210 | std::vector<cv::Point2f> points1, points2; |
||
211 | for (std::vector<cv::DMatch>::const_iterator it= matches.begin(); |
||
212 | it!= matches.end(); ++it) { |
||
213 | |||
214 | // Get the position of left keypoints |
||
215 | float x= keypoints1[it->queryIdx].pt.x; |
||
216 | float y= keypoints1[it->queryIdx].pt.y; |
||
217 | points1.push_back(cv::Point2f(x,y)); |
||
218 | // Get the position of right keypoints |
||
219 | x= keypoints2[it->trainIdx].pt.x; |
||
220 | y= keypoints2[it->trainIdx].pt.y; |
||
221 | points2.push_back(cv::Point2f(x,y)); |
||
222 | } |
||
223 | |||
224 | std::cout << points1.size() << " " << points2.size() << std::endl; |
||
225 | |||
226 | // Compute F matrix using RANSAC |
||
227 | std::vector<uchar> inliers(points1.size(),0); |
||
228 | fundemental= cv::findFundamentalMat( |
||
229 | cv::Mat(points1),cv::Mat(points2), // matching points |
||
230 | inliers, // match status (inlier ou outlier) |
||
231 | CV_FM_RANSAC, // RANSAC method |
||
232 | 1, // distance to epipolar line |
||
233 | 0.98); // confidence probability |
||
234 | |||
235 | // Read input images |
||
236 | image1= cv::imread("../church01.jpg",0); |
||
237 | image2= cv::imread("../church03.jpg",0); |
||
238 | |||
239 | // Draw the epipolar line of few points |
||
240 | cv::computeCorrespondEpilines(cv::Mat(selPoints1),1,fundemental,lines1); |
||
241 | for (vector<cv::Vec3f>::const_iterator it= lines1.begin(); |
||
242 | it!=lines1.end(); ++it) { |
||
243 | |||
244 | cv::line(image2,cv::Point(0,-(*it)[2]/(*it)[1]), |
||
245 | cv::Point(image2.cols,-((*it)[2]+(*it)[0]*image2.cols)/(*it)[1]), |
||
246 | cv::Scalar(255,255,255)); |
||
247 | } |
||
248 | |||
249 | cv::computeCorrespondEpilines(cv::Mat(selPoints2),2,fundemental,lines2); |
||
250 | for (vector<cv::Vec3f>::const_iterator it= lines2.begin(); |
||
251 | it!=lines2.end(); ++it) { |
||
252 | |||
253 | cv::line(image1,cv::Point(0,-(*it)[2]/(*it)[1]), |
||
254 | cv::Point(image1.cols,-((*it)[2]+(*it)[0]*image1.cols)/(*it)[1]), |
||
255 | cv::Scalar(255,255,255)); |
||
256 | } |
||
257 | |||
258 | // Draw the inlier points |
||
259 | std::vector<cv::Point2f> points1In, points2In; |
||
260 | std::vector<cv::Point2f>::const_iterator itPts= points1.begin(); |
||
261 | std::vector<uchar>::const_iterator itIn= inliers.begin(); |
||
262 | while (itPts!=points1.end()) { |
||
263 | |||
264 | // draw a circle at each inlier location |
||
265 | if (*itIn) { |
||
266 | cv::circle(image1,*itPts,3,cv::Scalar(255,255,255),2); |
||
267 | points1In.push_back(*itPts); |
||
268 | } |
||
269 | ++itPts; |
||
270 | ++itIn; |
||
271 | } |
||
272 | |||
273 | itPts= points2.begin(); |
||
274 | itIn= inliers.begin(); |
||
275 | while (itPts!=points2.end()) { |
||
276 | |||
277 | // draw a circle at each inlier location |
||
278 | if (*itIn) { |
||
279 | cv::circle(image2,*itPts,3,cv::Scalar(255,255,255),2); |
||
280 | points2In.push_back(*itPts); |
||
281 | } |
||
282 | ++itPts; |
||
283 | ++itIn; |
||
284 | } |
||
285 | |||
286 | // Display the images with points |
||
287 | cv::namedWindow("Right Image Epilines (RANSAC)"); |
||
288 | cv::imshow("Right Image Epilines (RANSAC)",image1); |
||
289 | cv::namedWindow("Left Image Epilines (RANSAC)"); |
||
290 | cv::imshow("Left Image Epilines (RANSAC)",image2); |
||
291 | |||
292 | cv::findHomography(cv::Mat(points1In),cv::Mat(points2In),inliers,CV_RANSAC,1.); |
||
293 | |||
294 | // Read input images |
||
295 | image1= cv::imread("../church01.jpg",0); |
||
296 | image2= cv::imread("../church03.jpg",0); |
||
297 | |||
298 | // Draw the inlier points |
||
299 | itPts= points1In.begin(); |
||
300 | itIn= inliers.begin(); |
||
301 | while (itPts!=points1In.end()) { |
||
302 | |||
303 | // draw a circle at each inlier location |
||
304 | if (*itIn) |
||
305 | cv::circle(image1,*itPts,3,cv::Scalar(255,255,255),2); |
||
306 | |||
307 | ++itPts; |
||
308 | ++itIn; |
||
309 | } |
||
310 | |||
311 | itPts= points2In.begin(); |
||
312 | itIn= inliers.begin(); |
||
313 | while (itPts!=points2In.end()) { |
||
314 | |||
315 | // draw a circle at each inlier location |
||
316 | if (*itIn) |
||
317 | cv::circle(image2,*itPts,3,cv::Scalar(255,255,255),2); |
||
318 | |||
319 | ++itPts; |
||
320 | ++itIn; |
||
321 | } |
||
322 | |||
323 | // Display the images with points |
||
324 | cv::namedWindow("Right Image Homography (RANSAC)"); |
||
325 | cv::imshow("Right Image Homography (RANSAC)",image1); |
||
326 | cv::namedWindow("Left Image Homography (RANSAC)"); |
||
327 | cv::imshow("Left Image Homography (RANSAC)",image2); |
||
328 | |||
329 | cv::waitKey(); |
||
330 | return 0; |
||
331 | } |