Вот некоторый источник 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
И это выходное изображение:
Что вы можете улучшить:
- Обрабатывать различные ориентации эллипса (в настоящее время япредположим, что оси перпендикулярны / горизонтальны).Это не составит труда с использованием моментов изображения.
- Проверка выпуклости объекта (взгляните на
cvConvexityDefects
)
Вероятно, ваш лучший способ отличить монеты от других объектовбудет по форме.Я не могу думать о каких-либо других низкоуровневых функциях изображения (цвет явно отсутствует).Итак, я могу придумать два подхода:
Традиционное обнаружение объектов
Ваша первая задача - отделить объекты (монеты и не монеты) от фона.Метод Оцу, предложенный Карньери, будет хорошо работать здесь.Вы, кажется, беспокоитесь о том, что изображения двудольные , но я не думаю, что это будет проблемой.До тех пор, пока видимое количество рабочих мест, вы гарантированно будете иметь один пик в гистограмме.И пока на столе есть пара визуально различимых объектов, вам гарантирован ваш второй пик.
Расширьте ваше двоичное изображение пару раз, чтобы избавиться от шума, оставленногопороговая.Монеты относительно большие, поэтому они должны пережить эту морфологическую операцию.
Сгруппируйте белые пиксели в объекты, используя увеличение области - просто итеративно соедините смежные пиксели переднего плана.В конце этой операции у вас будет список непересекающихся объектов, и вы будете знать, какие пиксели занимает каждый объект.
Из этой информации вы узнаете ширину и высоту объекта (из предыдущегошаг).Итак, теперь вы можете оценить размер эллипса, который будет окружать объект, а затем посмотреть, насколько хорошо этот конкретный объект соответствует эллипсу.Возможно, будет проще использовать соотношение ширины и высоты.
В качестве альтернативы вы можете использовать моменты , чтобы более точно определить форму объекта.