2011-11-15 16:37:40 +00:00
|
|
|
/*
|
2011-12-02 18:34:08 +00:00
|
|
|
* Copyright (C) 2011, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
2011-11-15 16:37:40 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
// Qt stuff
|
|
|
|
|
#include <QtCore/QTime>
|
|
|
|
|
#include <QtCore/QTimer>
|
|
|
|
|
#include <QtGui/QApplication>
|
|
|
|
|
#include <QtGui/QGraphicsRectItem>
|
|
|
|
|
#include <QtGui/QPen>
|
|
|
|
|
#include <QtGui/QColor>
|
|
|
|
|
|
|
|
|
|
// OpenCV stuff
|
|
|
|
|
#include <opencv2/core/core.hpp>
|
|
|
|
|
#include <opencv2/highgui/highgui.hpp>
|
|
|
|
|
#include <opencv2/features2d/features2d.hpp>
|
2012-05-07 22:36:59 +00:00
|
|
|
#include <opencv2/nonfree/features2d.hpp>
|
2011-11-15 16:37:40 +00:00
|
|
|
#include <opencv2/calib3d/calib3d.hpp> // for homography
|
|
|
|
|
|
|
|
|
|
// From this project (see src folder)
|
|
|
|
|
#include "ObjWidget.h"
|
2012-05-07 22:36:59 +00:00
|
|
|
#include "QtOpenCV.h"
|
2011-11-15 16:37:40 +00:00
|
|
|
|
|
|
|
|
void showUsage()
|
|
|
|
|
{
|
|
|
|
|
printf("\n");
|
|
|
|
|
printf("Usage :\n");
|
|
|
|
|
printf(" ./example object.png scene.png\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char * argv[])
|
|
|
|
|
{
|
|
|
|
|
if(argc<3)
|
|
|
|
|
{
|
|
|
|
|
showUsage();
|
|
|
|
|
}
|
|
|
|
|
QTime time;
|
|
|
|
|
|
|
|
|
|
// GUI stuff
|
|
|
|
|
QApplication app(argc, argv);
|
|
|
|
|
ObjWidget objWidget;
|
|
|
|
|
ObjWidget sceneWidget;
|
|
|
|
|
|
2012-05-10 13:53:25 +00:00
|
|
|
time.start();
|
2011-11-15 16:37:40 +00:00
|
|
|
//Load as grayscale
|
2012-05-07 22:36:59 +00:00
|
|
|
cv::Mat objectImg = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
|
|
|
|
|
cv::Mat sceneImg = cv::imread(argv[2], cv::IMREAD_GRAYSCALE);
|
2011-11-15 16:37:40 +00:00
|
|
|
|
2012-05-07 22:36:59 +00:00
|
|
|
if(!objectImg.empty() && !sceneImg.empty())
|
2011-11-15 16:37:40 +00:00
|
|
|
{
|
2012-05-10 13:53:25 +00:00
|
|
|
printf("Loading images: %d ms\n", time.restart());
|
2011-11-15 16:37:40 +00:00
|
|
|
std::vector<cv::KeyPoint> objectKeypoints;
|
|
|
|
|
std::vector<cv::KeyPoint> sceneKeypoints;
|
|
|
|
|
cv::Mat objectDescriptors;
|
|
|
|
|
cv::Mat sceneDescriptors;
|
|
|
|
|
|
|
|
|
|
////////////////////////////
|
|
|
|
|
// EXTRACT KEYPOINTS
|
|
|
|
|
////////////////////////////
|
|
|
|
|
// The detector can be any of (see OpenCV features2d.hpp):
|
2012-05-07 22:36:59 +00:00
|
|
|
// cv::FeatureDetector * detector = new cv::DenseFeatureDetector();
|
2011-11-15 16:37:40 +00:00
|
|
|
// cv::FeatureDetector * detector = new cv::FastFeatureDetector();
|
2012-05-07 22:36:59 +00:00
|
|
|
// cv::FeatureDetector * detector = new cv::GFTTDetector();
|
|
|
|
|
// cv::FeatureDetector * detector = new cv::MSER();
|
|
|
|
|
// cv::FeatureDetector * detector = new cv::ORB();
|
2012-08-31 14:09:25 +00:00
|
|
|
cv::FeatureDetector * detector = new cv::SIFT();
|
2011-11-15 16:37:40 +00:00
|
|
|
// cv::FeatureDetector * detector = new cv::StarFeatureDetector();
|
2012-08-31 14:09:25 +00:00
|
|
|
// cv::FeatureDetector * detector = new cv::SURF(600.0);
|
2011-11-15 16:37:40 +00:00
|
|
|
detector->detect(objectImg, objectKeypoints);
|
|
|
|
|
printf("Object: %d keypoints detected in %d ms\n", (int)objectKeypoints.size(), time.restart());
|
|
|
|
|
detector->detect(sceneImg, sceneKeypoints);
|
|
|
|
|
printf("Scene: %d keypoints detected in %d ms\n", (int)sceneKeypoints.size(), time.restart());
|
|
|
|
|
|
|
|
|
|
////////////////////////////
|
|
|
|
|
// EXTRACT DESCRIPTORS
|
|
|
|
|
////////////////////////////
|
|
|
|
|
// The extractor can be any of (see OpenCV features2d.hpp):
|
2011-11-22 14:28:49 +00:00
|
|
|
// cv::DescriptorExtractor * extractor = new cv::BriefDescriptorExtractor();
|
2012-05-07 22:36:59 +00:00
|
|
|
// cv::DescriptorExtractor * extractor = new cv::ORB();
|
2012-08-31 14:09:25 +00:00
|
|
|
cv::DescriptorExtractor * extractor = new cv::SIFT();
|
|
|
|
|
// cv::DescriptorExtractor * extractor = new cv::SURF(600.0);
|
2011-11-15 16:37:40 +00:00
|
|
|
extractor->compute(objectImg, objectKeypoints, objectDescriptors);
|
|
|
|
|
printf("Object: %d descriptors extracted in %d ms\n", objectDescriptors.rows, time.restart());
|
|
|
|
|
extractor->compute(sceneImg, sceneKeypoints, sceneDescriptors);
|
|
|
|
|
printf("Scene: %d descriptors extracted in %d ms\n", sceneDescriptors.rows, time.restart());
|
|
|
|
|
|
|
|
|
|
////////////////////////////
|
|
|
|
|
// NEAREST NEIGHBOR MATCHING USING FLANN LIBRARY (included in OpenCV)
|
|
|
|
|
////////////////////////////
|
2012-08-31 14:09:25 +00:00
|
|
|
cv::Mat results;
|
|
|
|
|
cv::Mat dists;
|
|
|
|
|
int k=2; // find the 2 nearest neighbors
|
|
|
|
|
if(objectDescriptors.type()==CV_8U)
|
|
|
|
|
{
|
|
|
|
|
// Binary descriptors detected (from ORB or Brief)
|
|
|
|
|
|
|
|
|
|
// Create Flann LSH index
|
2012-10-29 14:35:12 +00:00
|
|
|
cv::flann::Index flannIndex(sceneDescriptors, cv::flann::LshIndexParams(12, 20, 2));
|
2012-08-31 14:09:25 +00:00
|
|
|
printf("Time creating FLANN index = %d ms\n", time.restart());
|
|
|
|
|
results = cv::Mat(objectDescriptors.rows, k, CV_32SC1); // Results index
|
|
|
|
|
dists = cv::Mat(objectDescriptors.rows, k, CV_32FC1); // Distance results are CV_32FC1 ?!?!? NOTE OpenCV doc is not clear about that...
|
|
|
|
|
|
|
|
|
|
// search (nearest neighbor)
|
|
|
|
|
flannIndex.knnSearch(objectDescriptors, results, dists, k, cv::flann::SearchParams() );
|
|
|
|
|
printf("Time nearest neighbor search = %d ms\n", time.restart());
|
2011-11-15 16:37:40 +00:00
|
|
|
}
|
2012-08-31 14:09:25 +00:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// assume it is CV_32F
|
|
|
|
|
|
|
|
|
|
// Create Flann KDTree index
|
|
|
|
|
cv::flann::Index flannIndex(sceneDescriptors, cv::flann::KDTreeIndexParams());
|
|
|
|
|
printf("Time creating FLANN index = %d ms\n", time.restart());
|
|
|
|
|
results = cv::Mat(objectDescriptors.rows, k, CV_32SC1); // Results index
|
|
|
|
|
dists = cv::Mat(objectDescriptors.rows, k, CV_32FC1); // Distance results are CV_32FC1
|
|
|
|
|
|
|
|
|
|
// search (nearest neighbor)
|
|
|
|
|
flannIndex.knnSearch(objectDescriptors, results, dists, k, cv::flann::SearchParams() );
|
|
|
|
|
printf("Time nearest neighbor search = %d ms\n", time.restart());
|
2011-11-15 16:37:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-08-31 14:09:25 +00:00
|
|
|
|
|
|
|
|
|
2011-11-15 16:37:40 +00:00
|
|
|
|
|
|
|
|
////////////////////////////
|
|
|
|
|
// PROCESS NEAREST NEIGHBOR RESULTS
|
|
|
|
|
////////////////////////////
|
|
|
|
|
// Set gui data
|
2012-08-31 14:09:25 +00:00
|
|
|
objWidget.setData(objectKeypoints, objectDescriptors, objectImg, "", "");
|
|
|
|
|
sceneWidget.setData(sceneKeypoints, sceneDescriptors, sceneImg, "", "");
|
2011-11-15 16:37:40 +00:00
|
|
|
|
|
|
|
|
// Find correspondences by NNDR (Nearest Neighbor Distance Ratio)
|
|
|
|
|
float nndrRatio = 0.6;
|
|
|
|
|
std::vector<cv::Point2f> mpts_1, mpts_2; // Used for homography
|
|
|
|
|
std::vector<int> indexes_1, indexes_2; // Used for homography
|
|
|
|
|
std::vector<uchar> outlier_mask; // Used for homography
|
2012-08-31 14:09:25 +00:00
|
|
|
for(int i=0; i<objectDescriptors.rows; ++i)
|
2011-11-15 16:37:40 +00:00
|
|
|
{
|
|
|
|
|
// Check if this descriptor matches with those of the objects
|
|
|
|
|
// Apply NNDR
|
|
|
|
|
if(dists.at<float>(i,0) <= nndrRatio * dists.at<float>(i,1))
|
|
|
|
|
{
|
|
|
|
|
mpts_1.push_back(objectKeypoints.at(i).pt);
|
|
|
|
|
indexes_1.push_back(i);
|
|
|
|
|
|
|
|
|
|
mpts_2.push_back(sceneKeypoints.at(results.at<int>(i,0)).pt);
|
|
|
|
|
indexes_2.push_back(results.at<int>(i,0));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FIND HOMOGRAPHY
|
2011-11-23 18:08:33 +00:00
|
|
|
unsigned int minInliers = 8;
|
2011-11-15 16:37:40 +00:00
|
|
|
if(mpts_1.size() >= minInliers)
|
|
|
|
|
{
|
2011-11-15 18:45:10 +00:00
|
|
|
time.start();
|
2011-11-15 16:37:40 +00:00
|
|
|
cv::Mat H = findHomography(mpts_1,
|
|
|
|
|
mpts_2,
|
|
|
|
|
cv::RANSAC,
|
|
|
|
|
1.0,
|
|
|
|
|
outlier_mask);
|
2011-11-15 18:45:10 +00:00
|
|
|
printf("Time finding homography = %d ms\n", time.restart());
|
2011-11-15 16:37:40 +00:00
|
|
|
int inliers=0, outliers=0;
|
2011-11-23 18:08:33 +00:00
|
|
|
for(unsigned int k=0; k<mpts_1.size();++k)
|
2011-11-15 16:37:40 +00:00
|
|
|
{
|
|
|
|
|
if(outlier_mask.at(k))
|
|
|
|
|
{
|
|
|
|
|
++inliers;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++outliers;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
QTransform hTransform(
|
|
|
|
|
H.at<double>(0,0), H.at<double>(1,0), H.at<double>(2,0),
|
|
|
|
|
H.at<double>(0,1), H.at<double>(1,1), H.at<double>(2,1),
|
|
|
|
|
H.at<double>(0,2), H.at<double>(1,2), H.at<double>(2,2));
|
|
|
|
|
|
|
|
|
|
// GUI : Change color and add homography rectangle
|
2011-11-17 20:45:08 +00:00
|
|
|
QColor color(Qt::green);
|
2011-11-15 16:37:40 +00:00
|
|
|
int alpha = 130;
|
|
|
|
|
color.setAlpha(alpha);
|
2011-11-23 18:08:33 +00:00
|
|
|
for(unsigned int k=0; k<mpts_1.size();++k)
|
2011-11-15 16:37:40 +00:00
|
|
|
{
|
|
|
|
|
if(outlier_mask.at(k))
|
|
|
|
|
{
|
|
|
|
|
objWidget.setKptColor(indexes_1.at(k), color);
|
|
|
|
|
sceneWidget.setKptColor(indexes_2.at(k), color);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2011-11-17 20:45:08 +00:00
|
|
|
objWidget.setKptColor(indexes_1.at(k), QColor(255,0,0,alpha));
|
|
|
|
|
sceneWidget.setKptColor(indexes_2.at(k), QColor(255,0,0,alpha));
|
2011-11-15 16:37:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
QPen rectPen(color);
|
|
|
|
|
rectPen.setWidth(4);
|
2012-05-07 22:36:59 +00:00
|
|
|
QGraphicsRectItem * rectItem = new QGraphicsRectItem(objWidget.pixmap().rect());
|
2011-11-15 16:37:40 +00:00
|
|
|
rectItem->setPen(rectPen);
|
|
|
|
|
rectItem->setTransform(hTransform);
|
|
|
|
|
sceneWidget.addRect(rectItem);
|
|
|
|
|
printf("Inliers=%d Outliers=%d\n", inliers, outliers);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
printf("Not enough matches (%d) for homography...\n", (int)mpts_1.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wait for gui
|
|
|
|
|
objWidget.setGraphicsViewMode(false);
|
|
|
|
|
objWidget.setWindowTitle("Object");
|
2012-05-07 22:36:59 +00:00
|
|
|
if(objWidget.pixmap().width() <= 800)
|
2011-11-17 20:31:08 +00:00
|
|
|
{
|
2012-05-07 22:36:59 +00:00
|
|
|
objWidget.setMinimumSize(objWidget.pixmap().width(), objWidget.pixmap().height());
|
2011-11-17 20:31:08 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2012-05-07 22:36:59 +00:00
|
|
|
objWidget.setMinimumSize(800, 600);
|
2011-11-17 20:31:08 +00:00
|
|
|
objWidget.setAutoScale(false);
|
|
|
|
|
}
|
2012-05-07 22:36:59 +00:00
|
|
|
|
2011-11-15 16:37:40 +00:00
|
|
|
sceneWidget.setGraphicsViewMode(false);
|
|
|
|
|
sceneWidget.setWindowTitle("Scene");
|
2012-05-07 22:36:59 +00:00
|
|
|
if(sceneWidget.pixmap().width() <= 800)
|
2011-11-17 20:31:08 +00:00
|
|
|
{
|
2012-05-07 22:36:59 +00:00
|
|
|
sceneWidget.setMinimumSize(sceneWidget.pixmap().width(), sceneWidget.pixmap().height());
|
2011-11-17 20:31:08 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2012-05-07 22:36:59 +00:00
|
|
|
sceneWidget.setMinimumSize(800, 600);
|
2011-11-17 20:31:08 +00:00
|
|
|
sceneWidget.setAutoScale(false);
|
|
|
|
|
}
|
2012-05-07 22:36:59 +00:00
|
|
|
|
2011-11-15 16:37:40 +00:00
|
|
|
sceneWidget.show();
|
2012-05-07 22:36:59 +00:00
|
|
|
objWidget.show();
|
|
|
|
|
|
2011-11-15 16:37:40 +00:00
|
|
|
int r = app.exec();
|
2011-11-15 18:45:10 +00:00
|
|
|
printf("Closing...\n");
|
2012-05-07 22:36:59 +00:00
|
|
|
|
2011-11-15 16:37:40 +00:00
|
|
|
////////////////////////////
|
|
|
|
|
//Cleanup
|
|
|
|
|
////////////////////////////
|
|
|
|
|
delete detector;
|
|
|
|
|
delete extractor;
|
|
|
|
|
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
printf("Images are not valid!\n");
|
|
|
|
|
showUsage();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|