Jump to content
Compvision.ru
Sign in to follow this  
milk3d

Определение прямоугольной рамки

Recommended Posts

Недавно столкнулся с такой задачей, поиск прямоугольной рамки на фотографии. Что важно это определить её углы и узнать какой это именно угол. Рамка может быть так же затемнена и на неё можно смотреть под разными углами, т.е. на некоторых изображениях она может выглядеть как ромб.

Я знаю точно, что эту задачу можно решить с помощью OpenCV, я уже начал с ней разбираться. Но хотелось бы узнать в каком направлении нужно работать, что бы это сделать и подходит ли представленный рисунок для этого? До этого положение центров цветных прямоугольников я находил по их центрам масс, но при этом не учитывал вероятности принодлежности точек к ним. Для очень контрастных изображений результат был довольно хороший, а вот для фотографий всё сложнее.

Примерный вид рамки:

i-60.jpg

Share this post


Link to post
Share on other sites
Недавно столкнулся с такой задачей, поиск прямоугольной рамки на фотографии. Что важно это определить её углы и узнать какой это именно угол. Рамка может быть так же затемнена и на неё можно смотреть под разными углами, т.е. на некоторых изображениях она может выглядеть как ромб.

Я знаю точно, что эту задачу можно решить с помощью OpenCV, я уже начал с ней разбираться. Но хотелось бы узнать в каком направлении нужно работать, что бы это сделать и подходит ли представленный рисунок для этого? До этого положение центров цветных прямоугольников я находил по их центрам масс, но при этом не учитывал вероятности принодлежности точек к ним. Для очень контрастных изображений результат был довольно хороший, а вот для фотографий всё сложнее.

Примерный вид рамки:

i-60.jpg

Может что то типа программы для калибровки камеры подойдет + раскраска угловых клеток. Pамку взять в виде цветной шахматной доски, там как раз и матрица поворота возвращается.

Вообще, насчет ориентации, можно поискать по слову Homography или Homography transform, для начала в руководстве по OpenCV.

Еще можно пример с библиотекой блобов посмотреть, когда ставишь перед камерой шахматную доску, он клетки в разные цвета красит (Количество блобов я там ограничил, поэтому все не закрасит (для выделения/фильтрации объектов по размеру подойдет)).

Share this post


Link to post
Share on other sites

Спасибо за ответ. Всё попробую, может получится. Вот просматривал примеры, которые устанавливаются в месте с OpeCV, и наткнулся на пример демонстрирующий работу SURF Speeded Up Robust Features алгоритма (название примера find_obj). Этот алгоритм очень хорошо работает, вот только не уверен можно ли его использовать для работы с камерой. Если всё таки можно то рамки могут выглядеть так. Кстати он определяет и перевернутые объекты.

1.

i-61.jpg

2.

i-62.jpg

Share this post


Link to post
Share on other sites
Спасибо за ответ. Всё попробую, может получится. Вот просматривал примеры, которые устанавливаются в месте с OpeCV, и наткнулся на пример демонстрирующий работу SURF Speeded Up Robust Features алгоритма (название примера find_obj). Этот алгоритм очень хорошо работает, вот только не уверен можно ли его использовать для работы с камерой. Если всё таки можно то рамки могут выглядеть так. Кстати он определяет и перевернутые объекты.

1.

i-61.jpg

2.

i-62.jpg

Все примеры могут работать с камерой (некоторые после незначительной переделки), главное там функция обработки изображения, а как Вы его получили дело Ваше. Кстати, по моему на основе этого примера и была сделана программка из этой темы http://www.compvision.ru/forum/index.php?showtopic=11

Share this post


Link to post
Share on other sites
Кстати, по моему на основе этого примера и была сделана программка из этой темы http://www.compvision.ru/forum/index.php?showtopic=11

Не знаю точно почему но у меня эта программа не работает. Но я переделал стандартный пример, под видеокамеру. Только вот как и думал скорости мало ;) Зато различает разные рамки, а так же полу закрытые и повернутые до угла 45 градусов. Это хороший но медленный алгоритм, хотя процессор при его выполнении не нагружается по полной. Продолжаем работать.

