简体   繁体   English

OpenCV - C ++到Java - 模板匹配

[英]OpenCV - C++ to Java - Template Match

I am trying to detect more than a square (marker) in my image as I ask here Detect Marker Position in 2D image 我试图在我的图像中检测到超过正方形(标记),因为我在这里要求检测2D图像中的标记位置

There is one guy that showed me a solution in C++, here it is: 有一个人向我展示了一个C ++解决方案,这里是:

#include <iostream>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>


//See: http://docs.opencv.org/doc/tutorials/imgproc/histograms/template_matching/template_matching.html
//See: http://answers.opencv.org/question/60382/detect-markers-position-in-2d-images/
int main() {
  cv::Mat img, templateImg, result;
  cv::VideoCapture capture("http://answers.opencv.org/upfiles/14297307634571599.png");
  if(capture.isOpened()) {
    capture >> img;
  } else {
    return -1;
  }

  capture = cv::VideoCapture("http://answers.opencv.org/upfiles/14297308125543022.png");
  if(capture.isOpened()) {
    capture >> templateImg;
  } else {
    return -1;
  }

  /// Reduce the size of the image to display it on my screen
  cv::resize(img, img, cv::Size(), 0.5, 0.5);
  /// Reduce the size of the template image
  /// (first to fit the size used to create the image test, second to fit the size of the reduced image)
  cv::resize(templateImg, templateImg, cv::Size(), 0.25, 0.25);

  cv::Mat img_display;
  img.copyTo(img_display);

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

  result.create(result_rows, result_cols, CV_32FC1);

  /// Do the Matching and Normalize
  cv::matchTemplate(img, templateImg, result, CV_TM_CCORR_NORMED);
  cv::normalize(result, result, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());

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

  for(;;) {
    cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
    matchLoc = maxLoc;
    std::cout << "Max correlation=" << maxVal << std::endl;
    if(maxVal < 0.8) {
      break;
    }

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

    cv::imshow("result", result);
    cv::waitKey(0);

    /// Fill the detected location with a rectangle of zero
    cv::rectangle(result, cv::Point( matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2),
        cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2 ), cv::Scalar::all(0), -1);
  } while (maxVal > 0.9);


  cv::imshow("result", result);
  cv::imshow("img_display", img_display);
  cv::waitKey(0);

  return 0;
}

The for loop is responsible to find more than one marker and detect it, I am trying to adapt it to my java code and I am getting an infinite loop here is my code: for循环负责查找多个标记并检测它,我正在尝试将其调整为我的java代码,我在这里得到一个无限循环是我的代码:

public void run(String inFile, String templateFile, String outFile, int match_method) {
        System.out.println("\nRunning Template Matching");


    Mat img = Highgui.imread(inFile);
    Mat templ = Highgui.imread(templateFile);

    // / Create the result matrix
    int result_cols = img.cols() - templ.cols() + 1;
    int result_rows = img.rows() - templ.rows() + 1;
    Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1);

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

    Point matchLoc;
    Point maxLoc;
    Point minLoc;

    MinMaxLocResult mmr;

    boolean iterate = true;
    while(iterate){

    // / Localizing the best match with minMaxLoc
    mmr = Core.minMaxLoc(result);
    matchLoc = mmr.maxLoc;


    if(mmr.maxVal < 0.8)
    {
        iterate = false;
    }



    // / Show me what you got
    Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(),
            matchLoc.y + templ.rows()), new Scalar(0, 255, 0));

    }

    // Save the visualized detection.
    System.out.println("Writing "+ outFile);
    Highgui.imwrite(outFile, img);

}

I notice that the function minMaxLoc has more arguments in c++ than in java, maybe that's the problem? 我注意到函数minMaxLoc在c ++中比在java中有更多的参数,也许这就是问题所在? Why I can't get the same behaviour in java can someone help me? 为什么我不能在java中获得相同的行为有人可以帮助我吗?

Thank you so much in advance 非常感谢你提前

As cyriel said, You forgot to fill zeros for the maximum location and hence you got the infinite loop. 正如cyriel所说, 你忘了为最大位置填充零 ,因此你得到了无限循环。 May be he forgot to explain you that, 也许他忘了解释你,

   for each iteration
   find the max location
   check if max value is greater than desired threshold
   if true 
   show me what is max
   else
   break // not found anything that matches
   make the existing max to be zero and continue to search for other max// you forgot this and hence infinite loop
   end

For more insight to the problem, you can comment the following lines in the c++ code and run it, you will experience similar problem. 要更深入地了解问题,可以在c ++代码中注释以下行并运行它,您将遇到类似的问题。 (Here cv::Scalar::all(0),-1 args says fill the existing max/found region with 0 and continue) (这里cv :: Scalar :: all(0), - 1 args表示用0 填充现有的max / found区域并继续)

// Fill the detected location with a rectangle of zero
    cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols / 2, matchLoc.y - templateImg.rows / 2),
        cv::Point(matchLoc.x + templateImg.cols / 2, matchLoc.y + templateImg.rows / 2), cv::Scalar::all(0), -1);

Hope it helps. 希望能帮助到你。

You forgot to draw second rectangle. 你忘了画第二个矩形了。 Original code: 原始代码:

 cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2),
    cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2 ), cv::Scalar::all(0), 2, 8, 0);

...

/// Fill the detected location with a rectangle of zero
cv::rectangle(result, cv::Point( matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2),
    cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2 ), cv::Scalar::all(0), -1);

however in your code there is only this: 但是在你的代码中只有这个:

Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(),
        matchLoc.y + templ.rows()), new Scalar(0, 255, 0));

The second part of original code most likely is crucial - it draws filled, not just a shape like first line. 原始代码的第二部分很可能是至关重要的 - 它绘制了填充,而不仅仅是第一行的形状。

Btw this line Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0)); 顺便说一下这行Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0)); is probably wrong as well - 1) You should draw rectangle on result mat, not on img mat. 也许是错误的 - 1)你应该在result垫上绘制矩形, result不是在img垫上。 2) In original code there is Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0)); 2)在原始代码中有Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0)); and in your code new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()) . 并在您的代码中new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()) Are you sure it is fine? 你确定它没事吗?

Although it might help, you don't need OpenCV / JavaCV for this relatively easy task. 虽然它可能有所帮助,但您不需要OpenCV / JavaCV来完成这项相对简单的任务。

Basically, you want to find regions in you image that are: 基本上,您希望在图像中找到以下区域:

a: all black
b: square
c: a percentage of size of the total image

First, threshold the image so all pixels are either black or white. 首先,对图像进行阈值处理,使所有像素都为黑色或白色。

My approach would be to find the connected components by scanning the image pixel by pixel until you reach a black pixel. 我的方法是通过逐像素扫描图像直到找到黑色像素来找到连接的组件。 Then 'flood fill' until you have all the black pixels collected. 然后'泛水填充',直到收集到所有黑色像素。 Mark them (color them differently) and store them in some structure (list of points) and repeat until you reached the last pixel of the image. 标记它们(以不同方式着色)并将它们存储在某个结构(点列表)中并重复,直到到达图像的最后一个像素。

For each component: 对于每个组件:

  • determine the bounding box (min and max x and y values). 确定边界框(最小和最大x和y值)。
  • Calculate the size of the box and determine if it is small / large enough 计算盒子的大小,并确定它是否足够小/大
  • Calculate the percentage of matching pixels in that box. 计算该框中匹配像素的百分比。 If it is close to 100%, you have a square shape. 如果接近100%,则表示方形。

You can plot the matching bounding boxes back into your original image. 您可以将匹配的边界框绘制回原始图像。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM