web-dev-qa-db-fra.com

Reconstruction 3D à partir de 2 images sans informations sur la caméra

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

  • J'ai deux images et je connais le modèle de ma caméra (intrisics) que j'ai chargé à partir d'un XML par exemple loadXMLFromFile() => stereoRectify() => reprojectImageTo3D()

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

  • Je ne peux pas calibrer la caméra (c'est mon cas, car je n'ai pas la caméra qui a pris les 2 images, alors j'ai besoin de trouver des paires de points clés sur les deux images avec SURF, SIFT par exemple (je peux utiliser n'importe quel blob détecteur en fait), puis calculez les descripteurs de ces points-clés, puis faites correspondre les points-clés de l'image à droite et de l'image à gauche en fonction de leurs descripteurs, puis trouvez-en la matrice fondamentale. Le traitement est beaucoup plus difficile et ressemblerait à ceci:

    1. détecter les points clés (SURF, SIFT) =>
    2. extraire des descripteurs (SURF, SIFT) =>
    3. comparer et faire correspondre les descripteurs (BruteForce, approches basées sur Flann) =>
    4. trouver le mat fondamental (findFundamentalMat()) à partir de ces paires =>
    5. stereoRectifyUncalibrated() =>
    6. reprojectImageTo3D()

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

1) Est-ce vrai?

2) si ça va, j'ai un doute sur la dernière étape stereoRectifyUncalibrated() => reprojectImageTo3D(). La signature de la fonction reprojectImageTo3D() 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 - Entrez une image de disparité à virgule flottante 8 bits non signée, signée 16 bits, signée 32 bits ou 32 bits flottante.
  • _3dImage - Affiche une image en virgule flottante à 3 canaux de la même taille que disparity. Chaque élément de _3dImage(x,y) contient les coordonnées 3D du point (x,y) Calculées à partir de la carte de disparité.
  • Q - Matrice de transformation en perspective 4x4 qui peut être obtenue avec stereoRectify().
  • handleMissingValues - Indique si la fonction doit gérer les valeurs manquantes (c'est-à-dire 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 Z (actuellement fixée à 10000).
  • ddepth - La profondeur facultative du tableau de sortie. S'il vaut -1, l'image de sortie aura une profondeur de CV_32F. ddepth peut également être réglé sur CV_16S, CV_32S ou `CV_32F '.

Comment obtenir la matrice Q? Est-il possible d'obtenir la matrice Q avec F, H1 Et H2 Ou d'une autre manière?

3) Existe-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;
}
35
Fobi

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

Vous devrez peut-être ajouter quelques étapes pour estimer les caractéristiques intrinsèques des caméras et extraire l'orientation relative des caméras pour que votre flux fonctionne correctement. Je pense que les paramètres d'étalonnage 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 active utilisée.

Des solutions basées sur l'ajustement de blocs groupés sont également nécessaires pour affiner toutes les valeurs estimées en valeurs plus précises.

5
AGP
  1. la procédure me semble OK.

  2. pour 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 utiliserez ces choses de toute façon. l'appariement des paires de points correspondantes est certainement une approche très utilisée.

2
zinking

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 traduction entre deux caméras. Vous pouvez donc calculer les paramètres à l'aide de resolPnP. Cette fonction a besoin de coordonnées réelles en 3D de certains objets et de 2d points dans les images et leurs points correspondants

1
Mahdi