Подтвердить что ты не робот

Обнаружение монет (и совпадающих эллипсов) на изображении

В настоящее время я работаю над проектом, в котором я пытаюсь обнаружить несколько монет, лежащих на плоской поверхности (например, стол). Монеты не перекрываются и не скрыты другими предметами. Но могут быть видны другие объекты, и условия освещения могут быть не идеальными... По сути, подумайте, снимаете ли вы свой стол, на котором есть монеты.

Таким образом, каждая точка должна быть видна как эллипс. Поскольку я не знаю положение камеры, форма эллипсов может варьироваться от круга (вид сверху) до плоских эллипсов в зависимости от угла, из которого сняты монеты.

Моя проблема в том, что я не уверен, как извлечь монеты и, наконец, наложить на них эллипсы (которые я ищу для дальнейших расчетов).

На данный момент я только что сделал первую попытку, установив пороговое значение в OpenCV, используя findContours() для получения контурных линий и подгонки эллипса. К сожалению, контурные линии очень редко дают мне форму монет (отражения, плохое освещение,...), и этот способ также не является предпочтительным, так как я не хочу, чтобы пользователь устанавливал какой-либо порог.

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

Поэтому я хотел спросить, может ли кто-нибудь сказать мне метод, который будет работать в моем случае.

Есть ли быстрый способ извлечь три монеты из изображения? Расчеты должны быть выполнены в реальном времени на мобильных устройствах, и метод не должен быть слишком чувствительным для разных или меняющихся источников света или цвета фона.

Было бы замечательно, если бы кто-нибудь мог дать мне какие-либо советы о том, какой метод может работать для меня.

4b9b3361

Ответ 1

Здесь некоторый источник C99, реализующий традиционный подход (на основе OpenCV doco):

#include "cv.h"
#include "highgui.h"

#include <stdio.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

//
// We need this to be high enough to get rid of things that are too small too
// have a definite shape.  Otherwise, they will end up as ellipse false positives.
//
#define MIN_AREA 100.00    
//
// One way to tell if an object is an ellipse is to look at the relationship
// of its area to its dimensions.  If its actual occupied area can be estimated
// using the well-known area formula Area = PI*A*B, then it has a good chance of
// being an ellipse.
//
// This value is the maximum permissible error between actual and estimated area.
//
#define MAX_TOL  100.00

int main( int argc, char** argv )
{
    IplImage* src;
    // the first command line parameter must be file name of binary (black-n-white) image
    if( argc == 2 && (src=cvLoadImage(argv[1], 0))!= 0)
    {
        IplImage* dst  = cvCreateImage( cvGetSize(src), 8, 3 );
        CvMemStorage* storage = cvCreateMemStorage(0);
        CvSeq* contour = 0;    
        cvThreshold( src, src, 1, 255, CV_THRESH_BINARY );
        //
        // Invert the image such that white is foreground, black is background.
        // Dilate to get rid of noise.
        //
        cvXorS(src, cvScalar(255, 0, 0, 0), src, NULL);
        cvDilate(src, src, NULL, 2);    
        cvFindContours( src, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
        cvZero( dst );

        for( ; contour != 0; contour = contour->h_next )
        {
            double actual_area = fabs(cvContourArea(contour, CV_WHOLE_SEQ, 0));
            if (actual_area < MIN_AREA)
                continue;

            //
            // FIXME:
            // Assuming the axes of the ellipse are vertical/perpendicular.
            //
            CvRect rect = ((CvContour *)contour)->rect;
            int A = rect.width / 2; 
            int B = rect.height / 2;
            double estimated_area = M_PI * A * B;
            double error = fabs(actual_area - estimated_area);    
            if (error > MAX_TOL)
                continue;    
            printf
            (
                 "center x: %d y: %d A: %d B: %d\n",
                 rect.x + A,
                 rect.y + B,
                 A,
                 B
            );

            CvScalar color = CV_RGB( rand() % 255, rand() % 255, rand() % 255 );
            cvDrawContours( dst, contour, color, color, -1, CV_FILLED, 8, cvPoint(0,0));
        }

        cvSaveImage("coins.png", dst, 0);
    }
}

Учитывая двоичное изображение, предоставленное Carnieri, это результат:

./opencv-contour.out coin-ohtsu.pbm
center x: 291 y: 328 A: 54 B: 42
center x: 286 y: 225 A: 46 B: 32
center x: 471 y: 221 A: 48 B: 33
center x: 140 y: 210 A: 42 B: 28
center x: 419 y: 116 A: 32 B: 19

И это выводимое изображение:

coins

Что вы можете улучшить:

  • Обрабатывайте разные ориентации эллипса (в настоящее время я предполагаю, что оси перпендикулярны/горизонтальны). Это не составит труда сделать с использованием моментов изображения.
  • Проверьте выпуклость объекта (посмотрите cvConvexityDefects)

Ваш лучший способ отличить монеты от других объектов, вероятно, будет по форме. Я не могу думать о каких-либо других низкоуровневых функциях изображения (цвет явно отсутствует). Итак, я могу представить два подхода:

Обнаружение традиционных объектов

Ваша первая задача - отделить объекты (монеты и не монеты) от фона. Метод Ohtsu, предложенный Carnieri, будет хорошо работать здесь. Кажется, вы беспокоитесь о том, что изображения двудольные, но я не думаю, что это будет проблемой. До тех пор, пока на столе будет видно значительное количество стола, вы гарантированно получите один пик на своей гистограмме. И пока на столе есть несколько визуально различимых объектов, вам гарантирован второй пик.

Разбавьте свое двоичное изображение пару раз, чтобы избавиться от шума, оставленного порогом. Монеты относительно велики, поэтому они должны выжить в этой морфологической операции.

Группируйте белые пиксели в объекты с использованием области, растущей - просто итеративно соедините соседние пиксели переднего плана. В конце этой операции у вас будет список непересекающихся объектов, и вы будете знать, какие пиксели занимают каждый объект.

Из этой информации вы узнаете ширину и высоту объекта (с предыдущего шага). Итак, теперь вы можете оценить размер эллипса, который окружает объект, а затем посмотреть, насколько хорошо этот конкретный объект соответствует эллипсу. Может быть проще просто использовать соотношение ширины и высоты.

В качестве альтернативы вы можете использовать моменты, чтобы более точно определить форму объекта.

Ответ 2

Я не знаю, какой лучший метод для вашей проблемы. Однако о пороговых значениях можно использовать метод Otsu, который автоматически находит оптимальное пороговое значение на основе анализа гистограммы изображения. Используйте метод OpenCV threshold с параметром ThresholdType, равным THRESH_OTSU.

Помните, что метод Otsu хорошо работает только на изображениях с бимодальными гистограммами (например, изображения с яркими объектами на темном фоне).

Вероятно, вы видели это, но есть также метод установки эллипса вокруг набора двумерных точек (например, подключенный компонент).

EDIT: метод Otsu применяется к образцу образца:

Изображение в оттенках серого: grayscale image

Результат применения метода Otsu: Otsu image

Ответ 3

Если кто-то еще столкнется с этой проблемой в будущем, как я, но с помощью С++:

После того, как вы использовали findContours для поиска контуров (как в ответе Миши выше), вы можете легко поместить эллипсы с помощью fitEllipse, например

    vector<vector<Point> > contours;

    findContours(img, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0,0));

    RotatedRect rotRecs[contours.size()];

    for (int i = 0; i < contours.size(); i++) {
        rotRecs[i] = fitEllipse(contours[i]);
    }