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) 2015, Baisheng Lai (laibaisheng@gmail.com), Zhejiang University,
14 // all rights reserved.
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 /**
43 * This module was accepted as a GSoC 2015 project for OpenCV, authored by
44 * Baisheng Lai, mentored by Bo Li.
45
46 * This module implements points extraction from a "random" pattern image.
47 * It returns 3D object points and 2D image pixels that can be used for calibration.
48 * Compared with traditional calibration object like chessboard, it is useful when pattern is
49 * partly occluded or only a part of pattern can be observed in multiple cameras calibration.
50 * For detail, refer to this paper
51 * B. Li, L. Heng, K. Kevin and M. Pollefeys, "A Multiple-Camera System
52 * Calibration Toolbox Using A Feature Descriptor-Based Calibration
53 * Pattern", in IROS 2013.
54 */
55 #include "precomp.hpp"
56 #include "opencv2/ccalib/randpattern.hpp"
57 #include <iostream>
58 using namespace std;
59
60 namespace cv { namespace randpattern {
RandomPatternCornerFinder(float patternWidth,float patternHeight,int nminiMatch,int depth,int verbose,int showExtraction,Ptr<FeatureDetector> detector,Ptr<DescriptorExtractor> descriptor,Ptr<DescriptorMatcher> matcher)61 RandomPatternCornerFinder::RandomPatternCornerFinder(float patternWidth, float patternHeight,
62 int nminiMatch, int depth, int verbose, int showExtraction, Ptr<FeatureDetector> detector, Ptr<DescriptorExtractor> descriptor,
63 Ptr<DescriptorMatcher> matcher)
64 {
65 _patternHeight = patternHeight;
66 _patternWidth = patternWidth;
67 _nminiMatch = nminiMatch;
68 _objectPonits.resize(0);
69 _imagePoints.resize(0);
70 _depth = depth;
71 _detector = detector;
72 _descriptor = descriptor;
73 _matcher = matcher;
74 _showExtraction = showExtraction;
75 _verbose = verbose;
76 }
77
computeObjectImagePoints(std::vector<cv::Mat> inputImages)78 void RandomPatternCornerFinder::computeObjectImagePoints(std::vector<cv::Mat> inputImages)
79 {
80 CV_Assert(!_patternImage.empty());
81 CV_Assert(inputImages.size() > 0);
82
83 int nImages = (int)inputImages.size();
84 std::vector<Mat> imageObjectPoints;
85 for (int i = 0; i < nImages; ++i)
86 {
87 imageObjectPoints = computeObjectImagePointsForSingle(inputImages[i]);
88 if ((int)imageObjectPoints[0].total() > _nminiMatch)
89 {
90 _imagePoints.push_back(imageObjectPoints[0]);
91 _objectPonits.push_back(imageObjectPoints[1]);
92 }
93 }
94 }
95
keyPoints2MatchedLocation(const std::vector<cv::KeyPoint> & imageKeypoints,const std::vector<cv::KeyPoint> & patternKeypoints,const std::vector<cv::DMatch> matchces,cv::Mat & matchedImagelocation,cv::Mat & matchedPatternLocation)96 void RandomPatternCornerFinder::keyPoints2MatchedLocation(const std::vector<cv::KeyPoint>& imageKeypoints,
97 const std::vector<cv::KeyPoint>& patternKeypoints, const std::vector<cv::DMatch> matchces,
98 cv::Mat& matchedImagelocation, cv::Mat& matchedPatternLocation)
99 {
100 matchedImagelocation.release();
101 matchedPatternLocation.release();
102 std::vector<Vec2d> image, pattern;
103 for (int i = 0; i < (int)matchces.size(); ++i)
104 {
105 Point2f imgPt = imageKeypoints[matchces[i].queryIdx].pt;
106 Point2f patPt = patternKeypoints[matchces[i].trainIdx].pt;
107 image.push_back(Vec2d(imgPt.x, imgPt.y));
108 pattern.push_back(Vec2d(patPt.x, patPt.y));
109 }
110 Mat(image).convertTo(matchedImagelocation, CV_64FC2);
111 Mat(pattern).convertTo(matchedPatternLocation, CV_64FC2);
112 }
113
getFilteredLocation(cv::Mat & imageKeypoints,cv::Mat & patternKeypoints,const cv::Mat mask)114 void RandomPatternCornerFinder::getFilteredLocation(cv::Mat& imageKeypoints, cv::Mat& patternKeypoints, const cv::Mat mask)
115 {
116 Mat tmpKeypoint, tmpPattern;
117 imageKeypoints.copyTo(tmpKeypoint);
118 patternKeypoints.copyTo(tmpPattern);
119 imageKeypoints.release();
120 patternKeypoints.release();
121 std::vector<cv::Vec2d> vecKeypoint, vecPattern;
122 for(int i = 0; i < (int)mask.total(); ++i)
123 {
124 if (mask.at<uchar>(i) == 1)
125 {
126 vecKeypoint.push_back(tmpKeypoint.at<Vec2d>(i));
127 vecPattern.push_back(tmpPattern.at<Vec2d>(i));
128 }
129 }
130 Mat(vecKeypoint).convertTo(imageKeypoints, CV_64FC2);
131 Mat(vecPattern).convertTo(patternKeypoints, CV_64FC2);
132 }
133
getObjectImagePoints(const cv::Mat & imageKeypoints,const cv::Mat & patternKeypoints)134 void RandomPatternCornerFinder::getObjectImagePoints(const cv::Mat& imageKeypoints, const cv::Mat& patternKeypoints)
135 {
136 Mat imagePoints_i, objectPoints_i;
137 int imagePointsType = CV_MAKETYPE(_depth, 2);
138 int objectPointsType = CV_MAKETYPE(_depth, 3);
139 imageKeypoints.convertTo(imagePoints_i, imagePointsType);
140 _imagePoints.push_back(imagePoints_i);
141 if (patternKeypoints.total()!=CV_64FC2)
142 patternKeypoints.convertTo(patternKeypoints, CV_64FC2);
143
144 std::vector<Vec3d> objectPoints;
145
146 for (int i = 0; i < (int)patternKeypoints.total(); ++i)
147 {
148 double x = patternKeypoints.at<Vec2d>(i)[0];
149 double y = patternKeypoints.at<Vec2d>(i)[1];
150 x = x / _patternImageSize.width * _patternWidth;
151 y = y / _patternImageSize.height * _patternHeight;
152 objectPoints.push_back(Vec3d(x, y, 0));
153 }
154
155 Mat(objectPoints).convertTo(objectPoints_i, objectPointsType);
156 _objectPonits.push_back(objectPoints_i);
157 }
158
crossCheckMatching(Ptr<DescriptorMatcher> & descriptorMatcher,const Mat & descriptors1,const Mat & descriptors2,std::vector<DMatch> & filteredMatches12,int knn)159 void RandomPatternCornerFinder::crossCheckMatching( Ptr<DescriptorMatcher>& descriptorMatcher,
160 const Mat& descriptors1, const Mat& descriptors2,
161 std::vector<DMatch>& filteredMatches12, int knn )
162 {
163 filteredMatches12.clear();
164 std::vector<std::vector<DMatch> > matches12, matches21;
165 descriptorMatcher->knnMatch( descriptors1, descriptors2, matches12, knn );
166 descriptorMatcher->knnMatch( descriptors2, descriptors1, matches21, knn );
167 for( size_t m = 0; m < matches12.size(); m++ )
168 {
169 bool findCrossCheck = false;
170 for( size_t fk = 0; fk < matches12[m].size(); fk++ )
171 {
172 DMatch forward = matches12[m][fk];
173
174 for( size_t bk = 0; bk < matches21[forward.trainIdx].size(); bk++ )
175 {
176 DMatch backward = matches21[forward.trainIdx][bk];
177 if( backward.trainIdx == forward.queryIdx )
178 {
179 filteredMatches12.push_back(forward);
180 findCrossCheck = true;
181 break;
182 }
183 }
184 if( findCrossCheck ) break;
185 }
186 }
187 }
188
drawCorrespondence(const Mat & image1,const std::vector<cv::KeyPoint> keypoint1,const Mat & image2,const std::vector<cv::KeyPoint> keypoint2,const std::vector<cv::DMatch> matchces,const Mat & mask1,const Mat & mask2,const int step)189 void RandomPatternCornerFinder::drawCorrespondence(const Mat& image1, const std::vector<cv::KeyPoint> keypoint1,
190 const Mat& image2, const std::vector<cv::KeyPoint> keypoint2, const std::vector<cv::DMatch> matchces,
191 const Mat& mask1, const Mat& mask2, const int step)
192 {
193 Mat img_corr;
194 if(step == 1)
195 {
196 drawMatches(image1, keypoint1, image2, keypoint2, matchces, img_corr);
197 }
198 else if(step == 2)
199 {
200 std::vector<cv::DMatch> matchesFilter;
201 for (int i = 0; i < (int)mask1.total(); ++i)
202 {
203 if (!mask1.empty() && mask1.at<uchar>(i) == 1)
204 {
205 matchesFilter.push_back(matchces[i]);
206 }
207 }
208 drawMatches(image1, keypoint1, image2, keypoint2, matchesFilter, img_corr);
209 }
210 else if(step == 3)
211 {
212 std::vector<cv::DMatch> matchesFilter;
213 int j = 0;
214 for (int i = 0; i < (int)mask1.total(); ++i)
215 {
216 if (mask1.at<uchar>(i) == 1)
217 {
218 if (!mask2.empty() && mask2.at<uchar>(j) == 1)
219 {
220 matchesFilter.push_back(matchces[i]);
221 }
222 j++;
223 }
224 }
225 drawMatches(image1, keypoint1, image2, keypoint2, matchesFilter, img_corr);
226 }
227 imshow("correspondence", img_corr);
228 waitKey(0);
229 }
230
getObjectPoints()231 const std::vector<cv::Mat> &RandomPatternCornerFinder::getObjectPoints()
232 {
233 return _objectPonits;
234 }
235
getImagePoints()236 const std::vector<cv::Mat> &RandomPatternCornerFinder::getImagePoints()
237 {
238 return _imagePoints;
239 }
240
loadPattern(const cv::Mat & patternImage)241 void RandomPatternCornerFinder::loadPattern(const cv::Mat &patternImage)
242 {
243 _patternImage = patternImage.clone();
244 if (_patternImage.type()!= CV_8U)
245 _patternImage.convertTo(_patternImage, CV_8U);
246 _patternImageSize = _patternImage.size();
247 _detector->detect(patternImage, _keypointsPattern);
248 _descriptor->compute(patternImage, _keypointsPattern, _descriptorPattern);
249 _descriptorPattern.convertTo(_descriptorPattern, CV_32F);
250 }
251
loadPattern(const cv::Mat & patternImage,const std::vector<cv::KeyPoint> & patternKeyPoints,const cv::Mat & patternDescriptors)252 void RandomPatternCornerFinder::loadPattern(const cv::Mat &patternImage, const std::vector<cv::KeyPoint> &patternKeyPoints, const cv::Mat &patternDescriptors)
253 {
254 CV_Assert((int)patternKeyPoints.size()==patternDescriptors.rows);
255 CV_Assert(patternDescriptors.cols==_descriptor->descriptorSize());
256 CV_Assert(patternDescriptors.type()==_descriptor->descriptorType());
257
258 _patternImage=patternImage.clone();
259 if(_patternImage.type()!=CV_8U)
260 _patternImage.convertTo(_patternImage, CV_8U);
261 _patternImageSize=patternImage.size();
262 _keypointsPattern=patternKeyPoints;
263 _descriptorPattern=patternDescriptors.clone();
264 _descriptorPattern.convertTo(_descriptorPattern, CV_32F);
265 }
266
computeObjectImagePointsForSingle(cv::Mat inputImage)267 std::vector<cv::Mat> RandomPatternCornerFinder::computeObjectImagePointsForSingle(cv::Mat inputImage)
268 {
269 CV_Assert(!_patternImage.empty());
270 std::vector<cv::Mat> r(2);
271 Mat descriptorImage1, descriptorImage2,descriptorImage;
272 std::vector<cv::KeyPoint> keypointsImage1, keypointsImage2, keypointsImage;
273 if (inputImage.type()!=CV_8U)
274 {
275 inputImage.convertTo(inputImage, CV_8U);
276 }
277
278 Mat imageEquHist;
279 equalizeHist(inputImage, imageEquHist);
280
281 _detector->detect(inputImage, keypointsImage1);
282 _descriptor->compute(inputImage, keypointsImage1, descriptorImage1);
283 _detector->detect(imageEquHist, keypointsImage2);
284 _descriptor->compute(imageEquHist, keypointsImage2, descriptorImage2);
285 descriptorImage1.convertTo(descriptorImage1, CV_32F);
286 descriptorImage2.convertTo(descriptorImage2, CV_32F);
287
288 // match with pattern
289 std::vector<DMatch> matchesImgtoPat, matchesImgtoPat1, matchesImgtoPat2;
290
291 cv::Mat keypointsImageLocation, keypointsPatternLocation;
292
293 crossCheckMatching(this->_matcher, descriptorImage1, this->_descriptorPattern, matchesImgtoPat1, 1);
294 crossCheckMatching(this->_matcher, descriptorImage2, this->_descriptorPattern, matchesImgtoPat2, 1);
295 if ((int)matchesImgtoPat1.size() > (int)matchesImgtoPat2.size())
296 {
297 matchesImgtoPat = matchesImgtoPat1;
298 keypointsImage = keypointsImage1;
299 }
300 else
301 {
302 matchesImgtoPat = matchesImgtoPat2;
303 keypointsImage = keypointsImage2;
304 }
305
306 keyPoints2MatchedLocation(keypointsImage, this->_keypointsPattern, matchesImgtoPat,
307 keypointsImageLocation, keypointsPatternLocation);
308
309 Mat img_corr;
310
311 // innerMask is CV_8U type
312 Mat innerMask1, innerMask2;
313
314 // draw raw correspondence
315 if(this->_showExtraction)
316 {
317 drawCorrespondence(inputImage, keypointsImage, _patternImage, _keypointsPattern, matchesImgtoPat,
318 innerMask1, innerMask2, 1);
319 }
320
321 if (_verbose)
322 {
323 std::cout << "number of matched points " << (int)keypointsImageLocation.total() << std::endl;
324 }
325 // outlier remove
326 findFundamentalMat(keypointsImageLocation, keypointsPatternLocation,
327 FM_RANSAC, 1, 0.995, innerMask1);
328 getFilteredLocation(keypointsImageLocation, keypointsPatternLocation, innerMask1);
329
330 if (this->_showExtraction)
331 {
332 drawCorrespondence(inputImage, keypointsImage, _patternImage, _keypointsPattern, matchesImgtoPat,
333 innerMask1, innerMask2, 2);
334 }
335
336 findHomography(keypointsImageLocation, keypointsPatternLocation, RANSAC, 30*inputImage.cols/1000, innerMask2);
337 getFilteredLocation(keypointsImageLocation, keypointsPatternLocation, innerMask2);
338
339 if (_verbose)
340 {
341 std::cout << "number of filtered points " << (int)keypointsImageLocation.total() << std::endl;
342 }
343
344 // draw filtered correspondence
345 if (this->_showExtraction)
346 {
347 drawCorrespondence(inputImage, keypointsImage, _patternImage, _keypointsPattern, matchesImgtoPat,
348 innerMask1, innerMask2, 3);
349 }
350
351 std::vector<Vec3d> objectPoints;
352
353 int imagePointsType = CV_MAKETYPE(_depth, 2);
354 int objectPointsType = CV_MAKETYPE(_depth, 3);
355
356 keypointsImageLocation.convertTo(r[0], imagePointsType);
357
358 for (int i = 0; i < (int)keypointsPatternLocation.total(); ++i)
359 {
360 double x = keypointsPatternLocation.at<Vec2d>(i)[0];
361 double y = keypointsPatternLocation.at<Vec2d>(i)[1];
362 x = x / _patternImageSize.width * _patternWidth;
363 y = y / _patternImageSize.height * _patternHeight;
364 objectPoints.push_back(Vec3d(x, y, 0));
365 }
366 Mat(objectPoints).convertTo(r[1], objectPointsType);
367 return r;
368 }
369
RandomPatternGenerator(int imageWidth,int imageHeight)370 RandomPatternGenerator::RandomPatternGenerator(int imageWidth, int imageHeight)
371 {
372 _imageWidth = imageWidth;
373 _imageHeight = imageHeight;
374 }
375
generatePattern()376 void RandomPatternGenerator::generatePattern()
377 {
378 Mat pattern = Mat(_imageHeight, _imageWidth, CV_32F, Scalar(0.));
379
380 int m = 5;
381 int count = 0;
382
383 while(m < _imageWidth)
384 {
385 int n = (int)std::floor(double(_imageHeight) / double(_imageWidth) * double(m)) + 1;
386
387 Mat r = Mat(n, m, CV_32F);
388 cv::randn(r, Scalar::all(0), Scalar::all(1));
389 cv::resize(r, r, Size(_imageWidth ,_imageHeight));
390 double min_r, max_r;
391 minMaxLoc(r, &min_r, &max_r);
392
393 r = (r - (float)min_r) / float(max_r - min_r);
394
395 pattern += r;
396 count += 1;
397 m *= 2;
398 }
399 pattern = pattern / count * 255;
400 pattern.convertTo(pattern, CV_8U);
401 equalizeHist(pattern, pattern);
402 pattern.copyTo(_pattern);
403 }
404
getPattern()405 cv::Mat RandomPatternGenerator::getPattern()
406 {
407 return _pattern;
408 }
409
410 }} //namespace randpattern, cv