Jump to content
Compvision.ru
FormatCft

Есть такая задачка

Recommended Posts

Доброго времени суток!

Допустим есть такая задачка:

Есть набор разкиданных по столу объектов.

Каждый объект имеет свою форму и надпись. Надпись может быть едва читаема.

Нужно разпознать каждый из объектов и поместить каждый объект в своё определеное место.

Легко ли такая задача реализуема? Меня интересует прежде всего вопросы написания ПО для PC.

Хотя физическая реализация "манипулятора" тоже интересует.

Спасибо!

Share this post


Link to post
Share on other sites

Цитата(FormatCft @ 30.4.2009, 0:35)

Доброго времени суток!

Допустим есть такая задачка:

Есть набор разкиданных по столу объектов.

Каждый объект имеет свою форму и надпись. Надпись может быть едва читаема.

Нужно разпознать каждый из объектов и поместить каждый объект в своё определеное место.

Легко ли такая задача реализуема? Меня интересует прежде всего вопросы написания ПО для PC.

Хотя физическая реализация "манипулятора" тоже интересует.

Спасибо!

Доброго времени суток.

Есть такой проектик: ContourMatching.rar

Проект находит контуры и выводит степень их похожести, сравнение идет с первым найденным контуром см картинку.

ContourMatching.png

Код

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

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

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
CvCapture* capture = 0;
IplImage *frame, *frame_copy,*gray = 0;
void ProcessFrame( IplImage* image );
int Val=100,Val1=100; // Значение переменной (для разных нужд)
CvFont font; // Описатель шрифта (см. дальше)
float edge_thresh=100;

#define WIDTHBYTES(bits) ((((bits) + 31) / 32) * 4)
//---------------------------------------------------------------------------
// Создание API шного битмапа из интеловского RGB изображения
//---------------------------------------------------------------------------
HBITMAP CreateRGBBitmap(IplImage* _Grab)
{
char *App;
LPBITMAPINFO lpbi = new BITMAPINFO;
lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
lpbi->bmiHeader.biWidth = _Grab->width;
lpbi->bmiHeader.biHeight =_Grab->height;
lpbi->bmiHeader.biPlanes = 1;
lpbi->bmiHeader.biBitCount = 24;
lpbi->bmiHeader.biCompression = BI_RGB;
lpbi->bmiHeader.biSizeImage = WIDTHBYTES((DWORD)_Grab->width * 8) * _Grab->height;
lpbi->bmiHeader.biXPelsPerMeter = 0;
lpbi->bmiHeader.biYPelsPerMeter = 0;
lpbi->bmiHeader.biClrUsed = 0;
lpbi->bmiHeader.biClrImportant = 0;
void* pBits;
HBITMAP hBitmap = CreateDIBSection(
NULL,
lpbi,
DIB_RGB_COLORS,
(void **)&pBits,
NULL,
0 );
delete lpbi;
if ( hBitmap )
App=(char*)pBits;
long int length=0;
if(_Grab->nChannels==1) // Серое или бинарное
{
length = _Grab->width*(_Grab->height);

for (int i=0;i<_Grab->height;i++)
{
for (int j=0;j<_Grab->width;j++)
{
App[_Grab->width*3*(_Grab->height-i-1)+j*3]=_Grab->imageData[_Grab->width*(i)+j];
App[_Grab->width*3*(_Grab->height-i-1)+j*3+1]=_Grab->imageData[_Grab->width*(i)+j];
App[_Grab->width*3*(_Grab->height-i-1)+j*3+2]=_Grab->imageData[_Grab->width*(i)+j];
}
}
}
if(_Grab->nChannels==3) // Цветное
{
for (int i=0;i<_Grab->height;i++)
{
memcpy(App+_Grab->width*3*(_Grab->height-i-1),_Grab->imageData+_Grab->width*3*i,_Grab->width*3); // Копируем память
}

}
return hBitmap;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Функция вывода изображения на HANDLE оконного компонента
//---------------------------------------------------------------------------
void APIDrawIpl(int x,int y,IplImage* _Grab,void *HANDLE)
{
HDC hMemDC,hDC;
hDC=GetDC(HANDLE);
hMemDC = CreateCompatibleDC(hDC);
HBITMAP Bitmap=CreateRGBBitmap(_Grab);
SelectObject(hMemDC,Bitmap);
BitBlt(hDC,x,y,_Grab->width,_Grab->height,hMemDC,0,0,SRCCOPY);
DeleteObject(Bitmap);
DeleteDC(hMemDC);
DeleteDC(hDC);
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
capture = cvCaptureFromCAM(0);
// Инициализация шрифта (теперь можем вывести какой-нибудь текст)
cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX,0.5,0.5,0,1,8);

Application->OnIdle = IdleLoop; // Поток обработки простоя
}
//---------------------------------------------------------------------------

