Использование спичечной матрицы opencv для проверки блистерной упаковки

Я делаю проект, в котором я должен осмотреть фармацевтическую блистерную упаковку на предмет отсутствия таблеток.

Я пытаюсь использовать функцию matchTemplate opencv. Позвольте мне показать код и затем некоторые результаты.

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

провал 2

В настоящее время я понятия не имею, почему это происходит. Любое предложение и / или помощь приветствуется.

Оригинальный код, который я использовал и изменил, находится здесь: http://opencv-code.com/quick-tips/how-to-handle-template-matching-with-multiple-occurences/

3 ответа

Я нашел решение для своего собственного вопроса. Мне просто нужно применить детектор краев 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;
}

Любое предложение по улучшению приветствуется. Я сильно обеспокоен производительностью и надежностью моего кода, поэтому я ищу все идеи.

У меня сейчас две нервы: нижний порог Канни и отрицательная постоянная в функции adaptiveThreshold.

Изменить: вот результат, как вы просили:)

Шаблон:

шаблон

Тестовое изображение, отсутствует 2 таблетки:

тест, пропавших без вести 2 таблетки

Достоверные результаты шаблона и тестового изображения:

Консервный шаблон

Канни теста

Результат matchTemplate (преобразованный в CV_8U):

matchTemplate

После адаптивного порога:

пороговый

Конечный результат:

результат

Я не думаю, что адаптивный порог - это хороший выбор.

То, что вам нужно сделать здесь, называется не максимальным подавлением. У вас есть изображение с несколькими локальными максимумами, и вы хотите удалить все пиксели, которые не являются локальными максимумами.

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, поэтому я ожидаю худших результатов.

(тем не менее, если это работает для вас, это работает)

Вы пробовали алгоритм Surf, чтобы получить более подробные дескрипторы? Вы можете попытаться собрать дескриптор как для полного, так и для пустого образца изображения. И выполнить различные действия для каждого из обнаруженных объектов.

Другие вопросы по тегам