mirror of https://github.com/opencv/opencv.git
Add Java and Python code for AKAZE local features matching tutorial. Fix incorrect uses of Mat.mul() in Java code.
Uniform Lowe's ratio test in the code.pull/11873/head
parent
e4b51fa8ad
commit
481af5c469
13 changed files with 418 additions and 114 deletions
@ -0,0 +1,163 @@ |
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import javax.xml.parsers.DocumentBuilder; |
||||
import javax.xml.parsers.DocumentBuilderFactory; |
||||
import javax.xml.parsers.ParserConfigurationException; |
||||
|
||||
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.MatOfDMatch; |
||||
import org.opencv.core.MatOfKeyPoint; |
||||
import org.opencv.core.Scalar; |
||||
import org.opencv.features2d.AKAZE; |
||||
import org.opencv.features2d.DescriptorMatcher; |
||||
import org.opencv.features2d.Features2d; |
||||
import org.opencv.highgui.HighGui; |
||||
import org.opencv.imgcodecs.Imgcodecs; |
||||
import org.w3c.dom.Document; |
||||
import org.xml.sax.SAXException; |
||||
|
||||
class AKAZEMatch { |
||||
public void run(String[] args) { |
||||
//! [load]
|
||||
String filename1 = args.length > 2 ? args[0] : "../data/graf1.png"; |
||||
String filename2 = args.length > 2 ? args[1] : "../data/graf3.png"; |
||||
String filename3 = args.length > 2 ? args[2] : "../data/H1to3p.xml"; |
||||
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); |
||||
} |
||||
|
||||
File file = new File(filename3); |
||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); |
||||
DocumentBuilder documentBuilder; |
||||
Document document; |
||||
Mat homography = new Mat(3, 3, CvType.CV_64F); |
||||
double[] homographyData = new double[(int) (homography.total()*homography.channels())]; |
||||
try { |
||||
documentBuilder = documentBuilderFactory.newDocumentBuilder(); |
||||
document = documentBuilder.parse(file); |
||||
String homographyStr = document.getElementsByTagName("data").item(0).getTextContent(); |
||||
String[] splited = homographyStr.split("\\s+"); |
||||
int idx = 0; |
||||
for (String s : splited) { |
||||
if (!s.isEmpty()) { |
||||
homographyData[idx] = Double.parseDouble(s); |
||||
idx++; |
||||
} |
||||
} |
||||
} catch (ParserConfigurationException e) { |
||||
e.printStackTrace(); |
||||
System.exit(0); |
||||
} catch (SAXException e) { |
||||
e.printStackTrace(); |
||||
System.exit(0); |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
System.exit(0); |
||||
} |
||||
homography.put(0, 0, homographyData); |
||||
//! [load]
|
||||
|
||||
//! [AKAZE]
|
||||
AKAZE akaze = AKAZE.create(); |
||||
MatOfKeyPoint kpts1 = new MatOfKeyPoint(), kpts2 = new MatOfKeyPoint(); |
||||
Mat desc1 = new Mat(), desc2 = new Mat(); |
||||
akaze.detectAndCompute(img1, new Mat(), kpts1, desc1); |
||||
akaze.detectAndCompute(img2, new Mat(), kpts2, desc2); |
||||
//! [AKAZE]
|
||||
|
||||
//! [2-nn matching]
|
||||
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING); |
||||
List<MatOfDMatch> knnMatches = new ArrayList<>(); |
||||
matcher.knnMatch(desc1, desc2, knnMatches, 2); |
||||
//! [2-nn matching]
|
||||
|
||||
//! [ratio test filtering]
|
||||
float ratioThreshold = 0.8f; // Nearest neighbor matching ratio
|
||||
List<KeyPoint> listOfMatched1 = new ArrayList<>(); |
||||
List<KeyPoint> listOfMatched2 = new ArrayList<>(); |
||||
List<KeyPoint> listOfKeypoints1 = kpts1.toList(); |
||||
List<KeyPoint> listOfKeypoints2 = kpts2.toList(); |
||||
for (int i = 0; i < knnMatches.size(); i++) { |
||||
DMatch[] matches = knnMatches.get(i).toArray(); |
||||
float dist1 = matches[0].distance; |
||||
float dist2 = matches[1].distance; |
||||
if (dist1 < ratioThreshold * dist2) { |
||||
listOfMatched1.add(listOfKeypoints1.get(matches[0].queryIdx)); |
||||
listOfMatched2.add(listOfKeypoints2.get(matches[0].trainIdx)); |
||||
} |
||||
} |
||||
//! [ratio test filtering]
|
||||
|
||||
//! [homography check]
|
||||
double inlierThreshold = 2.5; // Distance threshold to identify inliers with homography check
|
||||
List<KeyPoint> listOfInliers1 = new ArrayList<>(); |
||||
List<KeyPoint> listOfInliers2 = new ArrayList<>(); |
||||
List<DMatch> listOfGoodMatches = new ArrayList<>(); |
||||
for (int i = 0; i < listOfMatched1.size(); i++) { |
||||
Mat col = new Mat(3, 1, CvType.CV_64F); |
||||
double[] colData = new double[(int) (col.total() * col.channels())]; |
||||
colData[0] = listOfMatched1.get(i).pt.x; |
||||
colData[1] = listOfMatched1.get(i).pt.y; |
||||
colData[2] = 1.0; |
||||
col.put(0, 0, colData); |
||||
|
||||
Mat colRes = new Mat(); |
||||
Core.gemm(homography, col, 1.0, new Mat(), 0.0, colRes); |
||||
colRes.get(0, 0, colData); |
||||
Core.multiply(colRes, new Scalar(1.0 / colData[2]), col); |
||||
col.get(0, 0, colData); |
||||
|
||||
double dist = Math.sqrt(Math.pow(colData[0] - listOfMatched2.get(i).pt.x, 2) + |
||||
Math.pow(colData[1] - listOfMatched2.get(i).pt.y, 2)); |
||||
|
||||
if (dist < inlierThreshold) { |
||||
listOfGoodMatches.add(new DMatch(listOfInliers1.size(), listOfInliers2.size(), 0)); |
||||
listOfInliers1.add(listOfMatched1.get(i)); |
||||
listOfInliers2.add(listOfMatched2.get(i)); |
||||
} |
||||
} |
||||
//! [homography check]
|
||||
|
||||
//! [draw final matches]
|
||||
Mat res = new Mat(); |
||||
MatOfKeyPoint inliers1 = new MatOfKeyPoint(listOfInliers1.toArray(new KeyPoint[listOfInliers1.size()])); |
||||
MatOfKeyPoint inliers2 = new MatOfKeyPoint(listOfInliers2.toArray(new KeyPoint[listOfInliers2.size()])); |
||||
MatOfDMatch goodMatches = new MatOfDMatch(listOfGoodMatches.toArray(new DMatch[listOfGoodMatches.size()])); |
||||
Features2d.drawMatches(img1, inliers1, img2, inliers2, goodMatches, res); |
||||
Imgcodecs.imwrite("akaze_result.png", res); |
||||
|
||||
double inlierRatio = listOfInliers1.size() / (double) listOfMatched1.size(); |
||||
System.out.println("A-KAZE Matching Results"); |
||||
System.out.println("*******************************"); |
||||
System.out.println("# Keypoints 1: \t" + listOfKeypoints1.size()); |
||||
System.out.println("# Keypoints 2: \t" + listOfKeypoints2.size()); |
||||
System.out.println("# Matches: \t" + listOfMatched1.size()); |
||||
System.out.println("# Inliers: \t" + listOfInliers1.size()); |
||||
System.out.println("# Inliers Ratio: \t" + inlierRatio); |
||||
|
||||
HighGui.imshow("result", res); |
||||
HighGui.waitKey(); |
||||
//! [draw final matches]
|
||||
|
||||
System.exit(0); |
||||
} |
||||
} |
||||
|
||||
public class AKAZEMatchDemo { |
||||
public static void main(String[] args) { |
||||
// Load the native OpenCV library
|
||||
System.loadLibrary(Core.NATIVE_LIBRARY_NAME); |
||||
|
||||
new AKAZEMatch().run(args); |
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
from __future__ import print_function |
||||
import cv2 as cv |
||||
import numpy as np |
||||
import argparse |
||||
from math import sqrt |
||||
|
||||
## [load] |
||||
parser = argparse.ArgumentParser(description='Code for AKAZE local features matching tutorial.') |
||||
parser.add_argument('--input1', help='Path to input image 1.', default='../data/graf1.png') |
||||
parser.add_argument('--input2', help='Path to input image 2.', default='../data/graf3.png') |
||||
parser.add_argument('--homography', help='Path to the homography matrix.', default='../data/H1to3p.xml') |
||||
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) |
||||
|
||||
fs = cv.FileStorage(args.homography, cv.FILE_STORAGE_READ) |
||||
homography = fs.getFirstTopLevelNode().mat() |
||||
## [load] |
||||
|
||||
## [AKAZE] |
||||
akaze = cv.AKAZE_create() |
||||
kpts1, desc1 = akaze.detectAndCompute(img1, None) |
||||
kpts2, desc2 = akaze.detectAndCompute(img2, None) |
||||
## [AKAZE] |
||||
|
||||
## [2-nn matching] |
||||
matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_BRUTEFORCE_HAMMING) |
||||
nn_matches = matcher.knnMatch(desc1, desc2, 2) |
||||
## [2-nn matching] |
||||
|
||||
## [ratio test filtering] |
||||
matched1 = [] |
||||
matched2 = [] |
||||
nn_match_ratio = 0.8 # Nearest neighbor matching ratio |
||||
for m, n in nn_matches: |
||||
if m.distance < nn_match_ratio * n.distance: |
||||
matched1.append(kpts1[m.queryIdx]) |
||||
matched2.append(kpts2[m.trainIdx]) |
||||
## [ratio test filtering] |
||||
|
||||
## [homography check] |
||||
inliers1 = [] |
||||
inliers2 = [] |
||||
good_matches = [] |
||||
inlier_threshold = 2.5 # Distance threshold to identify inliers with homography check |
||||
for i, m in enumerate(matched1): |
||||
col = np.ones((3,1), dtype=np.float64) |
||||
col[0:2,0] = m.pt |
||||
|
||||
col = np.dot(homography, col) |
||||
col /= col[2,0] |
||||
dist = sqrt(pow(col[0,0] - matched2[i].pt[0], 2) +\ |
||||
pow(col[1,0] - matched2[i].pt[1], 2)) |
||||
|
||||
if dist < inlier_threshold: |
||||
good_matches.append(cv.DMatch(len(inliers1), len(inliers2), 0)) |
||||
inliers1.append(matched1[i]) |
||||
inliers2.append(matched2[i]) |
||||
## [homography check] |
||||
|
||||
## [draw final matches] |
||||
res = np.empty((max(img1.shape[0], img2.shape[0]), img1.shape[1]+img2.shape[1], 3), dtype=np.uint8) |
||||
cv.drawMatches(img1, inliers1, img2, inliers2, good_matches, res) |
||||
cv.imwrite("akaze_result.png", res) |
||||
|
||||
inlier_ratio = len(inliers1) / float(len(matched1)) |
||||
print('A-KAZE Matching Results') |
||||
print('*******************************') |
||||
print('# Keypoints 1: \t', len(kpts1)) |
||||
print('# Keypoints 2: \t', len(kpts2)) |
||||
print('# Matches: \t', len(matched1)) |
||||
print('# Inliers: \t', len(inliers1)) |
||||
print('# Inliers Ratio: \t', inlier_ratio) |
||||
|
||||
cv.imshow('result', res) |
||||
cv.waitKey() |
||||
## [draw final matches] |
Loading…
Reference in new issue