IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

OpenCV Discussion :

stereoCalibration avec des caméras parallèles


Sujet :

OpenCV

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2015
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : Polynésie

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2015
    Messages : 11
    Points : 14
    Points
    14
    Par défaut stereoCalibration avec des caméras parallèles
    Bonjour à tous,

    A l'aide de la librairie "OpenCV", je dois calibrer deux caméras (ici côte à côte, parallèles, et par "chessboard" sous différentes positions) puis traquer une (petite) balle qui effectue une trajectoire circulaire autour de l'axe des Y (vertical). Pour enfin déterminer chaque coordonnées XYZ dans le repère réel pour effectuer une reprojection.

    Ainsi, chaque caméra ne voit la balle se déplacer que sur l'axe des X (horizontal) et s'agrandir et rapetisser (selon la profondeur Z) lorsque la balle effectue sa trajectoire.

    J'arrive à calibrer mes caméras, traquer la balle (coordonnées XY du repère plan) et reprojeter dans le repère monde (3D), sauf qu'au lieu d'obtenir une trajectoire circulaire, j'obtiens une ellipse !
    Mon problème est donc d'obtenir une meilleure profondeur (Z) grâce à la calibration. J'ai essayé bien entendu de placer mes caméras sous différents angles et distances, sauf que mon calcul de calibration obtient de "faux résultats". En tout cas inexploitable pour la reprojection.

    Avez-vous, s'il vous plaît, une ou plusieurs idées à me proposer ?

    Merci d'avance.

    Voici un screen shot de ma reprojection sous Ogre3D.

    Pièce jointe 173865

    Voici le code de stéréo calibration utilisé, et fournit en exemple dans OpenCV:

    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
     
     
    static void
    StereoCalib(const vector<string>& imagelist, Size boardSize, bool useCalibrated=true, bool showRectified=true)
    {
        if( imagelist.size() % 2 != 0 )
        {
            cout << "Error: the image list contains odd (non-even) number of elements\n";
            return;
        }
     
        bool displayCorners = false;//true;
        const int maxScale = 2;
        const float squareSize = 1.f;  // Set this to your actual square size
        // ARRAY AND VECTOR STORAGE:
     
        vector<vector<Point2f> > imagePoints[2];
        vector<vector<Point3f> > objectPoints;
        Size imageSize;
     
        int i, j, k, nimages = (int)imagelist.size()/2;
     
        imagePoints[0].resize(nimages);
        imagePoints[1].resize(nimages);
        vector<string> goodImageList;
     
        for( i = j = 0; i < nimages; i++ )
        {
            for( k = 0; k < 2; k++ )
            {
                const string& filename = imagelist[i*2+k];
                Mat img = imread(filename, 0);
                if(img.empty())
                    break;
                if( imageSize == Size() )
                    imageSize = img.size();
                else if( img.size() != imageSize )
                {
                    cout << "The image " << filename << " has the size different from the first image size. Skipping the pair\n";
                    break;
                }
                bool found = false;
                vector<Point2f>& corners = imagePoints[k][j];
                for( int scale = 1; scale <= maxScale; scale++ )
                {
                    Mat timg;
                    if( scale == 1 )
                        timg = img;
                    else
                        resize(img, timg, Size(), scale, scale);
                    found = findChessboardCorners(timg, boardSize, corners,
                        CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_NORMALIZE_IMAGE);
                    if( found )
                    {
                        if( scale > 1 )
                        {
                            Mat cornersMat(corners);
                            cornersMat *= 1./scale;
                        }
                        break;
                    }
                }
                if( displayCorners )
                {
                    cout << filename << endl;
                    Mat cimg, cimg1;
                    cvtColor(img, cimg, COLOR_GRAY2BGR);
                    drawChessboardCorners(cimg, boardSize, corners, found);
                    double sf = 640./MAX(img.rows, img.cols);
                    resize(cimg, cimg1, Size(), sf, sf);
                    imshow("corners", cimg1);
                    char c = (char)waitKey(500);
                    if( c == 27 || c == 'q' || c == 'Q' ) //Allow ESC to quit
                        exit(-1);
                }
                else
                    putchar('.');
                if( !found )
                    break;
                cornerSubPix(img, corners, Size(11,11), Size(-1,-1),
                             TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,
                                          30, 0.01));
            }
            if( k == 2 )
            {
                goodImageList.push_back(imagelist[i*2]);
                goodImageList.push_back(imagelist[i*2+1]);
                j++;
            }
        }
        cout << j << " pairs have been successfully detected.\n";
        nimages = j;
        if( nimages < 2 )
        {
            cout << "Error: too little pairs to run the calibration\n";
            return;
        }
     
        imagePoints[0].resize(nimages);
        imagePoints[1].resize(nimages);
        objectPoints.resize(nimages);
     
        for( i = 0; i < nimages; i++ )
        {
            for( j = 0; j < boardSize.height; j++ )
                for( k = 0; k < boardSize.width; k++ )
                    objectPoints[i].push_back(Point3f(j*squareSize, k*squareSize, 0));
        }
     
        cout << "Running stereo calibration ...\n";
     
        Mat cameraMatrix[2], distCoeffs[2];
        cameraMatrix[0] = Mat::eye(3, 3, CV_64F);
        cameraMatrix[1] = Mat::eye(3, 3, CV_64F);
        Mat R, T, E, F;
     
        double rms = stereoCalibrate(objectPoints, imagePoints[0], imagePoints[1],
                        cameraMatrix[0], distCoeffs[0],
                        cameraMatrix[1], distCoeffs[1],
                        imageSize, R, T, E, F,
                        TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 100, 1e-5),
                        CV_CALIB_FIX_ASPECT_RATIO +
                        CV_CALIB_ZERO_TANGENT_DIST +
                        CV_CALIB_SAME_FOCAL_LENGTH +
                        CV_CALIB_RATIONAL_MODEL +
                        CV_CALIB_FIX_K3 + CV_CALIB_FIX_K4 + CV_CALIB_FIX_K5);
        cout << "done with RMS error=" << rms << endl;
     
    // CALIBRATION QUALITY CHECK
    // because the output fundamental matrix implicitly
    // includes all the output information,
    // we can check the quality of calibration using the
    // epipolar geometry constraint: m2^t*F*m1=0
        double err = 0;
        int npoints = 0;
        vector<Vec3f> lines[2];
        for( i = 0; i < nimages; i++ )
        {
            int npt = (int)imagePoints[0][i].size();
            Mat imgpt[2];
            for( k = 0; k < 2; k++ )
            {
                imgpt[k] = Mat(imagePoints[k][i]);
                undistortPoints(imgpt[k], imgpt[k], cameraMatrix[k], distCoeffs[k], Mat(), cameraMatrix[k]);
                computeCorrespondEpilines(imgpt[k], k+1, F, lines[k]);
            }
            for( j = 0; j < npt; j++ )
            {
                double errij = fabs(imagePoints[0][i][j].x*lines[1][j][0] +
                                    imagePoints[0][i][j].y*lines[1][j][1] + lines[1][j][2]) +
                               fabs(imagePoints[1][i][j].x*lines[0][j][0] +
                                    imagePoints[1][i][j].y*lines[0][j][1] + lines[0][j][2]);
                err += errij;
            }
            npoints += npt;
        }
        cout << "average reprojection err = " <<  err/npoints << endl;
     
        // save intrinsic parameters
        FileStorage fs("intrinsics.yml", CV_STORAGE_WRITE);
        if( fs.isOpened() )
        {
            fs << "M1" << cameraMatrix[0] << "D1" << distCoeffs[0] <<
                "M2" << cameraMatrix[1] << "D2" << distCoeffs[1];
            fs.release();
        }
        else
            cout << "Error: can not save the intrinsic parameters\n";
     
        Mat R1, R2, P1, P2, Q;
        Rect validRoi[2];
     
        stereoRectify(cameraMatrix[0], distCoeffs[0],
                      cameraMatrix[1], distCoeffs[1],
                      imageSize, R, T, R1, R2, P1, P2, Q,
                      CALIB_ZERO_DISPARITY, 1, imageSize, &validRoi[0], &validRoi[1]);
     
        fs.open("extrinsics.yml", CV_STORAGE_WRITE);
        if( fs.isOpened() )
        {
            fs << "R" << R << "T" << T << "R1" << R1 << "R2" << R2 << "P1" << P1 << "P2" << P2 << "Q" << Q;
            fs.release();
        }
        else
            cout << "Error: can not save the intrinsic parameters\n";
     
        // OpenCV can handle left-right
        // or up-down camera arrangements
        bool isVerticalStereo = fabs(P2.at<double>(1, 3)) > fabs(P2.at<double>(0, 3));
     
    // COMPUTE AND DISPLAY RECTIFICATION
        if( !showRectified )
            return;
     
        Mat rmap[2][2];
    // IF BY CALIBRATED (BOUGUET'S METHOD)
        if( useCalibrated )
        {
            // we already computed everything
        }
    // OR ELSE HARTLEY'S METHOD
        else
     // use intrinsic parameters of each camera, but
     // compute the rectification transformation directly
     // from the fundamental matrix
        {
            vector<Point2f> allimgpt[2];
            for( k = 0; k < 2; k++ )
            {
                for( i = 0; i < nimages; i++ )
                    std::copy(imagePoints[k][i].begin(), imagePoints[k][i].end(), back_inserter(allimgpt[k]));
            }
            F = findFundamentalMat(Mat(allimgpt[0]), Mat(allimgpt[1]), FM_8POINT, 0, 0);
            Mat H1, H2;
            stereoRectifyUncalibrated(Mat(allimgpt[0]), Mat(allimgpt[1]), F, imageSize, H1, H2, 3);
     
            R1 = cameraMatrix[0].inv()*H1*cameraMatrix[0];
            R2 = cameraMatrix[1].inv()*H2*cameraMatrix[1];
            P1 = cameraMatrix[0];
            P2 = cameraMatrix[1];
        }
     
        //Precompute maps for cv::remap()
        initUndistortRectifyMap(cameraMatrix[0], distCoeffs[0], R1, P1, imageSize, CV_16SC2, rmap[0][0], rmap[0][1]);
        initUndistortRectifyMap(cameraMatrix[1], distCoeffs[1], R2, P2, imageSize, CV_16SC2, rmap[1][0], rmap[1][1]);
     
        Mat canvas;
        double sf;
        int w, h;
        if( !isVerticalStereo )
        {
            sf = 600./MAX(imageSize.width, imageSize.height);
            w = cvRound(imageSize.width*sf);
            h = cvRound(imageSize.height*sf);
            canvas.create(h, w*2, CV_8UC3);
        }
        else
        {
            sf = 300./MAX(imageSize.width, imageSize.height);
            w = cvRound(imageSize.width*sf);
            h = cvRound(imageSize.height*sf);
            canvas.create(h*2, w, CV_8UC3);
        }
     
        for( i = 0; i < nimages; i++ )
        {
            for( k = 0; k < 2; k++ )
            {
                Mat img = imread(goodImageList[i*2+k], 0), rimg, cimg;
                remap(img, rimg, rmap[k][0], rmap[k][1], CV_INTER_LINEAR);
                cvtColor(rimg, cimg, COLOR_GRAY2BGR);
                Mat canvasPart = !isVerticalStereo ? canvas(Rect(w*k, 0, w, h)) : canvas(Rect(0, h*k, w, h));
                resize(cimg, canvasPart, canvasPart.size(), 0, 0, CV_INTER_AREA);
                if( useCalibrated )
                {
                    Rect vroi(cvRound(validRoi[k].x*sf), cvRound(validRoi[k].y*sf),
                              cvRound(validRoi[k].width*sf), cvRound(validRoi[k].height*sf));
                    rectangle(canvasPart, vroi, Scalar(0,0,255), 3, 8);
                }
            }
     
            if( !isVerticalStereo )
                for( j = 0; j < canvas.rows; j += 16 )
                    line(canvas, Point(0, j), Point(canvas.cols, j), Scalar(0, 255, 0), 1, 8);
            else
                for( j = 0; j < canvas.cols; j += 16 )
                    line(canvas, Point(j, 0), Point(j, canvas.rows), Scalar(0, 255, 0), 1, 8);
            imshow("rectified", canvas);
            char c = (char)waitKey();
            if( c == 27 || c == 'q' || c == 'Q' )
                break;
        }
    }

  2. #2
    Membre actif

    Homme Profil pro
    Directeur technique
    Inscrit en
    Janvier 2008
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2008
    Messages : 170
    Points : 202
    Points
    202
    Par défaut Humm, hummm...
    Salut,

    Pourquoi dis-tu que ton résultat de calibration est faux? Tes erreurs de reprojection sont trop grande quand tu les verifie apres ta calib?

    J'image que ton code est celui qu'on trouve sur les tuto remis à ta sauce, et qu'a priori c'est du code plutot bien éprouvé, donc si à chaque fois tes calibration ont des erreur de reprojection de plusieurs pixels, c'est que cela vient de la manière dont tu fais tes calibration, ou à une instabilité mécanique de ta stéréo-paire de caméra.

    Les conseils pour des bonnes calibration, c'est de prendre beaucoup de photos de damier (20 au grand minimum, et je te conseille plutôt d'aller jusqu'à 30 voir 40 histoire d’être sur et certain). Évidemment, il faut bouger ton damier entre chaque prise de vue afin d’échantillonner tout l'espace 3D visible par ta caméra et de bien couvrir l'espace 3D où se déroulera ton expérience.

    Il faut que tu t'assures que ta caméra est stable : Pas d'auto-focus! Ça modifie la focale et donc toute ta calibration.

    Ensuite durant tes détection de balles, si tu travaille avec une vidéo, fais attention à l'encodage de celle-ci car le codec XVID peut se permettre de modifier le mouvement dans l'image.

    Mais en y réfléchissant, je pense que l'erreur pourrait venir de ta manière de détecter le centre de ta balle. Il fort possible que tu introduises un biais dans ta détection du centre 3D de ta balle. N'oublie pas qu'elle a une épaisseur, et que la projection du centre 3D de la balle n'est pas confondue avec le centre du disque visible sur l'image 2D de cette balle, et ce surtout quand la balle est au extrémité de sa trajectoire face à la caméra. Cela expliquerai assez bien ton ellipse 3D.

    Si c'est le cas (que tu prends juste le centre de ton disque segmenté), alors prends un une feuille, un crayon, une regle, et un compas, et repose ton modèle de détection du centre 3D de ta balle à partir de sa projection 2D sur l'image.

Discussions similaires

  1. Gérer une caméra avec des quaternions
    Par Kromagg dans le forum API graphiques
    Réponses: 5
    Dernier message: 25/03/2014, 19h57
  2. Création d'un panorama avec la pose des caméras
    Par Tralali dans le forum Traitement d'images
    Réponses: 5
    Dernier message: 01/06/2011, 11h25
  3. [AC-2003] Requete sur une table avec des "Left Join" en parallèle
    Par Currahee dans le forum Requêtes et SQL.
    Réponses: 3
    Dernier message: 27/08/2009, 09h37
  4. Réponses: 2
    Dernier message: 06/12/2002, 07h50
  5. Une fonction avec des attributs non obligatoires
    Par YanK dans le forum Langage
    Réponses: 5
    Dernier message: 15/11/2002, 13h39

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo