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 ratioThresh = 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 < ratioThresh * matches[1].distance) { 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.DrawMatchesFlags_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); } }