FormatCft 0 Report post Posted April 29, 2009 Доброго времени суток! Допустим есть такая задачка: Есть набор разкиданных по столу объектов. Каждый объект имеет свою форму и надпись. Надпись может быть едва читаема. Нужно разпознать каждый из объектов и поместить каждый объект в своё определеное место. Легко ли такая задача реализуема? Меня интересует прежде всего вопросы написания ПО для PC. Хотя физическая реализация "манипулятора" тоже интересует. Спасибо! Share this post Link to post Share on other sites
Smorodov 578 Report post Posted April 30, 2009 Цитата(FormatCft @ 30.4.2009, 0:35) Доброго времени суток! Допустим есть такая задачка: Есть набор разкиданных по столу объектов. Каждый объект имеет свою форму и надпись. Надпись может быть едва читаема. Нужно разпознать каждый из объектов и поместить каждый объект в своё определеное место. Легко ли такая задача реализуема? Меня интересует прежде всего вопросы написания ПО для PC. Хотя физическая реализация "манипулятора" тоже интересует. Спасибо! Доброго времени суток. Есть такой проектик: ContourMatching.rar Проект находит контуры и выводит степень их похожести, сравнение идет с первым найденным контуром см картинку. Код //--------------------------------------------------------------------------- #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
ProgerX 0 Report post Posted June 26, 2009 Smorodov По поводу картинки.. Насколько я понимаю, те подписанные числа, это и есть степени схожести контуров? Т.е. классифицировать один от другого можно по некоторому пороговому числу.. А к поворотам это решение случаем не чувствительно? Ну мало ли конечно, контур как контур, какая разница в какую сторону развёрнут предмет.. Интересно вот.. а числа и символы таким алгоритмом тоже можно распознавать по порогу? Хотя скорее всего там настолько близкие пороги будут, что не отличишь одно от другого.. PS постараюсь раздобыть и установить C-Builder, чтобы запустить этот проект. Винда где-то тоже валялась на дисках.. Share this post Link to post Share on other sites
Smorodov 578 Report post Posted June 26, 2009 Smorodov По поводу картинки.. Насколько я понимаю, те подписанные числа, это и есть степени схожести контуров? Т.е. классифицировать один от другого можно по некоторому пороговому числу.. А к поворотам это решение случаем не чувствительно? Ну мало ли конечно, контур как контур, какая разница в какую сторону развёрнут предмет.. Интересно вот.. а числа и символы таким алгоритмом тоже можно распознавать по порогу? Хотя скорее всего там настолько близкие пороги будут, что не отличишь одно от другого.. PS постараюсь раздобыть и установить C-Builder, чтобы запустить этот проект. Винда где-то тоже валялась на дисках.. Картинка с другого моего сайта (старого) (думаю с цифрами получится) Повороты не имеют значения. Правда и 6 от 9 тоже не отличить Цифры означают степень различия, насколько я помню. Красятся в красный или зеленый по порогу (у красного человечика пропорции другие, а зеленые - клоны повернутые). Share this post Link to post Share on other sites
ProgerX 0 Report post Posted June 26, 2009 Ух.. жаль, что картинка не большая Share this post Link to post Share on other sites
Smorodov 578 Report post Posted June 26, 2009 Ух.. жаль, что картинка не большая Пришлось напрячь художественные способности и повторить шедевр Share this post Link to post Share on other sites
ProgerX 0 Report post Posted June 26, 2009 Ааа.. вон оно в чём смысл!! Понятно. Спасибо большое за художество! Таким образом, в принципе, можно ведь и жесты пальцев распознавать? А к изменению масштаба как оно относится? Share this post Link to post Share on other sites
Smorodov 578 Report post Posted June 26, 2009 Ааа.. вон оно в чём смысл!! Понятно. Спасибо большое за художество! Таким образом, в принципе, можно ведь и жесты пальцев распознавать? А к изменению масштаба как оно относится? А не влияет масштаб Поправил картинку выше. Share this post Link to post Share on other sites
ProgerX 0 Report post Posted June 26, 2009 О, так ещё нагляднее получилось! Будем пробовать! Share this post Link to post Share on other sites
ProgerX 0 Report post Posted June 27, 2009 Изучаю тут код приведённый выше, есть несколько вопросов. Если не сложно - ответьте пожалуйста! // Необходимо удалить белый бордюр А зачем он там вообще появляется? Просто интересно.. // Аппр. контуров полигонами 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
Smorodov 578 Report post Posted June 27, 2009 Изучаю тут код приведённый выше, есть несколько вопросов. Если не сложно - ответьте пожалуйста! А зачем он там вообще появляется? Просто интересно.. Вот здесь не понял, зачем нам нужно сделать аппроксимацию контуров полигонами? Чувствую, что здесь ключевой момент, а он у меня остался за гранью понимания. Мы же сходство контуров определяем не по числу, представленному площадью фигуры.. Мало ли одинаковых площадей у разных фигур.. Что за читалка точек такая? Это массив хранящий все наши контурные точки, при необходимости к которым можно доступиться? Я, кстати, никак не могу найти официальное руководство к OpenCV. Это бы много вопросов решило.. Не подскажите где оно есть? Белый бордюр не знаю зачем, это у Интел надо спрашивать, долго мучился с этим, не мог понять, почему моменты неправильно ищет. Контуры в OpenCV задаются цепями Фримана (это последовательность команд такая вверх, вверх-влево, влево,... и т.д.), работать с этим не удобно. Аппроксимация нужна чтобы получить координаты границы контура в виде последовательности точек p(x,y), которые можно потом линией соединить, и получится граница контура (полигон). параметры функции аппроксиматора задают с какой погрешностью допустимо это сделать. CvSeq - аналог класса Vector в c++ CvSeqReader - это что то похожее на итераторы класса Vector в c++ Офиц. руководство Share this post Link to post Share on other sites
sigizmynd 0 Report post Posted May 18, 2010 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
Smorodov 578 Report post Posted May 18, 2010 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
sigizmynd 0 Report post Posted May 18, 2010 все получилось, оказалось не задал параметры для изображений. Спасибо Share this post Link to post Share on other sites