【问题标题】:Using OpenCV to recognise similar (not completely identical) simple images?使用 OpenCV 识别相似(不完全相同)的简单图像?
【发布时间】:2015-02-02 13:54:36
【问题描述】:

假设我有一个非常简单的图像或形状,例如这个火柴人绘图:

我还有一个其他简单图像库,我想将其与第一张图像进行比较并确定最接近的匹配:

请注意,这两个火柴人并不完全相同,但相当相似。

我希望能够将第一张图片与我库中的每张图片进行比较,直到找到相当接近的匹配。如有必要,我的图像库可以包含同一图像的许多变体,以帮助确定我拥有哪种类型的图像。例如:

我的问题是这是否是 OpenCV 能够做到的?以前有没有做过,如果有,你能指出一些例子的方向吗?非常感谢您的帮助。

编辑:通过我的搜索,我发现了许多比较图像的人的例子,甚至是比较被拉伸或倾斜的图像的人,例如:Checking images for similarity with OpenCV。不幸的是,正如您所看到的,我的图像不仅仅是彼此的翻译(旋转/倾斜/拉伸)版本 - 它们实际上是不同的图像,尽管它们非常相似。

【问题讨论】:

标签: opencv image-processing computer-vision image-comparison


【解决方案1】:

您应该可以使用 OpenCV 的特征模板匹配功能来做到这一点。您可以使用matchTemplate 函数查找该功能,然后使用minMaxLoc 查找其位置。查看 OpenCV 网站上matchTemplate 的教程。

【讨论】:

  • 当图像彼此略微旋转时,模板匹配是否有效? OpenCV 文档和谷歌搜索中的示例似乎都使用了相同方向的图像。
  • 应该适用于小旋转(如 5-10 度左右),但除此之外,它可能不会给出很好的结果。
【解决方案2】:
【解决方案3】:

对于您的特定类型的图像,您可能会通过将moments/HuMoments 用于连接的组件(您可以使用findContours 找到)来获得良好的结果。

【讨论】:

    【解决方案4】:

    由于涉及到轮换,我认为模板匹配效果不佳。您可能需要使用特征点检测,例如 SIFT 或 SURF。

    【讨论】:

    • 在商业应用中您应该小心 SURF,因为它的描述符已获得专利
    • @iwlagn,真的。我想知道它是如何在 opencv 中实现的?
    • SURF 是一种很好的算法,没有人禁止您实施它或将其用于您的研究目的。实施专利算法不违反 BSD 许可证(由 OpenCV 使用)。那么为什么 SURF 不应该在 OpenCV 中实现呢?
    • @iwlagn,我并不是说 SURF 不是一个好的算法,否则我不会一开始就推广它。我想知道专利算法如何不违反允许重新分发 opencv 二进制文件的 BSD 许可证。
    • 这里有两件事。首先是 BSD 许可证,它说“做任何你想做的事,但不要删除作者信息”。其次是 SURF 的专利描述符(据我所知,该算法尚未获得专利)。没有什么能阻止您实施任何算法,无论它们拥有多少专利(在 OpenCV 中完成)。但是专利算法会限制这种实现的使用,这应该只关心 OpenCV 用户。
    【解决方案5】:

    编辑:这不适用于旋转。 matchTemplate 也一样。我还没有尝试findContours + moments,就像 bjoernz 的答案一样,听起来很有希望。

    失败的解决方案: 我尝试使用 OpenCV 3.0 中提供的ShapeContextDistanceExtractor(1) 以及您的示例图像上的findContours 以获得良好的结果。样本图像被裁剪为与原始图像相同的大小(128 * 200)。你也可以在 OpenCV 中使用resize


    下面的代码将images 文件夹中的图像与1.png 作为基础图像进行比较。

    #include "opencv2/shape.hpp"
    #include "opencv2/opencv.hpp"
    #include <iostream>
    #include <string>
    
    using namespace std;
    using namespace cv;
    
    const int MAX_SHAPES = 7;
    
    vector<Point> findContours( const Mat& compareToImg )
    {
        vector<vector<Point> > contour2D;
        findContours(compareToImg, contour2D, RETR_LIST, CHAIN_APPROX_NONE);
    
        //converting 2d vector contours to 1D vector for comparison
        vector <Point> contour1D;
        for (size_t border=0; border < contour2D.size(); border++) {
            for (size_t p=0; p < contour2D[border].size(); p++) {
                contour1D.push_back( contour2D[border][p] );
            }
        }
    
        //limiting contours size to reduce distance comparison time
        contour1D.resize( 300 );
        return contour1D;
    }
    
    int main()
    {
        string path = "./images/";
        cv::Ptr <cv::ShapeContextDistanceExtractor> distanceExtractor = cv::createShapeContextDistanceExtractor();
    
        //base image
        Mat baseImage= imread( path + "1.png", IMREAD_GRAYSCALE);
    
        vector<Point> baseImageContours= findContours( baseImage );
    
        for ( int idx = 2; idx <= MAX_SHAPES; ++idx ) {
    
            stringstream imgName;
            imgName << path << idx << ".png";
            Mat compareToImg=imread( imgName.str(), IMREAD_GRAYSCALE ) ;
    
            vector<Point> contii = findContours( compareToImg );
            float distance = distanceExtractor->computeDistance( baseImageContours, contii );
    
            std::cout<<" distance to " << idx << " : " << distance << std::endl;
        }
    
        return 0;
    }
    

    结果

    到 2 的距离:89.7951
    到 3 的距离:14.6793
    到 4 的距离:6.0063
    到 5 的距离:4.79834
    到 6 的距离:0.0963184
    到 7 的距离:0.00212693

    【讨论】:

    • 为什么这个解决方案失败了?
    • @AlexL 如果您检查修订历史以获取他的答案,他会说它“失败”,因为它不适用于轮换。 [对我来说,这不是问题,因为我的图像从不旋转。]
    【解决方案6】:

    做三件事: 1. 忘记图像比较,因为你真的比较笔画符号。 2.从谷歌商店下载并使用手势搜索应用程序; 3. 意识到如果不使用有关笔画的时间戳信息,您将无法识别您的笔画,以获得良好的表现。否则我们将有一个成功的手写识别。然后你可以研究Android stroke reco 库来正确编写你的代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-12
      • 2014-11-30
      • 2010-10-15
      • 2012-07-17
      • 1970-01-01
      • 2020-05-23
      • 2012-07-17
      • 2012-03-15
      相关资源
      最近更新 更多