Детектирование объектов с помощью особенностей в OpenCV: FREAK. Детектирование множества объектов. / OpenCV / Recog.ru - Распознавание образов для программистов


Детектирование объектов с помощью особенностей в OpenCV: FREAK. Детектирование множества объектов.

Детектирование объектов с помощью SURF было ранее описано здесь. В этой статье использован C++ интерфейс, FREAK и детектирование множества объектов. Надежность детектирования объектов с помощью FREAK ниже, чем SURF, однако его работа намного быстрее, что позволяет использовать алгоритм на мобильных и встроенных системах. Пример работы представлен на рисунке:

Рассмотрим исходный код, который позволяет этого достигнуть. Код приведен полностью для желающих быстро вставить его в свой проект.
#include <iostream>
#include <string>
#include <vector>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/legacy/legacy.hpp>

using namespace cv; 

int main( int argc, char** argv )
{
	if( argc != 3 ) return 1;

	Mat ImageTemple = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE ); 
	if( !ImageTemple.data )
		return 2; // Ошибка

	Mat Image = imread(argv[2], CV_LOAD_IMAGE_GRAYSCALE ); 
	if( !Image.data )
		return 3; // Ошибка

	std::vector<KeyPoint> keypointsImageTemple, keypointsImage;
	Mat descriptorsImageTemple, descriptorsImage;
	 std::vector<DMatch> matches;
	// Инициалищация класса детектора особенностей, 1000 - пороговое значение для отсеивания 
	// малозначимых особенностей
	SurfFeatureDetector detector(1000);

	// Класс для FREAK особенностей. Можно настраивать режимы сравнения особенностей:
	// FREAK extractor(true, true, 22, 4, std::vector<int>());
	FREAK extractor;

	// Используется для определение совпадений особенностей - мера Хемминга
	BruteForceMatcher<Hamming> matcher;

	// Детектирование
	double t = (double)getTickCount();

	detector.detect( ImageTemple, keypointsImageTemple );
	detector.detect( Image, keypointsImage );

	t = ((double)getTickCount() - t)/getTickFrequency();
	std::cout << "detection time [s]: " << t/1.0 << std::endl;

	// Извлечение особенностей
	t = (double)getTickCount();

	extractor.compute( ImageTemple, keypointsImageTemple, descriptorsImageTemple );
	extractor.compute( Image, keypointsImage, descriptorsImage );

	t = ((double)getTickCount() - t)/getTickFrequency();
	std::cout << "extraction time [s]: " << t << std::endl;

	// Сравнение
	t = (double)getTickCount();

	matcher.match( descriptorsImageTemple, descriptorsImage, matches);

	t = ((double)getTickCount() - t)/getTickFrequency();
	std::cout << "matching time [s]: " << t << std::endl;

	// Отобразить на изображении
	Mat imgMatch;

	drawMatches( ImageTemple, keypointsImageTemple, Image, keypointsImage, matches, imgMatch);
	imwrite("matches.jpeg", imgMatch);
		
	std::vector<Point2f> obj;
	std::vector<Point2f> scene; 	
	for( int i = 0; i < matches.size(); i++ )
	{		
		obj.push_back( keypointsImageTemple[ matches[i].queryIdx ].pt );
		scene.push_back( keypointsImage[ matches[i].trainIdx ].pt );
	}  	Mat H = findHomography( obj, scene, CV_RANSAC );  	std::vector<Point2f> obj_corners(4);
	obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( ImageTemple.cols, 0 );
	obj_corners[2] = cvPoint( ImageTemple.cols, ImageTemple.rows ); obj_corners[3] = cvPoint( 0, ImageTemple.rows );
	std::vector<Point2f> scene_corners(4);

	perspectiveTransform( obj_corners, scene_corners, H);

	//-- Draw lines between the corners (the mapped object in the scene - image_2 )
	line( imgMatch, scene_corners[0] + Point2f( ImageTemple.cols, 0), scene_corners[1] + Point2f( ImageTemple.cols, 0), Scalar(0, 255, 0), 4 );
	line( imgMatch, scene_corners[1] + Point2f( ImageTemple.cols, 0), scene_corners[2] + Point2f( ImageTemple.cols, 0), Scalar( 0, 255, 0), 4 );
	line( imgMatch, scene_corners[2] + Point2f( ImageTemple.cols, 0), scene_corners[3] + Point2f( ImageTemple.cols, 0), Scalar( 0, 255, 0), 4 );
	line( imgMatch, scene_corners[3] + Point2f( ImageTemple.cols, 0), scene_corners[0] + Point2f( ImageTemple.cols, 0), Scalar( 0, 255, 0), 4 ); 	
	imwrite("matches3.jpeg", imgMatch);
	return 0;
}


Для любых особенностей в OpenCV необходимо инициализировать класс SurfFeatureDetector. Первое действие после различных инициализаций – это детектирование особенностей detector.detect для эталонного изображения и изображения сцены. После чего для каждого изображения по результатам работы детектора вычисляются FREAK особенности: extractor.compute.
Сравнение схожести особенностей осуществляется с помощью matcher.match.
Далее присутствует цикл с формированием точек из особенностей для обоих изображений. На основании точек вычисляется гомография изображений findHomography. Положение и поворот объекта вычисляется с помощью функции perspectiveTransform. Ну а затем – вывод на изображение.
Эталонное изображение:

Изображение сцены:

Результат представлен вначале.
Однако здесь возникает вопрос, как рассчитывать оптимальный порог особенностей: SurfFeatureDetector detector(1000);. Ответ – экспериментально. Некоторую информацию по данному вопросу вы можете получить здесь.
Предположим, что у нас на изображении несколько объектов:

