对摄像头进行标定

转自 http://wiki.opencv.org.cn/index.php/%E6%91%84%E5%83%8F%E5%A4%B4%E6%A0%87%E5%AE%9A

摄像头标定

目录

[隐藏]

[编辑]

标定原理介绍

[编辑]

标定程序1(opencv自带的示例程序)

[编辑]

简介

读者可以直接使用Opencv自带的摄像机标定示例程序,该程序位于 “\OpenCV\samples\c目录下的calibration.cpp”,程序的输入支持直接从USB摄像机读取图片标定,或者读取avi文件或者已经存放于电脑上图片进行标定。

[编辑]

使用说明

编译运行程序,如果未设置任何命令行参数,则程序会有提示,告诉你应该在你编译出来的程序添加必要的命令行,比如你的程序是calibration.exe(以windows操作系统为例)。则你可以添加如下命令行(以下加粗的字体所示):

calibration -w 6 -h 8 -s 2 -n 10 -o camera.yml -op -oe [<list_of_views.txt>]

[编辑]

调用命令行和参数介绍

Usage: calibration

    -w <board_width>         # 图片某一维方向上的交点个数
    -h <board_height>        # 图片另一维上的交点个数
    [-n <number_of_frames>]  # 标定用的图片帧数
                             # (if not specified, it will be set to the number
                             #  of board views actually available)
    [-d <delay>]             # a minimum delay in ms between subsequent attempts to capture a next view
                             # (used only for video capturing)
    [-s <square_size>]       # square size in some user-defined units (1 by default)
    [-o <out_camera_params>] # the output filename for intrinsic [and extrinsic] parameters
    [-op]                    # write detected feature points
    [-oe]                    # write extrinsic parameters
    [-zt]                    # assume zero tangential distortion
    [-a <aspect_ratio>]      # fix aspect ratio (fx/fy)
    [-p]                     # fix the principal point at the center
    [-v]                     # flip the captured images around the horizontal axis
    [input_data]             # 输入数据,是下面三种之中的一种:
                             #  - 指定的包含图片列表的txt文件
                             #  - name of video file with a video of the board
                             # if input_data not specified, a live view from the camera is used

标定图片示例

上图中,横向和纵向分别为9个交点和6个交点,对应上面的命令行的命令参数应该为: -w 9 -h 6

  • 经多次使用发现,不指定 -p参数时计算的结果误差较大,主要表现在对u0,v0的估计误差较大,因此建议使用时加上-p参数

[编辑]

list_of_views.txt

该txt文件表示的是你在电脑上面需要用以标定的图片列表。

view000.png
view001.png
#view002.png
view003.png
view010.png
one_extra_view.jpg

上面的例子中,前面加“井号”的图片被忽略。

  • 在windows的命令行中,有一种简便的办法来产生此txt文件。在CMD窗口中输入如下命令(假设当前目录里面的所有jpg文件都用作标定,并且生成的文件为a.txt)。
dir *.jpg /B >> a.txt

[编辑]

输入为摄像机或者avi文件时

        "When the live video from camera is used as input, the following hot-keys may be used:\n"
            "  <ESC>, ‘q‘ - quit the program\n"
            "  ‘g‘ - start capturing images\n"
            "  ‘u‘ - switch undistortion on/off\n";

[编辑]

代码

请直接复制 calibration.cpp 中的相关代码。

[编辑]

标定程序2

OPENCV没有提供完整的示例,自己整理了一下,贴出来记录。

  1. 首先自制一张标定图片,用A4纸打印出来,设定距离,再设定标定棋盘的格子数目,如8×6,以下是我做的图片8×8

  1. 然后利用cvFindChessboardCorners找到棋盘在摄像头中的2D位置,这里cvFindChessboardCorners不太稳定,有时不能工作,也许需要图像增强处理。
  2. 计算实际的距离,应该是3D的距离。我设定为21.6毫米,既在A4纸上为两厘米。
  3. 再用cvCalibrateCamera2计算内参,
  4. 最后用cvUndistort2纠正图像的变形。

结果如下:

[编辑]

