Reconstruction 3d à partir de 2 images sans information sur la caméra

je suis nouveau dans ce domaine et j'essaie de modéliser une scène simple en 3d à partir d'images 2d et je n'ai aucune information sur les caméras. Je sais qu'il y a 3 "options de 1519360920" :

  • j'ai deux images et je connais le modèle de mon appareil photo (intrisics) que j'ai chargé à partir D'un XML par exemple loadXMLFromFile() => stereoRectify() => reprojectImageTo3D()

  • Je ne les ai pas mais je peux calibrez mon appareil photo => stereoCalibrate() => stereoRectify() => reprojectImageTo3D()

  • Je ne peux pas calibrer la caméra (c'est mon cas, parce que je n'ai pas la caméra qui a pris les 2 images, alors j'ai besoin de trouver des points de touches de paire sur les deux images avec SURF, SIFT par exemple (je peux utiliser n'importe quel détecteur de blob en fait), puis calculer des descripteurs de ces points de touches, puis match points de touches de l'image droite et l'image gauche selon leurs descripteurs, et puis trouver la matrice fondamentale d'eux. Le traitement est beaucoup plus difficile et serait comme ceci:

    1. détecter les keypoints (SURF, TAMISER) =>
    2. descripteurs d'extrait (SURF, SIFT) = >
    3. comparer et faire correspondre les descripteurs (BruteForce, Flann les approches basées =>
    4. trouver le tapis fondamental ( findFundamentalMat() ) à partir de ces paires = >
    5. stereoRectifyUncalibrated() =>
    6. reprojectImageTo3D()

j'utilise la dernière approche et mes questions sont:

1) est-ce exact?

2) si c'est ok, j'ai un doute sur la dernière étape stereoRectifyUncalibrated() => reprojectImageTo3D() . La signature de reprojectImageTo3D() fonction est:

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

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

paramètres:

  • disparity – entrée single-channel 8-bit non signé, 16-bit image de disparité signée, 32 bits signée ou 32 bits à virgule flottante.
  • _3dImage – image de sortie à 3 canaux à virgule flottante de la même taille que disparity . Chaque élément de _3dImage(x,y) contient des coordonnées 3D du point (x,y) calculé à partir de la carte de disparité.
  • Q – 4x4 perspective de la matrice de transformation qui peut être obtenu avec stereoRectify() .
  • handleMissingValues - indique, si le la fonction devrait traiter les valeurs manquantes (c.-à-d. les points où la disparité n'a pas été calculée). Si handleMissingValues=true , alors les pixels avec la disparité minimale qui correspond aux valeurs aberrantes (voir StereoBM::operator() ) sont transformés en points 3D avec une très grande valeur de Z (actuellement fixée à 10000).
  • ddepth – la profondeur optionnelle du tableau de sortie. S'il est -1, l'image de sortie aura la profondeur CV_32F . ddepth peut aussi être réglé sur CV_16S , CV_32S ou "CV_32F".

Comment puis-je obtenir la matrice Q ? Est-il possible d'obtenir la matrice Q avec F , H1 et H2 ou d'une autre manière?

3) y a-t-il un autre moyen d'obtenir les coordonnées xyz sans calibrer les caméras?

mon code est:

#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;
}
31
demandé sur Community 2012-01-27 02:43:10

3 réponses

StereoRectifyUncalibrated calcule simplement plane transformation de perspective, pas de rectification de la transformation dans l'espace objet. Il est nécessaire de convertir cette transformation planaire en transformation de l'espace objet pour extraire Q matrice, et je pense que certains des paramètres d'étalonnage de la caméra sont nécessaires pour cela( comme la caméra intrinsèques ). Il se peut que certains sujets de recherche soient en cours dans ce domaine.

: Vous pouvez ajouter quelques étapes pour estimer la caméra intrinsèques, et l'orientation relative d'extraction des caméras pour faire fonctionner votre flux. Je pense que les paramètres de calibrage de la caméra sont essentiels pour extraire la structure 3d appropriée de la scène, s'il n'y a pas de méthode d'éclairage actif est utilisé.

également des solutions basées sur l'ajustement des blocs de faisceau sont nécessaires pour affiner toutes les valeurs estimées à des valeurs plus précises.

4
répondu AGP 2012-05-22 07:57:26
  1. la procédure me semble correcte .

  2. autant que je sache, en ce qui concerne la modélisation 3D basée sur L'Image, les caméras sont explicitement calibrées ou implicitement calibrées. vous ne voulez pas calibrer explicitement la caméra. vous allez faire usage de ces choses de toute façon. les paires de points correspondantes sont certainement une approche très utilisée.

2
répondu zinking 2012-05-29 01:49:49

je pense que vous devez utiliser StereoRectify pour rectifier vos images et obtenir Q. Cette fonction nécessite deux paramètres (R et T) la rotation et la translation entre deux caméras. Vous pouvez donc calculer les paramètres en utilisant solvePnP. Cette fonction nécessite des coordonnées réelles en 3d de certains objets et points 2d dans les images et leurs points correspondants

1
répondu user2580110 2013-07-14 23:13:27