Результат работы программы будет следующий:

Естественно, что такая ситуация не устраивает. Для того, чтобы детектировать все объекты, необходимо разделить изображение на несколько частей. Однако здесь следует помнить, что если изображение разделить на непересекающиеся блоки (пример изображение 100x100 разделить на 4 блока по 50x50), то может возникнуть ситуация, когда объект будет частично находиться в нескольких блоках и не будет детектирован. Для избегания этого необходимо делать пересекающиеся блоки, что несколько замедлит работу, но улучшит качество (пример изображение 100x100 разделить на 9 блоков по 50x50 так, как показано в примере). Пример программы детектирующий множество объектов ниже:
#include <iostream>
#include <string>
#include <vector>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/legacy/legacy.hpp>

using namespace cv; 

int main( int argc, char** argv )
{
	if( argc != 3 ) return 1;

	Mat ImageTemple = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE ); 
	if( !ImageTemple.data )
		return 2; // Ошибка

	Mat Image = imread(argv[2], CV_LOAD_IMAGE_GRAYSCALE ); 
	if( !Image.data )
		return 3; // Ошибка

	std::vector<KeyPoint> keypointsImageTemple;
    Mat descriptorsImageTemple;
    std::vector<DMatch> matches;
    
	// Инициалищация класса детектора особенностей, 1000 - пороговое значение для отсеивания 
	// малозначимых особенностей
    SurfFeatureDetector detector(1000);	

    detector.detect( ImageTemple, keypointsImageTemple );

	int maxy = 3;
	int maxx = 3;

	Mat Draw_mat = imread(argv[2], 1 ); 

	for( int y = 0; y < maxy; y++ )
		for( int x = 0; x < maxx; x++ )
		{
			// Класс для FREAK особенностей. Можно настраивать режимы сравнения особенностей:
			// FREAK extractor(true, true, 22, 4, std::vector<int>());
			FREAK extractor;

			// Используется для определение совпадений особенностей - мера Хемминга
			BruteForceMatcher<Hamming> matcher;

			std::vector<KeyPoint> keypointsImage;
			Mat descriptorsImage;
			CvRect Rect = cvRect( x * ( Image.cols / ( maxx + 1) ), y * ( Image.rows / ( maxy + 1) ), 
				2 * ( Image.cols / ( maxx + 1) ), 2 * ( Image.rows / ( maxy + 1) ) );
			Mat ImageROI( Image, Rect );

			detector.detect( ImageROI, keypointsImage );
		
			extractor.compute( ImageTemple, keypointsImageTemple, descriptorsImageTemple );
			extractor.compute( ImageROI, keypointsImage, descriptorsImage );

			matcher.match( descriptorsImageTemple, descriptorsImage, matches);			
		
			// Отброс слишком расходящихся значений
			for (int i = 0; i < matches.size(); i++ )
			{
				if( matches[i].distance > 150 )
				{
					matches.erase (matches.begin() + i);
				}
			}
	
 			std::vector<Point2f> obj;
			std::vector<Point2f> scene; 
			for( int i = 0; i < matches.size(); i++ )
			{		
				obj.push_back( keypointsImageTemple[ matches[i].queryIdx ].pt );
				scene.push_back( keypointsImage[ matches[i].trainIdx ].pt );
			}  			Mat H = findHomography( obj, scene, CV_RANSAC );  			std::vector<Point2f> obj_corners(4);
			obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( ImageTemple.cols, 0 );
			obj_corners[2] = cvPoint( ImageTemple.cols, ImageTemple.rows ); obj_corners[3] = cvPoint( 0, ImageTemple.rows );
			std::vector<Point2f> scene_corners(4);

			perspectiveTransform( obj_corners, scene_corners, H);

			//-- Draw lines between the corners (the mapped object in the scene - image_2 )
			line( Draw_mat, scene_corners[0] + Point2f( x * ( Image.cols / ( maxx + 1) ), y * ( Image.rows / ( maxy + 1) )), scene_corners[1] + Point2f( x * ( Image.cols / ( maxx + 1) ), y * ( Image.rows / ( maxy + 1) )), Scalar(0, 255, 0), 4 );
			line( Draw_mat, scene_corners[1] + Point2f( x * ( Image.cols / ( maxx + 1) ), y * ( Image.rows / ( maxy + 1) )), scene_corners[2] + Point2f( x * ( Image.cols / ( maxx + 1) ), y * ( Image.rows / ( maxy + 1) )), Scalar( 0, 255, 0), 4 );
			line( Draw_mat, scene_corners[2] + Point2f( x * ( Image.cols / ( maxx + 1) ), y * ( Image.rows / ( maxy + 1) )), scene_corners[3] + Point2f( x * ( Image.cols / ( maxx + 1) ), y * ( Image.rows / ( maxy + 1) )), Scalar( 0, 255, 0), 4 );
			line( Draw_mat, scene_corners[3] + Point2f( x * ( Image.cols / ( maxx + 1) ), y * ( Image.rows / ( maxy + 1) )), scene_corners[0] + Point2f( x * ( Image.cols / ( maxx + 1) ), y * ( Image.rows / ( maxy + 1) )), Scalar( 0, 255, 0), 4 );	
			
		}
	imwrite("draw_mat.jpeg", Draw_mat);
	return 0;
}


Результат работы следующий:

Видно, что все объекты детектированы. Причем некоторые дважды (из-за того, что попали в два блока).
  • 0
  • 18 декабря 2013, 19:01
  • vidikon

Комментарии (0)

RSS свернуть / развернуть

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.