使用opencv matchtemplate进行泡罩包装检查

使用opencv matchtemplate进行泡罩包装检查,opencv,inspection,matchtemplate,Opencv,Inspection,Matchtemplate,我正在做一个项目,我必须检查药物泡罩包装是否有丢失的药片 我正在尝试使用opencv的matchTemplate函数。让我先展示代码,然后展示一些结果 int match(string filename, string templatename) { Mat ref = cv::imread(filename + ".jpg"); Mat tpl = cv::imread(templatename + ".jpg"); if (ref.empty() || tpl.emp

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

我正在尝试使用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;
}
int匹配(字符串文件名、字符串模板名)
{
Mat ref=cv::imread(文件名+“.jpg”);
Mat tpl=cv::imread(templatename+“.jpg”);
如果(参考empty()| | tpl.empty())
{

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

我找到了解决我自己问题的方法。我只需要在图像和模板b上应用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;
}
int匹配(字符串文件名、字符串模板名)
{
Mat ref=cv::imread(文件名+“.jpg”);
Mat tpl=cv::imread(templatename+“.jpg”);
如果(参考empty()| | tpl.empty())
{

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

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

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滤镜会丢弃很多关于颜色和亮度的信息,因此我预计会有更糟糕的结果

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;
}

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

我已经为您编辑了中的图像。+1对于一个优秀的问题,您可以尝试使用不同的阈值?0.8是最高值吗?此外,您可以尝试其他互相关方法,如SQDIFF_NORMED,而不是oft CCOEFF_NORMED?仔细查看代码,将相关结果转换为8位,然后运行自适应thres按住它,这样你传递给MinMaxLoc的图像已经被二值化了,所以更改0.8阈值将没有任何效果。我不确定你从中获得了什么,在原始res_32f中搜索最大值不会得到更好的结果吗?@HugoRune:原始代码在CV_32FC1结果图像上运行MinMaxLoc。但是因为我正在尝试进行近似匹配es,在没有adaptiveThreshold的情况下运行minMaxLoc会首先返回太多的失火。干得好!你能提供你的结果吗!@Constantine:添加了结果图片;)谢谢你的帮助。你能再看看我的答案吗,我添加了结果图片。本地非最大抑制是否有效?我知道目前我的代码只能处理“剪影”平板电脑。但至少它比原始颜色/灰色图像的matchTemplate返回更好的结果。我计划添加另一轮检查匹配轮廓的颜色,以防混入错误颜色的平板电脑。我想测试您的方法,但无法找到
cv::set
。其他方法记录在和@handle我可能记错了,或者在我使用的c#包装器中重命名了它;它被称为