【问题标题】:Using opencv matchtemplate for blister pack inspection使用 opencv matchtemplate 进行泡罩包装检测
【发布时间】:2014-04-20 09:35:36
【问题描述】:

我正在做一个项目,我必须检查药品泡罩包装是否有丢失的药片。

我正在尝试使用 opencv 的 matchTemplate 函数。让我展示一下代码和一些结果。

int match(string filename, string templatename)
{
    Mat ref = cv::imread(filename + ".jpg");
    Mat tpl = cv::imread(templatename + ".jpg");
    if (ref.empty() || tpl.empty())
    {
        cout << "Error reading file(s)!" << endl;
        return -1;
    }

    imshow("file", ref);
    imshow("template", tpl);

    Mat res_32f(ref.rows - tpl.rows + 1, ref.cols - tpl.cols + 1, CV_32FC1);
    matchTemplate(ref, tpl, res_32f, CV_TM_CCOEFF_NORMED);

    Mat res;
    res_32f.convertTo(res, CV_8U, 255.0);
    imshow("result", res);

    int size = ((tpl.cols + tpl.rows) / 4) * 2 + 1; //force size to be odd
    adaptiveThreshold(res, res, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, size, -128);
    imshow("result_thresh", res);

    while (true) 
    {
        double minval, maxval, threshold = 0.8;
        Point minloc, maxloc;
        minMaxLoc(res, &minval, &maxval, &minloc, &maxloc);

        if (maxval >= threshold)
        {
            rectangle(ref, maxloc, Point(maxloc.x + tpl.cols, maxloc.y + tpl.rows), CV_RGB(0,255,0), 2);
            floodFill(res, maxloc, 0); //mark drawn blob
        }
        else
            break;
    }

    imshow("final", ref);
    waitKey(0);

    return 0;
}

这里有一些图片。

优质泡罩包装的“样品”图片:

从“示例”图像中裁剪的模板:

带有“样本”图像的结果:

检测到此包装中缺少的平板电脑:

但这里有问题:

我目前不知道为什么会发生这种情况。任何建议和/或帮助表示赞赏。

我遵循和修改的原始代码在这里:http://opencv-code.com/quick-tips/how-to-handle-template-matching-with-multiple-occurences/

【问题讨论】:

  • 我已经为你编辑了图片。 +1 提出一个很好的问题
  • 死你试试不同的阈值?你能达到的最高值是0.8吗?另外,您是否尝试使用其他互相关方法,例如 SQDIFF_NORMED 而不是 CCOEFF_NORMED?
  • 更仔细地查看代码,将相关结果转换为 8 位,然后对其运行自适应阈值,因此您传递给 MinMaxLoc 的图像已经二值化,因此更改 0.8 阈值将没有效果。我不确定您从中获得了什么,在原始 res_32f 中搜索最大值不会得到更好的结果吗?
  • @HugoRune:原始代码在 CV_32FC1 结果图像上运行 minMaxLoc。但是由于我正在尝试进行近似匹配,因此首先在没有 AdaptiveThreshold 的情况下运行 minMaxLoc 会返回太多的失火。

标签: opencv inspection matchtemplate


【解决方案1】:

我为自己的问题找到了解决方案。我只需要在图像和模板上应用 Canny 边缘检测器,然后再将它们扔给 matchTemplate 函数。完整的工作代码:

int match(string filename, string templatename)
{
    Mat ref = cv::imread(filename + ".jpg");
    Mat tpl = cv::imread(templatename + ".jpg");
    if(ref.empty() || tpl.empty())
    {
        cout << "Error reading file(s)!" << endl;
        return -1;
    }

    Mat gref, gtpl;
    cvtColor(ref, gref, CV_BGR2GRAY);
    cvtColor(tpl, gtpl, CV_BGR2GRAY);

    const int low_canny = 110;
    Canny(gref, gref, low_canny, low_canny*3);
    Canny(gtpl, gtpl, low_canny, low_canny*3);

    imshow("file", gref);
    imshow("template", gtpl);

    Mat res_32f(ref.rows - tpl.rows + 1, ref.cols - tpl.cols + 1, CV_32FC1);
    matchTemplate(gref, gtpl, res_32f, CV_TM_CCOEFF_NORMED);

    Mat res;
    res_32f.convertTo(res, CV_8U, 255.0);
    imshow("result", res);

    int size = ((tpl.cols + tpl.rows) / 4) * 2 + 1; //force size to be odd
    adaptiveThreshold(res, res, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, size, -64);
    imshow("result_thresh", res);

    while(1) 
    {
        double minval, maxval;
        Point minloc, maxloc;
        minMaxLoc(res, &minval, &maxval, &minloc, &maxloc);

        if(maxval > 0)
        {
            rectangle(ref, maxloc, Point(maxloc.x + tpl.cols, maxloc.y + tpl.rows), Scalar(0,255,0), 2);
            floodFill(res, maxloc, 0); //mark drawn blob
        }
        else
            break;
    }

    imshow("final", ref);
    waitKey(0);

    return 0;
}

