首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >使用opencv匹配一组图像中的图像,以便在C++中进行识别

使用opencv匹配一组图像中的图像,以便在C++中进行识别
EN

Stack Overflow用户
提问于 2013-02-07 12:10:41
回答 2查看 16.7K关注 0票数 23

编辑:我通过这篇文章获得了足够的声誉,可以通过更多的链接来编辑它,这将帮助我更好地理解我的观点

玩艾萨克捆绑游戏的人经常会在小基座上遇到重要的物品。

这样做的目的是让用户感到困惑,一个项目是什么,可以按下一个按钮,然后指示他“装箱”该项目(想想windows桌面装箱)。该框为我们提供了感兴趣的区域(实际项目加上一些背景环境),以便与将是整个项目网格的项目进行比较。

理论用户盒装项目

理论上的条目网格(没有太多了,我只是把它从isaac wiki的绑定中剥离出来)

被标识为用户装箱的项目的网格中的位置将表示图像上的某个区域,该区域与给出关于该项目的信息的isaac维基的绑定的适当链接相关联。

在网格中,该项目是倒数第三行的第1列。我在下面尝试的所有东西中都使用了这两个图像

我的目标是创建一个程序,它可以从游戏《艾萨克的捆绑》中手动裁剪一件物品,通过将图像与游戏中物品表格的图像进行比较来识别裁剪的物品,然后显示适当的维基页面。

这将是我的第一个“真正的项目”,从某种意义上说,它需要大量的图书馆学习来完成我想做的事情。这有点不堪重负。

我在谷歌上搜索了几个选项就弄乱了。(通过搜索方法的名称和opencv,您可以快速找到我使用的教程。由于某些原因,我的帐户受到链接帖子的严格限制)

使用bruteforcematcher:

http://docs.opencv.org/doc/tutorials/features2d/feature_description/feature_description.html

代码语言:javascript
复制
#include <stdio.h>
#include <iostream>
#include "opencv2/core/core.hpp"
#include <opencv2/legacy/legacy.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include "opencv2/highgui/highgui.hpp"

using namespace cv;

void readme();

/** @function main */
int main( int argc, char** argv )
{
  if( argc != 3 )
   { return -1; }

  Mat img_1 = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE );
  Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE );

  if( !img_1.data || !img_2.data )
   { return -1; }

  //-- Step 1: Detect the keypoints using SURF Detector
  int minHessian = 400;

  SurfFeatureDetector detector( minHessian );

  std::vector<KeyPoint> keypoints_1, keypoints_2;

  detector.detect( img_1, keypoints_1 );
  detector.detect( img_2, keypoints_2 );

  //-- Step 2: Calculate descriptors (feature vectors)
  SurfDescriptorExtractor extractor;

  Mat descriptors_1, descriptors_2;

  extractor.compute( img_1, keypoints_1, descriptors_1 );
  extractor.compute( img_2, keypoints_2, descriptors_2 );

  //-- Step 3: Matching descriptor vectors with a brute force matcher
  BruteForceMatcher< L2<float> > matcher;
  std::vector< DMatch > matches;
  matcher.match( descriptors_1, descriptors_2, matches );

  //-- Draw matches
  Mat img_matches;
  drawMatches( img_1, keypoints_1, img_2, keypoints_2, matches, img_matches );

  //-- Show detected matches
  imshow("Matches", img_matches );

  waitKey(0);

  return 0;
  }

 /** @function readme */
 void readme()
 { std::cout << " Usage: ./SURF_descriptor <img1> <img2>" << std::endl; }

导致看起来不是很有用的东西。使用flann得到更清晰但同样不可靠的结果。

http://docs.opencv.org/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.html

代码语言:javascript
复制
#include <stdio.h>
#include <iostream>
#include "opencv2/core/core.hpp"
#include <opencv2/legacy/legacy.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include "opencv2/highgui/highgui.hpp"

using namespace cv;

void readme();

