摄像机的标定问题是机器视觉领域的入门问题,可以分为传统的摄像机定标方法和摄像机自定标方法。定标的方法有很多中常见的有:Tsai(传统)和张正友(介于传统和自定标)等,

摄像机成像模型和四个坐标系(通用原理)。

摄像机模型采用经典的小孔模型,如图中Oc(光心),像面π表示的是视野平面,其到光心的距离为f(镜头焦距)。

四个坐标系分别为:世界坐标系(Ow),摄像机坐标系(Oc),图像物理坐标系(O1,单位mm),图像像素坐标系(O,位于视野平面的左上角,单位pix)。

空间某点P到其像点p的坐标转换过程主要是通过这四套坐标系的三次转换实现的,首先将世界坐标系进行平移和转换得到摄像机坐标系,然后根据三角几何变换得到图像物理坐标系,最后根据像素和公制单位的比率得到图像像素坐标系。(实际的应用过程是这个的逆过程,即由像素长度获知实际的长度)。

ps:通过摄像头的标定,可以得到视野平面上的mm/pix分辨率,对于视野平面以外的物体还是需要通过坐标转换得到视野平面上。

转化的过程和公式参见:摄像机标定原理(关键是三个坐标系).ppt

2 张正友算法的原理

zhang法通过对一定标板在不同方向多次(三次以上)完整拍照,不需要知道定标板的运动方式。直接获得相机的内参(参考文献上矩阵A)和畸变系数。该标定方法精度高于自定标法,且不需要高精度的定位仪器。

ZHANG的算法包含两个模型:一.经典针孔模型,包含四个坐标系,二畸变模型(这个来源未知)

公式三项依次表示,径向畸变,切线畸变,薄棱镜畸变。OPENCV中函数只能给出k1,k2,p1,p2。

还存在另外一种畸变模型,见《摄像机标定算法库的设计和试验验证》一文26 page。(也不知道出处)

ps:单从公式推导的过程来看,第一组公式等号右边应该是U0。

Key:这个方程怎么求?x,y 代表理想的图像坐标(mm),是未知数(不太可能是已知数,xike说的是不考虑畸变的投影值,这个就太简单了)。

*************************************************************************************
#include "cvut.h"
#include <iostream>
#include <fstream>
#include <string>

using namespace cvut;
using namespace std;

void main() {
 ifstream fin("calibdata.txt");
 ofstream fout("caliberation_result.txt");

cout<<"开始提取角点………………";
 int image_count=0; 
 CvSize image_size; 
 CvSize board_size = cvSize(5,7);   
 CvPoint2D32f * image_points_buf = new CvPoint2D32f[board_size.width*board_size.height];  
 Seq<CvPoint2D32f> image_points_seq; 
 
 string filename;
 while (std::getline(fin,filename))
 {
  cout<<"\n 将鼠标焦点移到标定图像所在窗口并输入回车进行下一幅图像的角点提取 \n";

image_count++;
  int count;
  Image<uchar> view(filename); 
  if (image_count == 1)
  {
   
   image_size.width = view.size().width;
   image_size.height = view.size().height;
  }

if (0 == cvFindChessboardCorners( view.cvimage, board_size,
            image_points_buf, &count, CV_CALIB_CB_ADAPTIVE_THRESH ))
   
  {
   cout<<"can not find chessboard corners!\n";
   exit(1);
  }
  
  else {
   Image<uchar> view_gray(view.size(),8,1);
   
   rgb2gray(view,view_gray);
   
   
   cvFindCornerSubPix( view_gray.cvimage, image_points_buf, count, cvSize(11,11),
    cvSize(-1,-1), cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 30, 0.1 ));
  
   
   image_points_seq.push_back(image_points_buf,count);
   
   
   cvDrawChessboardCorners( view.cvimage, board_size, image_points_buf, count, 1);
   view.show("calib");
  
   cvWaitKey(); 
   view.close();
  }

}

delete []image_points_buf; 
 cout<<"角点提取完成!\n"<<endl;

cout<<"开始定标………………"<<"\n"<<endl;
 CvSize square_size = cvSize(10,10); 
 
 
 Matrix<double> object_points(1,board_size.width*board_size.height*image_count,3);

Matrix<double> image_points(1,image_points_seq.cvseq->total,2);
 
 Matrix<int>    point_counts(1,image_count,1);

Matrix<double> intrinsic_matrix(3,3,1);
 Matrix<double> distortion_coeffs(1,4,1);
 Matrix<double> rotation_vectors(1,image_count,3);
 Matrix<double> translation_vectors(1,image_count,3);

int i,j,t;
 for (t=0;t<image_count;t++) {
  for (i=0;i<board_size.height;i++) {
   for (j=0;j<board_size.width;j++) {
    
    
    object_points(0,t*board_size.height*board_size.width + i*board_size.width + j,0) = i*square_size.width;
    object_points(0,t*board_size.height*board_size.width + i*board_size.width + j,1) = j*square_size.height;
    object_points(0,t*board_size.height*board_size.width + i*board_size.width + j,2) = 0;
   }
  }
 }

