title: OpenCV Stereo Camera Calibration/Image Rectification
body: <p>I'm trying to calibrate my two Point Grey (Blackfly) cameras for stereo vision. I'm using the tutorial stereo_calib.cpp that comes with OpenCV (code below). For some reason, I'm getting really bad results (RMS error=4.49756 and average reprojection err = 8.06533) and all my rectified images come out grey. I think my problem is that I'm not picking the right flags for the stereoCalibrate() function, but I've tried many different combinations and at best the rectified images would be warped. </p>
  
  <p>Here's a link to the images I used and a sample rectified pair: <a href="https://www.dropbox.com/sh/5wp31o8xcn6vmjl/AAADAfGiaT_NyXEB3zMpcEvVa#/" rel="noreferrer">https://www.dropbox.com/sh/5wp31o8xcn6vmjl/AAADAfGiaT_NyXEB3zMpcEvVa#/</a></p>
  
  <p>Any help would be appreciated!! </p>
  
  <pre><code>static void
  StereoCalib(const vector&lt;string&gt;&amp; imagelist, Size boardSize, bool useCalibrated=true, bool showRectified=true)
  {
      if( imagelist.size() % 2 != 0 )
      {
      cout &lt;&lt; "Error: the image list contains odd (non-even) number of elements\n";
      return;
      }
  
      bool displayCorners = true;//false;//true;
      const int maxScale = 1;//2;
      const float squareSize = 1.8;
      //const float squareSize = 1.f;  // Set this to your actual square size
  
      // ARRAY AND VECTOR STORAGE:
  
      vector&lt;vector&lt;Point2f&gt; &gt; imagePoints[2];
      vector&lt;vector&lt;Point3f&gt; &gt; objectPoints;
      Size imageSize;
  
      //int i, j, k, nimages = (int)imagelist.size()/2;
      int i, j, k, nimages = (int)imagelist.size();
  
      cout &lt;&lt; "nimages: " &lt;&lt; nimages &lt;&lt; "\n";
  
      imagePoints[0].resize(nimages);
      imagePoints[1].resize(nimages);
      vector&lt;string&gt; goodImageList;
  
      for( i = j = 0; i &lt; nimages; i++ )
      {
      for( k = 0; k &lt; 2; k++ )
      {
          const string&amp; 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 &lt;&lt; "The image " &lt;&lt; filename &lt;&lt; " has the size different from the first image size. Skipping the pair\n";
              break;
          }
          bool found = false;
          vector&lt;Point2f&gt;&amp; corners = imagePoints[k][j];
          for( int scale = 1; scale &lt;= 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 &gt; 1 )
                  {
                      Mat cornersMat(corners);
                      cornersMat *= 1./scale;
                  }
                  break;
              }
          }
          if( displayCorners )
          {
              cout &lt;&lt; filename &lt;&lt; endl;
              Mat cimg, cimg1;
              cvtColor(img, cimg, COLOR_GRAY2BGR);
              drawChessboardCorners(cimg, boardSize, corners, found);
              double sf = 1280./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 ) {
          cout &lt;&lt; "!found\n";
              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 &lt;&lt; j &lt;&lt; " pairs have been successfully detected.\n";
      nimages = j;
      if( nimages &lt; 2 )
      {
      cout &lt;&lt; "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 &lt; nimages; i++ )
      {
      for( j = 0; j &lt; boardSize.height; j++ )
          for( k = 0; k &lt; boardSize.width; k++ )
              objectPoints[i].push_back(Point3f(j*squareSize, k*squareSize, 0));
      }
  
      cout &lt;&lt; "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));
  
                  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_K2);
                  CV_CALIB_FIX_K3 + CV_CALIB_FIX_K4 + CV_CALIB_FIX_K5);
                  //CV_CALIB_FIX_K1 + CV_CALIB_FIX_K2 + CV_CALIB_FIX_K3 + CV_CALIB_FIX_K4 + CV_CALIB_FIX_K5);
      cout &lt;&lt; "done with RMS error=" &lt;&lt; rms &lt;&lt; endl;
  
      double err = 0;
      int npoints = 0;
      vector&lt;Vec3f&gt; lines[2];
      for( i = 0; i &lt; nimages; i++ )
      {
      int npt = (int)imagePoints[0][i].size();
      Mat imgpt[2];
      for( k = 0; k &lt; 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 &lt; 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 &lt;&lt; "average reprojection err = " &lt;&lt;  err/npoints &lt;&lt; endl;
  
      // save intrinsic parameters
      FileStorage fs("intrinsics.yml", CV_STORAGE_WRITE);
      if( fs.isOpened() )
      {
      fs &lt;&lt; "M1" &lt;&lt; cameraMatrix[0] &lt;&lt; "D1" &lt;&lt; distCoeffs[0] &lt;&lt;
          "M2" &lt;&lt; cameraMatrix[1] &lt;&lt; "D2" &lt;&lt; distCoeffs[1];
      fs.release();
      }
      else
      cout &lt;&lt; "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, &amp;validRoi[0], &amp;validRoi[1]);
                CALIB_ZERO_DISPARITY, 0, imageSize, &amp;validRoi[0], &amp;validRoi[1]);
  
      fs.open("extrinsics.yml", CV_STORAGE_WRITE);
      if( fs.isOpened() )
      {
      fs &lt;&lt; "R" &lt;&lt; R &lt;&lt; "T" &lt;&lt; T &lt;&lt; "R1" &lt;&lt; R1 &lt;&lt; "R2" &lt;&lt; R2 &lt;&lt; "P1" &lt;&lt; P1 &lt;&lt; "P2" &lt;&lt; P2 &lt;&lt; "Q" &lt;&lt; Q;
      fs.release();
      }
      else
      cout &lt;&lt; "Error: can not save the intrinsic parameters\n";
  
      // OpenCV can handle left-right
      // or up-down camera arrangements
      //bool isVerticalStereo = fabs(P2.at&lt;double&gt;(1, 3)) &gt; fabs(P2.at&lt;double&gt;(0, 3));
      bool isVerticalStereo = false;
  
  // 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&lt;Point2f&gt; allimgpt[2];
      for( k = 0; k &lt; 2; k++ )
      {
          for( i = 0; i &lt; 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 = 600./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 &lt; nimages; i++ )
      {
      for( k = 0; k &lt; 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 &lt; canvas.rows; j += 16 )
              line(canvas, Point(0, j), Point(canvas.cols, j), Scalar(0, 255, 0), 1, 8);
      else
          for( j = 0; j &lt; 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;
      }
  }
  </code></pre>