OpenCV C ++ - 查找图像中包含的图像?

时间:2017-03-06 20:12:20

标签: c++ opencv

我有用于在另一张图片中搜索一张小图片的代码:

int* MyLib::MatchingMethod(int, void*)
{
    /// Source image to 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_rows, result_cols, CV_32FC1);

    match_method = 0;

    /// Do the Matching and Normalize
    matchTemplate(img, templ, result, match_method);
    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;

    minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::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;
    }

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

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

    double  myX = (matchLoc.x + (templ.cols) / 2);
    double  myY = (matchLoc.y + (templ.rows) / 2);

    static int o[2];
    o[0] = myX;
    o[1] = myY;

    return o;
}

但是这段代码可能会错误地“找到”任何区域,即使较大的图像不包含小图像。

如何更改此代码,强制它“完全”搜索小图像。例如,如果较小的图像不在较大的图像上,则此代码必须显示任何信息消息“未找到图像”。

更新1。看起来,matchTemplate效果不佳。例如,我有3张图片 - 一张模板(http://s6.postimg.org/nj2ts3lf5/image.png),一张图片,其中包含来自模板的图片(http://s6.postimg.org/fp6tkg301/image.png),以及一张图片,其中不包含模板(http://s6.postimg.org/9x23zk3sh/image.png )。

对于包含模板的第一张图片,maxVal = 0.99999994039535522并正确选择了区域:http://s6.postimg.org/65x4qzfht/image.png

但是对于图片,包含模板,maxVal = 1.0000000000000000 错误选定区域,不包含t包含模板图片http://s6.postimg.org/5132llt0x/screenshot_544.png

谢谢!

1 个答案:

答案 0 :(得分:3)

无论算法执行匹配的确定性如何,您都可以将结果可视化。模板匹配总是会给你一个输出 - 你想要做的是试着弄清楚它是否有效。

根据minVal尝试输出maxValmatch_method。您应该在找到正确匹配的情况下以及在它给出误报的情况下比较值。那些实验应该允许你建立一个阈值,区分真正的命中和误报。因此,你可以说多大 - 例如 - maxVal必须确保它是一个匹配。伪代码会是这样的:

if maxVal > threshold:
     match_found = true
     match_position = maxLoc

现在这是一种理论方法。由于您没有提供任何图像,因此可能会或可能不会解决您的问题。

修改 如果您找不到明确的阈值(在我看来,在大多数情况下,如果您保持质量,尺寸等,我认为应该可以),请尝试执行以下两项操作之一:

  1. minMaxLoc之前尝试查看所有获得的结果,计算平均值,看看发现的maxVal是否比真阳性情况下的平均值大得多。也许您可以将阈值定义为平均值的百分比,从而说:if maxVal > meanVal + meanVal * n%: match_found = true
  2. 这是一种常见的情况,模板匹配对边缘的效果要好于真实图像。同样,您还没有提供样品,因此很难说这种方法有多可靠。但是如果你有足够的高频率,用Canny Edges照亮一个图像,这可能会给你一个更清晰的阈值来区分真假阳性。
  3. <强> EDIT2: 由于您使用match_method = 0,这意味着CV_TM_SQDIFF。要更好地控制该过程,请明确使用该名称。查找有关方法source的信息。

    另外,将cout放在if语句中,这样就可以打印正确的值,实际上就是匹配匹配(在你的情况下,它是minVal)。

    if (match_method == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED)
    {
        matchLoc = minLoc;
        std::cout << minVal << std::endl;
    }
    else
    {
        matchLoc = maxLoc;
        std::cout << maxVal << std::endl;
    }
    

    再次说明:如果没有达到预期效果,相当合理的轮廓检测几乎肯定会有所帮助。