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