【OpenCV】基于OpenCV的双目视觉测试

首先,本人写的文档可能比较冗余,所以请认真仔细读完再进行操作。

代码参考邹宇华老师的双目Camera calibration With OpenCVCamera Calibration and 3D Reconstruction部分,按照自己的情况进行了更改。
如果读者是想快速工程使用,那可以看我的这篇博客,如果想要系统学习,请先看相关教材,并辅以邹宇华老师的博客

准备环境

因为本文是进行双目立体视觉实验,所以你必须有两个摄像头,单摄像头标定的实验可以看我的前一篇文章。解决方案,并且有特别需要注意的点我都会仔细说明。

双目摄像头准备

  • 直接购买两个普通的usb摄像头,这个方案是我最早采用的,但是遇到不少坑。
    • 注意不要太广角,因为广角的畸变会很大,这在后面匹配的时候带来很大的问题
    • 安装的时候注意把两个安装的较为平行,虽然可以矫正出来,但是当然还是自身比较平行的好
    • 同时也要注意两个摄像头的轴距,不要太远或者太近,5-10cm为较为适宜的
    • 分辨率可以高一点,但实验上我把他限制在320*240,其实可以标定采用高分辨率,匹配采用低分辨率
  • 购买淘宝的一种双目摄像头
    • 同样注意是否广角,这点我觉得有点坑,因为不想涉及到打广告,淘宝那家集成的很好,但是,最广角的貌似有点问题,我换成不太广的了

标定板准备

  • 淘宝购买标定板,一个字贵,土豪可以忽略。
  • 自己打印,那么可以按照我的前一篇文章里的方法准备,注意记好到底是几乘几的。
    这个几乘以几是按照黑白格子的交叉的数量算的,例如,标定板就是9*6的,记好。

双目标定环节

最下面给了所有实验的源代码,代码有点乱,还是分开来说。双目标定环节就是需要得到两个摄像机各自的内参,以及他们俩的外参数。

双目摄像机读取

直接上双目读取的代码,我把两个摄像头的分辨率都改了一下。这个得看具体摄像头的支持程度,有的无法改,你改了也没效果。

#include "opencv2/opencv.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"

using namespace std;
using namespace cv;

int main(){
  VideoCapture camera_l(1);
  VideoCapture camera_r(0);
  if (!camera_l.isOpened()) { cout << "No left camera!" << endl; return -1; }
  if (!camera_r.isOpened()) { cout << "No right camera!" << endl; return -1; }
  camera_l.set(CAP_PROP_FRAME_WIDTH, 320);
  camera_l.set(CAP_PROP_FRAME_HEIGHT, 240);
  camera_r.set(CAP_PROP_FRAME_WIDTH, 320);
  camera_r.set(CAP_PROP_FRAME_HEIGHT, 240);
  cv::Mat frame_l, frame_r;
  while (1) {
    camera_l >> frame_l;
    camera_r >> frame_r;
    imshow("Left Camera", frame_l);
    imshow("Right Camera", frame_r);
    char key = waitKey(1);
    if (key == 27 || key == 'q' || key == 'Q') //Allow ESC to quit
      break;
  }
  return 0;
}

本来双目摄像机读取起来并没有什么可说的,但是我在后期做别的实验的时候发现,摄像机有时候读取的有问题,无法完成初始化,如果确定两个摄像头分辨读取没有问题,只是无法一起读取,有如下两种解决方案。
* 邹老师的方案
* 在两个摄像机分别都可以运行之后,我们改变初始化部分的代码,把两个if判断改成如下两行。

  while (!camera_l.isOpened()) { camera_l.open(1); };
  while (!camera_r.isOpened()) { camera_r.open(0); };

实验效果如下:
双目

注意到,右摄像头的图像相对于左摄像头的图像有点“左移”。这点自己分析一下原因。很重要,如果不是这项,你下面的工作会白做。因为匹配的算法就是遵循这种“左移”的。

标定环节

