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

2c2*_*c2c 23 c++ opencv image matching

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

玩isaac绑定的人经常在小基座上遇到重要的物品.

目标是让用户对项目能够按下按钮感到困惑,然后按钮将指示他"装箱"该项目(想想桌面装箱).该框为我们提供了感兴趣的区域(实际项目加上一些背景环境),以便与整个项目网格进行比较.

理论用户盒装项目 在此输入图像描述

项目的理论网格(没有更多,我只是从isaac维基的绑定中撕掉了这个) 在此输入图像描述

标识为用户装箱的项目的项目网格中的位置将表示图像上的特定区域,该区域与提供关于项目的信息的isaac wiki的绑定的适当链接相关.

在网格中,该项目是从底行开始的第3列第3列.我在下面尝试的所有内容中都使用了这两个图像


我的目标是创建一个程序,可以从游戏"Isaac的绑定"中手动裁剪项目,通过查找比较图像与游戏中项目表的图像来识别裁剪项目,然后显示正确的项目维基页面.

这将是我的第一个"真正的项目",因为它需要大量的图书馆学习才能得到我想要的东西.这有点令人难以招架.

我只是通过谷歌搜索搞砸了几个选项.(您可以通过搜索方法名称和opencv快速找到我使用过的教程.由于某种原因,我的帐户受链接发布限制很多)

使用bruteforcematcher:

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

#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; }
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

结果看起来不那么有用.使用flann的清洁但同样不可靠的结果.

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

#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; }
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

模板匹配是迄今为止我最好的方法.在6种方法中,它的范围从仅获得0-4正确的识别.

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

6失败,传球,失败,传球,传球,传球

这虽然是一个最好的案例结果.我试过的下一个项目是

在此输入图像描述 并导致失败,失败,失败,失败,失败,失败

从一个项目到另一个项目,所有这些方法都有一些运作良好,有些方法非常好

所以我会问:模板匹配是我最好的选择,还是有一种方法我不认为这将是我的圣杯?

如何让USER手动创建裁剪?Opencv的文档非常糟糕,我在网上找到的例子是非常老的cpp或直接C.

谢谢你的帮助.到目前为止,这项合作一直是一次有趣的经历 我不得不删除所有链接,这些链接可以更好地描述一切是如何运作的,但该网站说我发布了超过10个链接,即使我不是.


整个游戏中的一些项目示例:

摇滚是一种罕见的物品,也是屏幕上"无处不在"的少数物品之一.像摇滚这样的物品是用户裁剪物品是隔离物品的最佳方式的原因,否则他们的位置只在几个特定的​​地方.

在此输入图像描述

在此输入图像描述

老板打架后的项目,到处都是很多东西,中间是透明的.我认为这是正确工作的难度之一

在此输入图像描述

在此输入图像描述

很少的房间.简单的背景.没有项目透明度.

在此输入图像描述

在此输入图像描述

这里有两个表格,游戏中的所有项目都是......我最终将它们变成一个图像但是现在它们直接取自isaac wiki.

在此输入图像描述

在此输入图像描述

ffr*_*end 2

这里的一个重要细节是您拥有表格中每一项的纯粹图像。您知道背景的颜色,并且可以将项目与图片的其余部分分开。例如,除了表示图像本身的矩阵之外,您还可以存储相同大小的 1 和 0 的矩阵,其中 1 对应于图像区域,0 对应于背景。我们将这个矩阵称为“掩模”,并将该项目的纯图像称为“图案”。

比较图像有两种方法:将图像与模式匹配以及将模式与图像匹配。您所描述的是将图像与图案进行匹配 - 您有一些裁剪后的图像并且想要找到相似的图案。相反,考虑在 image 上搜索模式

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

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