char  str[10];
 itoa(image_points_seq.cvseq->total,str,10);
 cout<<str<<"\n"<<endl;

for (i=0;i<image_points_seq.cvseq->total;i++)
 {
  image_points(0,i,0) = image_points_seq[i].x;
  image_points(0,i,1) = image_points_seq[i].y;
 }

for (i=0;i<image_count;i++)
  point_counts(0,i) = board_size.width*board_size.height;
 
 
 cvCalibrateCamera2(object_points.cvmat,
        image_points.cvmat,
        point_counts.cvmat,
        image_size,
        intrinsic_matrix.cvmat,
        distortion_coeffs.cvmat,
        rotation_vectors.cvmat,
        translation_vectors.cvmat,
        0);
 cout<<"定标完成!\n";
 
 cout<<"标定结果显示\n";
 cout<<"*************************************************\n";
 cout<<"相机内参intrinsic_matrix\n";
 for(int h=0;h<3;h++)
 {
    cout<<"X:"<<intrinsic_matrix(h,0,0)<<"\tY:"<<intrinsic_matrix(h,1,0)<<"\tZ:"<<intrinsic_matrix(h,2,0)<<"\n";
 }

cout<<"\n畸变系数:distortion_coeffs\n";
 for(int ndis=0;ndis<4;ndis++)
 {
  cout<<distortion_coeffs(0,ndis,0)<<"\\";
 }
 cout<<"\n";

cout<<"\nrotation_vectors\n";
 for(int rot=0;rot<7;rot++)
 {
   cout<<"X:"<<rotation_vectors(0,rot,0)<<"\tY:"<<rotation_vectors(0,rot,1)<<"\tZ:"<<rotation_vectors(0,rot,2)<<"\n";
 }

cout<<"\ntranslation_vectors\n";
 for(i=0;i<7;i++)
 {
  cout<<"第"<<i+1<<"张图"<<"\tX:"<<translation_vectors(0,i,0)<<"\tY:"<<translation_vectors(0,i,1)<<"\tZ:"<<translation_vectors(0,i,2)<<"\n";
 }
 cout<<"***************************************************\n";
 
 cout<<"开始评价定标结果………………\n";
 double total_err = 0.0;
 double err = 0.0;
 Matrix<double> image_points2(1,point_counts(0,0,0),2);

int temp_num  = point_counts(0,0,0);

cout<<"\t每幅图像的定标误差:\n";
 
 fout<<"每幅图像的定标误差:\n";

for (i=0;i<image_count;i++)
 {
  
  cvProjectPoints2(object_points.get_cols(i * point_counts(0,0,0),(i+1)*point_counts(0,0,0)-1).cvmat,
      rotation_vectors.get_col(i).cvmat,
      translation_vectors.get_col(i).cvmat,
      intrinsic_matrix.cvmat,
      distortion_coeffs.cvmat,
     image_points2.cvmat,
      0,0,0,0);

err = cvNorm(image_points.get_cols(i*point_counts(0,0,0),(i+1)*point_counts(0,0,0)-1).cvmat,
     image_points2.cvmat,
     CV_L1);
  total_err += err/=point_counts(0,0,0);

cout<<"******************************************************************\n";
  cout<<"\t\t第"<<i+1<<"幅图像的平均误差:"<<err<<"像素"<<'\n';
  fout<<"\t第"<<i+1<<"幅图像的平均误差:"<<err<<"像素"<<'\n';
  
  cout<<"显示image_point2\n";
  for(int ih=0;ih<7;ih++)
  {
   cout<<"X:"<<image_points2(0,ih,0)<<"\tY:"<<image_points2(0,ih,1)<<"\n";
  }
  cout<<"显示object_Points\n";
  for(int iw=0;iw<7;iw++)
  {
   cout<<"X:"<<image_points.get_cols(i*point_counts(0,0,0),(i+1)*point_counts(0,0,0)-1)(0,iw,0)
    <<"\tY:"<<image_points.get_cols(i*point_counts(0,0,0),(i+1)*point_counts(0,0,0)-1)(0,iw,1)<<"\n";
  }
 }

cout<<"\t总体平均误差:"<<total_err/image_count<<"像素"<<'\n';
 fout<<"总体平均误差:"<<total_err/image_count<<"像素"<<'\n'<<'\n';
 cout<<"评价完成!\n";