感谢任何改进建议。我非常关心我的代码的性能和健壮性,所以我正在寻找所有的想法。

现在有两件事让我很紧张:较低的 Canny 阈值和adaptiveThreshold 函数的负常数。

编辑:这是你问的结果:)

模板:

测试图片,缺少 2 片:

模板和测试图像的Canny结果:

matchTemplate 结果(转换为 CV_8U):

自适应阈值后:

最终结果:

【讨论】:

  • 干得好!你能提供你的结果吗!
  • @Constantine: 结果图片已添加 ;)
【解决方案2】:

我不认为自适应阈值是一个好的选择。

这里需要做的就是所谓的非最大抑制。您有一个具有多个局部最大值的图像,并且您想要删除所有不是局部最大值的像素。

cv::dilate(res_32f, res_dilated, null, 5);
cv::compare(res_32f, res_dilated, mask_local_maxima, cv::CMP_GE);
cv::set(res_32f, 0, mask_local_maxima)

现在 res_32f 图像中所有不是局部最大值的像素都设置为零。所有的最大像素仍然是它们的原始值,所以你可以在后面的行中调整阈值

double minval, maxval, threshold = 0.8;

所有局部最大值现在也应该被足够多的零包围,这样填充不会延伸太远。

现在我认为您应该能够调整阈值以排除所有误报。


如果这还不够,这里还有一个建议:

我会使用多个模板来运行搜索,而不是只使用一个模板;您当前的模板,以及一个在包装的右侧和左侧带有平板电脑的模板。由于透视这些平板电脑看起来有很大的不同。跟踪找到的平板电脑,这样您就不会多次检测到 smae 平板电脑。

使用这些多个模板,您可以将门槛提高到更高。


进一步改进:如果检测仍然过于不稳定,请尝试模糊您的模板并使用高斯模糊搜索图像。这将删除可能会抛出 matchTemplate 函数的精细细节和噪音,同时保持较大的结构完好无损。

使用 canny 过滤器对我来说似乎不可靠:它似乎依赖于这样一个事实,即移除的平板电脑区域将在中心有更多边缘。但我不确定这是否会一直如此;并且您使用 canny 过滤器丢弃了很多关于颜色和亮度的信息,所以我预计结果会更糟。

(也就是说,如果它对你有用,它就有效)

【讨论】:

  • 感谢您的帮助。您能否再看看我的答案,我添加了结果图片。局部非极大值抑制会起作用吗?我知道目前我的代码只能处理平板电脑的“剪影”。但至少它比使用原始彩色/灰色图像的 matchTemplate 返回更好的结果。我计划添加另一轮检查匹配轮廓的颜色,以防混入错误颜色的药片。
  • 我想测试您的方法,但找不到cv::set。其他方法记录在 docs.opencv.org/modules/imgproc/doc/filtering.html#dilatedocs.opencv.org/modules/core/doc/…
  • @handle 我可能记错了,或者它在我使用的 c# 包装器中被重命名了;它被称为setTo
【解决方案3】:

您是否尝试过 Surf 算法以获得更详细的描述符?您可以尝试收集完整和空样本图像的描述符。并对检测到的每一个对象执行不同的操作。

【讨论】:

    猜你喜欢
    • 2021-04-13
    • 2017-11-25
    • 2014-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-31
    • 2021-06-18
    • 2012-06-14
    相关资源
    最近更新 更多