void __fastcall TForm1::IdleLoop(TObject*, bool& done)
{
done = false;// Поток обработки простоя
if( capture )
{
if( !cvGrabFrame( capture ))
goto skip;
frame = cvRetrieveFrame( capture );
if( !frame )
goto skip;
if( !frame_copy )
frame_copy = cvCreateImage( cvSize(frame->width,frame->height),
IPL_DEPTH_8U, frame->nChannels );
if( !gray )
gray = cvCreateImage(cvSize(frame->width,frame->height), IPL_DEPTH_8U, 1);
if( frame->origin == IPL_ORIGIN_TL )
cvCopy( frame, frame_copy, 0 );
else
cvFlip( frame, frame_copy, 0 );

ProcessFrame( frame_copy );
}
skip:;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
cvReleaseImage( &frame_copy );
cvReleaseCapture( &capture );
}
//---------------------------------------------------------------------------
void ProcessFrame( IplImage* Grab )
{

float XC[1000]={0};
float YC[1000]={0};
float S[1000]={0};
double mt=0;
int ID=0;
int NumCont=0;
int NumCont1=0;
CvMemStorage* storage = 0;
CvSeq* contours;
CvSeq* Templ;
CvSeq* result;
storage = cvCreateMemStorage(0);
CvSeq* polygons = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );
CvMoments moments;

cvCvtColor(Grab, gray, CV_BGR2GRAY); // Получаем серый цвет

if(Form1->CheckBox1->Checked)
{cvThreshold( gray, gray, Val,255,CV_THRESH_BINARY_INV);}
else
{cvThreshold( gray, gray, Val,255,CV_THRESH_BINARY);}
// Необходимо удалить белый бордюр
cvRectangle(gray, cvPoint(0,0), cvPoint(gray->width-1,gray->height-1),CV_RGB(0,0,0));
// Утолщаем контуры
cvDilate( gray, gray, 0, 1 );
// Нах. границы
cvCanny( gray, gray, 50, Val1, 5 );
// Нах. контуры
cvFindContours( gray, storage,&contours,sizeof(CvContour),CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE,cvPoint(0,0))
;
//--------------
if(contours!=0)
{
NumCont=contours->total; // количество найденных контуров
}

