2012-01-26 7 views
29

Я новичок в этом поле, и я пытаюсь смоделировать простую сцену в 3d из 2d-изображений, и у меня нет никакой информации о камерах. Я знаю, что есть 3 options:3d-реконструкция из 2-х изображений без информации о фотоаппарате

  • У меня есть два изображения, и я знаю, что модель моей камеры (intrisics), которые я загрузил из XML, например loadXMLFromFile() =>stereoRectify() =>reprojectImageTo3D()

  • I их не имеют, но я могу откалибровать камеру =>stereoCalibrate() =>stereoRectify() =>reprojectImageTo3D()

  • Я не могу откалибровать камеру (это мой случай, потому что у меня нет камеры, принявшей 2 i mages, тогда мне нужно найти пару ключевых точек на обоих изображениях с помощью SURF, SIFT (например, я могу использовать любой детектор blob), затем вычислить дескрипторы этих ключевых точек, затем сопоставить ключевые точки с изображением справа и изображением слева в соответствии с их дескрипторами и затем найдите из них фундаментальную матрицу. Обработка гораздо сложнее и будет выглядеть так:

    1. обнаружить ключевые точки (SURF, просеять) =>
    2. экстракт дескрипторы (SURF, просеять) =>
    3. сравнить и матч дескрипторы (на основе BruteForce, Flann подходы) =>
    4. найти фундаментальный мат (findFundamentalMat()) из этих пар =>
    5. stereoRectifyUncalibrated() =>
    6. reprojectImageTo3D()

Я использую последний подход и мои вопросы:

1) Правильно ли это?

2) если все в порядке, у меня есть сомнения по поводу последнего шага stereoRectifyUncalibrated() =>reprojectImageTo3D(). Подпись reprojectImageTo3D() функции:

void reprojectImageTo3D(InputArray disparity, OutputArray _3dImage, InputArray Q, bool handleMissingValues=false, int depth=-1) 

cv::reprojectImageTo3D(imgDisparity8U, xyz, Q, true) (in my code) 