代码

代码下载

具体的函数使用,请参考Cv照相机定标和三维重建#照相机定标

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// OpenCV
#include <cxcore.h>
#include <cv.h>
#include <highgui.h>
#include <cvaux.h>
 
 
void InitCorners3D(CvMat *Corners3D, CvSize ChessBoardSize, int Nimages, float SquareSize);
void makeChessBoard();
int myFindChessboardCorners( const void* image, CvSize pattern_size,
                             CvPoint2D32f* corners, int* corner_count=NULL,
                             int flags=CV_CALIB_CB_ADAPTIVE_THRESH );
 
 
inline int drawCorssMark(IplImage *dst,CvPoint pt)
/*************************************************
  Function:        main_loop
  Description:     绘制一个十字标记
  Calls:
  Called By:
  Input:           RGB image,  pt
  Output:
  Return:
  Others:          需要检查坐标是否越界 to do list
*************************************************/
{
 
	const int cross_len = 4;
	CvPoint pt1,pt2,pt3,pt4;
	pt1.x = pt.x;
	pt1.y = pt.y - cross_len;
	pt2.x = pt.x;
	pt2.y = pt.y + cross_len;
	pt3.x = pt.x - cross_len;
	pt3.y = pt.y;
	pt4.x = pt.x + cross_len;
	pt4.y = pt.y;
 
	cvLine(dst,pt1,pt2,CV_RGB(0,255,0),2,CV_AA, 0 );
	cvLine(dst,pt3,pt4,CV_RGB(0,255,0),2,CV_AA, 0 );
 
	return 0;
}
 
/* declarations for OpenCV */
IplImage                 *current_frame_rgb,grid;
IplImage                 *current_frame_gray;
IplImage                 *chessBoard_Img;
 
int                       Thresholdness = 120;
 
int image_width = 320;
int image_height = 240;
 
bool verbose = false;
 
const int ChessBoardSize_w = 7;
const int ChessBoardSize_h = 7;
// Calibration stuff
bool			calibration_done = false;
const CvSize 	ChessBoardSize = cvSize(ChessBoardSize_w,ChessBoardSize_h);
//float 			SquareWidth = 21.6f; //实际距离 毫米单位 在A4纸上为两厘米
float 			SquareWidth = 17; //投影实际距离 毫米单位  200
 
const   int NPoints = ChessBoardSize_w*ChessBoardSize_h;
const   int NImages = 20; //Number of images to collect
 
CvPoint2D32f corners[NPoints*NImages];
int corner_count[NImages] = {0};
int captured_frames = 0;
 