基本上的流程就是读取左右摄像头,分别检测棋盘的角点,当同时都检测到完整的角点之后,进行精细化处理,得到更精确地角点并存储。攒够一定的数量之后(20-30)之后进行参数计算。并将参数进行存储。还是直接上代码,并说明一些实现的细节部分。

请慢慢阅读。
一上来的这个ChessboardStable 是用来检测棋盘格是否稳定的,因为在我的试验中,双目摄像头是用手拿着的,或多或少会有一些抖动,这样如果只是检测是否存在角点,可能会通过不是很清晰稳定的图像进行分析,这样会带来比较大的误差,如果通过一个队列判断是否稳定,则可以避免这种误差。我是简单粗暴的使用vector代替队列的。
后面的部分需要注意的就是boardSizesquareSize 需要设置为你的标定板对应的尺寸,我拿A4纸简单的打印的一份,每个格子的大小经过测量时26mm,你可以根据你自己的标定板进行相应的设置。

#include <string>
#include <stdio.h>
#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"

using namespace std;
using namespace cv;

vector<vector<Point2f> >corners_l_array, corners_r_array;
int array_index = 0;
bool ChessboardStable(vector<Point2f>corners_l, vector<Point2f>corners_r){
  if (corners_l_array.size() < 10){
    corners_l_array.push_back(corners_l);
    corners_r_array.push_back(corners_r);
    return false;
  }
  else{
    corners_l_array[array_index % 10] = corners_l;
    corners_r_array[array_index % 10] = corners_r;
    array_index++;
    double error = 0.0;
    for (int i = 0; i < corners_l_array.size(); i++){
      for (int j = 0; j < corners_l_array[i].size(); j++){
        error += abs(corners_l[j].x - corners_l_array[i][j].x) + abs(corners_l[j].y - corners_l_array[i][j].y);
        error += abs(corners_r[j].x - corners_r_array[i][j].x) + abs(corners_r[j].y - corners_r_array[i][j].y);
      }
    }
    if (error < 1000)
    {
      corners_l_array.clear();
      corners_r_array.clear();
      array_index = 0;
      return true;
    }
    else
      return false;
  }
}


int main(){
  VideoCapture camera_l(1);
  VideoCapture camera_r(0);

  while (!camera_l.isOpened()) { camera_l.open(1); };
  while (!camera_r.isOpened()) { camera_r.open(0); };
  camera_l.set(CAP_PROP_FRAME_WIDTH, 320);
  camera_l.set(CAP_PROP_FRAME_HEIGHT, 240);
  camera_r.set(CAP_PROP_FRAME_WIDTH, 320);
  camera_r.set(CAP_PROP_FRAME_HEIGHT, 240);

  Mat frame_l, frame_r;

  Size boardSize(9, 6);
  const float squareSize = 26.f;  // Set this to your actual square size

  vector<vector<Point2f> > imagePoints_l;
  vector<vector<Point2f> > imagePoints_r;



  int nimages = 0;

  while (1){
    camera_l >> frame_l;
    camera_r >> frame_r;

    bool found_l = false, found_r = false;

    vector<Point2f>corners_l, corners_r;
    found_l = findChessboardCorners(frame_l, boardSize, corners_l,
      CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE);
    found_r = findChessboardCorners(frame_r, boardSize, corners_r,
      CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE);


    if (found_l && found_r &&ChessboardStable(corners_l, corners_r)) {
      Mat viewGray;
      cvtColor(frame_l, viewGray, COLOR_BGR2GRAY);
      cornerSubPix(viewGray, corners_l, Size(11, 11),
        Size(-1, -1), TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 30, 0.1));
      cvtColor(frame_r, viewGray, COLOR_BGR2GRAY);
      cornerSubPix(viewGray, corners_r, Size(11, 11),
        Size(-1, -1), TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 30, 0.1));

      imagePoints_l.push_back(corners_l);
      imagePoints_r.push_back(corners_r);
      ++nimages;
      frame_l += 100;
      frame_r += 100;

      drawChessboardCorners(
  • 11
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值