int NC=0;
for(;contours!=0;contours = contours->h_next)
{
// Аппр. контуров полигонами
result = cvApproxPoly( contours, sizeof(CvContour), storage,CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.001, 0 );
// Площадь полигона
double area=fabs(cvContourArea(result,CV_WHOLE_SEQ));
// Объявление читалки точек
CvSeqReader reader;
// Инициализация читалки точек
cvStartReadSeq( result, &reader, 0 );
// Две точки
CvPoint pt[2];
if(area>500)
{
// Вычисляем моменты
cvMoments( result, &moments);
// Центр тяжести
float xc=(moments.m10/moments.m00);
float yc=(moments.m01/moments.m00);
// Обрубаем клонов
bool ok=1;
for (int o=0;o<NC;o++)
{
if( ((xc-XC[o])*(xc-XC[o])+(yc-YC[o])*(yc-YC[o]))<10 && fabs((area-S[o])/(area+S[o]))<0.3){ok=0;}
}
//
if(ok){

// Запоминаем шаблон
if(NumCont1==0)
{Templ=cvCloneSeq(result);}
// Сравниваем с найденными контурами
mt=cvMatchShapes( Templ, result,CV_CONTOURS_MATCH_I3);

NumCont1++;
double M00=moments.m00;
double M20=moments.m20;
double M02=moments.m02;
double M11=moments.m11;

double A=(M20/M00)-xc*xc;
double B=2*((M11/M00)-xc*yc);
double C=(M02/M00)-yc*yc;

double LL=sqrt( ( (A+C)+sqrt(B*B+(A-C)*(A-C)) )/2)*2;
double LW=sqrt( ( (A+C)-sqrt(B*B+(A-C)*(A-C)) )/2)*2;

// Для вычисления угла нужны центральные моменты инерции
M20=moments.mu20;
M02=moments.mu02;
M11=moments.mu11;

double theta=(atan2(2*M11,(M02-M20))/2)*(180/M_PI);

// Отмечаем центр
cvCircle( Grab, cvPoint(xc,yc), 3, CV_RGB(0,255,255), -1, 8, 0 );

cvPutText( Grab,(FloatToStrF(mt,ffGeneral,4,3)).c_str(), cvPoint(xc,yc),&font, CV_RGB(255,255,0));

// Достаем точки из хранилища точек и рисуем линии
for (int i=0;i<result->total-1;i++)
{
CV_READ_SEQ_ELEM( pt[0], reader );
CV_READ_SEQ_ELEM( pt[1], reader );
if(mt>0.5){cvLine(Grab,pt[0],pt[1],CV_RGB(255,0,0));}
else{cvLine(Grab,pt[0],pt[1],CV_RGB(0,255,0));}
}
}
XC[NC]=xc;
YC[NC]=yc;
S[NC]=area;
NC++;
} // if
} // for
cvPutText( Grab,("NCont1="+FloatToStrF(NumCont1,ffGeneral,4,3)).c_str(), cvPoint(20,5),&font, CV_RGB(255,255,0));
//-----------------------------------------------------------------------------

APIDrawIpl(10,10,gray,Form1->Handle); // Рисуем результат
APIDrawIpl(10+Grab->width,10,Grab,Form1->Handle); // Рисуем результат

cvReleaseMemStorage(&storage);
}

void __fastcall TForm1::TrackBar1Change(TObject *Sender)
{
Val=TrackBar1->Position;
edge_thresh=float(Val);

}
//---------------------------------------------------------------------------

void __fastcall TForm1::TrackBar2Change(TObject *Sender)
{
Val1=TrackBar2->Position;
}
//---------------------------------------------------------------------------[/code]

Share this post


Link to post
Share on other sites

Smorodov

По поводу картинки.. Насколько я понимаю, те подписанные числа, это и есть степени схожести контуров?

Т.е. классифицировать один от другого можно по некоторому пороговому числу..

А к поворотам это решение случаем не чувствительно? Ну мало ли конечно, контур как контур, какая разница в какую сторону развёрнут предмет.. :)

Интересно вот.. а числа и символы таким алгоритмом тоже можно распознавать по порогу? :)

Хотя скорее всего там настолько близкие пороги будут, что не отличишь одно от другого..

PS постараюсь раздобыть и установить C-Builder, чтобы запустить этот проект. Винда где-то тоже валялась на дисках.. :)

Share this post


Link to post
Share on other sites
Smorodov

По поводу картинки.. Насколько я понимаю, те подписанные числа, это и есть степени схожести контуров?

Т.е. классифицировать один от другого можно по некоторому пороговому числу..

А к поворотам это решение случаем не чувствительно? Ну мало ли конечно, контур как контур, какая разница в какую сторону развёрнут предмет.. :)

Интересно вот.. а числа и символы таким алгоритмом тоже можно распознавать по порогу? :)

Хотя скорее всего там настолько близкие пороги будут, что не отличишь одно от другого..

PS постараюсь раздобыть и установить C-Builder, чтобы запустить этот проект. Винда где-то тоже валялась на дисках.. :)

Картинка с другого моего сайта (старого)

(думаю с цифрами получится)

Повороты не имеют значения. Правда и 6 от 9 тоже не отличить :)

CompVi4.jpg

Цифры означают степень различия, насколько я помню. Красятся в красный или зеленый по порогу (у красного человечика пропорции другие, а зеленые - клоны повернутые).

Share this post


Link to post
Share on other sites
Ух.. жаль, что картинка не большая :)

Пришлось напрячь художественные способности и повторить шедевр :)

Contours.jpg

Share this post


Link to post
Share on other sites

Ааа.. вон оно в чём смысл!! Понятно. Спасибо большое за художество! :)

Таким образом, в принципе, можно ведь и жесты пальцев распознавать?

А к изменению масштаба как оно относится?

Share this post


Link to post
Share on other sites
Ааа.. вон оно в чём смысл!! Понятно. Спасибо большое за художество! :)

Таким образом, в принципе, можно ведь и жесты пальцев распознавать?

А к изменению масштаба как оно относится?

А не влияет масштаб :)

Поправил картинку выше.

Share this post


Link to post
Share on other sites

Изучаю тут код приведённый выше, есть несколько вопросов. Если не сложно - ответьте пожалуйста! :)

// Необходимо удалить белый бордюр

А зачем он там вообще появляется? :) Просто интересно..

// Аппр. контуров полигонами

result = cvApproxPoly( contours, sizeof(CvContour), storage,CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.001, 0 );

Вот здесь не понял, зачем нам нужно сделать аппроксимацию контуров полигонами?

Чувствую, что здесь ключевой момент, а он у меня остался за гранью понимания. Мы же сходство контуров определяем не по числу, представленному площадью фигуры.. Мало ли одинаковых площадей у разных фигур.. :)

// Объявление читалки точек

CvSeqReader reader;

Что за читалка точек такая? Это массив хранящий все наши контурные точки, при необходимости к которым можно доступиться?

Я, кстати, никак не могу найти официальное руководство к OpenCV. Это бы много вопросов решило.. Не подскажите где оно есть?

Share this post


Link to post
Share on other sites
Изучаю тут код приведённый выше, есть несколько вопросов. Если не сложно - ответьте пожалуйста! :)

А зачем он там вообще появляется? :) Просто интересно..

Вот здесь не понял, зачем нам нужно сделать аппроксимацию контуров полигонами?

Чувствую, что здесь ключевой момент, а он у меня остался за гранью понимания. Мы же сходство контуров определяем не по числу, представленному площадью фигуры.. Мало ли одинаковых площадей у разных фигур.. :)

Что за читалка точек такая? Это массив хранящий все наши контурные точки, при необходимости к которым можно доступиться?

Я, кстати, никак не могу найти официальное руководство к OpenCV. Это бы много вопросов решило.. Не подскажите где оно есть?

Белый бордюр не знаю зачем, это у Интел надо спрашивать, долго мучился с этим, не мог понять, почему моменты неправильно ищет.

Контуры в OpenCV задаются цепями Фримана (это последовательность команд такая вверх, вверх-влево, влево,... и т.д.), работать с этим не удобно.

Аппроксимация нужна чтобы получить координаты границы контура в виде последовательности точек p(x,y), которые можно потом линией соединить, и получится граница контура (полигон). параметры функции аппроксиматора задают с какой погрешностью допустимо это сделать.

CvSeq - аналог класса Vector в c++

CvSeqReader - это что то похожее на итераторы класса Vector в c++

Офиц. руководство

Share this post


Link to post
Share on other sites

OpenCV ERROR: Null pointer (NULL array pointer is passed)

in function cvGetMat, cxarray.cpp(2781)

выдаёт вот такую вот ошибку. в чем может быть проблема?

Share this post


Link to post
Share on other sites
OpenCV ERROR: Null pointer (NULL array pointer is passed)

in function cvGetMat, cxarray.cpp(2781)

выдаёт вот такую вот ошибку. в чем может быть проблема?

Какой то массив (изображение) не создано (загружено). Прогоните в отладке по строчкам, посмотрите на какой строчке выдает ошибку.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Recently Browsing   0 members

    No registered users viewing this page.

×