CvMat *intrinsics;
CvMat *distortion_coeff;
CvMat *rotation_vectors;
CvMat *translation_vectors;
CvMat *object_points;
CvMat *point_counts;
CvMat *image_points;
int find_corners_result =0 ;
 
 
void on_mouse( int event, int x, int y, int flags, void* param )
{
 
    if( event == CV_EVENT_LBUTTONDOWN )
    {
		//calibration_done = true;
    }
}
 
 
int main(int argc, char *argv[])
{
 
 
  CvFont font;
  cvInitFont( &font, CV_FONT_VECTOR0,5, 5, 0, 7, 8);
 
  intrinsics 		= cvCreateMat(3,3,CV_32FC1);
  distortion_coeff 	= cvCreateMat(1,4,CV_32FC1);
  rotation_vectors 	= cvCreateMat(NImages,3,CV_32FC1);
  translation_vectors 	= cvCreateMat(NImages,3,CV_32FC1);
 
  point_counts 		= cvCreateMat(NImages,1,CV_32SC1);
 
  object_points 	= cvCreateMat(NImages*NPoints,3,CV_32FC1);
  image_points 		= cvCreateMat(NImages*NPoints,2,CV_32FC1);
 
 
  // Function to fill in the real-world points of the checkerboard
  InitCorners3D(object_points, ChessBoardSize, NImages, SquareWidth);
 
 
  CvCapture* capture = 0;
 
 
  if( argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0])))
	  capture = cvCaptureFromCAM( argc == 2 ? argv[1][0] - ‘0‘ : 0 );
  else if( argc == 2 )
	  capture = cvCaptureFromAVI( argv[1] );
 
  if( !capture )
  {
	  fprintf(stderr,"Could not initialize capturing...\n");
	  return -1;
  }
 
 
  // Initialize all of the IplImage structures
  current_frame_rgb = cvCreateImage(cvSize(image_width, image_height), IPL_DEPTH_8U, 3);
 
  IplImage *current_frame_rgb2 = cvCreateImage(cvSize(image_width, image_height), IPL_DEPTH_8U, 3);
  current_frame_gray = cvCreateImage(cvSize(image_width, image_height), IPL_DEPTH_8U, 1);
 
  chessBoard_Img   = cvCreateImage(cvSize(image_width, image_height), IPL_DEPTH_8U, 3);
  current_frame_rgb2->origin = chessBoard_Img->origin  = current_frame_gray->origin = current_frame_rgb->origin = 1;
 
  makeChessBoard();
 
  cvNamedWindow( "result", 0);
  cvNamedWindow( "Window 0", 0);
  cvNamedWindow( "grid", 0);
  cvMoveWindow( "grid", 100,100);
  cvSetMouseCallback( "Window 0", on_mouse, 0 );
  cvCreateTrackbar("Thresholdness","Window 0",&Thresholdness, 255,0);
 
  while (!calibration_done)
  {
 
	while (captured_frames < NImages)
    {
	  current_frame_rgb = cvQueryFrame( capture );
	  //current_frame_rgb = cvLoadImage( "c:\\BoardStereoL3.jpg" );
	  //cvCopy(chessBoard_Img,current_frame_rgb);
 
	  if( !current_frame_rgb )
		  break;
 
	  cvCopy(current_frame_rgb,current_frame_rgb2);
	  cvCvtColor(current_frame_rgb, current_frame_gray, CV_BGR2GRAY);
	  //cvThreshold(current_frame_gray,current_frame_gray,Thresholdness,255,CV_THRESH_BINARY);
	  //cvThreshold(current_frame_gray,current_frame_gray,150,255,CV_THRESH_BINARY_INV);
 
/*
	int pos = 1;
	IplConvKernel* element = 0;
	const int element_shape = CV_SHAPE_ELLIPSE;
	element = cvCreateStructuringElementEx( pos*2+1, pos*2+1, pos, pos, element_shape, 0 );
	cvDilate(current_frame_gray,current_frame_gray,element,1);
	cvErode(current_frame_gray,current_frame_gray,element,1);
	cvReleaseStructuringElement(&element);
*/
 
	find_corners_result = cvFindChessboardCorners(current_frame_gray,
                                          ChessBoardSize,
                                          &corners[captured_frames*NPoints],
                                          &corner_count[captured_frames],
                                          0);
 
 
 
	cvDrawChessboardCorners(current_frame_rgb2, ChessBoardSize, &corners[captured_frames*NPoints], NPoints, find_corners_result);
 
 
	cvShowImage("Window 0",current_frame_rgb2);
	cvShowImage("grid",chessBoard_Img);
 
	if(find_corners_result==1)
	{
		cvWaitKey(2000);
		cvSaveImage("c:\\hardyinCV.jpg",current_frame_rgb2);
		captured_frames++;
	}
	//cvShowImage("result",current_frame_gray);
 
	intrinsics->data.fl[0] = 256.8093262;   //fx
	intrinsics->data.fl[2] = 160.2826538;   //cx
	intrinsics->data.fl[4] = 254.7511139;   //fy
	intrinsics->data.fl[5] = 127.6264572;   //cy
 
	intrinsics->data.fl[1] = 0;
	intrinsics->data.fl[3] = 0;
	intrinsics->data.fl[6] = 0;
	intrinsics->data.fl[7] = 0;
	intrinsics->data.fl[8] = 1;
 
	distortion_coeff->data.fl[0] = -0.193740;  //k1
	distortion_coeff->data.fl[1] = -0.378588;  //k2
	distortion_coeff->data.fl[2] = 0.028980;   //p1
	distortion_coeff->data.fl[3] = 0.008136;   //p2
 
	cvWaitKey(40);
	find_corners_result = 0;
    }
	//if (find_corners_result !=0)
	{
 
		printf("\n");
 
		cvSetData( image_points, corners, sizeof(CvPoint2D32f));
		cvSetData( point_counts, &corner_count, sizeof(int));
 
 
		cvCalibrateCamera2( object_points,
			image_points,
			point_counts,
			cvSize(image_width,image_height),
			intrinsics,
			distortion_coeff,
			rotation_vectors,
			translation_vectors,
			0);
 
 
		// [fx 0 cx; 0 fy cy; 0 0 1].
		cvUndistort2(current_frame_rgb,current_frame_rgb,intrinsics,distortion_coeff);
		cvShowImage("result",current_frame_rgb);
 
 
		float intr[3][3] = {0.0};
		float dist[4] = {0.0};
		float tranv[3] = {0.0};
		float rotv[3] = {0.0};
 
		for ( int i = 0; i < 3; i++)
		{
			for ( int j = 0; j < 3; j++)
			{
				intr[i][j] = ((float*)(intrinsics->data.ptr + intrinsics->step*i))[j];
			}
			dist[i] = ((float*)(distortion_coeff->data.ptr))[i];
			tranv[i] = ((float*)(translation_vectors->data.ptr))[i];
			rotv[i] = ((float*)(rotation_vectors->data.ptr))[i];
		}
		dist[3] = ((float*)(distortion_coeff->data.ptr))[3];
 
		printf("-----------------------------------------\n");
		printf("INTRINSIC MATRIX: \n");
		printf("[ %6.4f %6.4f %6.4f ] \n", intr[0][0], intr[0][1], intr[0][2]);
		printf("[ %6.4f %6.4f %6.4f ] \n", intr[1][0], intr[1][1], intr[1][2]);
		printf("[ %6.4f %6.4f %6.4f ] \n", intr[2][0], intr[2][1], intr[2][2]);
		printf("-----------------------------------------\n");
		printf("DISTORTION VECTOR: \n");
		printf("[ %6.4f %6.4f %6.4f %6.4f ] \n", dist[0], dist[1], dist[2], dist[3]);
		printf("-----------------------------------------\n");
		printf("ROTATION VECTOR: \n");
		printf("[ %6.4f %6.4f %6.4f ] \n", rotv[0], rotv[1], rotv[2]);
		printf("TRANSLATION VECTOR: \n");
		printf("[ %6.4f %6.4f %6.4f ] \n", tranv[0], tranv[1], tranv[2]);
		printf("-----------------------------------------\n");
 
		cvWaitKey(0);
 
		calibration_done = true;
	}
 
  }
 
  exit(0);
  cvDestroyAllWindows();
}
 
