我正在尝试使用Android和OpenCV构建一个简单的叶识别应用程序;我的数据库只包含3个条目(3种类型的叶子的3张图片),我希望能够识别数据库中的一张图片是否在智能手机拍摄的另一张图片内。 我正在使用SURF方法从数据库图像中提取关键点,然后将它们与捕获图像的提取关键点进行比较,以寻找匹配。 我的问题是结果显示为“颜色匹配”,而不是“特征匹配”:当我比较数据库中的图片和捕获的图片时,匹配的数量与所有3个条目相等,因此我得到了错误的匹配。
这是数据库中的一张图片(注意没有背景)
这是我得到的结果:
顶部的图像是从智能手机捕获的图像,下面的图像是突出显示匹配的结果。
以下是我实施的代码:
Mat orig = Highgui.imread(photoPathwithoutFile);
Mat origBW = new Mat();
Imgproc.cvtColor(orig, origBW, Imgproc.COLOR_RGB2GRAY);
MatOfKeyPoint kpOrigin = createSURFdetector(origBW);
Mat descOrig = extractDescription(kpOrigin, origBW);
Leaf result = findMatches(descOrig);
Mat imageOut = orig.clone();
Features2d.drawMatches(orig, kpOrigin, maple, keypointsMaple, resultMaple, imageOut);
public MatOfKeyPoint createSURFdetector (Mat origBW) {
FeatureDetector surf = FeatureDetector.create(FeatureDetector.FAST);
MatOfKeyPoint keypointsOrig = new MatOfKeyPoint();
surf.detect(origBW, keypointsOrig);
return keypointsOrig;
}
public Mat extractDescription (MatOfKeyPoint kpOrig, Mat origBW) {
DescriptorExtractor surfExtractor = DescriptorExtractor.create(FeatureDetector.SURF);
Mat origDesc = new Mat();
surfExtractor.compute(origBW, kpOrig, origDesc);
return origDesc;
}
public Leaf findMatches (Mat descriptors) {
DescriptorMatcher m = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE);
MatOfDMatch max = new MatOfDMatch();
resultMaple = new MatOfDMatch();
resultChestnut = new MatOfDMatch();
resultSwedish = new MatOfDMatch();
Leaf match = null;
m.match(descriptors, mapleDescriptors, resultMaple);
Log.d("Origin", resultMaple.toList().size()+" matches with Maples");
if (resultMaple.toList().size() > max.toList().size()) { max = resultMaple; match = Leaf.MAPLE; }
m.match(descriptors, chestnutDescriptors, resultChestnut);
Log.d("Origin", resultChestnut.toList().size()+" matches with Chestnut");
if (resultChestnut.toList().size() > max.toList().size()) { max = resultChestnut; match = Leaf.CHESTNUT; }
m.match(descriptors, swedishDescriptors, resultSwedish);
Log.d("Origin", resultSwedish.toList().size()+" matches with Swedish");
if (resultSwedish.toList().size() > max.toList().size()) { max = resultSwedish; match = Leaf.SWEDISH; }
//return the match object with more matches
return match;
}
如何才能获得更准确的匹配,而不是基于颜色,而是基于图片的实际奇点?
答案 0 :(得分:1)
嗯,SURF不是这项任务的最佳人选。 SURF描述符基本上在角落的小邻域中编码一些梯度统计。这为你提供了很多变换的不变性,但你却失去了大局。这样做的时候。该描述符用于缩小要匹配的点之间的对应范围,然后一些几何约束起作用。
在你的情况下,似乎描述符在匹配点上没有做得很好,并且因为有很多它们每个点最终得到一个匹配(尽管奇怪的是几何测试并没有阻止它)
我可以建议你尝试不同的匹配方法,也许是HOG与经过训练的描述符来检测叶子类型,甚至是基于轮廓的东西,因为形状是你的图像之间真正不同的东西。例如,您可以检测叶子的轮廓,标准化它的长度,找到它的中心,然后以相等的间隔计算从每个点到中心的距离 - 这将是您的描述符。然后找到最大长度并循环移动此描述符以从极值开始并除以此值 - 这将为您提供选择轮廓起点,旋转和缩放的基本不变性。但这很可能会在视角和仿射变换下失败。
如果您想进一步尝试特征点 - 尝试检测更少的特征点,但尝试更具代表性的特征点(按渐变强度,角落分数或其他东西过滤)。也许使用SIFT而不是SURF - 它应该更精确一些。匹配后检查内部数量 - 最佳匹配应具有更高的比率。
但说实话,这似乎更像是机器学习任务而不是计算机视觉。
编辑:我检查了你的代码,发现你没有对匹配进行几何检查,因此你得到了不正确的匹配。尝试在匹配后执行findHomography,然后仅考虑mask
输出参数中已设置为1的点。这将使您只考虑使用单应性可以相互扭曲的点,并且可以改善匹配。
Edit2:添加了一个代码段(抱歉,但我目前无法测试Java,所以它在Python中)
import cv2
import numpy as np
# read input
a = cv2.imread(r'C:\Temp\leaf1.jpg')
b = cv2.imread(r'C:\Temp\leaf2.jpg')
# convert to gray
agray = cv2.cvtColor(a, cv2.COLOR_BGR2GRAY)
bgray = cv2.cvtColor(b, cv2.COLOR_BGR2GRAY)
# detect features and compute descriptors
surf = cv2.SURF() # better use SIFT instead
kp1, d1 = surf.detectAndCompute(agray,None)
kp2, d2 = surf.detectAndCompute(bgray,None)
print 'numFeatures1 =', len(kp1)
print 'numFeatures2 =', len(kp2)
# use KNN matcher
bf = cv2.BFMatcher()
matches = bf.knnMatch(d1,d2, k=2)
# Apply Lowe ratio test
good = []
for m,n in matches:
if m.distance < 0.75*n.distance:
good.append(m)
print 'numMatches =', len(matches)
print 'numGoodMatches =', len(good)
# if have enough matches - try to calculare homography to discard matches
# that don't fit perspective transformation model
if len(good)>10:
# convert matches into correct format (python-specific)
src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
print 'numMatches =', sum(mask.ravel().tolist()) # calc number of 1s in mask
else:
print "not enough good matches are found"
它使用SURF
为我提供了不同叶子的以下输出numFeatures1 = 685
numFeatures2 = 1566
numMatches = 685
numGoodMatches = 52
numMatches = 11
你可以看到真实的数量&#39;比赛非常小。但不幸的是,当我们匹配相同叶子类型的不同图像时,numMatches是相似的。也许你可以通过调整参数来改善结果,但我认为在这里使用关键点只是一种不太好的方法。也许这是由于即使在同一个班级内的叶子变化。