/** @function main */
int main( int argc, char** argv )
{
  if( argc != 3 )
  { readme(); return -1; }

  Mat img_1 = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE );
  Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE );

  if( !img_1.data || !img_2.data )
  { std::cout<< " --(!) Error reading images " << std::endl; return -1; }

  //-- Step 1: Detect the keypoints using SURF Detector
  int minHessian = 400;

  SurfFeatureDetector detector( minHessian );

  std::vector<KeyPoint> keypoints_1, keypoints_2;

  detector.detect( img_1, keypoints_1 );
  detector.detect( img_2, keypoints_2 );

  //-- Step 2: Calculate descriptors (feature vectors)
  SurfDescriptorExtractor extractor;

  Mat descriptors_1, descriptors_2;

  extractor.compute( img_1, keypoints_1, descriptors_1 );
  extractor.compute( img_2, keypoints_2, descriptors_2 );

  //-- Step 3: Matching descriptor vectors using FLANN matcher
  FlannBasedMatcher matcher;
  std::vector< DMatch > matches;
  matcher.match( descriptors_1, descriptors_2, matches );

  double max_dist = 0; double min_dist = 100;

  //-- Quick calculation of max and min distances between keypoints
  for( int i = 0; i < descriptors_1.rows; i++ )
  { double dist = matches[i].distance;
    if( dist < min_dist ) min_dist = dist;
    if( dist > max_dist ) max_dist = dist;
  }

  printf("-- Max dist : %f \n", max_dist );
  printf("-- Min dist : %f \n", min_dist );

  //-- Draw only "good" matches (i.e. whose distance is less than 2*min_dist )
  //-- PS.- radiusMatch can also be used here.
  std::vector< DMatch > good_matches;

  for( int i = 0; i < descriptors_1.rows; i++ )
  { if( matches[i].distance < 2*min_dist )
    { good_matches.push_back( matches[i]); }
  }

  //-- Draw only "good" matches
  Mat img_matches;
  drawMatches( img_1, keypoints_1, img_2, keypoints_2,
               good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
               vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );

  //-- Show detected matches
  imshow( "Good Matches", img_matches );

  for( int i = 0; i < good_matches.size(); i++ )
  { printf( "-- Good Match [%d] Keypoint 1: %d  -- Keypoint 2: %d  \n", i, good_matches[i].queryIdx, good_matches[i].trainIdx ); }

  waitKey(0);

  return 0;
 }

 /** @function readme */
 void readme()
 { std::cout << " Usage: ./SURF_FlannMatcher <img1> <img2>" << std::endl; }

到目前为止,模板匹配是我最好的方法。在6种方法中,它的范围从只得到0-4个正确的标识。

http://docs.opencv.org/doc/tutorials/imgproc/histograms/template_matching/template_matching.html

代码语言:javascript
复制
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>

using namespace std;
using namespace cv;

/// Global Variables
Mat img; Mat templ; Mat result;
char* image_window = "Source Image";
char* result_window = "Result window";

int match_method;
int max_Trackbar = 5;

/// Function Headers
void MatchingMethod( int, void* );

/** @function main */
int main( int argc, char** argv )
{
  /// Load image and template
  img = imread( argv[1], 1 );
  templ = imread( argv[2], 1 );

  /// Create windows
  namedWindow( image_window, CV_WINDOW_AUTOSIZE );
  namedWindow( result_window, CV_WINDOW_AUTOSIZE );

  /// Create Trackbar
  char* trackbar_label = "Method: \n 0: SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED";
  createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );

  MatchingMethod( 0, 0 );

  waitKey(0);
  return 0;
}

/**
 * @function MatchingMethod
 * @brief Trackbar callback
 */
void MatchingMethod( int, void* )
{
  /// Source image to display
  Mat img_display;
  img.copyTo( img_display );

  /// Create the result matrix
  int result_cols =  img.cols - templ.cols + 1;
  int result_rows = img.rows - templ.rows + 1;

  result.create( result_cols, result_rows, CV_32FC1 );

  /// Do the Matching and Normalize
  matchTemplate( img, templ, result, match_method );
  normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );

  /// Localizing the best match with minMaxLoc
  double minVal; double maxVal; Point minLoc; Point maxLoc;
  Point matchLoc;

  minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );

  /// For SQDIFF and SQDIFF_NORMED, the best matches are lower values. For all the other methods, the higher the better
  if( match_method  == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED )
    { matchLoc = minLoc; }
  else
    { matchLoc = maxLoc; }

  /// Show me what you got
  rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
  rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );

  imshow( image_window, img_display );
  imshow( result_window, result );

  return;
}

http://imgur.com/pIRBPQM,h0wkqer,1JG0QY0,haLJzRF,CmrlTeL,DZuW73V#3

在6个失败,通过,失败,通过

不过,这是一种最好的结果。我尝试的下一个项目是

并导致失败,失败

从一项到另一项,所有这些方法都有一些工作得很好,有些做得很糟糕

所以我会问:模板匹配是我最好的选择,还是有一种我没有考虑过的方法将成为我的圣杯?

如何让用户手动创建裁剪?Opencv在这方面的文档非常糟糕,我在网上找到的例子都是非常旧的cpp或直接的C语言。

谢谢你的帮助。到目前为止,这是一次有趣的经历。我不得不去掉所有的链接,这将更好地描述一切是如何工作的,但网站说我发布了超过10个链接,即使我不是。

下面是游戏中更多的物品示例:

