使用OpenCV检测来自一个图像的对象是否在另一个图像中

时间:2011-10-24 19:50:44

标签: c# python image-processing opencv computer-vision

我有一个包含对象的示例图像,例如下图中的耳环:

http://imgur.com/luj2Z

然后我有一个大的候选图像集,我需要确定哪一个最有可能包含该对象,例如:

http://imgur.com/yBWgc

所以我需要为每个图像生成一个分数,其中最高分对应于最可能包含目标对象的图像。现在,在这种情况下,我有以下条件/约束来处理/周围:

1)我可以从不同的角度获得多个样本图像。

2)样本图像可能与候选图像的分辨率,角度和距离不同。

3)有很多候选图像(> 10,000),所以它必须相当快。

4)我愿意为速度牺牲一些精确度,所以如果这意味着我们必须搜索前100名而不是前10名,那很好,可以手动完成。

5)我可以手动操作样本图像,例如概述我想要检测的对象;候选图像不能手动操作,因为太多了。

6)我根本没有OpenCV或计算机视觉的真实背景,所以我从头开始。

我最初的想法是首先在样本图像中围绕对象绘制粗略轮廓。然后,我可以识别候选图像中的对象和角落的角落。我可以分析每个角落周围的像素,看它们是否看起来相似,然后按每个角落的最大相似度得分的总和进行排名。我也不确定如何量化类似的像素。我猜只是他们的RGB值的欧几里德距离?

问题在于它忽略了对象的中心。在上面的例子中,如果耳环的角落都在金框附近,那么它就不会考虑耳环内的红色,绿色和蓝色宝石。我想我可以通过查看所有角点并通过沿着它们之间的线采样一些点来确定相似性来改进这一点。

所以我有几个问题:

A)这种思路一般是否有意义,或者我有什么遗漏?

B)我应该调查OpenCV的哪些特定算法?我知道有多个角点检测算法,但我只需要一个,如果差异都是在边缘上进行优化那么我就没那么快了。

C)使用算法的任何示例代码都有助于我理解?

我的语言选择是Python或C#。

4 个答案:

答案 0 :(得分:5)

幸运的是,来自OpenCV的那些人为你做了那件事。检入您的samples文件夹“opencv \ samples \ cpp \ matching_to_many_images.cpp”。编译并尝试使用默认图像。

可以轻松调整算法,使其更快或更精确。

主要是,对象识别算法分为两部分:关键点检测和关键点检测算法。描述和对象匹配。对于他们两个,有许多算法/变体,你可以直接进入OpenCV。

检测/描述可以通过以下方式完成:SIFT / SURF / ORB / GFTT / STAR / FAST等。

对于匹配,你有:暴力,汉明等(某些方法特定于给定的检测算法)

开始提示:

  • 裁剪原始图像,使有趣的对象尽可能多地覆盖图像区域。将其用作培训。

  • SIFT是最准确和最懒的描述符。 FAST是精确度和准确度的完美结合。 GFTT陈旧且非常不可靠。 ORB是OPENCV新增的,在速度和准确性方面非常有前景。

  • 结果取决于另一幅图像中对象的姿势。如果它被调整大小,旋转,挤压,部分覆盖等,请尝试SIFT。如果它是一个简单的任务(即它出现在几乎相同的大小/旋转/等,大多数描述符将很好地应对)
  • ORB可能还没有出现在OpenCV版本中。尝试从openCV trunk下载最新版本并编译https://code.ros.org/svn/opencv/trunk

因此,您可以通过反复试验为您找到最佳组合。

有关每个实现的详细信息,您应该阅读原始论文/教程。谷歌学者是一个好的开始

答案 1 :(得分:4)

查看SURF功能,这些功能是openCV的一部分。这里的想法是你有一个算法在两个图像中找到“兴趣点”。您还有一个算法,用于计算每个兴趣点周围的图像块的描述符。通常,该描述符捕获补丁中边缘方向的分布。然后你试着找到点对应,我。即对于图像A中的每个兴趣点,尝试在图像B中找到对应的兴趣点。这通过比较描述符并寻找最接近的匹配来实现。然后,如果您有一组通过某种几何变换相关的对应关系,则可以进行检测。

当然,这是一个非常高级别的解释。魔鬼在细节中,对于那些你应该阅读一些文章。从David Lowe的Distinctive image features from scale-invariant keypoints开始,然后阅读有关SURF的论文。

另外,请考虑将此问题移至Signal and Image Processing Stack Exchange

答案 2 :(得分:2)

如果将来有人出现,这是一个使用openCV做这个的小样本。它基于opencv sample,但(在我看来),这有点清晰,所以我也包括它。

使用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()

答案 3 :(得分:0)

如上所述,像SIFT和SURF这样的算法包含一个特征点,该特征点对于许多失真和描述符是不变的,其目的是对其周围的特征点进行鲁棒建模。

后者越来越多地用于图像分类和识别,通常称为“词袋”或“视觉词”方法。

以最简单的形式,可以从所有图像中收集所有描述符中的所有数据并对其进行聚类,例如使用k-means。然后,每个原始图像都具有对许多聚类有贡献的描述符。这些聚类的质心,即视觉词,可以用作图像的新描述符。然后,这些可用于具有反向文件设计的体系结构中。

这种方法允许软匹配和一定量的概括,例如用飞机取回所有图像。

  • VLfeat website旁边是一个优秀的SIFT库,这个方法的一个很好的演示,对caltech 101数据集进行分类。

  • Caltech本身提供Matlab / C ++软件以及相关出版物。

  • 同样好的开始是LEAR

  • 的工作