Перейти к содержимому
Compvision.ru

Recommended Posts

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

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

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

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

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

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

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

Спасибо!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Цитата(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]

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Smorodov

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

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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Smorodov

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

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

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

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

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

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

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

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

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

CompVi4.jpg

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Ух.. жаль, что картинка не большая :)

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

Contours.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

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

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

Поделиться сообщением


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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

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

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

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

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

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

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

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

CvSeqReader reader;

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

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

Поделиться сообщением


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

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

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

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

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

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

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

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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

in function cvGetMat, cxarray.cpp(2781)

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
OpenCV ERROR: Null pointer (NULL array pointer is passed)

in function cvGetMat, cxarray.cpp(2781)

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

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

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×