diff --git a/doc/opencv.bib b/doc/opencv.bib index 1cb3d0b0af..edb7033e8d 100644 --- a/doc/opencv.bib +++ b/doc/opencv.bib @@ -20,6 +20,21 @@ volume = {34}, number = {7} } +@INPROCEEDINGS{Arandjelovic:2012:TTE:2354409.2355123, + author = {Arandjelovic, Relja}, + title = {Three Things Everyone Should Know to Improve Object Retrieval}, + booktitle = {Proceedings of the 2012 IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}, + series = {CVPR '12}, + year = {2012}, + isbn = {978-1-4673-1226-4}, + pages = {2911--2918}, + numpages = {8}, + url = {http://dl.acm.org/citation.cfm?id=2354409.2355123}, + acmid = {2355123}, + publisher = {IEEE Computer Society}, + address = {Washington, DC, USA}, + keywords = {Vectors,Visualization,Kernel,Standards,Support vector machines,Indexes,Euclidean distance}, +} @ARTICLE{BA83, author = {Burt, Peter J and Adelson, Edward H}, title = {A multiresolution spline with application to image mosaics}, @@ -515,6 +530,25 @@ volume = {1}, organization = {IEEE} } +@ARTICLE{Lowe:2004:DIF:993451.996342, + author = {Lowe, David G.}, + title = {Distinctive Image Features from Scale-Invariant Keypoints}, + journal = {Int. J. Comput. Vision}, + issue_date = {November 2004}, + volume = {60}, + number = {2}, + month = nov, + year = {2004}, + issn = {0920-5691}, + pages = {91--110}, + numpages = {20}, + url = {https://doi.org/10.1023/B:VISI.0000029664.99615.94}, + doi = {10.1023/B:VISI.0000029664.99615.94}, + acmid = {996342}, + publisher = {Kluwer Academic Publishers}, + address = {Hingham, MA, USA}, + keywords = {image matching, invariant features, object recognition, scale invariance}, +} @INPROCEEDINGS{Lucas81, author = {Lucas, Bruce D and Kanade, Takeo and others}, title = {An iterative image registration technique with an application to stereo vision.}, diff --git a/doc/tutorials/features2d/feature_description/feature_description.markdown b/doc/tutorials/features2d/feature_description/feature_description.markdown index eea5a29c1d..ec3cd0e4c5 100644 --- a/doc/tutorials/features2d/feature_description/feature_description.markdown +++ b/doc/tutorials/features2d/feature_description/feature_description.markdown @@ -10,74 +10,35 @@ In this tutorial you will learn how to: to the keypoints. Specifically: - Use cv::xfeatures2d::SURF and its function cv::xfeatures2d::SURF::compute to perform the required calculations. - - Use a @ref cv::BFMatcher to match the features vector + - Use a @ref cv::DescriptorMatcher to match the features vector - Use the function @ref cv::drawMatches to draw the detected matches. +\warning You need the OpenCV contrib modules to be able to use the SURF features +(alternatives are ORB, KAZE, ... features). + Theory ------ Code ---- -This tutorial code's is shown lines below. -@code{.cpp} -#include -#include -#include "opencv2/core.hpp" -#include "opencv2/features2d.hpp" -#include "opencv2/highgui.hpp" -#include "opencv2/xfeatures2d.hpp" +@add_toggle_cpp +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/features2D/feature_description/SURF_matching_Demo.cpp) +@include samples/cpp/tutorial_code/features2D/feature_description/SURF_matching_Demo.cpp +@end_toggle -using namespace cv; -using namespace cv::xfeatures2d; +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/features2D/feature_description/SURFMatchingDemo.java) +@include samples/java/tutorial_code/features2D/feature_description/SURFMatchingDemo.java +@end_toggle -void readme(); - -/* @function main */ -int main( int argc, char** argv ) -{ - if( argc != 3 ) - { return -1; } - - Mat img_1 = imread( argv[1], IMREAD_GRAYSCALE ); - Mat img_2 = imread( argv[2], IMREAD_GRAYSCALE ); - - if( !img_1.data || !img_2.data ) - { return -1; } - - //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors - int minHessian = 400; - - Ptr detector = SURF::create(); - detector->setHessianThreshold(minHessian); - - std::vector keypoints_1, keypoints_2; - Mat descriptors_1, descriptors_2; - - detector->detectAndCompute( img_1, Mat(), keypoints_1, descriptors_1 ); - detector->detectAndCompute( img_2, Mat(), keypoints_2, descriptors_2 ); - - //-- Step 2: Matching descriptor vectors with a brute force matcher - BFMatcher matcher(NORM_L2); - std::vector< DMatch > matches; - matcher.match( descriptors_1, descriptors_2, matches ); - - //-- Draw matches - Mat img_matches; - drawMatches( img_1, keypoints_1, img_2, keypoints_2, matches, img_matches ); - - //-- Show detected matches - imshow("Matches", img_matches ); - - waitKey(0); - - return 0; - } - - /* @function readme */ - void readme() - { std::cout << " Usage: ./SURF_descriptor " << std::endl; } -@endcode +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/features2D/feature_description/SURF_matching_Demo.py) +@include samples/python/tutorial_code/features2D/feature_description/SURF_matching_Demo.py +@end_toggle Explanation ----------- diff --git a/doc/tutorials/features2d/feature_detection/feature_detection.markdown b/doc/tutorials/features2d/feature_detection/feature_detection.markdown index 8b2f423ee2..d0996512ef 100644 --- a/doc/tutorials/features2d/feature_detection/feature_detection.markdown +++ b/doc/tutorials/features2d/feature_detection/feature_detection.markdown @@ -11,67 +11,32 @@ In this tutorial you will learn how to: detection process - Use the function @ref cv::drawKeypoints to draw the detected keypoints +\warning You need the OpenCV contrib modules to be able to use the SURF features +(alternatives are ORB, KAZE, ... features). + Theory ------ Code ---- -This tutorial code's is shown lines below. -@code{.cpp} -#include -#include -#include "opencv2/core.hpp" -#include "opencv2/features2d.hpp" -#include "opencv2/xfeatures2d.hpp" -#include "opencv2/highgui.hpp" +@add_toggle_cpp +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/features2D/feature_detection/SURF_detection_Demo.cpp) +@include samples/cpp/tutorial_code/features2D/feature_detection/SURF_detection_Demo.cpp +@end_toggle -using namespace cv; -using namespace cv::xfeatures2d; +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/features2D/feature_detection/SURFDetectionDemo.java) +@include samples/java/tutorial_code/features2D/feature_detection/SURFDetectionDemo.java +@end_toggle -void readme(); - -/* @function main */ -int main( int argc, char** argv ) -{ - if( argc != 3 ) - { readme(); return -1; } - - Mat img_1 = imread( argv[1], IMREAD_GRAYSCALE ); - Mat img_2 = imread( argv[2], IMREAD_GRAYSCALE ); - - if( !img_1.data || !img_2.data ) - { std::cout<< " --(!) Error reading images " << std::endl; return -1; } - - //-- Step 1: Detect the keypoints using SURF Detector - int minHessian = 400; - - Ptr detector = SURF::create( minHessian ); - - std::vector keypoints_1, keypoints_2; - - detector->detect( img_1, keypoints_1 ); - detector->detect( img_2, keypoints_2 ); - - //-- Draw keypoints - Mat img_keypoints_1; Mat img_keypoints_2; - - drawKeypoints( img_1, keypoints_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT ); - drawKeypoints( img_2, keypoints_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT ); - - //-- Show detected (drawn) keypoints - imshow("Keypoints 1", img_keypoints_1 ); - imshow("Keypoints 2", img_keypoints_2 ); - - waitKey(0); - - return 0; - } - - /* @function readme */ - void readme() - { std::cout << " Usage: ./SURF_detector " << std::endl; } -@endcode +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/features2D/feature_detection/SURF_detection_Demo.py) +@include samples/python/tutorial_code/features2D/feature_detection/SURF_detection_Demo.py +@end_toggle Explanation ----------- @@ -79,10 +44,10 @@ Explanation Result ------ --# Here is the result of the feature detection applied to the first image: +-# Here is the result of the feature detection applied to the `box.png` image: ![](images/Feature_Detection_Result_a.jpg) --# And here is the result for the second image: +-# And here is the result for the `box_in_scene.png` image: ![](images/Feature_Detection_Result_b.jpg) diff --git a/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.markdown b/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.markdown index 8243b430a6..e7f865c3ce 100644 --- a/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.markdown +++ b/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.markdown @@ -9,114 +9,57 @@ In this tutorial you will learn how to: - Use the @ref cv::FlannBasedMatcher interface in order to perform a quick and efficient matching by using the @ref flann module +\warning You need the OpenCV contrib modules to be able to use the SURF features +(alternatives are ORB, KAZE, ... features). + Theory ------ +Classical feature descriptors (SIFT, SURF, ...) are usually compared and matched using the Euclidean distance (or L2-norm). +Since SIFT and SURF descriptors represent the histogram of oriented gradient (of the Haar wavelet response for SURF) +in a neighborhood, alternatives of the Euclidean distance are histogram-based metrics (\f$ \chi^{2} \f$, Earth Mover’s Distance (EMD), ...). + +Arandjelovic et al. proposed in @cite Arandjelovic:2012:TTE:2354409.2355123 to extend to the RootSIFT descriptor: +> a square root (Hellinger) kernel instead of the standard Euclidean distance to measure the similarity between SIFT descriptors +> leads to a dramatic performance boost in all stages of the pipeline. + +Binary descriptors (ORB, BRISK, ...) are matched using the Hamming distance. +This distance is equivalent to count the number of different elements for binary strings (population count after applying a XOR operation): +\f[ d_{hamming} \left ( a,b \right ) = \sum_{i=0}^{n-1} \left ( a_i \oplus b_i \right ) \f] + +To filter the matches, Lowe proposed in @cite Lowe:2004:DIF:993451.996342 to use a distance ratio test to try to eliminate false matches. +The distance ratio between the two nearest matches of a considered keypoint is computed and it is a good match when this value is below +a thresold. Indeed, this ratio allows helping to discriminate between ambiguous matches (distance ratio between the two nearest neighbors is +close to one) and well discriminated matches. The figure below from the SIFT paper illustrates the probability that a match is correct +based on the nearest-neighbor distance ratio test. + +![](images/Feature_FlannMatcher_Lowe_ratio_test.png) + +Alternative or additional filterering tests are: +- cross check test (good match \f$ \left( f_a, f_b \right) \f$ if feature \f$ f_b \f$ is the best match for \f$ f_a \f$ in \f$ I_b \f$ + and feature \f$ f_a \f$ is the best match for \f$ f_b \f$ in \f$ I_a \f$) +- geometric test (eliminate matches that do not fit to a geometric model, e.g. RANSAC or robust homography for planar objects) + Code ---- -This tutorial code's is shown lines below. -@code{.cpp} -/* - * @file SURF_FlannMatcher - * @brief SURF detector + descriptor + FLANN Matcher - * @author A. Huaman - */ +@add_toggle_cpp +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.cpp) +@include samples/cpp/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.cpp +@end_toggle -#include -#include -#include -#include -#include "opencv2/core.hpp" -#include "opencv2/features2d.hpp" -#include "opencv2/imgcodecs.hpp" -#include "opencv2/highgui.hpp" -#include "opencv2/xfeatures2d.hpp" +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/features2D/feature_flann_matcher/SURFFLANNMatchingDemo.java) +@include samples/java/tutorial_code/features2D/feature_flann_matcher/SURFFLANNMatchingDemo.java +@end_toggle -using namespace std; -using namespace cv; -using namespace cv::xfeatures2d; - -void readme(); - -/* - * @function main - * @brief Main function - */ -int main( int argc, char** argv ) -{ - if( argc != 3 ) - { readme(); return -1; } - - Mat img_1 = imread( argv[1], IMREAD_GRAYSCALE ); - Mat img_2 = imread( argv[2], IMREAD_GRAYSCALE ); - - if( !img_1.data || !img_2.data ) - { std::cout<< " --(!) Error reading images " << std::endl; return -1; } - - //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors - int minHessian = 400; - - Ptr detector = SURF::create(); - detector->setHessianThreshold(minHessian); - - std::vector keypoints_1, keypoints_2; - Mat descriptors_1, descriptors_2; - - detector->detectAndCompute( img_1, Mat(), keypoints_1, descriptors_1 ); - detector->detectAndCompute( img_2, Mat(), keypoints_2, descriptors_2 ); - - //-- Step 2: Matching descriptor vectors using FLANN matcher - FlannBasedMatcher matcher; - std::vector< DMatch > matches; - matcher.match( descriptors_1, descriptors_2, matches ); - - double max_dist = 0; double min_dist = 100; - - //-- Quick calculation of max and min distances between keypoints - for( int i = 0; i < descriptors_1.rows; i++ ) - { double dist = matches[i].distance; - if( dist < min_dist ) min_dist = dist; - if( dist > max_dist ) max_dist = dist; - } - - printf("-- Max dist : %f \n", max_dist ); - printf("-- Min dist : %f \n", min_dist ); - - //-- Draw only "good" matches (i.e. whose distance is less than 2*min_dist, - //-- or a small arbitrary value ( 0.02 ) in the event that min_dist is very - //-- small) - //-- PS.- radiusMatch can also be used here. - std::vector< DMatch > good_matches; - - for( int i = 0; i < descriptors_1.rows; i++ ) - { if( matches[i].distance <= max(2*min_dist, 0.02) ) - { good_matches.push_back( matches[i]); } - } - - //-- Draw only "good" matches - Mat img_matches; - drawMatches( img_1, keypoints_1, img_2, keypoints_2, - good_matches, img_matches, Scalar::all(-1), Scalar::all(-1), - vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); - - //-- Show detected matches - imshow( "Good Matches", img_matches ); - - for( int i = 0; i < (int)good_matches.size(); i++ ) - { printf( "-- Good Match [%d] Keypoint 1: %d -- Keypoint 2: %d \n", i, good_matches[i].queryIdx, good_matches[i].trainIdx ); } - - waitKey(0); - - return 0; -} - -/* - * @function readme - */ -void readme() -{ std::cout << " Usage: ./SURF_FlannMatcher " << std::endl; } -@endcode +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py) +@include samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py +@end_toggle Explanation ----------- @@ -124,10 +67,6 @@ Explanation Result ------ --# Here is the result of the feature detection applied to the first image: +- Here is the result of the SURF feature matching using the distance ratio test: - ![](images/Featur_FlannMatcher_Result.jpg) - --# Additionally, we get as console output the keypoints filtered: - - ![](images/Feature_FlannMatcher_Keypoints_Result.jpg) + ![](images/Feature_FlannMatcher_Result_ratio_test.jpg) diff --git a/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Lowe_ratio_test.png b/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Lowe_ratio_test.png new file mode 100644 index 0000000000..f56a640b67 Binary files /dev/null and b/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Lowe_ratio_test.png differ diff --git a/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Result_ratio_test.jpg b/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Result_ratio_test.jpg new file mode 100644 index 0000000000..e4a88d0020 Binary files /dev/null and b/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Result_ratio_test.jpg differ diff --git a/doc/tutorials/features2d/feature_homography/feature_homography.markdown b/doc/tutorials/features2d/feature_homography/feature_homography.markdown index ec7913c330..c4f0c00e55 100644 --- a/doc/tutorials/features2d/feature_homography/feature_homography.markdown +++ b/doc/tutorials/features2d/feature_homography/feature_homography.markdown @@ -9,125 +9,40 @@ In this tutorial you will learn how to: - Use the function @ref cv::findHomography to find the transform between matched keypoints. - Use the function @ref cv::perspectiveTransform to map the points. +\warning You need the OpenCV contrib modules to be able to use the SURF features +(alternatives are ORB, KAZE, ... features). + Theory ------ Code ---- -This tutorial code's is shown lines below. -@code{.cpp} -#include -#include -#include "opencv2/core.hpp" -#include "opencv2/imgproc.hpp" -#include "opencv2/features2d.hpp" -#include "opencv2/highgui.hpp" -#include "opencv2/calib3d.hpp" -#include "opencv2/xfeatures2d.hpp" +@add_toggle_cpp +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.cpp) +@include samples/cpp/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.cpp +@end_toggle -using namespace cv; -using namespace cv::xfeatures2d; +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/features2D/feature_homography/SURFFLANNMatchingHomographyDemo.java) +@include samples/java/tutorial_code/features2D/feature_homography/SURFFLANNMatchingHomographyDemo.java +@end_toggle -void readme(); +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py) +@include samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py +@end_toggle -/* @function main */ -int main( int argc, char** argv ) -{ - if( argc != 3 ) - { readme(); return -1; } - - Mat img_object = imread( argv[1], IMREAD_GRAYSCALE ); - Mat img_scene = imread( argv[2], IMREAD_GRAYSCALE ); - - if( !img_object.data || !img_scene.data ) - { std::cout<< " --(!) Error reading images " << std::endl; return -1; } - - //-- Step 1: Detect the keypoints and extract descriptors using SURF - int minHessian = 400; - - Ptr detector = SURF::create( minHessian ); - - std::vector keypoints_object, keypoints_scene; - Mat descriptors_object, descriptors_scene; - - detector->detectAndCompute( img_object, Mat(), keypoints_object, descriptors_object ); - detector->detectAndCompute( img_scene, Mat(), keypoints_scene, descriptors_scene ); - - //-- Step 2: Matching descriptor vectors using FLANN matcher - FlannBasedMatcher matcher; - std::vector< DMatch > matches; - matcher.match( descriptors_object, descriptors_scene, matches ); - - double max_dist = 0; double min_dist = 100; - - //-- Quick calculation of max and min distances between keypoints - for( int i = 0; i < descriptors_object.rows; i++ ) - { double dist = matches[i].distance; - if( dist < min_dist ) min_dist = dist; - if( dist > max_dist ) max_dist = dist; - } - - printf("-- Max dist : %f \n", max_dist ); - printf("-- Min dist : %f \n", min_dist ); - - //-- Draw only "good" matches (i.e. whose distance is less than 3*min_dist ) - std::vector< DMatch > good_matches; - - for( int i = 0; i < descriptors_object.rows; i++ ) - { if( matches[i].distance <= 3*min_dist ) - { good_matches.push_back( matches[i]); } - } - - Mat img_matches; - drawMatches( img_object, keypoints_object, img_scene, keypoints_scene, - good_matches, img_matches, Scalar::all(-1), Scalar::all(-1), - std::vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); - - //-- Localize the object - std::vector obj; - std::vector scene; - - for( size_t i = 0; i < good_matches.size(); i++ ) - { - //-- Get the keypoints from the good matches - obj.push_back( keypoints_object[ good_matches[i].queryIdx ].pt ); - scene.push_back( keypoints_scene[ good_matches[i].trainIdx ].pt ); - } - - Mat H = findHomography( obj, scene, RANSAC ); - - //-- Get the corners from the image_1 ( the object to be "detected" ) - std::vector obj_corners(4); - obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( img_object.cols, 0 ); - obj_corners[2] = cvPoint( img_object.cols, img_object.rows ); obj_corners[3] = cvPoint( 0, img_object.rows ); - std::vector scene_corners(4); - - perspectiveTransform( obj_corners, scene_corners, H); - - //-- Draw lines between the corners (the mapped object in the scene - image_2 ) - line( img_matches, scene_corners[0] + Point2f( img_object.cols, 0), scene_corners[1] + Point2f( img_object.cols, 0), Scalar(0, 255, 0), 4 ); - line( img_matches, scene_corners[1] + Point2f( img_object.cols, 0), scene_corners[2] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 ); - line( img_matches, scene_corners[2] + Point2f( img_object.cols, 0), scene_corners[3] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 ); - line( img_matches, scene_corners[3] + Point2f( img_object.cols, 0), scene_corners[0] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 ); - - //-- Show detected matches - imshow( "Good Matches & Object detection", img_matches ); - - waitKey(0); - return 0; - } - - /* @function readme */ - void readme() - { std::cout << " Usage: ./SURF_descriptor " << std::endl; } -@endcode Explanation ----------- Result ------ --# And here is the result for the detected object (highlighted in green) +- And here is the result for the detected object (highlighted in green). Note that since the homography is estimated with a RANSAC approach, + detected false matches will not impact the homography calculation. ![](images/Feature_Homography_Result.jpg) diff --git a/doc/tutorials/features2d/feature_homography/images/Feature_Homography_Result.jpg b/doc/tutorials/features2d/feature_homography/images/Feature_Homography_Result.jpg index d043a5a6e9..35b1bb634c 100644 Binary files a/doc/tutorials/features2d/feature_homography/images/Feature_Homography_Result.jpg and b/doc/tutorials/features2d/feature_homography/images/Feature_Homography_Result.jpg differ diff --git a/doc/tutorials/features2d/table_of_content_features2d.markdown b/doc/tutorials/features2d/table_of_content_features2d.markdown index a5d5a91676..37e4e41a1d 100644 --- a/doc/tutorials/features2d/table_of_content_features2d.markdown +++ b/doc/tutorials/features2d/table_of_content_features2d.markdown @@ -6,39 +6,51 @@ OpenCV. - @subpage tutorial_harris_detector + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán - Why is it a good idea to track corners? We learn to use the Harris method to detect - corners + Why is it a good idea to track corners? We learn how to use the Harris method to detect + corners. - @subpage tutorial_good_features_to_track + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán - Where we use an improved method to detect corners more accuratelyI + Where we use an improved method to detect corners more accurately. - @subpage tutorial_generic_corner_detector + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán Here you will learn how to use OpenCV functions to make your personalized corner detector! -- @subpage tutorial_corner_subpixeles + *Languages:* C++, Java, Python + +- @subpage tutorial_corner_subpixels + + *Languages:* C++, Java, Python *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán - Is pixel resolution enough? Here we learn a simple method to improve our accuracy. + Is pixel resolution enough? Here we learn a simple method to improve our corner location accuracy. - @subpage tutorial_feature_detection + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán @@ -47,6 +59,8 @@ OpenCV. - @subpage tutorial_feature_description + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán @@ -55,6 +69,8 @@ OpenCV. - @subpage tutorial_feature_flann_matcher + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán @@ -63,6 +79,8 @@ OpenCV. - @subpage tutorial_feature_homography + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán diff --git a/doc/tutorials/features2d/trackingmotion/corner_subpixeles/corner_subpixeles.markdown b/doc/tutorials/features2d/trackingmotion/corner_subpixeles/corner_subpixeles.markdown deleted file mode 100644 index 946fd77b29..0000000000 --- a/doc/tutorials/features2d/trackingmotion/corner_subpixeles/corner_subpixeles.markdown +++ /dev/null @@ -1,32 +0,0 @@ -Detecting corners location in subpixeles {#tutorial_corner_subpixeles} -======================================== - -Goal ----- - -In this tutorial you will learn how to: - -- Use the OpenCV function @ref cv::cornerSubPix to find more exact corner positions (more exact - than integer pixels). - -Theory ------- - -Code ----- - -This tutorial code's is shown lines below. You can also download it from -[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp) -@include samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp - -Explanation ------------ - -Result ------- - -![](images/Corner_Subpixeles_Original_Image.jpg) - -Here is the result: - -![](images/Corner_Subpixeles_Result.jpg) diff --git a/doc/tutorials/features2d/trackingmotion/corner_subpixels/corner_subpixels.markdown b/doc/tutorials/features2d/trackingmotion/corner_subpixels/corner_subpixels.markdown new file mode 100644 index 0000000000..82b33dd256 --- /dev/null +++ b/doc/tutorials/features2d/trackingmotion/corner_subpixels/corner_subpixels.markdown @@ -0,0 +1,46 @@ +Detecting corners location in subpixels {#tutorial_corner_subpixels} +======================================= + +Goal +---- + +In this tutorial you will learn how to: + +- Use the OpenCV function @ref cv::cornerSubPix to find more exact corner positions (more exact + than integer pixels). + +Theory +------ + +Code +---- + +@add_toggle_cpp +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp) +@include samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp +@end_toggle + +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/TrackingMotion/corner_subpixels/CornerSubPixDemo.java) +@include samples/java/tutorial_code/TrackingMotion/corner_subpixels/CornerSubPixDemo.java +@end_toggle + +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py) +@include samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py +@end_toggle + +Explanation +----------- + +Result +------ + +![](images/Corner_Subpixels_Original_Image.jpg) + +Here is the result: + +![](images/Corner_Subpixels_Result.jpg) diff --git a/doc/tutorials/features2d/trackingmotion/corner_subpixeles/images/Corner_Subpixeles_Original_Image.jpg b/doc/tutorials/features2d/trackingmotion/corner_subpixels/images/Corner_Subpixels_Original_Image.jpg similarity index 100% rename from doc/tutorials/features2d/trackingmotion/corner_subpixeles/images/Corner_Subpixeles_Original_Image.jpg rename to doc/tutorials/features2d/trackingmotion/corner_subpixels/images/Corner_Subpixels_Original_Image.jpg diff --git a/doc/tutorials/features2d/trackingmotion/corner_subpixeles/images/Corner_Subpixeles_Result.jpg b/doc/tutorials/features2d/trackingmotion/corner_subpixels/images/Corner_Subpixels_Result.jpg similarity index 100% rename from doc/tutorials/features2d/trackingmotion/corner_subpixeles/images/Corner_Subpixeles_Result.jpg rename to doc/tutorials/features2d/trackingmotion/corner_subpixels/images/Corner_Subpixels_Result.jpg diff --git a/doc/tutorials/features2d/trackingmotion/generic_corner_detector/generic_corner_detector.markdown b/doc/tutorials/features2d/trackingmotion/generic_corner_detector/generic_corner_detector.markdown index 7aba636746..f10d3efe4e 100644 --- a/doc/tutorials/features2d/trackingmotion/generic_corner_detector/generic_corner_detector.markdown +++ b/doc/tutorials/features2d/trackingmotion/generic_corner_detector/generic_corner_detector.markdown @@ -1,5 +1,5 @@ -Creating yor own corner detector {#tutorial_generic_corner_detector} -================================ +Creating your own corner detector {#tutorial_generic_corner_detector} +================================= Goal ---- @@ -10,7 +10,7 @@ In this tutorial you will learn how to: to determine if a pixel is a corner. - Use the OpenCV function @ref cv::cornerMinEigenVal to find the minimum eigenvalues for corner detection. -- To implement our own version of the Harris detector as well as the Shi-Tomasi detector, by using +- Implement our own version of the Harris detector as well as the Shi-Tomasi detector, by using the two functions above. Theory @@ -19,10 +19,26 @@ Theory Code ---- +@add_toggle_cpp This tutorial code's is shown lines below. You can also download it from [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp) -@include cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp +@include samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp +@end_toggle + +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/TrackingMotion/generic_corner_detector/CornerDetectorDemo.java) + +@include samples/java/tutorial_code/TrackingMotion/generic_corner_detector/CornerDetectorDemo.java +@end_toggle + +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/TrackingMotion/generic_corner_detector/cornerDetector_Demo.py) + +@include samples/python/tutorial_code/TrackingMotion/generic_corner_detector/cornerDetector_Demo.py +@end_toggle Explanation ----------- diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.markdown b/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.markdown index 7c48aa189a..70d25ab9e2 100644 --- a/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.markdown +++ b/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.markdown @@ -6,7 +6,7 @@ Goal In this tutorial you will learn how to: -- Use the function @ref cv::goodFeaturesToTrack to detect corners using the Shi-Tomasi method. +- Use the function @ref cv::goodFeaturesToTrack to detect corners using the Shi-Tomasi method (@cite Shi94). Theory ------ @@ -14,9 +14,23 @@ Theory Code ---- +@add_toggle_cpp This tutorial code's is shown lines below. You can also download it from [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/TrackingMotion/goodFeaturesToTrack_Demo.cpp) @include samples/cpp/tutorial_code/TrackingMotion/goodFeaturesToTrack_Demo.cpp +@end_toggle + +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/TrackingMotion/good_features_to_track/GoodFeaturesToTrackDemo.java) +@include samples/java/tutorial_code/TrackingMotion/good_features_to_track/GoodFeaturesToTrackDemo.java +@end_toggle + +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py) +@include samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py +@end_toggle Explanation ----------- @@ -24,4 +38,4 @@ Explanation Result ------ -![](images/Feature_Detection_Result_a.jpg) +![](images/good_features_to_track_Shi_Tomasi.jpg) diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_a.jpg b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_a.jpg deleted file mode 100644 index cca9a2b438..0000000000 Binary files a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_a.jpg and /dev/null differ diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_b.jpg b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_b.jpg deleted file mode 100644 index 129450eb4f..0000000000 Binary files a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_b.jpg and /dev/null differ diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/good_features_to_track_Shi_Tomasi.jpg b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/good_features_to_track_Shi_Tomasi.jpg new file mode 100644 index 0000000000..007f09a203 Binary files /dev/null and b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/good_features_to_track_Shi_Tomasi.jpg differ diff --git a/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.markdown b/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.markdown index b1b8b67f1b..bbf4fdbd5b 100644 --- a/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.markdown +++ b/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.markdown @@ -118,9 +118,23 @@ In this tutorial we will study the *corner* features, specifically. Code ---- +@add_toggle_cpp This tutorial code's is shown lines below. You can also download it from [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp) @include samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp +@end_toggle + +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/TrackingMotion/harris_detector/CornerHarrisDemo.java) +@include samples/java/tutorial_code/TrackingMotion/harris_detector/CornerHarrisDemo.java +@end_toggle + +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/TrackingMotion/harris_detector/cornerHarris_Demo.py) +@include samples/python/tutorial_code/TrackingMotion/harris_detector/cornerHarris_Demo.py +@end_toggle Explanation ----------- diff --git a/samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp b/samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp index 2b8471d35b..894b01ce56 100644 --- a/samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp +++ b/samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp @@ -13,15 +13,15 @@ using namespace std; /// Global variables Mat src, src_gray; -Mat myHarris_dst; Mat myHarris_copy; Mat Mc; -Mat myShiTomasi_dst; Mat myShiTomasi_copy; +Mat myHarris_dst, myHarris_copy, Mc; +Mat myShiTomasi_dst, myShiTomasi_copy; int myShiTomasi_qualityLevel = 50; int myHarris_qualityLevel = 50; int max_qualityLevel = 100; -double myHarris_minVal; double myHarris_maxVal; -double myShiTomasi_minVal; double myShiTomasi_maxVal; +double myHarris_minVal, myHarris_maxVal; +double myShiTomasi_minVal, myShiTomasi_maxVal; RNG rng(12345); @@ -37,56 +37,54 @@ void myHarris_function( int, void* ); */ int main( int argc, char** argv ) { - /// Load source image and convert it to gray - CommandLineParser parser( argc, argv, "{@input | ../data/stuff.jpg | input image}" ); - src = imread( parser.get( "@input" ), IMREAD_COLOR ); - if ( src.empty() ) - { - cout << "Could not open or find the image!\n" << endl; - cout << "Usage: " << argv[0] << " " << endl; - return -1; - } - cvtColor( src, src_gray, COLOR_BGR2GRAY ); + /// Load source image and convert it to gray + CommandLineParser parser( argc, argv, "{@input | ../data/building.jpg | input image}" ); + src = imread( parser.get( "@input" ) ); + if ( src.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + cout << "Usage: " << argv[0] << " " << endl; + return -1; + } + cvtColor( src, src_gray, COLOR_BGR2GRAY ); - /// Set some parameters - int blockSize = 3; int apertureSize = 3; + /// Set some parameters + int blockSize = 3, apertureSize = 3; - /// My Harris matrix -- Using cornerEigenValsAndVecs - myHarris_dst = Mat::zeros( src_gray.size(), CV_32FC(6) ); - Mc = Mat::zeros( src_gray.size(), CV_32FC1 ); + /// My Harris matrix -- Using cornerEigenValsAndVecs + cornerEigenValsAndVecs( src_gray, myHarris_dst, blockSize, apertureSize ); - cornerEigenValsAndVecs( src_gray, myHarris_dst, blockSize, apertureSize, BORDER_DEFAULT ); + /* calculate Mc */ + Mc = Mat( src_gray.size(), CV_32FC1 ); + for( int i = 0; i < src_gray.rows; i++ ) + { + for( int j = 0; j < src_gray.cols; j++ ) + { + float lambda_1 = myHarris_dst.at(i, j)[0]; + float lambda_2 = myHarris_dst.at(i, j)[1]; + Mc.at(i, j) = lambda_1*lambda_2 - 0.04f*pow( ( lambda_1 + lambda_2 ), 2 ); + } + } - /* calculate Mc */ - for( int j = 0; j < src_gray.rows; j++ ) - { for( int i = 0; i < src_gray.cols; i++ ) - { - float lambda_1 = myHarris_dst.at(j, i)[0]; - float lambda_2 = myHarris_dst.at(j, i)[1]; - Mc.at(j,i) = lambda_1*lambda_2 - 0.04f*pow( ( lambda_1 + lambda_2 ), 2 ); - } - } + minMaxLoc( Mc, &myHarris_minVal, &myHarris_maxVal ); - minMaxLoc( Mc, &myHarris_minVal, &myHarris_maxVal, 0, 0, Mat() ); + /* Create Window and Trackbar */ + namedWindow( myHarris_window ); + createTrackbar( "Quality Level:", myHarris_window, &myHarris_qualityLevel, max_qualityLevel, myHarris_function ); + myHarris_function( 0, 0 ); - /* Create Window and Trackbar */ - namedWindow( myHarris_window, WINDOW_AUTOSIZE ); - createTrackbar( " Quality Level:", myHarris_window, &myHarris_qualityLevel, max_qualityLevel, myHarris_function ); - myHarris_function( 0, 0 ); + /// My Shi-Tomasi -- Using cornerMinEigenVal + cornerMinEigenVal( src_gray, myShiTomasi_dst, blockSize, apertureSize ); - /// My Shi-Tomasi -- Using cornerMinEigenVal - myShiTomasi_dst = Mat::zeros( src_gray.size(), CV_32FC1 ); - cornerMinEigenVal( src_gray, myShiTomasi_dst, blockSize, apertureSize, BORDER_DEFAULT ); + minMaxLoc( myShiTomasi_dst, &myShiTomasi_minVal, &myShiTomasi_maxVal ); - minMaxLoc( myShiTomasi_dst, &myShiTomasi_minVal, &myShiTomasi_maxVal, 0, 0, Mat() ); + /* Create Window and Trackbar */ + namedWindow( myShiTomasi_window ); + createTrackbar( "Quality Level:", myShiTomasi_window, &myShiTomasi_qualityLevel, max_qualityLevel, myShiTomasi_function ); + myShiTomasi_function( 0, 0 ); - /* Create Window and Trackbar */ - namedWindow( myShiTomasi_window, WINDOW_AUTOSIZE ); - createTrackbar( " Quality Level:", myShiTomasi_window, &myShiTomasi_qualityLevel, max_qualityLevel, myShiTomasi_function ); - myShiTomasi_function( 0, 0 ); - - waitKey(0); - return(0); + waitKey(); + return 0; } /** @@ -94,18 +92,20 @@ int main( int argc, char** argv ) */ void myShiTomasi_function( int, void* ) { - myShiTomasi_copy = src.clone(); + myShiTomasi_copy = src.clone(); + myShiTomasi_qualityLevel = MAX(myShiTomasi_qualityLevel, 1); - if( myShiTomasi_qualityLevel < 1 ) { myShiTomasi_qualityLevel = 1; } - - for( int j = 0; j < src_gray.rows; j++ ) - { for( int i = 0; i < src_gray.cols; i++ ) - { - if( myShiTomasi_dst.at(j,i) > myShiTomasi_minVal + ( myShiTomasi_maxVal - myShiTomasi_minVal )*myShiTomasi_qualityLevel/max_qualityLevel ) - { circle( myShiTomasi_copy, Point(i,j), 4, Scalar( rng.uniform(0,255), rng.uniform(0,255), rng.uniform(0,255) ), -1, 8, 0 ); } - } - } - imshow( myShiTomasi_window, myShiTomasi_copy ); + for( int i = 0; i < src_gray.rows; i++ ) + { + for( int j = 0; j < src_gray.cols; j++ ) + { + if( myShiTomasi_dst.at(i,j) > myShiTomasi_minVal + ( myShiTomasi_maxVal - myShiTomasi_minVal )*myShiTomasi_qualityLevel/max_qualityLevel ) + { + circle( myShiTomasi_copy, Point(j,i), 4, Scalar( rng.uniform(0,256), rng.uniform(0,256), rng.uniform(0,256) ), FILLED ); + } + } + } + imshow( myShiTomasi_window, myShiTomasi_copy ); } /** @@ -113,16 +113,18 @@ void myShiTomasi_function( int, void* ) */ void myHarris_function( int, void* ) { - myHarris_copy = src.clone(); + myHarris_copy = src.clone(); + myHarris_qualityLevel = MAX(myHarris_qualityLevel, 1); - if( myHarris_qualityLevel < 1 ) { myHarris_qualityLevel = 1; } - - for( int j = 0; j < src_gray.rows; j++ ) - { for( int i = 0; i < src_gray.cols; i++ ) - { - if( Mc.at(j,i) > myHarris_minVal + ( myHarris_maxVal - myHarris_minVal )*myHarris_qualityLevel/max_qualityLevel ) - { circle( myHarris_copy, Point(i,j), 4, Scalar( rng.uniform(0,255), rng.uniform(0,255), rng.uniform(0,255) ), -1, 8, 0 ); } - } - } - imshow( myHarris_window, myHarris_copy ); + for( int i = 0; i < src_gray.rows; i++ ) + { + for( int j = 0; j < src_gray.cols; j++ ) + { + if( Mc.at(i,j) > myHarris_minVal + ( myHarris_maxVal - myHarris_minVal )*myHarris_qualityLevel/max_qualityLevel ) + { + circle( myHarris_copy, Point(j,i), 4, Scalar( rng.uniform(0,256), rng.uniform(0,256), rng.uniform(0,256) ), FILLED ); + } + } + } + imshow( myHarris_window, myHarris_copy ); } diff --git a/samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp b/samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp index 2d44eeb4f6..35672706bc 100644 --- a/samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp +++ b/samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp @@ -27,26 +27,26 @@ void cornerHarris_demo( int, void* ); */ int main( int argc, char** argv ) { - /// Load source image and convert it to gray - CommandLineParser parser( argc, argv, "{@input | ../data/building.jpg | input image}" ); - src = imread( parser.get( "@input" ), IMREAD_COLOR ); - if ( src.empty() ) - { - cout << "Could not open or find the image!\n" << endl; - cout << "Usage: " << argv[0] << " " << endl; - return -1; - } - cvtColor( src, src_gray, COLOR_BGR2GRAY ); + /// Load source image and convert it to gray + CommandLineParser parser( argc, argv, "{@input | ../data/building.jpg | input image}" ); + src = imread( parser.get( "@input" ) ); + if ( src.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + cout << "Usage: " << argv[0] << " " << endl; + return -1; + } + cvtColor( src, src_gray, COLOR_BGR2GRAY ); - /// Create a window and a trackbar - namedWindow( source_window, WINDOW_AUTOSIZE ); - createTrackbar( "Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo ); - imshow( source_window, src ); + /// Create a window and a trackbar + namedWindow( source_window ); + createTrackbar( "Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo ); + imshow( source_window, src ); - cornerHarris_demo( 0, 0 ); + cornerHarris_demo( 0, 0 ); - waitKey(0); - return(0); + waitKey(); + return 0; } /** @@ -55,33 +55,33 @@ int main( int argc, char** argv ) */ void cornerHarris_demo( int, void* ) { + /// Detector parameters + int blockSize = 2; + int apertureSize = 3; + double k = 0.04; - Mat dst, dst_norm, dst_norm_scaled; - dst = Mat::zeros( src.size(), CV_32FC1 ); + /// Detecting corners + Mat dst = Mat::zeros( src.size(), CV_32FC1 ); + cornerHarris( src_gray, dst, blockSize, apertureSize, k ); - /// Detector parameters - int blockSize = 2; - int apertureSize = 3; - double k = 0.04; + /// Normalizing + Mat dst_norm, dst_norm_scaled; + normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() ); + convertScaleAbs( dst_norm, dst_norm_scaled ); - /// Detecting corners - cornerHarris( src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT ); + /// Drawing a circle around corners + for( int i = 0; i < dst_norm.rows ; i++ ) + { + for( int j = 0; j < dst_norm.cols; j++ ) + { + if( (int) dst_norm.at(i,j) > thresh ) + { + circle( dst_norm_scaled, Point(j,i), 5, Scalar(0), 2, 8, 0 ); + } + } + } - /// Normalizing - normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() ); - convertScaleAbs( dst_norm, dst_norm_scaled ); - - /// Drawing a circle around corners - for( int j = 0; j < dst_norm.rows ; j++ ) - { for( int i = 0; i < dst_norm.cols; i++ ) - { - if( (int) dst_norm.at(j,i) > thresh ) - { - circle( dst_norm_scaled, Point( i, j ), 5, Scalar(0), 2, 8, 0 ); - } - } - } - /// Showing the result - namedWindow( corners_window, WINDOW_AUTOSIZE ); - imshow( corners_window, dst_norm_scaled ); + /// Showing the result + namedWindow( corners_window ); + imshow( corners_window, dst_norm_scaled ); } diff --git a/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp b/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp index 0addc5503e..853078bada 100644 --- a/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp +++ b/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp @@ -28,29 +28,29 @@ void goodFeaturesToTrack_Demo( int, void* ); */ int main( int argc, char** argv ) { - /// Load source image and convert it to gray - CommandLineParser parser( argc, argv, "{@input | ../data/pic3.png | input image}" ); - src = imread(parser.get( "@input" ), IMREAD_COLOR); - if ( src.empty() ) - { - cout << "Could not open or find the image!\n" << endl; - cout << "Usage: " << argv[0] << " " << endl; - return -1; - } - cvtColor( src, src_gray, COLOR_BGR2GRAY ); + /// Load source image and convert it to gray + CommandLineParser parser( argc, argv, "{@input | ../data/pic3.png | input image}" ); + src = imread( parser.get( "@input" ) ); + if( src.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + cout << "Usage: " << argv[0] << " " << endl; + return -1; + } + cvtColor( src, src_gray, COLOR_BGR2GRAY ); - /// Create Window - namedWindow( source_window, WINDOW_AUTOSIZE ); + /// Create Window + namedWindow( source_window ); - /// Create Trackbar to set the number of corners - createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, goodFeaturesToTrack_Demo ); + /// Create Trackbar to set the number of corners + createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, goodFeaturesToTrack_Demo ); - imshow( source_window, src ); + imshow( source_window, src ); - goodFeaturesToTrack_Demo( 0, 0 ); + goodFeaturesToTrack_Demo( 0, 0 ); - waitKey(0); - return(0); + waitKey(); + return 0; } /** @@ -59,52 +59,54 @@ int main( int argc, char** argv ) */ void goodFeaturesToTrack_Demo( int, void* ) { - if( maxCorners < 1 ) { maxCorners = 1; } + /// Parameters for Shi-Tomasi algorithm + maxCorners = MAX(maxCorners, 1); + vector corners; + double qualityLevel = 0.01; + double minDistance = 10; + int blockSize = 3, gradientSize = 3; + bool useHarrisDetector = false; + double k = 0.04; - /// Parameters for Shi-Tomasi algorithm - vector corners; - double qualityLevel = 0.01; - double minDistance = 10; - int blockSize = 3, gradiantSize = 3; - bool useHarrisDetector = false; - double k = 0.04; + /// Copy the source image + Mat copy = src.clone(); - /// Copy the source image - Mat copy; - copy = src.clone(); - - /// Apply corner detection - goodFeaturesToTrack( src_gray, - corners, - maxCorners, - qualityLevel, - minDistance, - Mat(), - blockSize, - gradiantSize, - useHarrisDetector, - k ); + /// Apply corner detection + goodFeaturesToTrack( src_gray, + corners, + maxCorners, + qualityLevel, + minDistance, + Mat(), + blockSize, + gradientSize, + useHarrisDetector, + k ); - /// Draw corners detected - cout<<"** Number of corners detected: "<( "@input" ), IMREAD_COLOR ); - if( src.empty() ) - { - cout << "Could not open or find the image!\n" << endl; - cout << "Usage: " << argv[0] << " " << endl; - return -1; - } - cvtColor( src, src_gray, COLOR_BGR2GRAY ); + /// Load source image and convert it to gray + CommandLineParser parser( argc, argv, "{@input | ../data/pic3.png | input image}" ); + src = imread( parser.get( "@input" ) ); + if( src.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + cout << "Usage: " << argv[0] << " " << endl; + return -1; + } + cvtColor( src, src_gray, COLOR_BGR2GRAY ); - /// Create Window - namedWindow( source_window, WINDOW_AUTOSIZE ); + /// Create Window + namedWindow( source_window ); - /// Create Trackbar to set the number of corners - createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, goodFeaturesToTrack_Demo ); + /// Create Trackbar to set the number of corners + createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, goodFeaturesToTrack_Demo ); - imshow( source_window, src ); + imshow( source_window, src ); - goodFeaturesToTrack_Demo( 0, 0 ); + goodFeaturesToTrack_Demo( 0, 0 ); - waitKey(0); - return(0); + waitKey(); + return 0; } /** @@ -60,40 +60,40 @@ int main( int argc, char** argv ) */ void goodFeaturesToTrack_Demo( int, void* ) { - if( maxCorners < 1 ) { maxCorners = 1; } + /// Parameters for Shi-Tomasi algorithm + maxCorners = MAX(maxCorners, 1); + vector corners; + double qualityLevel = 0.01; + double minDistance = 10; + int blockSize = 3, gradientSize = 3; + bool useHarrisDetector = false; + double k = 0.04; - /// Parameters for Shi-Tomasi algorithm - vector corners; - double qualityLevel = 0.01; - double minDistance = 10; - int blockSize = 3, gradiantSize = 3; - bool useHarrisDetector = false; - double k = 0.04; + /// Copy the source image + Mat copy = src.clone(); - /// Copy the source image - Mat copy; - copy = src.clone(); - - /// Apply corner detection - goodFeaturesToTrack( src_gray, - corners, - maxCorners, - qualityLevel, - minDistance, - Mat(), - blockSize, - gradiantSize, - useHarrisDetector, - k ); + /// Apply corner detection + goodFeaturesToTrack( src_gray, + corners, + maxCorners, + qualityLevel, + minDistance, + Mat(), + blockSize, + gradientSize, + useHarrisDetector, + k ); - /// Draw corners detected - cout<<"** Number of corners detected: "< +#include "opencv2/core.hpp" +#ifdef HAVE_OPENCV_XFEATURES2D +#include "opencv2/highgui.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/xfeatures2d.hpp" + +using namespace cv; +using namespace cv::xfeatures2d; +using std::cout; +using std::endl; + +const char* keys = + "{ help h | | Print help message. }" + "{ input1 | ../data/box.png | Path to input image 1. }" + "{ input2 | ../data/box_in_scene.png | Path to input image 2. }"; + +int main( int argc, char* argv[] ) +{ + CommandLineParser parser( argc, argv, keys ); + Mat img1 = imread( parser.get("input1"), IMREAD_GRAYSCALE ); + Mat img2 = imread( parser.get("input2"), IMREAD_GRAYSCALE ); + if ( img1.empty() || img2.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + parser.printMessage(); + return -1; + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + int minHessian = 400; + Ptr detector = SURF::create( minHessian ); + std::vector keypoints1, keypoints2; + Mat descriptors1, descriptors2; + detector->detectAndCompute( img1, noArray(), keypoints1, descriptors1 ); + detector->detectAndCompute( img2, noArray(), keypoints2, descriptors2 ); + + //-- Step 2: Matching descriptor vectors with a brute force matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + Ptr matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE); + std::vector< DMatch > matches; + matcher->match( descriptors1, descriptors2, matches ); + + //-- Draw matches + Mat img_matches; + drawMatches( img1, keypoints1, img2, keypoints2, matches, img_matches ); + + //-- Show detected matches + imshow("Matches", img_matches ); + + waitKey(); + return 0; +} +#else +int main() +{ + std::cout << "This tutorial code needs the xfeatures2d contrib module to be run." << std::endl; + return 0; +} +#endif diff --git a/samples/cpp/tutorial_code/features2D/feature_detection/SURF_detection_Demo.cpp b/samples/cpp/tutorial_code/features2D/feature_detection/SURF_detection_Demo.cpp new file mode 100755 index 0000000000..ba9494ed2c --- /dev/null +++ b/samples/cpp/tutorial_code/features2D/feature_detection/SURF_detection_Demo.cpp @@ -0,0 +1,46 @@ +#include +#include "opencv2/core.hpp" +#ifdef HAVE_OPENCV_XFEATURES2D +#include "opencv2/highgui.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/xfeatures2d.hpp" + +using namespace cv; +using namespace cv::xfeatures2d; +using std::cout; +using std::endl; + +int main( int argc, char* argv[] ) +{ + CommandLineParser parser( argc, argv, "{@input | ../data/box.png | input image}" ); + Mat src = imread( parser.get( "@input" ), IMREAD_GRAYSCALE ); + if ( src.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + cout << "Usage: " << argv[0] << " " << endl; + return -1; + } + + //-- Step 1: Detect the keypoints using SURF Detector + int minHessian = 400; + Ptr detector = SURF::create( minHessian ); + std::vector keypoints; + detector->detect( src, keypoints ); + + //-- Draw keypoints + Mat img_keypoints; + drawKeypoints( src, keypoints, img_keypoints ); + + //-- Show detected (drawn) keypoints + imshow("SURF Keypoints", img_keypoints ); + + waitKey(); + return 0; +} +#else +int main() +{ + std::cout << "This tutorial code needs the xfeatures2d contrib module to be run." << std::endl; + return 0; +} +#endif diff --git a/samples/cpp/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.cpp b/samples/cpp/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.cpp new file mode 100755 index 0000000000..e22155f471 --- /dev/null +++ b/samples/cpp/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.cpp @@ -0,0 +1,72 @@ +#include +#include "opencv2/core.hpp" +#ifdef HAVE_OPENCV_XFEATURES2D +#include "opencv2/highgui.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/xfeatures2d.hpp" + +using namespace cv; +using namespace cv::xfeatures2d; +using std::cout; +using std::endl; + +const char* keys = + "{ help h | | Print help message. }" + "{ input1 | ../data/box.png | Path to input image 1. }" + "{ input2 | ../data/box_in_scene.png | Path to input image 2. }"; + +int main( int argc, char* argv[] ) +{ + CommandLineParser parser( argc, argv, keys ); + Mat img1 = imread( parser.get("input1"), IMREAD_GRAYSCALE ); + Mat img2 = imread( parser.get("input2"), IMREAD_GRAYSCALE ); + if ( img1.empty() || img2.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + parser.printMessage(); + return -1; + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + int minHessian = 400; + Ptr detector = SURF::create( minHessian ); + std::vector keypoints1, keypoints2; + Mat descriptors1, descriptors2; + detector->detectAndCompute( img1, noArray(), keypoints1, descriptors1 ); + detector->detectAndCompute( img2, noArray(), keypoints2, descriptors2 ); + + //-- Step 2: Matching descriptor vectors with a FLANN based matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + Ptr matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED); + std::vector< std::vector > knn_matches; + matcher->knnMatch( descriptors1, descriptors2, knn_matches, 2 ); + + //-- Filter matches using the Lowe's ratio test + const float ratio_thresh = 0.7f; + std::vector good_matches; + for (size_t i = 0; i < knn_matches.size(); i++) + { + if (knn_matches[i].size() > 1 && knn_matches[i][0].distance / knn_matches[i][1].distance <= ratio_thresh) + { + good_matches.push_back(knn_matches[i][0]); + } + } + + //-- Draw matches + Mat img_matches; + drawMatches( img1, keypoints1, img2, keypoints2, good_matches, img_matches, Scalar::all(-1), + Scalar::all(-1), std::vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); + + //-- Show detected matches + imshow("Good Matches", img_matches ); + + waitKey(); + return 0; +} +#else +int main() +{ + std::cout << "This tutorial code needs the xfeatures2d contrib module to be run." << std::endl; + return 0; +} +#endif diff --git a/samples/cpp/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.cpp b/samples/cpp/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.cpp new file mode 100755 index 0000000000..68b1d2a720 --- /dev/null +++ b/samples/cpp/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.cpp @@ -0,0 +1,107 @@ +#include +#include "opencv2/core.hpp" +#ifdef HAVE_OPENCV_XFEATURES2D +#include "opencv2/calib3d.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/xfeatures2d.hpp" + +using namespace cv; +using namespace cv::xfeatures2d; +using std::cout; +using std::endl; + +const char* keys = + "{ help h | | Print help message. }" + "{ input1 | ../data/box.png | Path to input image 1. }" + "{ input2 | ../data/box_in_scene.png | Path to input image 2. }"; + +int main( int argc, char* argv[] ) +{ + CommandLineParser parser( argc, argv, keys ); + Mat img_object = imread( parser.get("input1"), IMREAD_GRAYSCALE ); + Mat img_scene = imread( parser.get("input2"), IMREAD_GRAYSCALE ); + if ( img_object.empty() || img_scene.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + parser.printMessage(); + return -1; + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + int minHessian = 400; + Ptr detector = SURF::create( minHessian ); + std::vector keypoints_object, keypoints_scene; + Mat descriptors_object, descriptors_scene; + detector->detectAndCompute( img_object, noArray(), keypoints_object, descriptors_object ); + detector->detectAndCompute( img_scene, noArray(), keypoints_scene, descriptors_scene ); + + //-- Step 2: Matching descriptor vectors with a FLANN based matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + Ptr matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED); + std::vector< std::vector > knn_matches; + matcher->knnMatch( descriptors_object, descriptors_scene, knn_matches, 2 ); + + //-- Filter matches using the Lowe's ratio test + const float ratio_thresh = 0.75f; + std::vector good_matches; + for (size_t i = 0; i < knn_matches.size(); i++) + { + if (knn_matches[i].size() > 1 && knn_matches[i][0].distance / knn_matches[i][1].distance <= ratio_thresh) + { + good_matches.push_back(knn_matches[i][0]); + } + } + + //-- Draw matches + Mat img_matches; + drawMatches( img_object, keypoints_object, img_scene, keypoints_scene, good_matches, img_matches, Scalar::all(-1), + Scalar::all(-1), std::vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); + + //-- Localize the object + std::vector obj; + std::vector scene; + + for( size_t i = 0; i < good_matches.size(); i++ ) + { + //-- Get the keypoints from the good matches + obj.push_back( keypoints_object[ good_matches[i].queryIdx ].pt ); + scene.push_back( keypoints_scene[ good_matches[i].trainIdx ].pt ); + } + + Mat H = findHomography( obj, scene, RANSAC ); + + //-- Get the corners from the image_1 ( the object to be "detected" ) + std::vector obj_corners(4); + obj_corners[0] = Point2f(0, 0); + obj_corners[1] = Point2f( (float)img_object.cols, 0 ); + obj_corners[2] = Point2f( (float)img_object.cols, (float)img_object.rows ); + obj_corners[3] = Point2f( 0, (float)img_object.rows ); + std::vector scene_corners(4); + + perspectiveTransform( obj_corners, scene_corners, H); + + //-- Draw lines between the corners (the mapped object in the scene - image_2 ) + line( img_matches, scene_corners[0] + Point2f((float)img_object.cols, 0), + scene_corners[1] + Point2f((float)img_object.cols, 0), Scalar(0, 255, 0), 4 ); + line( img_matches, scene_corners[1] + Point2f((float)img_object.cols, 0), + scene_corners[2] + Point2f((float)img_object.cols, 0), Scalar( 0, 255, 0), 4 ); + line( img_matches, scene_corners[2] + Point2f((float)img_object.cols, 0), + scene_corners[3] + Point2f((float)img_object.cols, 0), Scalar( 0, 255, 0), 4 ); + line( img_matches, scene_corners[3] + Point2f((float)img_object.cols, 0), + scene_corners[0] + Point2f((float)img_object.cols, 0), Scalar( 0, 255, 0), 4 ); + + //-- Show detected matches + imshow("Good Matches & Object detection", img_matches ); + + waitKey(); + return 0; +} +#else +int main() +{ + std::cout << "This tutorial code needs the xfeatures2d contrib module to be run." << std::endl; + return 0; +} +#endif diff --git a/samples/java/tutorial_code/TrackingMotion/corner_subpixels/CornerSubPixDemo.java b/samples/java/tutorial_code/TrackingMotion/corner_subpixels/CornerSubPixDemo.java new file mode 100644 index 0000000000..3be2e58056 --- /dev/null +++ b/samples/java/tutorial_code/TrackingMotion/corner_subpixels/CornerSubPixDemo.java @@ -0,0 +1,158 @@ +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Image; +import java.util.Random; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.MatOfPoint; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.core.Size; +import org.opencv.core.TermCriteria; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +class CornerSubPix { + private Mat src = new Mat(); + private Mat srcGray = new Mat(); + private JFrame frame; + private JLabel imgLabel; + private static final int MAX_CORNERS = 25; + private int maxCorners = 10; + private Random rng = new Random(12345); + + public CornerSubPix(String[] args) { + /// Load source image and convert it to gray + String filename = args.length > 0 ? args[0] : "../data/pic3.png"; + src = Imgcodecs.imread(filename); + if (src.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + + Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY); + + // Create and set up the window. + frame = new JFrame("Shi-Tomasi corner detector demo"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // Set up the content pane. + Image img = HighGui.toBufferedImage(src); + addComponentsToPane(frame.getContentPane(), img); + // Use the content pane's default BorderLayout. No need for + // setLayout(new BorderLayout()); + // Display the window. + frame.pack(); + frame.setVisible(true); + update(); + } + + private void addComponentsToPane(Container pane, Image img) { + if (!(pane.getLayout() instanceof BorderLayout)) { + pane.add(new JLabel("Container doesn't use BorderLayout!")); + return; + } + + JPanel sliderPanel = new JPanel(); + sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); + + sliderPanel.add(new JLabel("Max corners:")); + JSlider slider = new JSlider(0, MAX_CORNERS, maxCorners); + slider.setMajorTickSpacing(20); + slider.setMinorTickSpacing(10); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + maxCorners = source.getValue(); + update(); + } + }); + sliderPanel.add(slider); + pane.add(sliderPanel, BorderLayout.PAGE_START); + + imgLabel = new JLabel(new ImageIcon(img)); + pane.add(imgLabel, BorderLayout.CENTER); + } + + private void update() { + /// Parameters for Shi-Tomasi algorithm + maxCorners = Math.max(maxCorners, 1); + MatOfPoint corners = new MatOfPoint(); + double qualityLevel = 0.01; + double minDistance = 10; + int blockSize = 3, gradientSize = 3; + boolean useHarrisDetector = false; + double k = 0.04; + + /// Copy the source image + Mat copy = src.clone(); + + /// Apply corner detection + Imgproc.goodFeaturesToTrack(srcGray, corners, maxCorners, qualityLevel, minDistance, new Mat(), + blockSize, gradientSize, useHarrisDetector, k); + + /// Draw corners detected + System.out.println("** Number of corners detected: " + corners.rows()); + int[] cornersData = new int[(int) (corners.total() * corners.channels())]; + corners.get(0, 0, cornersData); + int radius = 4; + Mat matCorners = new Mat(corners.rows(), 2, CvType.CV_32F); + float[] matCornersData = new float[(int) (matCorners.total() * matCorners.channels())]; + matCorners.get(0, 0, matCornersData); + for (int i = 0; i < corners.rows(); i++) { + Imgproc.circle(copy, new Point(cornersData[i * 2], cornersData[i * 2 + 1]), radius, + new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)), Core.FILLED); + matCornersData[i * 2] = cornersData[i * 2]; + matCornersData[i * 2 + 1] = cornersData[i * 2 + 1]; + } + matCorners.put(0, 0, matCornersData); + + imgLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(copy))); + frame.repaint(); + + /// Set the needed parameters to find the refined corners + Size winSize = new Size(5, 5); + Size zeroZone = new Size(-1, -1); + TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.COUNT, 40, 0.001); + + /// Calculate the refined corner locations + Imgproc.cornerSubPix(srcGray, matCorners, winSize, zeroZone, criteria); + + /// Write them down + matCorners.get(0, 0, matCornersData); + for (int i = 0; i < corners.rows(); i++) { + System.out.println( + " -- Refined Corner [" + i + "] (" + matCornersData[i * 2] + "," + matCornersData[i * 2 + 1] + ")"); + } + } +} + +public class CornerSubPixDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new CornerSubPix(args); + } + }); + } +} diff --git a/samples/java/tutorial_code/TrackingMotion/generic_corner_detector/CornerDetectorDemo.java b/samples/java/tutorial_code/TrackingMotion/generic_corner_detector/CornerDetectorDemo.java new file mode 100644 index 0000000000..30450f8db1 --- /dev/null +++ b/samples/java/tutorial_code/TrackingMotion/generic_corner_detector/CornerDetectorDemo.java @@ -0,0 +1,190 @@ +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Image; +import java.util.Random; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.opencv.core.Core; +import org.opencv.core.Core.MinMaxLocResult; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +class CornerDetector { + private Mat src = new Mat(); + private Mat srcGray = new Mat(); + private Mat harrisDst = new Mat(); + private Mat shiTomasiDst = new Mat(); + private Mat harrisCopy = new Mat(); + private Mat shiTomasiCopy = new Mat(); + private Mat Mc = new Mat(); + private JFrame frame; + private JLabel harrisImgLabel; + private JLabel shiTomasiImgLabel; + private static final int MAX_QUALITY_LEVEL = 100; + private int qualityLevel = 50; + private double harrisMinVal; + private double harrisMaxVal; + private double shiTomasiMinVal; + private double shiTomasiMaxVal; + private Random rng = new Random(12345); + + public CornerDetector(String[] args) { + /// Load source image and convert it to gray + String filename = args.length > 0 ? args[0] : "../data/building.jpg"; + src = Imgcodecs.imread(filename); + if (src.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + + Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY); + + // Create and set up the window. + frame = new JFrame("Creating your own corner detector demo"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // Set up the content pane. + Image img = HighGui.toBufferedImage(src); + addComponentsToPane(frame.getContentPane(), img); + // Use the content pane's default BorderLayout. No need for + // setLayout(new BorderLayout()); + // Display the window. + frame.pack(); + frame.setVisible(true); + + /// Set some parameters + int blockSize = 3, apertureSize = 3; + + /// My Harris matrix -- Using cornerEigenValsAndVecs + Imgproc.cornerEigenValsAndVecs(srcGray, harrisDst, blockSize, apertureSize); + + /* calculate Mc */ + Mc = Mat.zeros(srcGray.size(), CvType.CV_32F); + + float[] harrisData = new float[(int) (harrisDst.total() * harrisDst.channels())]; + harrisDst.get(0, 0, harrisData); + float[] McData = new float[(int) (Mc.total() * Mc.channels())]; + Mc.get(0, 0, McData); + + for( int i = 0; i < srcGray.rows(); i++ ) { + for( int j = 0; j < srcGray.cols(); j++ ) { + float lambda1 = harrisData[(i*srcGray.cols() + j) * 6]; + float lambda2 = harrisData[(i*srcGray.cols() + j) * 6 + 1]; + McData[i*srcGray.cols()+j] = (float) (lambda1*lambda2 - 0.04f*Math.pow( ( lambda1 + lambda2 ), 2 )); + } + } + Mc.put(0, 0, McData); + + MinMaxLocResult res = Core.minMaxLoc(Mc); + harrisMinVal = res.minVal; + harrisMaxVal = res.maxVal; + + /// My Shi-Tomasi -- Using cornerMinEigenVal + Imgproc.cornerMinEigenVal(srcGray, shiTomasiDst, blockSize, apertureSize); + res = Core.minMaxLoc(shiTomasiDst); + shiTomasiMinVal = res.minVal; + shiTomasiMaxVal = res.maxVal; + + update(); + } + + private void addComponentsToPane(Container pane, Image img) { + if (!(pane.getLayout() instanceof BorderLayout)) { + pane.add(new JLabel("Container doesn't use BorderLayout!")); + return; + } + + JPanel sliderPanel = new JPanel(); + sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); + + sliderPanel.add(new JLabel("Max corners:")); + JSlider slider = new JSlider(0, MAX_QUALITY_LEVEL, qualityLevel); + slider.setMajorTickSpacing(20); + slider.setMinorTickSpacing(10); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + qualityLevel = source.getValue(); + update(); + } + }); + sliderPanel.add(slider); + pane.add(sliderPanel, BorderLayout.PAGE_START); + + JPanel imgPanel = new JPanel(); + harrisImgLabel = new JLabel(new ImageIcon(img)); + shiTomasiImgLabel = new JLabel(new ImageIcon(img)); + imgPanel.add(harrisImgLabel); + imgPanel.add(shiTomasiImgLabel); + pane.add(imgPanel, BorderLayout.CENTER); + } + + private void update() { + int qualityLevelVal = Math.max(qualityLevel, 1); + + //Harris + harrisCopy = src.clone(); + + float[] McData = new float[(int) (Mc.total() * Mc.channels())]; + Mc.get(0, 0, McData); + for (int i = 0; i < srcGray.rows(); i++) { + for (int j = 0; j < srcGray.cols(); j++) { + if (McData[i * srcGray.cols() + j] > harrisMinVal + + (harrisMaxVal - harrisMinVal) * qualityLevelVal / MAX_QUALITY_LEVEL) { + Imgproc.circle(harrisCopy, new Point(j, i), 4, + new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)), Core.FILLED); + } + } + } + + //Shi-Tomasi + shiTomasiCopy = src.clone(); + + float[] shiTomasiData = new float[(int) (shiTomasiDst.total() * shiTomasiDst.channels())]; + shiTomasiDst.get(0, 0, shiTomasiData); + for (int i = 0; i < srcGray.rows(); i++) { + for (int j = 0; j < srcGray.cols(); j++) { + if (shiTomasiData[i * srcGray.cols() + j] > shiTomasiMinVal + + (shiTomasiMaxVal - shiTomasiMinVal) * qualityLevelVal / MAX_QUALITY_LEVEL) { + Imgproc.circle(shiTomasiCopy, new Point(j, i), 4, + new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)), Core.FILLED); + } + } + } + + harrisImgLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(harrisCopy))); + shiTomasiImgLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(shiTomasiCopy))); + frame.repaint(); + } +} + +public class CornerDetectorDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new CornerDetector(args); + } + }); + } +} diff --git a/samples/java/tutorial_code/TrackingMotion/good_features_to_track/GoodFeaturesToTrackDemo.java b/samples/java/tutorial_code/TrackingMotion/good_features_to_track/GoodFeaturesToTrackDemo.java new file mode 100644 index 0000000000..b5ee732e84 --- /dev/null +++ b/samples/java/tutorial_code/TrackingMotion/good_features_to_track/GoodFeaturesToTrackDemo.java @@ -0,0 +1,134 @@ +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Image; +import java.util.Random; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.MatOfPoint; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +class GoodFeaturesToTrack { + private Mat src = new Mat(); + private Mat srcGray = new Mat(); + private JFrame frame; + private JLabel imgLabel; + private static final int MAX_THRESHOLD = 100; + private int maxCorners = 23; + private Random rng = new Random(12345); + + public GoodFeaturesToTrack(String[] args) { + /// Load source image and convert it to gray + String filename = args.length > 0 ? args[0] : "../data/pic3.png"; + src = Imgcodecs.imread(filename); + if (src.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + + Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY); + + // Create and set up the window. + frame = new JFrame("Shi-Tomasi corner detector demo"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // Set up the content pane. + Image img = HighGui.toBufferedImage(src); + addComponentsToPane(frame.getContentPane(), img); + // Use the content pane's default BorderLayout. No need for + // setLayout(new BorderLayout()); + // Display the window. + frame.pack(); + frame.setVisible(true); + update(); + } + + private void addComponentsToPane(Container pane, Image img) { + if (!(pane.getLayout() instanceof BorderLayout)) { + pane.add(new JLabel("Container doesn't use BorderLayout!")); + return; + } + + JPanel sliderPanel = new JPanel(); + sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); + + sliderPanel.add(new JLabel("Max corners:")); + JSlider slider = new JSlider(0, MAX_THRESHOLD, maxCorners); + slider.setMajorTickSpacing(20); + slider.setMinorTickSpacing(10); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + maxCorners = source.getValue(); + update(); + } + }); + sliderPanel.add(slider); + pane.add(sliderPanel, BorderLayout.PAGE_START); + + imgLabel = new JLabel(new ImageIcon(img)); + pane.add(imgLabel, BorderLayout.CENTER); + } + + private void update() { + /// Parameters for Shi-Tomasi algorithm + maxCorners = Math.max(maxCorners, 1); + MatOfPoint corners = new MatOfPoint(); + double qualityLevel = 0.01; + double minDistance = 10; + int blockSize = 3, gradientSize = 3; + boolean useHarrisDetector = false; + double k = 0.04; + + /// Copy the source image + Mat copy = src.clone(); + + /// Apply corner detection + Imgproc.goodFeaturesToTrack(srcGray, corners, maxCorners, qualityLevel, minDistance, new Mat(), + blockSize, gradientSize, useHarrisDetector, k); + + /// Draw corners detected + System.out.println("** Number of corners detected: " + corners.rows()); + int[] cornersData = new int[(int) (corners.total() * corners.channels())]; + corners.get(0, 0, cornersData); + int radius = 4; + for (int i = 0; i < corners.rows(); i++) { + Imgproc.circle(copy, new Point(cornersData[i * 2], cornersData[i * 2 + 1]), radius, + new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)), Core.FILLED); + } + + imgLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(copy))); + frame.repaint(); + } +} + +public class GoodFeaturesToTrackDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new GoodFeaturesToTrack(args); + } + }); + } +} diff --git a/samples/java/tutorial_code/TrackingMotion/harris_detector/CornerHarrisDemo.java b/samples/java/tutorial_code/TrackingMotion/harris_detector/CornerHarrisDemo.java new file mode 100644 index 0000000000..b3c759d28f --- /dev/null +++ b/samples/java/tutorial_code/TrackingMotion/harris_detector/CornerHarrisDemo.java @@ -0,0 +1,142 @@ +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Image; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +class CornerHarris { + private Mat srcGray = new Mat(); + private Mat dst = new Mat(); + private Mat dstNorm = new Mat(); + private Mat dstNormScaled = new Mat(); + private JFrame frame; + private JLabel imgLabel; + private JLabel cornerLabel; + private static final int MAX_THRESHOLD = 255; + private int threshold = 200; + + public CornerHarris(String[] args) { + /// Load source image and convert it to gray + String filename = args.length > 0 ? args[0] : "../data/building.jpg"; + Mat src = Imgcodecs.imread(filename); + if (src.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + + Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY); + + // Create and set up the window. + frame = new JFrame("Harris corner detector demo"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // Set up the content pane. + Image img = HighGui.toBufferedImage(src); + addComponentsToPane(frame.getContentPane(), img); + // Use the content pane's default BorderLayout. No need for + // setLayout(new BorderLayout()); + // Display the window. + frame.pack(); + frame.setVisible(true); + update(); + } + + private void addComponentsToPane(Container pane, Image img) { + if (!(pane.getLayout() instanceof BorderLayout)) { + pane.add(new JLabel("Container doesn't use BorderLayout!")); + return; + } + + JPanel sliderPanel = new JPanel(); + sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); + + sliderPanel.add(new JLabel("Threshold: ")); + JSlider slider = new JSlider(0, MAX_THRESHOLD, threshold); + slider.setMajorTickSpacing(20); + slider.setMinorTickSpacing(10); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + threshold = source.getValue(); + update(); + } + }); + sliderPanel.add(slider); + pane.add(sliderPanel, BorderLayout.PAGE_START); + + JPanel imgPanel = new JPanel(); + imgLabel = new JLabel(new ImageIcon(img)); + imgPanel.add(imgLabel); + + Mat blackImg = Mat.zeros(srcGray.size(), CvType.CV_8U); + cornerLabel = new JLabel(new ImageIcon(HighGui.toBufferedImage(blackImg))); + imgPanel.add(cornerLabel); + + pane.add(imgPanel, BorderLayout.CENTER); + } + + private void update() { + dst = Mat.zeros(srcGray.size(), CvType.CV_32F); + + /// Detector parameters + int blockSize = 2; + int apertureSize = 3; + double k = 0.04; + + /// Detecting corners + Imgproc.cornerHarris(srcGray, dst, blockSize, apertureSize, k); + + /// Normalizing + Core.normalize(dst, dstNorm, 0, 255, Core.NORM_MINMAX); + Core.convertScaleAbs(dstNorm, dstNormScaled); + + /// Drawing a circle around corners + float[] dstNormData = new float[(int) (dstNorm.total() * dstNorm.channels())]; + dstNorm.get(0, 0, dstNormData); + + for (int i = 0; i < dstNorm.rows(); i++) { + for (int j = 0; j < dstNorm.cols(); j++) { + if ((int) dstNormData[i * dstNorm.cols() + j] > threshold) { + Imgproc.circle(dstNormScaled, new Point(j, i), 5, new Scalar(0), 2, 8, 0); + } + } + } + + cornerLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(dstNormScaled))); + frame.repaint(); + } +} + +public class CornerHarrisDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new CornerHarris(args); + } + }); + } +} diff --git a/samples/java/tutorial_code/features2D/feature_description/SURFMatchingDemo.java b/samples/java/tutorial_code/features2D/feature_description/SURFMatchingDemo.java new file mode 100644 index 0000000000..ac64417d93 --- /dev/null +++ b/samples/java/tutorial_code/features2D/feature_description/SURFMatchingDemo.java @@ -0,0 +1,56 @@ +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.MatOfDMatch; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.features2d.DescriptorMatcher; +import org.opencv.features2d.Features2d; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.xfeatures2d.SURF; + +class SURFMatching { + public void run(String[] args) { + String filename1 = args.length > 1 ? args[0] : "../data/box.png"; + String filename2 = args.length > 1 ? args[1] : "../data/box_in_scene.png"; + Mat img1 = Imgcodecs.imread(filename1, Imgcodecs.IMREAD_GRAYSCALE); + Mat img2 = Imgcodecs.imread(filename2, Imgcodecs.IMREAD_GRAYSCALE); + if (img1.empty() || img2.empty()) { + System.err.println("Cannot read images!"); + System.exit(0); + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + double hessianThreshold = 400; + int nOctaves = 4, nOctaveLayers = 3; + boolean extended = false, upright = false; + SURF detector = SURF.create(hessianThreshold, nOctaves, nOctaveLayers, extended, upright); + MatOfKeyPoint keypoints1 = new MatOfKeyPoint(), keypoints2 = new MatOfKeyPoint(); + Mat descriptors1 = new Mat(), descriptors2 = new Mat(); + detector.detectAndCompute(img1, new Mat(), keypoints1, descriptors1); + detector.detectAndCompute(img2, new Mat(), keypoints2, descriptors2); + + //-- Step 2: Matching descriptor vectors with a brute force matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE); + MatOfDMatch matches = new MatOfDMatch(); + matcher.match(descriptors1, descriptors2, matches); + + //-- Draw matches + Mat imgMatches = new Mat(); + Features2d.drawMatches(img1, keypoints1, img2, keypoints2, matches, imgMatches); + + HighGui.imshow("Matches", imgMatches); + HighGui.waitKey(0); + + System.exit(0); + } +} + +public class SURFMatchingDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + new SURFMatching().run(args); + } +} diff --git a/samples/java/tutorial_code/features2D/feature_detection/SURFDetectionDemo.java b/samples/java/tutorial_code/features2D/feature_detection/SURFDetectionDemo.java new file mode 100644 index 0000000000..c78a0c66bd --- /dev/null +++ b/samples/java/tutorial_code/features2D/feature_detection/SURFDetectionDemo.java @@ -0,0 +1,44 @@ +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.features2d.Features2d; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.xfeatures2d.SURF; + +class SURFDetection { + public void run(String[] args) { + String filename = args.length > 0 ? args[0] : "../data/box.png"; + Mat src = Imgcodecs.imread(filename, Imgcodecs.IMREAD_GRAYSCALE); + if (src.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + + //-- Step 1: Detect the keypoints using SURF Detector + double hessianThreshold = 400; + int nOctaves = 4, nOctaveLayers = 3; + boolean extended = false, upright = false; + SURF detector = SURF.create(hessianThreshold, nOctaves, nOctaveLayers, extended, upright); + MatOfKeyPoint keypoints = new MatOfKeyPoint(); + detector.detect(src, keypoints); + + //-- Draw keypoints + Features2d.drawKeypoints(src, keypoints, src); + + //-- Show detected (drawn) keypoints + HighGui.imshow("SURF Keypoints", src); + HighGui.waitKey(0); + + System.exit(0); + } +} + +public class SURFDetectionDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + new SURFDetection().run(args); + } +} diff --git a/samples/java/tutorial_code/features2D/feature_flann_matcher/SURFFLANNMatchingDemo.java b/samples/java/tutorial_code/features2D/feature_flann_matcher/SURFFLANNMatchingDemo.java new file mode 100644 index 0000000000..e02af9cadb --- /dev/null +++ b/samples/java/tutorial_code/features2D/feature_flann_matcher/SURFFLANNMatchingDemo.java @@ -0,0 +1,78 @@ +import java.util.ArrayList; +import java.util.List; + +import org.opencv.core.Core; +import org.opencv.core.DMatch; +import org.opencv.core.Mat; +import org.opencv.core.MatOfByte; +import org.opencv.core.MatOfDMatch; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.core.Scalar; +import org.opencv.features2d.DescriptorMatcher; +import org.opencv.features2d.Features2d; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.xfeatures2d.SURF; + +class SURFFLANNMatching { + public void run(String[] args) { + String filename1 = args.length > 1 ? args[0] : "../data/box.png"; + String filename2 = args.length > 1 ? args[1] : "../data/box_in_scene.png"; + Mat img1 = Imgcodecs.imread(filename1, Imgcodecs.IMREAD_GRAYSCALE); + Mat img2 = Imgcodecs.imread(filename2, Imgcodecs.IMREAD_GRAYSCALE); + if (img1.empty() || img2.empty()) { + System.err.println("Cannot read images!"); + System.exit(0); + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + double hessianThreshold = 400; + int nOctaves = 4, nOctaveLayers = 3; + boolean extended = false, upright = false; + SURF detector = SURF.create(hessianThreshold, nOctaves, nOctaveLayers, extended, upright); + MatOfKeyPoint keypoints1 = new MatOfKeyPoint(), keypoints2 = new MatOfKeyPoint(); + Mat descriptors1 = new Mat(), descriptors2 = new Mat(); + detector.detectAndCompute(img1, new Mat(), keypoints1, descriptors1); + detector.detectAndCompute(img2, new Mat(), keypoints2, descriptors2); + + //-- Step 2: Matching descriptor vectors with a FLANN based matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED); + List knnMatches = new ArrayList<>(); + matcher.knnMatch(descriptors1, descriptors2, knnMatches, 2); + + //-- Filter matches using the Lowe's ratio test + float ratio_thresh = 0.7f; + List listOfGoodMatches = new ArrayList<>(); + for (int i = 0; i < knnMatches.size(); i++) { + if (knnMatches.get(i).rows() > 1) { + DMatch[] matches = knnMatches.get(i).toArray(); + if (matches[0].distance / matches[1].distance <= ratio_thresh) { + listOfGoodMatches.add(matches[0]); + } + } + } + MatOfDMatch goodMatches = new MatOfDMatch(); + goodMatches.fromList(listOfGoodMatches); + + //-- Draw matches + Mat imgMatches = new Mat(); + Features2d.drawMatches(img1, keypoints1, img2, keypoints2, goodMatches, imgMatches, Scalar.all(-1), + Scalar.all(-1), new MatOfByte(), Features2d.NOT_DRAW_SINGLE_POINTS); + + //-- Show detected matches + HighGui.imshow("Good Matches", imgMatches); + HighGui.waitKey(0); + + System.exit(0); + } +} + +public class SURFFLANNMatchingDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + new SURFFLANNMatching().run(args); + } +} diff --git a/samples/java/tutorial_code/features2D/feature_homography/SURFFLANNMatchingHomographyDemo.java b/samples/java/tutorial_code/features2D/feature_homography/SURFFLANNMatchingHomographyDemo.java new file mode 100644 index 0000000000..1a5cbe7f30 --- /dev/null +++ b/samples/java/tutorial_code/features2D/feature_homography/SURFFLANNMatchingHomographyDemo.java @@ -0,0 +1,130 @@ +import java.util.ArrayList; +import java.util.List; + +import org.opencv.calib3d.Calib3d; +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.DMatch; +import org.opencv.core.KeyPoint; +import org.opencv.core.Mat; +import org.opencv.core.MatOfByte; +import org.opencv.core.MatOfDMatch; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.core.MatOfPoint2f; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.features2d.DescriptorMatcher; +import org.opencv.features2d.Features2d; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; +import org.opencv.xfeatures2d.SURF; + +class SURFFLANNMatchingHomography { + public void run(String[] args) { + String filenameObject = args.length > 1 ? args[0] : "../data/box.png"; + String filenameScene = args.length > 1 ? args[1] : "../data/box_in_scene.png"; + Mat imgObject = Imgcodecs.imread(filenameObject, Imgcodecs.IMREAD_GRAYSCALE); + Mat imgScene = Imgcodecs.imread(filenameScene, Imgcodecs.IMREAD_GRAYSCALE); + if (imgObject.empty() || imgScene.empty()) { + System.err.println("Cannot read images!"); + System.exit(0); + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + double hessianThreshold = 400; + int nOctaves = 4, nOctaveLayers = 3; + boolean extended = false, upright = false; + SURF detector = SURF.create(hessianThreshold, nOctaves, nOctaveLayers, extended, upright); + MatOfKeyPoint keypointsObject = new MatOfKeyPoint(), keypointsScene = new MatOfKeyPoint(); + Mat descriptorsObject = new Mat(), descriptorsScene = new Mat(); + detector.detectAndCompute(imgObject, new Mat(), keypointsObject, descriptorsObject); + detector.detectAndCompute(imgScene, new Mat(), keypointsScene, descriptorsScene); + + //-- Step 2: Matching descriptor vectors with a FLANN based matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED); + List knnMatches = new ArrayList<>(); + matcher.knnMatch(descriptorsObject, descriptorsScene, knnMatches, 2); + + //-- Filter matches using the Lowe's ratio test + float ratio_thresh = 0.75f; + List listOfGoodMatches = new ArrayList<>(); + for (int i = 0; i < knnMatches.size(); i++) { + if (knnMatches.get(i).rows() > 1) { + DMatch[] matches = knnMatches.get(i).toArray(); + if (matches[0].distance / matches[1].distance <= ratio_thresh) { + listOfGoodMatches.add(matches[0]); + } + } + } + MatOfDMatch goodMatches = new MatOfDMatch(); + goodMatches.fromList(listOfGoodMatches); + + //-- Draw matches + Mat imgMatches = new Mat(); + Features2d.drawMatches(imgObject, keypointsObject, imgScene, keypointsScene, goodMatches, imgMatches, Scalar.all(-1), + Scalar.all(-1), new MatOfByte(), Features2d.NOT_DRAW_SINGLE_POINTS); + + //-- Localize the object + List obj = new ArrayList<>(); + List scene = new ArrayList<>(); + + List listOfKeypointsObject = keypointsObject.toList(); + List listOfKeypointsScene = keypointsScene.toList(); + for (int i = 0; i < listOfGoodMatches.size(); i++) { + //-- Get the keypoints from the good matches + obj.add(listOfKeypointsObject.get(listOfGoodMatches.get(i).queryIdx).pt); + scene.add(listOfKeypointsScene.get(listOfGoodMatches.get(i).trainIdx).pt); + } + + MatOfPoint2f objMat = new MatOfPoint2f(), sceneMat = new MatOfPoint2f(); + objMat.fromList(obj); + sceneMat.fromList(scene); + double ransacReprojThreshold = 3.0; + Mat H = Calib3d.findHomography( objMat, sceneMat, Calib3d.RANSAC, ransacReprojThreshold ); + + //-- Get the corners from the image_1 ( the object to be "detected" ) + Mat objCorners = new Mat(4, 1, CvType.CV_32FC2), sceneCorners = new Mat(); + float[] objCornersData = new float[(int) (objCorners.total() * objCorners.channels())]; + objCorners.get(0, 0, objCornersData); + objCornersData[0] = 0; + objCornersData[1] = 0; + objCornersData[2] = imgObject.cols(); + objCornersData[3] = 0; + objCornersData[4] = imgObject.cols(); + objCornersData[5] = imgObject.rows(); + objCornersData[6] = 0; + objCornersData[7] = imgObject.rows(); + objCorners.put(0, 0, objCornersData); + + Core.perspectiveTransform(objCorners, sceneCorners, H); + float[] sceneCornersData = new float[(int) (sceneCorners.total() * sceneCorners.channels())]; + sceneCorners.get(0, 0, sceneCornersData); + + //-- Draw lines between the corners (the mapped object in the scene - image_2 ) + Imgproc.line(imgMatches, new Point(sceneCornersData[0] + imgObject.cols(), sceneCornersData[1]), + new Point(sceneCornersData[2] + imgObject.cols(), sceneCornersData[3]), new Scalar(0, 255, 0), 4); + Imgproc.line(imgMatches, new Point(sceneCornersData[2] + imgObject.cols(), sceneCornersData[3]), + new Point(sceneCornersData[4] + imgObject.cols(), sceneCornersData[5]), new Scalar(0, 255, 0), 4); + Imgproc.line(imgMatches, new Point(sceneCornersData[4] + imgObject.cols(), sceneCornersData[5]), + new Point(sceneCornersData[6] + imgObject.cols(), sceneCornersData[7]), new Scalar(0, 255, 0), 4); + Imgproc.line(imgMatches, new Point(sceneCornersData[6] + imgObject.cols(), sceneCornersData[7]), + new Point(sceneCornersData[0] + imgObject.cols(), sceneCornersData[1]), new Scalar(0, 255, 0), 4); + + //-- Show detected matches + HighGui.imshow("Good Matches & Object detection", imgMatches); + HighGui.waitKey(0); + + System.exit(0); + } +} + +public class SURFFLANNMatchingHomographyDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + new SURFFLANNMatchingHomography().run(args); + } +} diff --git a/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py b/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py new file mode 100644 index 0000000000..72ce96b1a6 --- /dev/null +++ b/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py @@ -0,0 +1,70 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse +import random as rng + +source_window = 'Image' +maxTrackbar = 25 +rng.seed(12345) + +def goodFeaturesToTrack_Demo(val): + maxCorners = max(val, 1) + + # Parameters for Shi-Tomasi algorithm + qualityLevel = 0.01 + minDistance = 10 + blockSize = 3 + gradientSize = 3 + useHarrisDetector = False + k = 0.04 + + # Copy the source image + copy = np.copy(src) + + # Apply corner detection + corners = cv.goodFeaturesToTrack(src_gray, maxCorners, qualityLevel, minDistance, None, \ + blockSize=blockSize, gradientSize=gradientSize, useHarrisDetector=useHarrisDetector, k=k) + + # Draw corners detected + print('** Number of corners detected:', corners.shape[0]) + radius = 4 + for i in range(corners.shape[0]): + cv.circle(copy, (corners[i,0,0], corners[i,0,1]), radius, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) + + # Show what you got + cv.namedWindow(source_window) + cv.imshow(source_window, copy) + + # Set the needed parameters to find the refined corners + winSize = (5, 5) + zeroZone = (-1, -1) + criteria = (cv.TERM_CRITERIA_EPS + cv.TermCriteria_COUNT, 40, 0.001) + + # Calculate the refined corner locations + corners = cv.cornerSubPix(src_gray, corners, winSize, zeroZone, criteria) + + # Write them down + for i in range(corners.shape[0]): + print(" -- Refined Corner [", i, "] (", corners[i,0,0], ",", corners[i,0,1], ")") + +# Load source image and convert it to gray +parser = argparse.ArgumentParser(description='Code for Shi-Tomasi corner detector tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/pic3.png') +args = parser.parse_args() + +src = cv.imread(args.input) +if src is None: + print('Could not open or find the image:', args.input) + exit(0) + +src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) + +# Create a window and a trackbar +cv.namedWindow(source_window) +maxCorners = 10 # initial threshold +cv.createTrackbar('Threshold: ', source_window, maxCorners, maxTrackbar, goodFeaturesToTrack_Demo) +cv.imshow(source_window, src) +goodFeaturesToTrack_Demo(maxCorners) + +cv.waitKey() diff --git a/samples/python/tutorial_code/TrackingMotion/generic_corner_detector/cornerDetector_Demo.py b/samples/python/tutorial_code/TrackingMotion/generic_corner_detector/cornerDetector_Demo.py new file mode 100644 index 0000000000..d135367fc2 --- /dev/null +++ b/samples/python/tutorial_code/TrackingMotion/generic_corner_detector/cornerDetector_Demo.py @@ -0,0 +1,80 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse +import random as rng + +myHarris_window = 'My Harris corner detector' +myShiTomasi_window = 'My Shi Tomasi corner detector' +myHarris_qualityLevel = 50 +myShiTomasi_qualityLevel = 50 +max_qualityLevel = 100 +rng.seed(12345) + +def myHarris_function(val): + myHarris_copy = np.copy(src) + myHarris_qualityLevel = max(val, 1) + + for i in range(src_gray.shape[0]): + for j in range(src_gray.shape[1]): + if Mc[i,j] > myHarris_minVal + ( myHarris_maxVal - myHarris_minVal )*myHarris_qualityLevel/max_qualityLevel: + cv.circle(myHarris_copy, (j,i), 4, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) + + cv.imshow(myHarris_window, myHarris_copy) + +def myShiTomasi_function(val): + myShiTomasi_copy = np.copy(src) + myShiTomasi_qualityLevel = max(val, 1) + + for i in range(src_gray.shape[0]): + for j in range(src_gray.shape[1]): + if myShiTomasi_dst[i,j] > myShiTomasi_minVal + ( myShiTomasi_maxVal - myShiTomasi_minVal )*myShiTomasi_qualityLevel/max_qualityLevel: + cv.circle(myShiTomasi_copy, (j,i), 4, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) + + cv.imshow(myShiTomasi_window, myShiTomasi_copy) + +# Load source image and convert it to gray +parser = argparse.ArgumentParser(description='Code for Creating your own corner detector tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/building.jpg') +args = parser.parse_args() + +src = cv.imread(args.input) +if src is None: + print('Could not open or find the image:', args.input) + exit(0) + +src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) + +# Set some parameters +blockSize = 3 +apertureSize = 3 + +# My Harris matrix -- Using cornerEigenValsAndVecs +myHarris_dst = cv.cornerEigenValsAndVecs(src_gray, blockSize, apertureSize) + +# calculate Mc +Mc = np.empty(src_gray.shape, dtype=np.float32) +for i in range(src_gray.shape[0]): + for j in range(src_gray.shape[1]): + lambda_1 = myHarris_dst[i,j,0] + lambda_2 = myHarris_dst[i,j,1] + Mc[i,j] = lambda_1*lambda_2 - 0.04*pow( ( lambda_1 + lambda_2 ), 2 ) + +myHarris_minVal, myHarris_maxVal, _, _ = cv.minMaxLoc(Mc) + +# Create Window and Trackbar +cv.namedWindow(myHarris_window) +cv.createTrackbar('Quality Level:', myHarris_window, myHarris_qualityLevel, max_qualityLevel, myHarris_function) +myHarris_function(myHarris_qualityLevel) + +# My Shi-Tomasi -- Using cornerMinEigenVal +myShiTomasi_dst = cv.cornerMinEigenVal(src_gray, blockSize, apertureSize) + +myShiTomasi_minVal, myShiTomasi_maxVal, _, _ = cv.minMaxLoc(myShiTomasi_dst) + +# Create Window and Trackbar +cv.namedWindow(myShiTomasi_window) +cv.createTrackbar('Quality Level:', myShiTomasi_window, myShiTomasi_qualityLevel, max_qualityLevel, myShiTomasi_function) +myShiTomasi_function(myShiTomasi_qualityLevel) + +cv.waitKey() diff --git a/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py b/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py new file mode 100644 index 0000000000..57e767ccee --- /dev/null +++ b/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py @@ -0,0 +1,58 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse +import random as rng + +source_window = 'Image' +maxTrackbar = 100 +rng.seed(12345) + +def goodFeaturesToTrack_Demo(val): + maxCorners = max(val, 1) + + # Parameters for Shi-Tomasi algorithm + qualityLevel = 0.01 + minDistance = 10 + blockSize = 3 + gradientSize = 3 + useHarrisDetector = False + k = 0.04 + + # Copy the source image + copy = np.copy(src) + + # Apply corner detection + corners = cv.goodFeaturesToTrack(src_gray, maxCorners, qualityLevel, minDistance, None, \ + blockSize=blockSize, gradientSize=gradientSize, useHarrisDetector=useHarrisDetector, k=k) + + # Draw corners detected + print('** Number of corners detected:', corners.shape[0]) + radius = 4 + for i in range(corners.shape[0]): + cv.circle(copy, (corners[i,0,0], corners[i,0,1]), radius, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) + + # Show what you got + cv.namedWindow(source_window) + cv.imshow(source_window, copy) + +# Load source image and convert it to gray +parser = argparse.ArgumentParser(description='Code for Shi-Tomasi corner detector tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/pic3.png') +args = parser.parse_args() + +src = cv.imread(args.input) +if src is None: + print('Could not open or find the image:', args.input) + exit(0) + +src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) + +# Create a window and a trackbar +cv.namedWindow(source_window) +maxCorners = 23 # initial threshold +cv.createTrackbar('Threshold: ', source_window, maxCorners, maxTrackbar, goodFeaturesToTrack_Demo) +cv.imshow(source_window, src) +goodFeaturesToTrack_Demo(maxCorners) + +cv.waitKey() diff --git a/samples/python/tutorial_code/TrackingMotion/harris_detector/cornerHarris_Demo.py b/samples/python/tutorial_code/TrackingMotion/harris_detector/cornerHarris_Demo.py new file mode 100644 index 0000000000..cee7679adf --- /dev/null +++ b/samples/python/tutorial_code/TrackingMotion/harris_detector/cornerHarris_Demo.py @@ -0,0 +1,55 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse + +source_window = 'Source image' +corners_window = 'Corners detected' +max_thresh = 255 + +def cornerHarris_demo(val): + thresh = val + + # Detector parameters + blockSize = 2 + apertureSize = 3 + k = 0.04 + + # Detecting corners + dst = cv.cornerHarris(src_gray, blockSize, apertureSize, k) + + # Normalizing + dst_norm = np.empty(dst.shape, dtype=np.float32) + cv.normalize(dst, dst_norm, alpha=0, beta=255, norm_type=cv.NORM_MINMAX) + dst_norm_scaled = cv.convertScaleAbs(dst_norm) + + # Drawing a circle around corners + for i in range(dst_norm.shape[0]): + for j in range(dst_norm.shape[1]): + if int(dst_norm[i,j]) > thresh: + cv.circle(dst_norm_scaled, (j,i), 5, (0), 2) + + # Showing the result + cv.namedWindow(corners_window) + cv.imshow(corners_window, dst_norm_scaled) + +# Load source image and convert it to gray +parser = argparse.ArgumentParser(description='Code for Harris corner detector tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/building.jpg') +args = parser.parse_args() + +src = cv.imread(args.input) +if src is None: + print('Could not open or find the image:', args.input) + exit(0) + +src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) + +# Create a window and a trackbar +cv.namedWindow(source_window) +thresh = 200 # initial threshold +cv.createTrackbar('Threshold: ', source_window, thresh, max_thresh, cornerHarris_demo) +cv.imshow(source_window, src) +cornerHarris_demo(thresh) + +cv.waitKey() diff --git a/samples/python/tutorial_code/features2D/feature_description/SURF_matching_Demo.py b/samples/python/tutorial_code/features2D/feature_description/SURF_matching_Demo.py new file mode 100644 index 0000000000..f50e48d858 --- /dev/null +++ b/samples/python/tutorial_code/features2D/feature_description/SURF_matching_Demo.py @@ -0,0 +1,35 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse + +parser = argparse.ArgumentParser(description='Code for Feature Detection tutorial.') +parser.add_argument('--input1', help='Path to input image 1.', default='../data/box.png') +parser.add_argument('--input2', help='Path to input image 2.', default='../data/box_in_scene.png') +args = parser.parse_args() + +img1 = cv.imread(args.input1, cv.IMREAD_GRAYSCALE) +img2 = cv.imread(args.input2, cv.IMREAD_GRAYSCALE) +if img1 is None or img2 is None: + print('Could not open or find the images!') + exit(0) + +#-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors +minHessian = 400 +detector = cv.xfeatures2d_SURF.create(hessianThreshold=minHessian) +keypoints1, descriptors1 = detector.detectAndCompute(img1, None) +keypoints2, descriptors2 = detector.detectAndCompute(img2, None) + +#-- Step 2: Matching descriptor vectors with a brute force matcher +# Since SURF is a floating-point descriptor NORM_L2 is used +matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_BRUTEFORCE) +matches = matcher.match(descriptors1, descriptors2) + +#-- Draw matches +img_matches = np.empty((max(img1.shape[0], img2.shape[0]), img1.shape[1]+img2.shape[1], 3), dtype=np.uint8) +cv.drawMatches(img1, keypoints1, img2, keypoints2, matches, img_matches) + +#-- Show detected matches +cv.imshow('Matches', img_matches) + +cv.waitKey() diff --git a/samples/python/tutorial_code/features2D/feature_detection/SURF_detection_Demo.py b/samples/python/tutorial_code/features2D/feature_detection/SURF_detection_Demo.py new file mode 100644 index 0000000000..717d9f13c0 --- /dev/null +++ b/samples/python/tutorial_code/features2D/feature_detection/SURF_detection_Demo.py @@ -0,0 +1,27 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse + +parser = argparse.ArgumentParser(description='Code for Feature Detection tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/box.png') +args = parser.parse_args() + +src = cv.imread(args.input, cv.IMREAD_GRAYSCALE) +if src is None: + print('Could not open or find the image:', args.input) + exit(0) + +#-- Step 1: Detect the keypoints using SURF Detector +minHessian = 400 +detector = cv.xfeatures2d_SURF.create(hessianThreshold=minHessian) +keypoints = detector.detect(src) + +#-- Draw keypoints +img_keypoints = np.empty((src.shape[0], src.shape[1], 3), dtype=np.uint8) +cv.drawKeypoints(src, keypoints, img_keypoints) + +#-- Show detected (drawn) keypoints +cv.imshow('SURF Keypoints', img_keypoints) + +cv.waitKey() diff --git a/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py b/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py new file mode 100644 index 0000000000..d22f9a8a6f --- /dev/null +++ b/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py @@ -0,0 +1,43 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse + +parser = argparse.ArgumentParser(description='Code for Feature Matching with FLANN tutorial.') +parser.add_argument('--input1', help='Path to input image 1.', default='../data/box.png') +parser.add_argument('--input2', help='Path to input image 2.', default='../data/box_in_scene.png') +args = parser.parse_args() + +img1 = cv.imread(args.input1, cv.IMREAD_GRAYSCALE) +img2 = cv.imread(args.input2, cv.IMREAD_GRAYSCALE) +if img1 is None or img2 is None: + print('Could not open or find the images!') + exit(0) + +#-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors +minHessian = 400 +detector = cv.xfeatures2d_SURF.create(hessianThreshold=minHessian) +keypoints1, descriptors1 = detector.detectAndCompute(img1, None) +keypoints2, descriptors2 = detector.detectAndCompute(img2, None) + +#-- Step 2: Matching descriptor vectors with a FLANN based matcher +# Since SURF is a floating-point descriptor NORM_L2 is used +matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_FLANNBASED) +knn_matches = matcher.knnMatch(descriptors1, descriptors2, 2) + +#-- Filter matches using the Lowe's ratio test +ratio_thresh = 0.7 +good_matches = [] +for matches in knn_matches: + if len(matches) > 1: + if matches[0].distance / matches[1].distance <= ratio_thresh: + good_matches.append(matches[0]) + +#-- Draw matches +img_matches = np.empty((max(img1.shape[0], img2.shape[0]), img1.shape[1]+img2.shape[1], 3), dtype=np.uint8) +cv.drawMatches(img1, keypoints1, img2, keypoints2, good_matches, img_matches, flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) + +#-- Show detected matches +cv.imshow('Good Matches', img_matches) + +cv.waitKey() diff --git a/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py b/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py new file mode 100644 index 0000000000..8820addce2 --- /dev/null +++ b/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py @@ -0,0 +1,78 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse + +parser = argparse.ArgumentParser(description='Code for Feature Matching with FLANN tutorial.') +parser.add_argument('--input1', help='Path to input image 1.', default='../data/box.png') +parser.add_argument('--input2', help='Path to input image 2.', default='../data/box_in_scene.png') +args = parser.parse_args() + +img_object = cv.imread(args.input1, cv.IMREAD_GRAYSCALE) +img_scene = cv.imread(args.input2, cv.IMREAD_GRAYSCALE) +if img_object is None or img_scene is None: + print('Could not open or find the images!') + exit(0) + +#-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors +minHessian = 400 +detector = cv.xfeatures2d_SURF.create(hessianThreshold=minHessian) +keypoints_obj, descriptors_obj = detector.detectAndCompute(img_object, None) +keypoints_scene, descriptors_scene = detector.detectAndCompute(img_scene, None) + +#-- Step 2: Matching descriptor vectors with a FLANN based matcher +# Since SURF is a floating-point descriptor NORM_L2 is used +matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_FLANNBASED) +knn_matches = matcher.knnMatch(descriptors_obj, descriptors_scene, 2) + +#-- Filter matches using the Lowe's ratio test +ratio_thresh = 0.75 +good_matches = [] +for matches in knn_matches: + if len(matches) > 1: + if matches[0].distance / matches[1].distance <= ratio_thresh: + good_matches.append(matches[0]) + +#-- Draw matches +img_matches = np.empty((max(img_object.shape[0], img_scene.shape[0]), img_object.shape[1]+img_scene.shape[1], 3), dtype=np.uint8) +cv.drawMatches(img_object, keypoints_obj, img_scene, keypoints_scene, good_matches, img_matches, flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) + +#-- Localize the object +obj = np.empty((len(good_matches),2), dtype=np.float32) +scene = np.empty((len(good_matches),2), dtype=np.float32) +for i in range(len(good_matches)): + #-- Get the keypoints from the good matches + obj[i,0] = keypoints_obj[good_matches[i].queryIdx].pt[0] + obj[i,1] = keypoints_obj[good_matches[i].queryIdx].pt[1] + scene[i,0] = keypoints_scene[good_matches[i].trainIdx].pt[0] + scene[i,1] = keypoints_scene[good_matches[i].trainIdx].pt[1] + +H, _ = cv.findHomography(obj, scene, cv.RANSAC) + +#-- Get the corners from the image_1 ( the object to be "detected" ) +obj_corners = np.empty((4,1,2), dtype=np.float32) +obj_corners[0,0,0] = 0 +obj_corners[0,0,1] = 0 +obj_corners[1,0,0] = img_object.shape[1] +obj_corners[1,0,1] = 0 +obj_corners[2,0,0] = img_object.shape[1] +obj_corners[2,0,1] = img_object.shape[0] +obj_corners[3,0,0] = 0 +obj_corners[3,0,1] = img_object.shape[0] + +scene_corners = cv.perspectiveTransform(obj_corners, H) + +#-- Draw lines between the corners (the mapped object in the scene - image_2 ) +cv.line(img_matches, (int(scene_corners[0,0,0] + img_object.shape[1]), int(scene_corners[0,0,1])),\ + (int(scene_corners[1,0,0] + img_object.shape[1]), int(scene_corners[1,0,1])), (0,255,0), 4) +cv.line(img_matches, (int(scene_corners[1,0,0] + img_object.shape[1]), int(scene_corners[1,0,1])),\ + (int(scene_corners[2,0,0] + img_object.shape[1]), int(scene_corners[2,0,1])), (0,255,0), 4) +cv.line(img_matches, (int(scene_corners[2,0,0] + img_object.shape[1]), int(scene_corners[2,0,1])),\ + (int(scene_corners[3,0,0] + img_object.shape[1]), int(scene_corners[3,0,1])), (0,255,0), 4) +cv.line(img_matches, (int(scene_corners[3,0,0] + img_object.shape[1]), int(scene_corners[3,0,1])),\ + (int(scene_corners[0,0,0] + img_object.shape[1]), int(scene_corners[0,0,1])), (0,255,0), 4) + +#-- Show detected matches +cv.imshow('Good Matches & Object detection', img_matches) + +cv.waitKey()