3д реконструкция из 2х изображений без информации о камере

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

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

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

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

    1. detect keypoints (SURF, SIFT) =>
    2. извлечь дескрипторы (SURF, SIFT) =>
    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) содержит трехмерные координаты точки (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;
}

person Fobi    schedule 26.01.2012    source источник
comment
Я думаю, что это так, но вы чего-то упускаете. Несоответствие можно получить с помощью нескольких функций, вам следует проверить документацию openCV. opencv.willowgarage.com/documentation/   -  person Jav_Rock    schedule 27.01.2012
comment
@Jav_Rock хорошо ... но не могли бы вы быть более конкретными? Если вы рассмотрите мой код, какую функцию я могу использовать? Это мой код:   -  person Fobi    schedule 27.01.2012
comment
Я не знаю, потому что я не использовал функции с несоответствием в качестве входных данных, но если бы я выполнял вашу работу, я бы просто попробовал одну из них, например cvFindStereoCorrespondenceBM (). Проблема в том, что я не знаю, как получить состояние, поэтому я не стал конкретным. Но вы можете попробовать вручную задать значения (придуманные), чтобы иметь возможность что-то вычислить. Чем больше вы будете пытаться и ошибиться, тем больше узнаете. Мне очень жаль, что я больше ничем не могу помочь.   -  person Jav_Rock    schedule 27.01.2012
comment
@Jav_Rock благодарит за вашу помощь. Я решил предыдущий шаг, но теперь у меня другая проблема ...   -  person Fobi    schedule 28.01.2012
comment
@jmartel что ты об этом думаешь?   -  person Fobi    schedule 29.01.2012
comment
Это то, что вы ищите? youtube.com/   -  person Seçkin Savaşçı    schedule 22.05.2012
comment
Сечкин Савашчи, а как он должен помочь ??   -  person Maryna Klokova    schedule 17.05.2018


Ответы (3)


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

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

Также требуются решения на основе настройки блока связки для уточнения всех оценочных значений до более точных значений.

person AGP    schedule 22.05.2012

  1. мне кажется, что процедура нормальная.

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

person zinking    schedule 29.05.2012

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

person Mahdi    schedule 14.07.2013