Если вы не против я буду писать сюда небольшие отчеты о продвижении.

Share this post


Link to post
Share on other sites
Если вы не против я буду писать сюда небольшие отчеты о продвижении.

Думаю всем это будет интересно.

Для скорости надо поставить IPP, если еще не установлены, с ними заметно быстрее работает. У меня кадров 15-20 дает на Athlon64x2 5000 (вэбка примерно 350х280 пикселей). На Вашей же картинке (вэбкой с экрана) все отлично. Кстати, в каком разрешении работаете? Может после считывания изображения уменьшать его, до разумных размеров?

IPP существенно (в разы) ускоряющая работу OpenCV хреновина берется с сайта Intel.

Ставим интелловский компилятор Intel C Plus Plus Compiler v10.1.025

Google рулит.

IPP ставится после установки компилятора. И использует его лицензию smile.gif

Подключается автоматически, нужно только указать в системной переменной Path путь к директории, /bin библиотеки IPP, в самой программе ничего отдельно указывать не нужно.

Share this post


Link to post
Share on other sites

У меня обычная камера, подключаемая к компьютеру через DV. Её разрешение 720 на 576, довольно большое. Я уменьшил его в два раза до 360 на 288, производительность выросла примерно до того уровня что и у вас 15-20 фпс.

Насчёт IPP я не могу понять, он бесплатный или нет? На сайте Интела я нашел вот это http://software.intel.com/en-us/intel-ipp это оно?

Если не трудно, дайте прямую ссылку.

Спасибо.

Share this post


Link to post
Share on other sites
У меня обычная камера, подключаемая к компьютеру через DV. Её разрешение 720 на 576, довольно большое. Я уменьшил его в два раза до 360 на 288, производительность выросла примерно до того уровня что и у вас 15-20 фпс.

Насчёт IPP я не могу понять, он бесплатный или нет? На сайте Интела я нашел вот это http://software.intel.com/en-us/intel-ipp это оно?

Если не трудно, дайте прямую ссылку.

Спасибо.

Для линукс, насколько я знаю бесплатный, а для windows платный.

Качается отсюда раздел "Performance Libraries" : http://software.intel.com/en-us/articles/i...luation-center/

Share this post


Link to post
Share on other sites

Недавно меня спросили о коде получившейся программы. Я вот и подумал, что он может пригодиться еще кому то, и хотя эта программа всего лишь переделанный пример из OpenCV архива, под работу с видео. Я ничего не менял касающегося SURF алгоритма, только добавил работу с видео. Программа написана для MV С++.

image.bmp - имя файла картинки, которая обводится рамкой.

#include <cv.h>

#include <highgui.h>

#include <ctype.h>

#include <stdio.h>

#include <stdlib.h>


#include <iostream>

#include <vector>


#define ZOOM 2


using namespace std;


IplImage *image = 0;


double compareSURFDescriptors( const float* d1, const float* d2, double best, int length )

{

	double total_cost = 0;

	assert( length % 4 == 0 );

	for( int i = 0; i < length; i += 4 )

	{

		double t0 = d1[i] - d2[i];

		double t1 = d1[i+1] - d2[i+1];

		double t2 = d1[i+2] - d2[i+2];

		double t3 = d1[i+3] - d2[i+3];

		total_cost += t0*t0 + t1*t1 + t2*t2 + t3*t3;

		if( total_cost > best )

			break;

	}

	return total_cost;

}


int naiveNearestNeighbor( const float* vec, int laplacian,

					  const CvSeq* model_keypoints,

					  const CvSeq* model_descriptors )