Параметры:

  • disparity - Входное одноканальной 8-битовые беззнаковая, 16-разрядный, 32-разрядный или 32-бит с плавающей точкой разрыва образ.
  • _3dImage - Выход 3-канального изображения с плавающей точкой того же размера, что и disparity. Каждый элемент _3dImage(x,y) содержит 3D-координаты точки (x,y), вычисленной по карте несоответствий.
  • Q - матрица перспективной трансформации 4x4, которая может быть получена с помощью stereoRectify().
  • handleMissingValues - Указывает, должна ли функция обрабатывать отсутствующие значения (т. Е. Точки, где несоответствие не было вычислено). Если handleMissingValues=true, то пиксели с минимальным несоответствием, которые соответствуют выбросам (см. StereoBM::operator()), преобразуются в 3D-точки с очень большим значением Z (в настоящее время установлено 10000).
  • ddepth - Необязательная глубина выходного массива.Если оно равно -1, выходное изображение будет иметь глубину CV_32F. ddepth также может быть установлен на CV_16S, CV_32S или `CV_32F '.

Как я могу получить матрицу Q? Можно ли получить матрицу Q с F, H1 и H2 или по-другому?

3) Есть ли другой способ для получения координат xyz без калибровки камер?

Мой код:

#include <opencv2/core/core.hpp> 
#include <opencv2/calib3d/calib3d.hpp> 
#include <opencv2/imgproc/imgproc.hpp> 
#include <opencv2/highgui/highgui.hpp> 
#include <opencv2/contrib/contrib.hpp> 
#include <opencv2/features2d/features2d.hpp> 
#include <stdio.h> 
#include <iostream> 
#include <vector> 
#include <conio.h> 
#include <opencv/cv.h> 
#include <opencv/cxcore.h> 
#include <opencv/cvaux.h> 


using namespace cv; 
using namespace std; 

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

    // Read the images 
    Mat imgLeft = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE); 
    Mat imgRight = imread(argv[2], CV_LOAD_IMAGE_GRAYSCALE); 

    // check 
    if (!imgLeft.data || !imgRight.data) 
      return 0; 

    // 1] find pair keypoints on both images (SURF, SIFT)::::::::::::::::::::::::::::: 

    // vector of keypoints 
    std::vector<cv::KeyPoint> keypointsLeft; 
    std::vector<cv::KeyPoint> keypointsRight; 

    // Construct the SURF feature detector object 
    cv::SiftFeatureDetector sift(
      0.01, // feature threshold 
      10); // threshold to reduce 
       // sensitivity to lines 
       // Detect the SURF features 

    // Detection of the SIFT features 
    sift.detect(imgLeft,keypointsLeft); 
    sift.detect(imgRight,keypointsRight); 

    std::cout << "Number of SURF points (1): " << keypointsLeft.size() << std::endl; 
    std::cout << "Number of SURF points (2): " << keypointsRight.size() << std::endl; 

    // 2] compute descriptors of these keypoints (SURF,SIFT) :::::::::::::::::::::::::: 

    // Construction of the SURF descriptor extractor 
    cv::SurfDescriptorExtractor surfDesc; 

    // Extraction of the SURF descriptors 
    cv::Mat descriptorsLeft, descriptorsRight; 
    surfDesc.compute(imgLeft,keypointsLeft,descriptorsLeft); 
    surfDesc.compute(imgRight,keypointsRight,descriptorsRight); 

    std::cout << "descriptor matrix size: " << descriptorsLeft.rows << " by " << descriptorsLeft.cols << std::endl; 

    // 3] matching keypoints from image right and image left according to their descriptors (BruteForce, Flann based approaches) 

    // Construction of the matcher 
    cv::BruteForceMatcher<cv::L2<float> > matcher; 

    // Match the two image descriptors 
    std::vector<cv::DMatch> matches; 
    matcher.match(descriptorsLeft,descriptorsRight, matches); 

    std::cout << "Number of matched points: " << matches.size() << std::endl; 


    // 4] find the fundamental mat :::::::::::::::::::::::::::::::::::::::::::::::::::: 

    // Convert 1 vector of keypoints into 
    // 2 vectors of Point2f for compute F matrix 
    // with cv::findFundamentalMat() function 
    std::vector<int> pointIndexesLeft; 
    std::vector<int> pointIndexesRight; 
    for (std::vector<cv::DMatch>::const_iterator it= matches.begin(); it!= matches.end(); ++it) { 

     // Get the indexes of the selected matched keypoints 
     pointIndexesLeft.push_back(it->queryIdx); 
     pointIndexesRight.push_back(it->trainIdx); 
    } 

    // Convert keypoints into Point2f 
    std::vector<cv::Point2f> selPointsLeft, selPointsRight; 
    cv::KeyPoint::convert(keypointsLeft,selPointsLeft,pointIndexesLeft); 
    cv::KeyPoint::convert(keypointsRight,selPointsRight,pointIndexesRight); 

    /* check by drawing the points 
    std::vector<cv::Point2f>::const_iterator it= selPointsLeft.begin(); 
    while (it!=selPointsLeft.end()) { 

      // draw a circle at each corner location 
      cv::circle(imgLeft,*it,3,cv::Scalar(255,255,255),2); 
      ++it; 
    } 

    it= selPointsRight.begin(); 
    while (it!=selPointsRight.end()) { 

      // draw a circle at each corner location 
      cv::circle(imgRight,*it,3,cv::Scalar(255,255,255),2); 
      ++it; 
    } */ 

    // Compute F matrix from n>=8 matches 
    cv::Mat fundemental= cv::findFundamentalMat(
      cv::Mat(selPointsLeft), // points in first image 
      cv::Mat(selPointsRight), // points in second image 
      CV_FM_RANSAC);  // 8-point method 

    std::cout << "F-Matrix size= " << fundemental.rows << "," << fundemental.cols << std::endl; 

    /* draw the left points corresponding epipolar lines in right image 
    std::vector<cv::Vec3f> linesLeft; 
    cv::computeCorrespondEpilines(
      cv::Mat(selPointsLeft), // image points 
      1,      // in image 1 (can also be 2) 
      fundemental,   // F matrix 
      linesLeft);    // vector of epipolar lines 

    // for all epipolar lines 
    for (vector<cv::Vec3f>::const_iterator it= linesLeft.begin(); it!=linesLeft.end(); ++it) { 

     // draw the epipolar line between first and last column 
     cv::line(imgRight,cv::Point(0,-(*it)[2]/(*it)[1]),cv::Point(imgRight.cols,-((*it)[2]+(*it)[0]*imgRight.cols)/(*it)[1]),cv::Scalar(255,255,255)); 
    } 

    // draw the left points corresponding epipolar lines in left image 
    std::vector<cv::Vec3f> linesRight; 
    cv::computeCorrespondEpilines(cv::Mat(selPointsRight),2,fundemental,linesRight); 
    for (vector<cv::Vec3f>::const_iterator it= linesRight.begin(); it!=linesRight.end(); ++it) { 

     // draw the epipolar line between first and last column 
     cv::line(imgLeft,cv::Point(0,-(*it)[2]/(*it)[1]), cv::Point(imgLeft.cols,-((*it)[2]+(*it)[0]*imgLeft.cols)/(*it)[1]), cv::Scalar(255,255,255)); 
    } 

    // Display the images with points and epipolar lines 
    cv::namedWindow("Right Image Epilines"); 
    cv::imshow("Right Image Epilines",imgRight); 
    cv::namedWindow("Left Image Epilines"); 
    cv::imshow("Left Image Epilines",imgLeft); 
    */ 

    // 5] stereoRectifyUncalibrated():::::::::::::::::::::::::::::::::::::::::::::::::: 

    //H1, H2 – The output rectification homography matrices for the first and for the second images. 
    cv::Mat H1(4,4, imgRight.type()); 
    cv::Mat H2(4,4, imgRight.type()); 
    cv::stereoRectifyUncalibrated(selPointsRight, selPointsLeft, fundemental, imgRight.size(), H1, H2); 


    // create the image in which we will save our disparities 
    Mat imgDisparity16S = Mat(imgLeft.rows, imgLeft.cols, CV_16S); 
    Mat imgDisparity8U = Mat(imgLeft.rows, imgLeft.cols, CV_8UC1); 

    // Call the constructor for StereoBM 
    int ndisparities = 16*5;  // < Range of disparity > 
    int SADWindowSize = 5;  // < Size of the block window > Must be odd. Is the 
            // size of averaging window used to match pixel 
            // blocks(larger values mean better robustness to 
            // noise, but yield blurry disparity maps) 

    StereoBM sbm(StereoBM::BASIC_PRESET, 
     ndisparities, 
     SADWindowSize); 

    // Calculate the disparity image 
    sbm(imgLeft, imgRight, imgDisparity16S, CV_16S); 

    // Check its extreme values 
    double minVal; double maxVal; 

    minMaxLoc(imgDisparity16S, &minVal, &maxVal); 

    printf("Min disp: %f Max value: %f \n", minVal, maxVal); 

    // Display it as a CV_8UC1 image 
    imgDisparity16S.convertTo(imgDisparity8U, CV_8UC1, 255/(maxVal - minVal)); 

    namedWindow("windowDisparity", CV_WINDOW_NORMAL); 
    imshow("windowDisparity", imgDisparity8U); 


    // 6] reprojectImageTo3D() ::::::::::::::::::::::::::::::::::::::::::::::::::::: 

    //Mat xyz; 
    //cv::reprojectImageTo3D(imgDisparity8U, xyz, Q, true); 

    //How can I get the Q matrix? Is possibile to obtain the Q matrix with 
    //F, H1 and H2 or in another way? 
    //Is there another way for obtain the xyz coordinates? 

    cv::waitKey(); 
    return 0; 
} 
+0

Fabio - язык? – Tim

+0

@Tim C++ с OpenCV2.3.1 – Fobi

+0

Я думаю, что это только вы чего-то не хватает. Несоответствие может быть получено с помощью нескольких функций, вы должны проверить документацию openCV. http://opencv.willowgarage.com/documentation/camera_calibration_and_3d_reconstruction.html –

ответ

4

StereoRectifyUncalibrated вычисляет просто плоская перспектива преобразования не преобразование выпрямления в пространстве объекта. Необходимо преобразовать это плоское преобразование в преобразование пространства объектов, чтобы извлечь Q-матрицу, и я думаю, что для этого требуются некоторые параметры калибровки камеры (например, встроенные камеры). Там могут быть некоторые темы исследований, посвященных этому вопросу.

Возможно, у вас есть несколько шагов для оценки свойств камеры и извлечения относительной ориентации камер, чтобы ваш поток работал правильно. Я думаю, что параметры калибровки камеры жизненно важны для извлечения правильной 3d-структуры сцены, если не используется активный метод освещения.

Кроме того, для уточнения всех оценочных значений необходимы более точные значения.

2
  1. процедура выглядит нормально для меня.

  2. Насколько я знаю, что касается 3D-моделирования на основе изображений, камеры явно откалиброваны или неявно откалиброваны. вы не хотите явно откалибровать камеру. вы все равно будете использовать эти вещи. сопоставление соответствующих пар точек определенно является сильно используемым подходом.

1

Я думаю, вам нужно использовать StereoRectify, чтобы исправить ваши изображения и получить Q. Эта функция требует двух параметров (R и T) вращения и перемещения между двумя камерами. Итак, вы можете вычислить параметры с помощью solvePnP. Эта функция нуждается в некоторых трехмерных действительных координатах определенного объекта и 2d точек в изображениях и их соответствующих точках