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