Обнаружение объекта с использованием Surf

Я пытаюсь обнаружить транспортное средство из видео, я сделаю это в режиме реального времени, но пока и для хорошего понимания, я делаю это на видео, код ниже:

void surf_detection(Mat img_1,Mat img_2); /** @function main */

int main( int argc, char** argv )
{

 int i;
 int key;

 CvCapture* capture = cvCaptureFromAVI("try2.avi");// Read the video file

 if (!capture){

     std::cout <<" Error in capture video file";
     return -1;
 }

 Mat img_template = imread("images.jpg"); // read template image

int numFrames = (int) cvGetCaptureProperty(capture,  CV_CAP_PROP_FRAME_COUNT);



IplImage* img = 0; 

for(i=0;i<numFrames;i++){
  cvGrabFrame(capture);          // capture a frame
  img=cvRetrieveFrame(capture);  // retrieve the captured frame


  surf_detection (img_template,img);

  cvShowImage("mainWin", img); 
  key=cvWaitKey(20);           

}

 return 0;
 }

void surf_detection(Mat img_1,Mat img_2)
{ 

if( !img_1.data || !img_2.data )
{ 
    std::cout<< " --(!) Error reading images " << std::endl; 

}




//-- Step 1: Detect the keypoints using SURF Detector
int minHessian = 400;
SurfFeatureDetector detector( minHessian );
std::vector<KeyPoint> keypoints_1, keypoints_2;

std::vector< DMatch > good_matches;

do{ 

detector.detect( img_1, keypoints_1 );
detector.detect( img_2, keypoints_2 );

//-- Draw keypoints

Mat img_keypoints_1; Mat img_keypoints_2;
drawKeypoints( img_1, keypoints_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT );
drawKeypoints( img_2, keypoints_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT );

//-- Step 2: Calculate descriptors (feature vectors)
SurfDescriptorExtractor extractor;
Mat descriptors_1, descriptors_2;
extractor.compute( img_1, keypoints_1, descriptors_1 );
extractor.compute( img_2, keypoints_2, descriptors_2 );


//-- Step 3: Matching descriptor vectors using FLANN matcher
FlannBasedMatcher matcher;
std::vector< DMatch > matches;
matcher.match( descriptors_1, descriptors_2, matches );
double max_dist = 0; 
double min_dist = 100;

//-- Quick calculation of max and min distances between keypoints
for( int i = 0; i < descriptors_1.rows; i++ )
{ 
    double dist = matches[i].distance;
if( dist < min_dist )
    min_dist = dist;
if( dist > max_dist ) 
    max_dist = dist;
}


//-- Draw only "good" matches (i.e. whose distance is less than 2*min_dist )


for( int i = 0; i < descriptors_1.rows; i++ )
{ 
    if( matches[i].distance < 2*min_dist )
        { 
                good_matches.push_back( matches[i]);
        }
}

}while(good_matches.size()<100);

//-- Draw only "good" matches
Mat img_matches;
drawMatches( img_1, keypoints_1, img_2, keypoints_2,good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );

//-- Localize the object
std::vector<Point2f> obj;
std::vector<Point2f> scene;
for( int i = 0; i < good_matches.size(); i++ )
{
//-- Get the keypoints from the good matches
obj.push_back( keypoints_1[ good_matches[i].queryIdx ].pt );
scene.push_back( keypoints_2[ good_matches[i].trainIdx ].pt );
}


Mat H = findHomography( obj, scene, CV_RANSAC );


//-- Get the corners from the image_1 ( the object to be "detected" )
std::vector<Point2f> obj_corners(4);
obj_corners[0] = Point2f(0,0); 
obj_corners[1] = Point2f( img_1.cols, 0 );
obj_corners[2] = Point2f( img_1.cols, img_1.rows ); 
obj_corners[3] = Point2f( 0, img_1.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( img_matches, scene_corners[0] , scene_corners[1] , Scalar(0, 255, 0), 4 );
line( img_matches, scene_corners[1], scene_corners[2], Scalar( 0, 255, 0), 4 );
line( img_matches, scene_corners[2] , scene_corners[3], Scalar( 0, 255, 0), 4 );
line( img_matches, scene_corners[3] , scene_corners[0], Scalar( 0, 255, 0), 4 );
imshow( "Good Matches & Object detection", img_matches );

}

Я получаю следующий вывод

enter image description here

и std:: cout < scene_corners [i] (Результат)

std::cout << scene_corners[i] (Result)

Значение H:

enter image description here

Но мой вопрос заключается в том, почему он не рисует прямоугольник на объекте, который определяется как:

Rectangle is visible on detected object

Я делаю это на простое видео и изображение, но когда я сделал это на неподвижной камере, так что это может затрудниться без этого прямоугольника

Ответ 1

Во-первых, на изображении, который вы показываете, прямоугольник не рисуется вообще. Вы можете нарисовать прямоугольник, скажем, посередине вашего изображения?

Затем, глядя на следующий код:

int x1 , x2 , y1 , y2 ;
x1 = scene_corners[0].x + Point2f( img_1.cols, 0).x ; 
y1 = scene_corners[0].y + Point2f( img_1.cols, 0).y ; 
x2 = scene_corners[0].x + Point2f( img_1.cols, 0).x + in_box.width ; 
y2 = scene_corners[0].y + Point2f( img_1.cols, 0).y + in_box.height ;

Я не понимаю, почему вы добавляете in_box.width и in_box.height в каждый угол (где они определены?). Вместо этого вы должны использовать scene_corners[2]. Но прокомментированные строки должны печатать прямоугольник где-то.

Поскольку вы запрашивали более подробную информацию, давайте посмотрим, что происходит в вашем коде.

Во-первых, как вы дойдете до perspectiveTransform()?

  • Вы обнаруживаете точки функции с помощью detector.detect. Это дает вам интерес к обоим изображениям.
  • Вы описываете эти функции с помощью extractor.compute. Это дает вам возможность сравнить достопримечательности. Сравнение дескрипторов двух функций отвечает на вопрос: насколько похожи эти точки? *
  • Фактически вы сравниваете каждую функцию на первом изображении со всеми функциями второго изображения (вроде) и сохраняете наилучшее соответствие для каждой функции. На данный момент вы знаете пары функций, которые выглядят наиболее похожими.
  • Вы сохраняете только good_matches. Потому что может случиться так, что для одной функции наиболее похожая на другое изображение на самом деле совершенно другая (она по-прежнему наиболее похожа, так как у вас не было лучшего выбора). Это первый фильтр для удаления неправильных совпадений.
  • Вы найдете преобразование гомографии, соответствующее найденным совпадениям. Это означает, что вы пытаетесь найти способ проецирования точки на первом изображении во втором изображении. Полученная тогда матрица гомографии позволяет проецировать любую точку первого изображения на его соответствие во втором изображении.

Во-вторых, что вы делаете с этим?

Теперь это становится интересным. У вас есть гомографическая матрица, которая позволяет проецировать любую точку первого изображения на его соответствие во втором изображении. Таким образом, вы можете выбрать рисование прямоугольника вокруг вашего объекта (то есть obj_corners) и проецировать его на второе изображение (perspectiveTransform( obj_corners, scene_corners, H);). Результат находится в scene_corners.

Теперь вы хотите нарисовать прямоугольник с помощью scene_corners. Но есть еще одна точка: drawMatches(), по-видимому, помещает оба ваших изображения рядом друг с другом в img_matches. Но проекция (матрица гомографии) вычислялась на изображениях отдельно! Это означает, что каждый scene_corner должен быть переведен соответствующим образом. Поскольку изображение сцены было нанесено справа от изображения объекта, вы должны добавить ширину изображения объекта к каждому scene_corner, чтобы перевести их вправо.

Вот почему вы добавляете 0 в y1 и y2, так как вам не нужно переводить их по вертикали. Но для x1 и x2 вам нужно добавить img_1.cols.

//-- Draw lines between the corners (the mapped object in the scene - image_2 )
line( img_matches, scene_corners[0] + Point2f( img_1.cols, 0), scene_corners[1] + Point2f( img_1.cols, 0), Scalar(0, 255, 0), 4 );
line( img_matches, scene_corners[1] + Point2f( img_1.cols, 0), scene_corners[2] + Point2f( img_1.cols, 0), Scalar( 0, 255, 0), 4 );
line( img_matches, scene_corners[2] + Point2f( img_1.cols, 0), scene_corners[3] + Point2f( img_1.cols, 0), Scalar( 0, 255, 0), 4 );
line( img_matches, scene_corners[3] + Point2f( img_1.cols, 0), scene_corners[0] + Point2f( img_1.cols, 0), Scalar( 0, 255, 0), 4 );

Поэтому я предлагаю вам раскомментировать эти строки и посмотреть, нарисован ли прямоугольник. Если нет, попробуйте выполнить жесткие значения кода (например, Point2f(0, 0) и Point2f(100, 100)), пока ваш прямоугольник не будет успешно выполнен. Возможно, ваша проблема связана с использованием cvPoint и Point2f вместе. Также попробуйте использовать Scalar(0, 255, 0, 255)...

Надеюсь, что это поможет.

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

Ответ 2

Вы выполнили следующие шаги:

  • Сопоставьте ключевые точки на 2 изображениях.
  • Предполагая, что совпадение верное, вычислите гомографию (матрицу проекции).
  • Используйте гомографию для проецирования углов исходного изображения, чтобы нарисовать прямоугольную форму (которую вы называете прямоугольником под).

Проблема, с которой вы сталкиваетесь, заключается в том, что при неудачном шаге 1 вы получаете неправильную гомографию на шаге 2 (неправильная матрица), и когда вы проецируете углы на шаге 3, они могут выпасть из изображения, и вы не видите линии.

То, что вы на самом деле хотите, - это способ узнать, является ли рассчитанная вами гомология правильной формой. Для этого см. Ответ здесь: Как проверить правильность полученной матрицы гомографии? Используйте его, чтобы проверить правильность вашей гомографии. Если нет, вы знаете, что в результате матча произошел сбой. Если это правильно, вы можете нарисовать прямоугольник, и вы увидите его, но это может быть не так точно, если совпадение между ключевыми точками неточно.

Наконец, я считаю, что ваш алгоритмический подход неверен. Признание/обнаружение транспортного средства с высоты обзора путем сопоставления его с изображением транспортного средства с фронтальной точки зрения является тупиком. Вы не должны использовать совпадение ключевых точек. Просто отметьте вручную все транспортные средства на изображениях и отправьте их в SVM. Если это слишком большая работа, используйте платформу Mechanical Turk для автоматической маркировки транспортных средств. В заключение - матч с ключевыми точками - это подход, который просто не подходит вашим потребностям, потому что у него есть сильное предположение, что внешний вид автомобиля на обоих изображениях аналогичен. В вашем случае эти изображения слишком разные (из-за трехмерной структуры автомобиля и разных углов обзора)

Ответ 3

То, что вы на самом деле делаете, - найти опорные точки в изображениях (ключевые моменты) и сравнить их друг с другом, чтобы найти их повторно на других изображениях (на основе вектор-функции SURF). Это важный шаг в обнаружении и распознавании объектов, но не следует ошибаться с сегментацией изображения (http://en.wikipedia.org/wiki/Image_segmentation) или локализацией объекта, где вы найдите точные контуры (или набор пикселей или суперпикселей) желаемого объекта.

Получение ограничивающего прямоугольника объекта, особенно одного, поставленного в перспективе, как в вашем примере, не является тривиальной задачей. Вы можете начать с ограничивающей рамки ключевых точек, которые были найдены. Однако это будет охватывать только часть объекта. Особенно ограничительная рамка в перспективе в вашем примере может быть трудно найти без 3D-регистрации изображения, то есть зная значение третьего измерения (значение z, глубина) каждого пикселя в изображении.

Ответ 4

Как это? Рисование прямоугольника вокруг обнаруженного объекта с использованием SURF

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

/*   
//-- Draw lines between the corners (the mapped object in the scene - image_2 )
line( img_matches, scene_corners[0] + Point2f( img_1.cols, 0), scene_corners[1] + Point2f( img_1.cols, 0), Scalar(0, 255, 0), 4 );
line( img_matches, scene_corners[1] + Point2f( img_1.cols, 0), scene_corners[2] + Point2f( img_1.cols, 0), Scalar( 0, 255, 0), 4 );
line( img_matches, scene_corners[2] + Point2f( img_1.cols, 0), scene_corners[3] + Point2f( img_1.cols, 0), Scalar( 0, 255, 0), 4 );
line( img_matches, scene_corners[3] + Point2f( img_1.cols, 0), scene_corners[0] + Point2f( img_1.cols, 0), Scalar( 0, 255, 0), 4 );   */

Вероятно, вы не хотите рисовать прямоугольник вокруг совпадающего шаблона в видеоизображении, потому что он может быть искажен. Вместо этого подключите деформированный scene_corners к строкам. Я бы удалил все те вещи x1, x2, y1, y2 и cvRect square.

Обратите внимание, что scene_corners не дает вам прямоугольника, потому что объект может поворачиваться в видео по-другому, чем в изображении шаблона. Изображение мобильного телефона, представленное выше, является отличным примером, - зеленый контур вокруг экрана телефона - это четырехугольник. Если вы хотите работать с прямоугольным ROI, он содержит весь объект, вы можете подумать о поиске ограничивающего прямоугольника, который содержит весь объект в видео. Вот как бы я это сделал:

// draw the *rectangle* that contains the entire detected object (a quadrilateral)
// i.e. bounding box in the scene (not the corners)

// upper left corner of bounding box
cv::Point2f low_bound = cv::Point2f( min(scene_corners[0].x, scene_corners[3].x) , min(scene_corners[0].y, scene_corners[1].y) );

// lower right corner of bounding box
cv::Point2f high_bound = cv::Point2f( max(scene_corners[2].x, scene_corners[1].x) , max(scene_corners[2].y, scene_corners[3].y) );

// bounding box offset introduced by displaying the images side-by-side
// *only for side-by-side display*
cv::Point2f matches_offset = cv::Point2f( img_1.cols, 0);

// draw the bounding rectangle in the side-by-side display
cv::rectangle( img_matches , low_bound +  matches_offset , high_bound + matches_offset , cv::Scalar::all(255) , 2 );

/* 
if you want the rectangle around the object in the original video images, don't add the
offset and use the following line instead:

cv::rectangle( img_matches , low_bound , high_bound , cv::Scalar::all(255) , 2 );
*/

// Here is the actual rectangle, you can use as the ROI in you video images:
cv::Rect video_rect = cv::Rect( low_bound , high_bound );

В последней строке в приведенном выше блоке кода может быть прямоугольник, который вы пытались найти в своем первоначально опубликованном коде. Он должен быть прямоугольником в вашем видеоизображении, img. Вы можете использовать его для работы с подмножеством вашего изображения, которое содержит объект (ROI).

Как упоминал Анум, вы также смешиваете старый и новый стиль OpenCV. Вы можете очистить вещи, последовательно используя Point2f, а не cvPoint, между прочим.