void InitCorners3D(CvMat *Corners3D, CvSize ChessBoardSize, int NImages, float SquareSize)
{
  int CurrentImage = 0;
  int CurrentRow = 0;
  int CurrentColumn = 0;
  int NPoints = ChessBoardSize.height*ChessBoardSize.width;
  float * temppoints = new float[NImages*NPoints*3];
 
  // for now, assuming we‘re row-scanning
  for (CurrentImage = 0 ; CurrentImage < NImages ; CurrentImage++)
  {
    for (CurrentRow = 0; CurrentRow < ChessBoardSize.height; CurrentRow++)
    {
      for (CurrentColumn = 0; CurrentColumn < ChessBoardSize.width; CurrentColumn++)
      {
		  temppoints[(CurrentImage*NPoints*3)+(CurrentRow*ChessBoardSize.width + CurrentColumn)*3]=(float)CurrentRow*SquareSize;
		  temppoints[(CurrentImage*NPoints*3)+(CurrentRow*ChessBoardSize.width + CurrentColumn)*3+1]=(float)CurrentColumn*SquareSize;
		  temppoints[(CurrentImage*NPoints*3)+(CurrentRow*ChessBoardSize.width + CurrentColumn)*3+2]=0.f;
      }
    }
  }
  (*Corners3D) = cvMat(NImages*NPoints,3,CV_32FC1, temppoints);
}
 