{

	int length = (int)(model_descriptors->elem_size/sizeof(float));

	int i, neighbor = -1;

	double d, dist1 = 1e6, dist2 = 1e6;

	CvSeqReader reader, kreader;

	cvStartReadSeq( model_keypoints, &kreader, 0 );

	cvStartReadSeq( model_descriptors, &reader, 0 );


	for( i = 0; i < model_descriptors->total; i++ )

	{

		const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;

		const float* mvec = (const float*)reader.ptr;

		CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );

		CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );

		if( laplacian != kp->laplacian )

			continue;

		d = compareSURFDescriptors( vec, mvec, dist2, length );

		if( d < dist1 )

		{

			dist2 = dist1;

			dist1 = d;

			neighbor = i;

		}

		else if ( d < dist2 )

			dist2 = d;

	}

	if ( dist1 < 0.6*dist2 )

		return neighbor;

	return -1;

}


void findPairs( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors,

		   const CvSeq* imageKeypoints, const CvSeq* imageDescriptors, vector<int>& ptpairs )

{

	int i;

	CvSeqReader reader, kreader;

	cvStartReadSeq( objectKeypoints, &kreader );

	cvStartReadSeq( objectDescriptors, &reader );

	ptpairs.clear();


	for( i = 0; i < objectDescriptors->total; i++ )

	{

		const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;

		const float* descriptor = (const float*)reader.ptr;

		CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );

		CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );

		int nearest_neighbor = naiveNearestNeighbor( descriptor, kp->laplacian, imageKeypoints, imageDescriptors );

		if( nearest_neighbor >= 0 )

		{

			ptpairs.push_back(i);

			ptpairs.push_back(nearest_neighbor);

		}

	}

}


/* a rough implementation for object location */

int locatePlanarObject( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors,

					const CvSeq* imageKeypoints, const CvSeq* imageDescriptors,

					const CvPoint src_corners[4], CvPoint dst_corners[4] )

{

	double h[9];

	CvMat _h = cvMat(3, 3, CV_64F, h);

	vector<int> ptpairs;

	vector<CvPoint2D32f> pt1, pt2;

	CvMat _pt1, _pt2;

	int i, n;


	findPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );

	n = ptpairs.size()/2;

	if( n < 4 )

		return 0;


	pt1.resize(n);

	pt2.resize(n);

	for( i = 0; i < n; i++ )

	{

		pt1[i] = ((CvSURFPoint*)cvGetSeqElem(objectKeypoints,ptpairs[i*2]))->pt;

		pt2[i] = ((CvSURFPoint*)cvGetSeqElem(imageKeypoints,ptpairs[i*2+1]))->pt;

	}


	_pt1 = cvMat(1, n, CV_32FC2, &pt1[0] );

	_pt2 = cvMat(1, n, CV_32FC2, &pt2[0] );

	if( !cvFindHomography( &_pt1, &_pt2, &_h, CV_RANSAC, 5 ))

		return 0;


	for( i = 0; i < 4; i++ )

	{

		double x = src_corners[i].x, y = src_corners[i].y;

		double Z = 1./(h[6]*x + h[7]*y + h[8]);

		double X = (h[0]*x + h[1]*y + h[2])*Z;

		double Y = (h[3]*x + h[4]*y + h[5])*Z;

		dst_corners[i] = cvPoint(cvRound(X), cvRound(Y));

	}


	return 1;

}


int main(int argc, char** argv)

