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