这块石头是一种罕见的物品,也是为数不多的可以出现在屏幕上“任何地方”的物品之一。像岩石这样的项目是为什么用户裁剪项目是隔离项目的最佳方式,否则它们的位置只在几个特定的地方。

老板打架后的物品,到处都是东西,中间是透明的。我认为这是一个很难正确工作的问题。

罕见的房间。简单的背景。没有项目透明度。

这是两张表,游戏中所有的物品都是..我最终会将它们制作成一张图片,但目前它们是直接从艾萨克维基上获取的。

EN

回答 2

Stack Overflow用户

发布于 2013-02-07 21:35:09

这里一个重要的细节是,您拥有表中每个项目的纯图像。你知道背景的颜色,可以从图片的其余部分分离项目。例如,除了代表图像本身的矩阵外,您还可以存储大小相同的1-s和0-s矩阵,其中1对应于图像区域,0对应于背景。让我们将这个矩阵称为“掩码”和项目的纯图像-“模式”。

有两种方法可以比较图像:将图像与模式匹配和将模式与图像匹配。您所描述的是将图像与模式进行匹配-您有一些裁剪的图像,并希望找到类似的模式。相反,可以考虑在图像上使用搜索模式。

让我们首先定义函数match(),它获取相同大小的pattern、掩模和图像,并检查掩模下的pattern上的区域是否与图像中的完全相同(伪代码):

代码语言:javascript
复制
def match(pattern, mask, image):
    for x = 0 to pattern.width:
        for y = 0 to pattern.height: 
           if mask[x, y] == 1 and              # if in pattern this pixel is not part of background
              pattern[x, y] != image[x, y]:    # and pixels on pattern and image differ
               return False  
    return True

但图案和裁剪图像的大小可能不同。标准的解决方案(例如,在级联分类器中使用)是使用sliding window -只需在图像上移动模式“窗口”,并检查模式是否与所选区域匹配。这就是OpenCV中图像检测的工作原理。

当然,这种解决方案不是很健壮--裁剪、调整大小或任何其他图像转换都可能会更改某些像素,在这种情况下,方法match()将始终返回false。为了克服这一点,您可以使用图像和模式之间的距离,而不是布尔答案。在这种情况下,函数match()应该返回一些相似度的值,比如在0和1之间,其中1代表“完全相同”,而0代表“完全不同”。然后,您可以设置相似性阈值(例如,图像与模式的相似度应至少为85% ),或者只选择相似度最高的模式。

由于游戏中的物品都是人工图像,而且变化很小,这种方法应该足够了。但是,对于更复杂的情况,您将需要其他功能,而不仅仅是遮罩下的像素。正如我已经在我的评论中建议的那样,像特征脸、使用类似Haar特征的级联分类器甚至是主动外观模型的方法对于这些任务可能更有效。至于SURF,据我所知,它更适合于不同角度和对象大小的任务,但不适用于不同的背景和诸如此类的事情。

票数 2
EN

Stack Overflow用户

发布于 2014-12-19 13:56:24

我在试图找出我自己的模板匹配问题时遇到了你的问题,现在我回来根据我自己的经验分享我认为可能是你最好的选择。你可能早就放弃了这一点,但是嘿,也许有一天其他人也会遇到类似的情况。

你分享的项目都不是实心的矩形,而且由于opencv中的模板匹配不能与遮罩一起工作,所以你总是将参考图像与我必须假设的至少几个不同的背景进行比较(更不用说在不同背景上的不同位置发现的项目,这使得模板匹配变得更加糟糕)。

它将始终比较背景像素,除非您可以收集可以找到参考图像的每个单独情况的裁剪,否则会混淆您的匹配。如果血液等的贴花在物品周围的背景中也引入了更多的可变性,那么模板匹配可能不会得到很好的结果。

因此,如果我是你,我会尝试两件事,这取决于一些细节:

  1. 如果可能,裁剪找到项目的每种情况的参考模板(这不是一个好时机),然后将用户指定的区域与每个项目的每个模板进行比较。从这些比较中得到最好的结果,如果幸运的话,你会有一个正确的匹配。
  2. 你分享的示例屏幕截图没有任何黑/黑的背景线,所以所有项目的轮廓都很突出。如果这在整个游戏中都是一致的,您可以在用户指定的区域内查找边,并检测外部轮廓。在此之前,您将处理每个参考项的外轮廓并存储这些轮廓。然后,您可以将用户裁剪中的等高线与数据库中的每个等高线进行比较,并将最佳匹配作为答案。

我相信其中任何一个都可以为你工作,这取决于你的截图是否很好地代表了游戏。

注意:与模板匹配相比,轮廓匹配的速度要快得多。速度足够快,可以实时运行,而且可能不需要用户裁剪任何东西。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14743389

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档