{


	const char* object_filename = argc == 3 ? argv[1] : "image.bmp";


	CvMemStorage* storage = cvCreateMemStorage(0);


	cvNamedWindow("Object", 1);

	cvNamedWindow("Object Correspond", 1);


	static CvScalar colors[] = 

	{

		{{0,0,255}},

		{{0,128,255}},

		{{0,255,255}},

		{{0,255,0}},

		{{255,128,0}},

		{{255,255,0}},

		{{255,0,0}},

		{{255,0,255}},

		{{255,255,255}}

	};


	IplImage* object = cvLoadImage( object_filename, CV_LOAD_IMAGE_GRAYSCALE );


	IplImage* object_color = cvCreateImage(cvGetSize(object), 8, 3);

	cvCvtColor( object, object_color, CV_GRAY2BGR );


	CvSeq *objectKeypoints = 0, *objectDescriptors = 0;

	int i;

	CvSURFParams params = cvSURFParams(500, 1);



	cvExtractSURF( object, 0, &objectKeypoints, &objectDescriptors, storage, params );


	CvPoint src_corners[4] = {{0,0}, {object->width,0}, {object->width, object->height}, {0, object->height}};

	CvPoint dst_corners[4];


	for( i = 0; i < objectKeypoints->total; i++ )

	{

		CvSURFPoint* r = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, i );

		CvPoint center;

		int radius;

		center.x = cvRound(r->pt.x);

		center.y = cvRound(r->pt.y);

		radius = cvRound(r->size*1.2/9.*2);

		cvCircle( object_color, center, radius, colors[0], 1, 8, 0 );

	}

	cvShowImage( "Object", object_color );


	CvCapture* capture = cvCreateCameraCapture(0);

	IplImage* frame;

	//Размеры входного изображения

	int width = (int)cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_WIDTH);

	int height= (int)cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_HEIGHT);

	CvSize size = cvSize(width,height);

	//Для серого изображения

	IplImage* image1 = cvCreateImage(size, 8, 1);

	//Для уменьшенного в ZOOM раз серого изображения

	IplImage* image2 = cvCreateImage(cvSize((int)(width/ZOOM),(int)(height/ZOOM)), 8, 1);


	while(1){

		frame = cvQueryFrame( capture );

		if( !frame ) break;


		//Делаем входное изображение серым

		cvCvtColor( frame, image1, CV_RGBA2GRAY );

		//Уменьшаем серое изображение

		cvResize(image1,image2,CV_INTER_LINEAR);


		CvSeq *imageKeypoints = 0, *imageDescriptors = 0;

		cvExtractSURF( image2, 0, &imageKeypoints, &imageDescriptors, storage, params );


		if( locatePlanarObject( objectKeypoints, objectDescriptors, imageKeypoints,

			imageDescriptors, src_corners, dst_corners ))

		{

			//Рисуем линии контура рамки на входном изображении(frame)

			for( i = 0; i < 4; i++ )

			{

				CvPoint r1 = dst_corners[i%4];

				CvPoint r2 = dst_corners[(i+1)%4];

				cvLine( frame, cvPoint(r1.x*ZOOM, r1.y*ZOOM),

					cvPoint(r2.x*ZOOM, r2.y*ZOOM), colors[8] );


			}

		}

		//Выводим входное изображение(frame) в окно программы

		cvShowImage( "Object Correspond", frame );

		char c = cvWaitKey(33);

		if( c == 27 ) break;

	}

	cvReleaseCapture( &capture );

	cvDestroyWindow("Object");

	cvDestroyWindow("Object Correspond");


	return 0;

}

  • Like 1

Share this post


Link to post
Share on other sites

Здравствуйте!

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

Для некоммерческого использования Intel® C++ Composer XE 2011 for Linux, который ключает в себя Intel® C++ Compiler, Intel® Integrated Performance Primitives (IPP), Intel® Math Kernel Library, Intel® Parallel Building Blocks (PBB), можно бесплатно скачать отсюда.

Share this post


Link to post
Share on other sites

У меня программа вылетала с ошибкой "divizion by zero" на строчке:

if( !cvFindHomography( &_pt1, &_pt2, &_h, CV_RANSAC, 5 ))
Стал искать причину, обнаружил, что функция naiveNearestNeighbor может возвращать одну и ту же точку сколько угодно раз. То есть, для примера, у нас найдено n особенностей для исходного изображения и 1 особенность для изображения с камеры, тогда эта одна точка почему-то будет считаться парой для всех n точек из шаблона. Скорее всего функция naiveNearestNeighbor не очень верно работает (я с ней особо не разбирался), возможно, необходимо добавить туда еще какой-то порог или изменить существующие параметры. Добавил в функцию findPairs вот такую заглушку:
                if( nearest_neighbor >= 0 && nearest_neighbor != last_neighbor)

                {

                        ptpairs.push_back(i);

                        ptpairs.push_back(nearest_neighbor);

                        last_neighbor = nearest_neighbor;

                }

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

На всякий случай прикрепляю проект для borland builder 6, вдруг кому-то пригодится.

OpenCV SURF.rar

  • Like 2

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

Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×