int myFindChessboardCorners( const void* image, CvSize pattern_size,
                             CvPoint2D32f* corners, int* corner_count,
                             int flags )
 
{
 
 
	IplImage* eig = cvCreateImage( cvGetSize(image), 32, 1 );
	IplImage* temp = cvCreateImage( cvGetSize(image), 32, 1 );
	double quality = 0.01;
	double min_distance = 5;
	int win_size =10;
 
	int count = pattern_size.width * pattern_size.height;
	cvGoodFeaturesToTrack( image, eig, temp, corners, &count,
		quality, min_distance, 0, 3, 0, 0.04 );
	cvFindCornerSubPix( image, corners, count,
		cvSize(win_size,win_size), cvSize(-1,-1),
		cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));
 
	cvReleaseImage( &eig );
	cvReleaseImage( &temp );
 
	return 1;
}
 
void makeChessBoard()
{
 
  CvScalar e;
  e.val[0] =255;
  e.val[1] =255;
  e.val[2] =255;
  cvSet(chessBoard_Img,e,0);
  for(int i = 0;i<ChessBoardSize.width+1;i++)
	  for(int j = 0;j<ChessBoardSize.height+1;j++)
	  {
		  int w =(image_width)/2/(ChessBoardSize.width);
		  int h = w; //(image_height)/2/(ChessBoardSize.height);
 
		  int ii = i+1;
		  int iii = ii+1;
		  int jj =j+1;
		  int jjj =jj+1;
		  int s_x = image_width/6;
 
		if((i+j)%2==1)
		   cvRectangle( chessBoard_Img, cvPoint(w*i+s_x,h*j+s_x),cvPoint(w*ii-1+s_x,h*jj-1+s_x), CV_RGB(0,0,0),CV_FILLED, 8, 0 );
	  }
}
时间: 2024-10-22 04:16:29

对摄像头进行标定的相关文章

摄像头距离标定方法研究(得到像素和毫米的转换比)

一般在高精度测量时需要做以下几个标定,一光学畸变标定(如果您不是用的软件镜头,一般都必须标定),二投影畸变的标定,也就是因为您安装位置误差代表的图像畸变校正,三物像空间的标定,也就是具体算出每个像素对应物空间的尺寸. 前两者应该都可以通过“张正友”标定方法进行解决:对于空间的标定,基本上都是通过获得比对现实中的已经知道长度的物体,获得像素当量到长度的转化. 现实拍摄的物体,或多或少会有噪音干扰,在标定的过程中还需要图像处理算法进行纠正. 比如我获得这样的图片.这幅图片的获取,基于我自己改造的图像

基于opencv的摄像头的标定

四个坐标系分别为:世界坐标系(Ow),摄像机坐标系(Oc),图像物理坐标系(O1,单位mm),图像像素坐标系(O,位于视野平面的左上角,单位pix). 空间某点P到其像点p的坐标转换过程主要是通过这四套坐标系的三次转换实现的,首先将世界坐标系进行平移和转换得到摄像机坐标系,然后根据三角几何变换得到图像物理坐标系,最后根据像素和公制单位的比率得到图像像素坐标系.(实际的应用过程是这个的逆过程,即由像素长度获知实际的长度) OpenCV中使用的求解焦距和成像原点的算法是基于张正友的方法( pdf )

【opencv学习】使用opencv与两个摄像头实现双目标定与测距

目录 目录 说明 之前文章中的双目测距代码 效果更好的双目视觉代码 效果更好的双目视觉代码的实现 1 标定过程 2 测距过程 一些问题以及解决方法 要说的 1 说明 我之前写过一篇文章<完全基于opencv的双目景深与测距的实现>:http://blog.csdn.net/hysteric314/article/details/50456570 但是之前文章中的双目视觉代码并不完善,所以就想再找找看有没有更好的实现方法. 然后就在youtube上找到一个视频:https://www.youtu

C#下的摄像机标定

