使用OpenCV检测一个图像中的对象是否在另一个图像中
我有一张样本图片,上面有一个物体,比如下面这张图片里的耳环:
https://i.stack.imgur.com/N5w9a.jpg
接下来,我有一大堆候选图片,我需要找出哪一张最有可能包含这个物体,比如:
https://i.stack.imgur.com/xYL90.jpg
所以我需要给每张图片打个分,分数最高的那张就是最有可能包含目标物体的图片。现在,我有以下几个条件需要考虑:
1) 我可以从不同角度获取多张样本图片。
2) 样本图片的分辨率、角度和距离可能和候选图片不同。
3) 候选图片数量非常多(超过10,000张),所以处理速度必须快。
4) 我愿意在速度上牺牲一些精度,如果需要查看前100张而不是前10张,那也没问题,可以手动处理。
5) 我可以手动处理样本图片,比如给我想要检测的物体画个轮廓;但候选图片太多,不能手动处理。
6) 我对OpenCV或计算机视觉没有什么背景,所以我从零开始。
我最初的想法是先在样本图片上给物体画个大致的轮廓。然后,我可以在物体和候选图片中找到角点。接着,我可以分析每个角点周围的像素,看看它们是否相似,然后根据每个角点的最大相似度分数的总和进行排名。不过,我不太确定如何量化相似的像素。我想可能就是它们RGB值的欧几里得距离吧?
但这样做有个问题,就是有点忽略了物体的中心。在上面的例子中,如果耳环的角点都靠近金色框架,那么就不会考虑耳环内部的红、绿、蓝宝石。我想我可以通过查看所有角点的组合,来改进这个方法,判断相似度时可以在它们之间的线段上取一些点进行采样。
所以我有几个问题:
A) 这种思路总体上合理吗,还是说我漏掉了什么?
B) 我应该研究OpenCV中的哪些具体算法?我知道有多种角点检测算法,但我只需要一种,如果它们的差别都不大,那我就选择最快的。
C) 有没有使用这些算法的示例代码,能帮助我理解?
我可以使用的编程语言是Python或C#。
4 个回答
如果将来有人看到这个,这里有一个使用openCV的小示例。这个示例是基于opencv的示例,但(我觉得)这个版本更清晰,所以我也把它放在这里。
这个示例是在openCV 2.4.4版本上测试的。
#!/usr/bin/env python
'''
Uses SURF to match two images.
Finds common features between two images and draws them
Based on the sample code from opencv:
samples/python2/find_obj.py
USAGE
find_obj.py <image1> <image2>
'''
import sys
import numpy
import cv2
###############################################################################
# Image Matching
###############################################################################
def match_images(img1, img2, img1_features=None, img2_features=None):
"""Given two images, returns the matches"""
detector = cv2.SURF(3200)
matcher = cv2.BFMatcher(cv2.NORM_L2)
if img1_features is None:
kp1, desc1 = detector.detectAndCompute(img1, None)
else:
kp1, desc1 = img1_features
if img2_features is None:
kp2, desc2 = detector.detectAndCompute(img2, None)
else:
kp2, desc2 = img2_features
#print 'img1 - %d features, img2 - %d features' % (len(kp1), len(kp2))
raw_matches = matcher.knnMatch(desc1, trainDescriptors=desc2, k=2)
kp_pairs = filter_matches(kp1, kp2, raw_matches)
return kp_pairs
def filter_matches(kp1, kp2, matches, ratio=0.75):
"""Filters features that are common to both images"""
mkp1, mkp2 = [], []
for m in matches:
if len(m) == 2 and m[0].distance < m[1].distance * ratio:
m = m[0]
mkp1.append(kp1[m.queryIdx])
mkp2.append(kp2[m.trainIdx])
kp_pairs = zip(mkp1, mkp2)
return kp_pairs
###############################################################################
# Match Diplaying
###############################################################################
def draw_matches(window_name, kp_pairs, img1, img2):
"""Draws the matches"""
mkp1, mkp2 = zip(*kp_pairs)
H = None
status = None
if len(kp_pairs) >= 4:
p1 = numpy.float32([kp.pt for kp in mkp1])
p2 = numpy.float32([kp.pt for kp in mkp2])
H, status = cv2.findHomography(p1, p2, cv2.RANSAC, 5.0)
if len(kp_pairs):
explore_match(window_name, img1, img2, kp_pairs, status, H)
def explore_match(win, img1, img2, kp_pairs, status=None, H=None):
"""Draws lines between the matched features"""
h1, w1 = img1.shape[:2]
h2, w2 = img2.shape[:2]
vis = numpy.zeros((max(h1, h2), w1 + w2), numpy.uint8)
vis[:h1, :w1] = img1
vis[:h2, w1:w1 + w2] = img2
vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)
if H is not None:
corners = numpy.float32([[0, 0], [w1, 0], [w1, h1], [0, h1]])
reshaped = cv2.perspectiveTransform(corners.reshape(1, -1, 2), H)
reshaped = reshaped.reshape(-1, 2)
corners = numpy.int32(reshaped + (w1, 0))
cv2.polylines(vis, [corners], True, (255, 255, 255))
if status is None:
status = numpy.ones(len(kp_pairs), numpy.bool_)
p1 = numpy.int32([kpp[0].pt for kpp in kp_pairs])
p2 = numpy.int32([kpp[1].pt for kpp in kp_pairs]) + (w1, 0)
green = (0, 255, 0)
red = (0, 0, 255)
for (x1, y1), (x2, y2), inlier in zip(p1, p2, status):
if inlier:
col = green
cv2.circle(vis, (x1, y1), 2, col, -1)
cv2.circle(vis, (x2, y2), 2, col, -1)
else:
col = red
r = 2
thickness = 3
cv2.line(vis, (x1 - r, y1 - r), (x1 + r, y1 + r), col, thickness)
cv2.line(vis, (x1 - r, y1 + r), (x1 + r, y1 - r), col, thickness)
cv2.line(vis, (x2 - r, y2 - r), (x2 + r, y2 + r), col, thickness)
cv2.line(vis, (x2 - r, y2 + r), (x2 + r, y2 - r), col, thickness)
vis0 = vis.copy()
for (x1, y1), (x2, y2), inlier in zip(p1, p2, status):
if inlier:
cv2.line(vis, (x1, y1), (x2, y2), green)
cv2.imshow(win, vis)
###############################################################################
# Test Main
###############################################################################
if __name__ == '__main__':
if len(sys.argv) < 3:
print "No filenames specified"
print "USAGE: find_obj.py <image1> <image2>"
sys.exit(1)
fn1 = sys.argv[1]
fn2 = sys.argv[2]
img1 = cv2.imread(fn1, 0)
img2 = cv2.imread(fn2, 0)
if img1 is None:
print 'Failed to load fn1:', fn1
sys.exit(1)
if img2 is None:
print 'Failed to load fn2:', fn2
sys.exit(1)
kp_pairs = match_images(img1, img2)
if kp_pairs:
draw_matches('find_obj', kp_pairs, img1, img2)
else:
print "No matches found"
cv2.waitKey()
cv2.destroyAllWindows()
幸运的是,OpenCV的好心人已经为你做了这些。你可以在你的样本文件夹中找到“opencv\samples\cpp\matching_to_many_images.cpp”。编译一下,试试默认的图片。
这个算法可以很容易地调整,以便让它更快或者更精确。
主要来说,物体识别算法分为两个部分:关键点检测和描述,以及物体匹配。对于这两部分,有很多算法和变种,你可以直接在OpenCV中尝试。
检测和描述可以通过:SIFT、SURF、ORB、GFTT、STAR、FAST等方法来完成。
而匹配的方法有:暴力匹配、汉明距离等。(有些方法是针对特定的检测算法的)
开始的提示:
裁剪你的原始图片,让有趣的物体尽量覆盖更多的图片区域。用这个作为训练。
SIFT是最准确的描述符,但比较慢。FAST是精度和速度的好结合。GFTT比较老,可靠性不高。ORB是新加入OpenCV的,速度和准确性都很有前景。
- 结果会受到物体在另一张图片中姿态的影响。如果物体被缩放、旋转、压缩、部分遮挡等,试试SIFT。如果是简单的任务(比如物体几乎在同样的大小/旋转等),大多数描述符都能很好地处理。
- ORB可能还没有在OpenCV的正式版本中。你可以尝试从OpenCV的最新版本下载并编译,链接在这里:https://code.ros.org/svn/opencv/trunk
所以,你可以通过反复尝试找到最适合你的组合。
关于每个实现的详细信息,你应该阅读原始论文或教程。谷歌学术是个不错的起点。
可以看看这个SURF特征,它是openCV的一部分。简单来说,这个技术是用来在两张图片中找到“兴趣点”的。你有一个算法可以找到这些兴趣点,然后还有一个算法可以计算每个兴趣点周围小区域的描述符。通常,这个描述符会捕捉到小区域内边缘方向的分布。接下来,你会尝试找到点的对应关系,也就是说,对于图片A中的每个兴趣点,试着在图片B中找到一个对应的兴趣点。这是通过比较描述符来实现的,寻找最接近的匹配点。如果你找到了通过某种几何变换相关联的一组对应点,那就算是检测成功了。
当然,这只是一个很高层次的解释,具体的细节还需要深入了解,建议你阅读一些论文。可以从David Lowe的《从尺度不变关键点提取独特的图像特征》开始,然后再看看关于SURF的论文。
另外,可以考虑把这个问题移到信号与图像处理的讨论区。