1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 // By downloading, copying, installing or using the software you agree to this license.
6 // If you do not agree to this license, do not download, install,
7 // copy or use the software.
8 //
9 //
10 // License Agreement
11 // For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2014, OpenCV Foundation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
21 //
22 // * Redistribution's in binary form must reproduce the above copyright notice,
23 // this list of conditions and the following disclaimer in the documentation
24 // and/or other materials provided with the distribution.
25 //
26 // * The name of the copyright holders may not be used to endorse or promote products
27 // derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 #ifndef __OPENCV_CCALIB_CPP__
43 #define __OPENCV_CCALIB_CPP__
44 #ifdef __cplusplus
45
46 #include "precomp.hpp"
47 #include "opencv2/ccalib.hpp"
48
49 #include <opencv2/core.hpp>
50 #include <opencv2/core/types_c.h> // CV_TERM
51 #include <opencv2/imgproc.hpp>
52 #include <opencv2/calib3d.hpp>
53 #include <opencv2/features2d.hpp>
54
55 #include <vector>
56 #include <cstring>
57
58 namespace cv{ namespace ccalib{
59
60 using namespace std;
61
62 const int MIN_CONTOUR_AREA_PX = 100;
63 const float MIN_CONTOUR_AREA_RATIO = 0.2f;
64 const float MAX_CONTOUR_AREA_RATIO = 5.0f;
65
66 const int MIN_POINTS_FOR_H = 10;
67
68 const float MAX_PROJ_ERROR_PX = 5.0f;
69
CustomPattern()70 CustomPattern::CustomPattern()
71 {
72 initialized = false;
73 }
74
create(InputArray pattern,const Size2f boardSize,OutputArray output)75 bool CustomPattern::create(InputArray pattern, const Size2f boardSize, OutputArray output)
76 {
77 CV_Assert(!pattern.empty() && (boardSize.area() > 0));
78
79 Mat img = pattern.getMat();
80 float pixel_size = (boardSize.width > boardSize.height)? // Choose the longer side for more accurate calculation
81 float(img.cols) / boardSize.width: // width is longer
82 float(img.rows) / boardSize.height; // height is longer
83 return init(img, pixel_size, output);
84 }
85
init(Mat & image,const float pixel_size,OutputArray output)86 bool CustomPattern::init(Mat& image, const float pixel_size, OutputArray output)
87 {
88 image.copyTo(img_roi);
89 //Setup object corners
90 obj_corners = std::vector<Point2f>(4);
91 obj_corners[0] = Point2f(0, 0); obj_corners[1] = Point2f(float(img_roi.cols), 0);
92 obj_corners[2] = Point2f(float(img_roi.cols), float(img_roi.rows)); obj_corners[3] = Point2f(0, float(img_roi.rows));
93
94 if (!detector) // if no detector chosen, use default
95 {
96 Ptr<ORB> orb = ORB::create();
97 orb->setMaxFeatures(2000);
98 orb->setScaleFactor(1.15);
99 orb->setNLevels(30);
100 detector = orb;
101 }
102
103 detector->detect(img_roi, keypoints);
104 if (keypoints.empty())
105 {
106 initialized = false;
107 return initialized;
108 }
109 refineKeypointsPos(img_roi, keypoints);
110
111 if (!descriptorExtractor) // if no extractor chosen, use default
112 descriptorExtractor = ORB::create();
113 descriptorExtractor->compute(img_roi, keypoints, descriptor);
114
115 if (!descriptorMatcher)
116 descriptorMatcher = DescriptorMatcher::create("BruteForce-Hamming(2)");
117
118 // Scale found points by pixelSize
119 pxSize = pixel_size;
120 scaleFoundPoints(pxSize, keypoints, points3d);
121
122 if (output.needed())
123 {
124 Mat out;
125 drawKeypoints(img_roi, keypoints, out, Scalar(0, 0, 255));
126 out.copyTo(output);
127 }
128
129 initialized = !keypoints.empty();
130 return initialized; // initialized if any keypoints are found
131 }
132
~CustomPattern()133 CustomPattern::~CustomPattern() {}
134
isInitialized()135 bool CustomPattern::isInitialized()
136 {
137 return initialized;
138 }
139
setFeatureDetector(Ptr<FeatureDetector> featureDetector)140 bool CustomPattern::setFeatureDetector(Ptr<FeatureDetector> featureDetector)
141 {
142 if (!initialized)
143 {
144 this->detector = featureDetector;
145 return true;
146 }
147 else
148 return false;
149 }
150
setDescriptorExtractor(Ptr<DescriptorExtractor> extractor)151 bool CustomPattern::setDescriptorExtractor(Ptr<DescriptorExtractor> extractor)
152 {
153 if (!initialized)
154 {
155 this->descriptorExtractor = extractor;
156 return true;
157 }
158 else
159 return false;
160 }
161
setDescriptorMatcher(Ptr<DescriptorMatcher> matcher)162 bool CustomPattern::setDescriptorMatcher(Ptr<DescriptorMatcher> matcher)
163 {
164 if (!initialized)
165 {
166 this->descriptorMatcher = matcher;
167 return true;
168 }
169 else
170 return false;
171 }
172
getFeatureDetector()173 Ptr<FeatureDetector> CustomPattern::getFeatureDetector()
174 {
175 return detector;
176 }
177
getDescriptorExtractor()178 Ptr<DescriptorExtractor> CustomPattern::getDescriptorExtractor()
179 {
180 return descriptorExtractor;
181 }
182
getDescriptorMatcher()183 Ptr<DescriptorMatcher> CustomPattern::getDescriptorMatcher()
184 {
185 return descriptorMatcher;
186 }
187
scaleFoundPoints(const double pixelSize,const vector<KeyPoint> & corners,vector<Point3f> & pts3d)188 void CustomPattern::scaleFoundPoints(const double pixelSize,
189 const vector<KeyPoint>& corners, vector<Point3f>& pts3d)
190 {
191 for (unsigned int i = 0; i < corners.size(); ++i)
192 {
193 pts3d.push_back(Point3f(
194 float(corners[i].pt.x * pixelSize),
195 float(corners[i].pt.y * pixelSize),
196 0));
197 }
198 }
199
200 //Takes a descriptor and turns it into an (x,y) point
keypoints2points(const vector<KeyPoint> & in,vector<Point2f> & out)201 void CustomPattern::keypoints2points(const vector<KeyPoint>& in, vector<Point2f>& out)
202 {
203 out.clear();
204 out.reserve(in.size());
205 for (size_t i = 0; i < in.size(); ++i)
206 {
207 out.push_back(in[i].pt);
208 }
209 }
210
updateKeypointsPos(vector<KeyPoint> & in,const vector<Point2f> & new_pos)211 void CustomPattern::updateKeypointsPos(vector<KeyPoint>& in, const vector<Point2f>& new_pos)
212 {
213 for (size_t i = 0; i < in.size(); ++i)
214 {
215 in[i].pt= new_pos[i];
216 }
217 }
218
refinePointsPos(const Mat & img,vector<Point2f> & p)219 void CustomPattern::refinePointsPos(const Mat& img, vector<Point2f>& p)
220 {
221 Mat gray;
222 cvtColor(img, gray, COLOR_RGB2GRAY);
223 cornerSubPix(gray, p, Size(10, 10), Size(-1, -1),
224 TermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 30, 0.1));
225
226 }
227
refineKeypointsPos(const Mat & img,vector<KeyPoint> & kp)228 void CustomPattern::refineKeypointsPos(const Mat& img, vector<KeyPoint>& kp)
229 {
230 vector<Point2f> points;
231 keypoints2points(kp, points);
232 refinePointsPos(img, points);
233 updateKeypointsPos(kp, points);
234 }
235
236 template<typename Tstve>
deleteStdVecElem(vector<Tstve> & v,int idx)237 void deleteStdVecElem(vector<Tstve>& v, int idx)
238 {
239 v[idx] = v.back();
240 v.pop_back();
241 }
242
check_matches(vector<Point2f> & matched,const vector<Point2f> & pattern,vector<DMatch> & good,vector<Point3f> & pattern_3d,const Mat & H)243 void CustomPattern::check_matches(vector<Point2f>& matched, const vector<Point2f>& pattern, vector<DMatch>& good,
244 vector<Point3f>& pattern_3d, const Mat& H)
245 {
246 vector<Point2f> proj;
247 perspectiveTransform(pattern, proj, H);
248
249 int deleted = 0;
250 double error_sum = 0;
251 double error_sum_filtered = 0;
252 for (uint i = 0; i < proj.size(); ++i)
253 {
254 double error = norm(matched[i] - proj[i]);
255 error_sum += error;
256 if (error >= MAX_PROJ_ERROR_PX)
257 {
258 deleteStdVecElem(good, i);
259 deleteStdVecElem(matched, i);
260 deleteStdVecElem(pattern_3d, i);
261 ++deleted;
262 }
263 else
264 {
265 error_sum_filtered += error;
266 }
267 }
268 }
269
findPatternPass(const Mat & image,vector<Point2f> & matched_features,vector<Point3f> & pattern_points,Mat & H,vector<Point2f> & scene_corners,const double pratio,const double proj_error,const bool refine_position,const Mat & mask,OutputArray output)270 bool CustomPattern::findPatternPass(const Mat& image, vector<Point2f>& matched_features, vector<Point3f>& pattern_points,
271 Mat& H, vector<Point2f>& scene_corners, const double pratio, const double proj_error,
272 const bool refine_position, const Mat& mask, OutputArray output)
273 {
274 if (!initialized) {return false; }
275 matched_features.clear();
276 pattern_points.clear();
277
278 vector<vector<DMatch> > matches;
279 vector<KeyPoint> f_keypoints;
280 Mat f_descriptor;
281
282 detector->detect(image, f_keypoints, mask);
283 if (refine_position) refineKeypointsPos(image, f_keypoints);
284
285 descriptorExtractor->compute(image, f_keypoints, f_descriptor);
286 descriptorMatcher->knnMatch(f_descriptor, descriptor, matches, 2); // k = 2;
287 vector<DMatch> good_matches;
288 vector<Point2f> obj_points;
289
290 for(int i = 0; i < f_descriptor.rows; ++i)
291 {
292 if(matches[i][0].distance < pratio * matches[i][1].distance)
293 {
294 const DMatch& dm = matches[i][0];
295 good_matches.push_back(dm);
296 // "keypoints1[matches[i].queryIdx] has a corresponding point in keypoints2[matches[i].trainIdx]"
297 matched_features.push_back(f_keypoints[dm.queryIdx].pt);
298 pattern_points.push_back(points3d[dm.trainIdx]);
299 obj_points.push_back(keypoints[dm.trainIdx].pt);
300 }
301 }
302
303 if (good_matches.size() < MIN_POINTS_FOR_H) return false;
304
305 Mat h_mask;
306 H = findHomography(obj_points, matched_features, RANSAC, proj_error, h_mask);
307 if (H.empty())
308 {
309 // cout << "findHomography() returned empty Mat." << endl;
310 return false;
311 }
312
313 for(unsigned int i = 0; i < good_matches.size(); ++i)
314 {
315 if(!h_mask.data[i])
316 {
317 deleteStdVecElem(good_matches, i);
318 deleteStdVecElem(matched_features, i);
319 deleteStdVecElem(pattern_points, i);
320 }
321 }
322
323 if (good_matches.empty()) return false;
324
325 size_t numb_elem = good_matches.size();
326 check_matches(matched_features, obj_points, good_matches, pattern_points, H);
327 if (good_matches.empty() || numb_elem < good_matches.size()) return false;
328
329 // Get the corners from the image
330 scene_corners = vector<Point2f>(4);
331 perspectiveTransform(obj_corners, scene_corners, H);
332
333 // Check correctnes of H
334 // Is it a convex hull?
335 bool cConvex = isContourConvex(scene_corners);
336 if (!cConvex) return false;
337
338 // Is the hull too large or small?
339 double scene_area = contourArea(scene_corners);
340 if (scene_area < MIN_CONTOUR_AREA_PX) return false;
341 double ratio = scene_area/img_roi.size().area();
342 if ((ratio < MIN_CONTOUR_AREA_RATIO) ||
343 (ratio > MAX_CONTOUR_AREA_RATIO)) return false;
344
345 // Is any of the projected points outside the hull?
346 for(unsigned int i = 0; i < good_matches.size(); ++i)
347 {
348 if(pointPolygonTest(scene_corners, f_keypoints[good_matches[i].queryIdx].pt, false) < 0)
349 {
350 deleteStdVecElem(good_matches, i);
351 deleteStdVecElem(matched_features, i);
352 deleteStdVecElem(pattern_points, i);
353 }
354 }
355
356 if (output.needed())
357 {
358 Mat out;
359 drawMatches(image, f_keypoints, img_roi, keypoints, good_matches, out);
360 // Draw lines between the corners (the mapped object in the scene - image_2 )
361 line(out, scene_corners[0], scene_corners[1], Scalar(0, 255, 0), 2);
362 line(out, scene_corners[1], scene_corners[2], Scalar(0, 255, 0), 2);
363 line(out, scene_corners[2], scene_corners[3], Scalar(0, 255, 0), 2);
364 line(out, scene_corners[3], scene_corners[0], Scalar(0, 255, 0), 2);
365 out.copyTo(output);
366 }
367
368 return (!good_matches.empty()); // return true if there are enough good matches
369 }
370
findPattern(InputArray image,OutputArray matched_features,OutputArray pattern_points,const double ratio,const double proj_error,const bool refine_position,OutputArray out,OutputArray H,OutputArray pattern_corners)371 bool CustomPattern::findPattern(InputArray image, OutputArray matched_features, OutputArray pattern_points,
372 const double ratio, const double proj_error, const bool refine_position, OutputArray out,
373 OutputArray H, OutputArray pattern_corners)
374 {
375 CV_Assert(!image.empty() && proj_error > 0);
376
377 Mat img = image.getMat();
378 vector<Point2f> m_ftrs;
379 vector<Point3f> pattern_pts;
380 Mat _H;
381 vector<Point2f> scene_corners;
382 if (!findPatternPass(img, m_ftrs, pattern_pts, _H, scene_corners, 0.6, proj_error, refine_position))
383 return false; // pattern not found
384
385 Mat mask = Mat::zeros(img.size(), CV_8UC1);
386 vector<vector<Point> > obj(1);
387 vector<Point> scorners_int(scene_corners.size());
388 for (uint i = 0; i < scene_corners.size(); ++i)
389 scorners_int[i] = (Point)scene_corners[i]; // for drawContours
390 obj[0] = scorners_int;
391 drawContours(mask, obj, 0, Scalar(255), FILLED);
392
393 // Second pass
394 Mat output;
395 if (!findPatternPass(img, m_ftrs, pattern_pts, _H, scene_corners,
396 ratio, proj_error, refine_position, mask, output))
397 return false; // pattern not found
398
399 Mat(m_ftrs).copyTo(matched_features);
400 Mat(pattern_pts).copyTo(pattern_points);
401 if (out.needed()) output.copyTo(out);
402 if (H.needed()) _H.copyTo(H);
403 if (pattern_corners.needed()) Mat(scene_corners).copyTo(pattern_corners);
404
405 return (!m_ftrs.empty());
406 }
407
getPatternPoints(std::vector<KeyPoint> & original_points)408 void CustomPattern::getPatternPoints(std::vector<KeyPoint>& original_points)
409 {
410 original_points = keypoints;
411 }
412
getPixelSize()413 double CustomPattern::getPixelSize()
414 {
415 return pxSize;
416 }
417
calibrate(InputArrayOfArrays objectPoints,InputArrayOfArrays imagePoints,Size imageSize,InputOutputArray cameraMatrix,InputOutputArray distCoeffs,OutputArrayOfArrays rvecs,OutputArrayOfArrays tvecs,int flags,TermCriteria criteria)418 double CustomPattern::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints,
419 Size imageSize, InputOutputArray cameraMatrix, InputOutputArray distCoeffs,
420 OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, int flags,
421 TermCriteria criteria)
422 {
423 return calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs,
424 rvecs, tvecs, flags, criteria);
425 }
426
findRt(InputArray objectPoints,InputArray imagePoints,InputArray cameraMatrix,InputArray distCoeffs,InputOutputArray rvec,InputOutputArray tvec,bool useExtrinsicGuess,int flags)427 bool CustomPattern::findRt(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix,
428 InputArray distCoeffs, InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess, int flags)
429 {
430 return solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess, flags);
431 }
432
findRt(InputArray image,InputArray cameraMatrix,InputArray distCoeffs,InputOutputArray rvec,InputOutputArray tvec,bool useExtrinsicGuess,int flags)433 bool CustomPattern::findRt(InputArray image, InputArray cameraMatrix, InputArray distCoeffs,
434 InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess, int flags)
435 {
436 vector<Point2f> imagePoints;
437 vector<Point3f> objectPoints;
438
439 if (!findPattern(image, imagePoints, objectPoints))
440 return false;
441 return solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess, flags);
442 }
443
findRtRANSAC(InputArray objectPoints,InputArray imagePoints,InputArray cameraMatrix,InputArray distCoeffs,InputOutputArray rvec,InputOutputArray tvec,bool useExtrinsicGuess,int iterationsCount,float reprojectionError,int minInliersCount,OutputArray inliers,int flags)444 bool CustomPattern::findRtRANSAC(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs,
445 InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess, int iterationsCount,
446 float reprojectionError, int minInliersCount, OutputArray inliers, int flags)
447 {
448 int npoints = imagePoints.getMat().checkVector(2);
449 CV_Assert(npoints > 0);
450 double confidence_factor = (double)minInliersCount / (double)npoints;
451 double confidence = confidence_factor < 0.001 ? 0.001 : confidence_factor > 0.999 ? 0.999 : confidence_factor;
452
453 solvePnPRansac(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess,
454 iterationsCount, reprojectionError, confidence, inliers, flags);
455 return true; // for consistency with the other methods
456 }
457
findRtRANSAC(InputArray image,InputArray cameraMatrix,InputArray distCoeffs,InputOutputArray rvec,InputOutputArray tvec,bool useExtrinsicGuess,int iterationsCount,float reprojectionError,int minInliersCount,OutputArray inliers,int flags)458 bool CustomPattern::findRtRANSAC(InputArray image, InputArray cameraMatrix, InputArray distCoeffs,
459 InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess, int iterationsCount,
460 float reprojectionError, int minInliersCount, OutputArray inliers, int flags)
461 {
462 vector<Point2f> imagePoints;
463 vector<Point3f> objectPoints;
464
465 if (!findPattern(image, imagePoints, objectPoints))
466 return false;
467
468 double confidence_factor = (double)minInliersCount / (double)imagePoints.size();
469 double confidence = confidence_factor < 0.001 ? 0.001 : confidence_factor > 0.999 ? 0.999 : confidence_factor;
470
471 solvePnPRansac(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess,
472 iterationsCount, reprojectionError, confidence, inliers, flags);
473 return true;
474 }
475
drawOrientation(InputOutputArray image,InputArray tvec,InputArray rvec,InputArray cameraMatrix,InputArray distCoeffs,double axis_length,int axis_width)476 void CustomPattern::drawOrientation(InputOutputArray image, InputArray tvec, InputArray rvec,
477 InputArray cameraMatrix, InputArray distCoeffs,
478 double axis_length, int axis_width)
479 {
480 Point3f ptrCtr3d = Point3f(float((img_roi.cols * pxSize)/2.0), float((img_roi.rows * pxSize)/2.0), 0);
481
482 vector<Point3f> axis(4);
483 float alen = float(axis_length * pxSize);
484 axis[0] = ptrCtr3d;
485 axis[1] = Point3f(alen, 0, 0) + ptrCtr3d;
486 axis[2] = Point3f(0, alen, 0) + ptrCtr3d;
487 axis[3] = Point3f(0, 0, -alen) + ptrCtr3d;
488
489 vector<Point2f> proj_axis;
490 projectPoints(axis, rvec, tvec, cameraMatrix, distCoeffs, proj_axis);
491
492 Mat img = image.getMat();
493 line(img, proj_axis[0], proj_axis[1], Scalar(0, 0, 255), axis_width); // red
494 line(img, proj_axis[0], proj_axis[2], Scalar(0, 255, 0), axis_width); // green
495 line(img, proj_axis[0], proj_axis[3], Scalar(255, 0, 0), axis_width); // blue
496
497 img.copyTo(image);
498 }
499
500 }} // namespace ccalib, cv
501
502 #endif // __OPENCV_CCALIB_CPP__
503 #endif // cplusplus
504
505