cout<<"开始保存定标结果………………";
 Matrix<double> rotation_vector(3,1);
 Matrix<double> rotation_matrix(3,3);
 
 fout<<"相机内参数矩阵:\n";
 fout<<intrinsic_matrix<<'\n';
 fout<<"畸变系数:\n";
 fout<<distortion_coeffs<<'\n';
 for (i=0;i<image_count;i++) {
  fout<<"第"<<i+1<<"幅图像的旋转向量:\n";
  fout<<rotation_vectors.get_col(i);
  
  for (j=0;j<3;j++) {
   rotation_vector(j,0,0) = rotation_vectors(0,i,j);
  }

cvRodrigues2(rotation_vector.cvmat,rotation_matrix.cvmat);

fout<<"第"<<i+1<<"幅图像的旋转矩阵:\n";
  fout<<rotation_matrix;
  fout<<"第"<<i+1<<"幅图像的平移向量:\n";
  fout<<translation_vectors.get_col(i)<<'\n';
 }
 cout<<"完成保存\n";
}

张正友相机标定算法原理与源代码(OpenCV+C++)相关推荐

  1. 张正友相机标定算法详解

    张正友相机标定算法详解 1.齐次表示法与一些基本结论 1.1 点与直线的齐次表示 ​ 在射影几何中,通常用齐次方式来表达点与直线.比如p=(u,v)p=(u,v)p=(u,v)被表示成p^=(x1,x ...

  2. 【计算机视觉】张正友相机标定Calibration原理过程结果

    一.数学原理 1.将世界坐标转换为图像坐标: 2.图像坐标转换为像素坐标系: 3.针孔成像下的透视投影矩阵: 4.世界坐标系转换为像素坐标系 5.张氏标定法的畸变模型: 以上参考:https://bl ...

  3. 张正友相机标定数学原理

  4. [毕设系列] 一、张正友相机标定

    张正友相机标定 预备知识 0.1 刚体.仿射.线性.旋转变换 0.2 什么是标定? 0.3 为什么要进行标定? 0.4 什么是畸变? 一.张正友标定法 1.1 简介 1.2 流程 1.3 畸变公式 二 ...

  5. 张正友相机标定程序实现

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/hongbin_xu/article/details/78988450 前言 在前面的博客中( 三维重 ...

  6. 张正友相机标定Opencv实现以及标定流程标定结果评价图像矫正流程解析(附标定程序和棋盘图)

    from:https://blog.csdn.net/dcrmg/article/details/52939318 使用Opencv实现张正友法相机标定之前,有几个问题事先要确认一下,那就是相机为什么 ...

  7. OpenCV实现张正友相机标定源代码

    本源代码基于VC++和opencv Opencv2.4.13.6版本开发,实现张正友相机标定源代码,资源包括完整源代码和12张棋盘图片,完美运行. Opencv2.4.13.6安装包下载地址:http ...

  8. opencv C++ 张正友相机标定

    //张正友相机标定 //https://blog.csdn.net/u010925447/article/details/77997735 #include <opencv2/core/core ...

  9. 三维重建学习(3):张正友相机标定推导

    前言 前面的几篇博客中介绍了有关相机标定的基础知识(三维重建学习(1):基础知识:旋转矩阵与旋转向量.三维重建学习(2):相机标定基础).这次介绍一个十分经典的单目相机标定方法--张正友标定,并给出数 ...

最新文章

  1. Servlet 工作原理解析
  2. ELF文件的加载和动态链接过程
  3. free() invalid next size异常
  4. V记录2(文档)Vue.extend构造器
  5. Spring Boot静态资源映射规则
  6. Scrapy添加随机ip
  7. mysql alter更新_MySQL 操作命令梳理(2)-alter(update、insert)
  8. java http 双向认证_HttpsURLConnection使用,并实现双向认证
  9. 2019-12-02 调用C++高精度时钟 std::chrono::high_resolution_clock的方法
  10. 腾讯微博-转播到微博的简单使用
  11. 年终总结:我的2016
  12. 南京邮电大学离散数学实验一(求主析取和主合取范式)
  13. 三星显示器 测试软件,3D+TV+APP!三星27英寸LED显示器全国首测
  14. android 系统重装,手机系统重装教程[多图]
  15. 调整Oracle用户密码期限及错误登录次数
  16. 计算机中的云指的是什么,古语有云,古语有云中的云指的是什么
  17. Count the Tetris
  18. 保密工作人员计算机方面知识,【保密知识】个人,如何做好保密工作?4个方面...
  19. QT 支持多客户端网络视频监控的实现
  20. [一维前缀和]leetcode303:区域和检索 - 数组不可变(easy)

热门文章

  1. Windows10局域网共享文件
  2. python编程如何驱动机器人_python编程如何驱动机器人
  3. Vue3 Ref 语法糖,告别 .value 的写法
  4. 如何使用Apache POI删除行和去除合并单元格-20220530
  5. 三维电子沙盘开发教程 数字沙盘培训教程
  6. 构建基于Spring Cloud向Service Mesh框架迁移的解决方案及思路
  7. 娱乐小游戏威信小程序源码
  8. 太平洋热带风情的体验,爸爸去哪儿2大电影 萌娃萌爸的圣诞之旅
  9. 服务器怎么设置程序自动开启,服务器安全狗 定时任务设置图文教程
  10. 自动化代码生成器 v1.2