前言:计算机视觉的基本任务之一是从摄像机获取的图像信息出发计算三维空间中物体的几何信息,并由此重建和识别物体,而空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系是由摄像机成像的几何模型决定的,这些几何模型参数就是摄像机参数.在大多数条件下,这些参数必须通过实验与计算才能得到,这个过程称为摄像机标定.标定过程就是确定摄像机的几何和光学参数.摄像机相对于世界坐标系的方位. 内容: 1.假设摄像机所拍摄到的图像与三维空间中的物体之间存在以下一种简单的线性关系:[像]=M[物],这里,矩阵

关于opencv标定的一些疑问,首先声明这篇文章转载于纸异兽,由于暂时联系不到他本人,只好请教各位了。有些问题想请教,各位大神可以留下联系方式帮我解决,万分感谢

在做SLAM时,希望用到深度图来辅助生成场景,所以要构建立体视觉,在这里使用OpenCV的Stereo库和python来进行双目立体视觉的图像处理. 立体标定 应用标定数据 转换成深度图 标定 在开始之前,需要准备的当然是两个摄相头,根据你的需求将两个摄像头进行相对位置的固定,我是按平行来进行固定的(如果为了追求两个双目图像更高的生命度,也可以将其按一定钝角固定,这样做又限制了场景深度的扩展,根据实际需求选择) 由于摄像头目前是我们手动进行定位的,我们现在还不知道两张图像与世界坐标之间的耦合关系

ORB-SLAM2 运行 —— ROS + Android 手机摄像头

转载请注明出处,谢谢 原创作者:Mingrui 原创链接:https://www.cnblogs.com/MingruiYu/p/12404730.html 本文要点: ROS 配置安装 解决 sudo rosdep init 报错 Website may be down. ORB-SLAM2 ROS 配置安装 解决报错 DSO missing from command line Android 手机摄像头与 PC 进行基于 ROS 的通信 手机摄像头标定 采集标定图像 OpenCV sampl

交互能力的提升

随着电 脑科技的进步,影像呈现的精致程度和效率都大幅提升,全景拍摄/剪接技术也更成熟了. 动作捕捉和空间定位技术也丰富了 VR /AR内容的互动性. 伴随着VR的交互式叙事方式带来的视觉.触觉.听觉.味觉.嗅觉等多元互动体验是前所未有的. VR 的诞生将颠覆人类与计算机交互的方式. 在VR的交互体验方面,除了VR设备提供准确的运动跟踪数据之外.在VR软件中还需要分析一段时间内的运动数据,并与标准的动作模式进行匹配,来理解用户的操作意图. ***  << 听觉相关技术 >>  ***

双摄像头立体成像(三)-畸变矫正与立体校正

畸变矫正是上一篇博文的遗留问题,当畸变系数和内外参数矩阵标定完成后,就应该进行畸变的矫正,以达到消除畸变的目的,此其一. 在该系列第一部分的博文中介绍的立体成像原理中提到,要通过两幅图像估计物点的深度信息,就必须在两幅图像中准确的匹配到同一物点,这样才能根据该物点在两幅图像中的位置关系,计算物体深度.为了降低匹配的计算量,两个摄像头的成像平面应处于同一平面.但是,单单依靠严格的摆放摄像头来达到这个目的显然有些困难.立体校正就是利用几何图形变换(Geometric Image Transforma

车道线检测文献解读系列(一) 基于机器视觉的高速车道标志线检测算法的研究_李晗

作者背景 基于机器视觉的高速车道标志线检测算法的研究_李晗 东北大学车辆工程硕士学位论文 2006年 [GB/T 7714]李晗. 基于机器视觉的高速车道标志线检测算法的研究[D]. 东北大学, 2006. DOI:10.7666/d.y852642.` 论文结构一览 预处理 灰度化 [亮点]模式判别 选择日间模式还是夜间模式: 在每个检测周期开始时,首先判断采用日间模式还是夜间模式工作.摄像机视野中的上半部分为天空背景,天空亮度可以显著区分日间和夜间环境.由于天空的颜色为蓝离,日间天空的蓝色分