Serie de calibración de cámaras --- operadores de calibración relacionados con opencv

Tabla de contenido

【1】Introducción relacionada a la calibración

【2】Introducción al flujo de algoritmos y operadores relacionados

(1) El proceso del algoritmo consta principalmente de cinco partes:

(2) Introducción a los operadores relacionados

1. Encuentra los puntos de las esquinas en el tablero de calibración del tablero de ajedrez.

2. Precisión de esquinas de subpíxeles

3. Visualiza los puntos de las esquinas

4.Calibración de la cámara

5. Cálculo de errores

【3】Código completo


【1】Introducción relacionada a la calibración

(1) Propósito de la calibración

        Antes de realizar la calibración de la cámara, debe comprender el propósito de la calibración de la cámara. En pocas palabras, la calibración de la cámara consiste principalmente en encontrar los parámetros internos y externos de la cámara en función de las diferentes posiciones de la placa de calibración debajo de la cámara. Los parámetros internos son propiedades inherentes de la cámara, lo que significa que no importa cómo coloque la placa de calibración, su cámara no está fija y los parámetros internos de la cámara no se ven afectados. Sin embargo, la precisión de los parámetros internos está relacionada con el número de calibraciones. imágenes de la placa En términos generales, la placa de calibración Cuanto más completa sea la ubicación, más precisa será la referencia interna de la cámara y menor será el error. Para los parámetros externos de la cámara, es la transformación espacial (matriz de rotación y matriz de traducción) del sistema de coordenadas mundial en relación con el sistema de coordenadas de la cámara.

(2) Introducción a los parámetros internos.

        gif.latex?f_%7Bx%7D: el número de píxeles en la dirección X dentro de la unidad de milímetro;

        gif.latex?f_%7By%7D: El número de píxeles en la dirección Y dentro de la unidad de milímetro;

        gif.latex?u_%7B0%7D, gif.latex?v_%7B0%7D: representa las coordenadas xey del centro de la imagen;

La matriz de parámetros internos (3 x 3) es como se muestra a continuación:

8f8d8af185cd469e895427c16165a122.png

En aplicaciones prácticas, se utiliza una lente telecéntrica. Dentro del rango de profundidad de campo, el campo de visión de la imagen es constante, lo que significa que la lente telecéntrica no se distorsionará y los parámetros internos son ciertos. Cuando se usa, puede configurarse según los parámetros de la cámara. Pero para las cámaras normales, similares a las lentes ojo de pez, los parámetros internos deben corregirse al usarlas, y no importa si la cámara está fija o no.

(3) Coeficiente de distorsión

       El coeficiente de distorsión tiene principalmente 5 parámetros: k1, k2, k3 coeficientes de distorsión radial, p1, p2 son coeficientes de distorsión tangencial. La distorsión radial ocurre en el proceso de convertir el sistema de coordenadas de la cámara al sistema de coordenadas físicas de la imagen. La distorsión tangencial ocurre durante el proceso de fabricación de la cámara porque el plano del fotorreceptor no es paralelo a la lente. La distorsión radial, es decir, la distorsión causada por las diferentes distancias focales de diferentes áreas de la lente, se divide en distorsión en cojín y distorsión en barril, como se muestra en la figura siguiente. La distorsión es más obvia más cerca del borde de la lente. La deformación es como se muestra en la figura:

5a72ee7fbd2a42568a25bdc058d49f7b.png

 (4) Parámetros externos de la cámara

Los parámetros externos de la cámara son la descripción del sistema de coordenadas mundial en el sistema de coordenadas de la cámara. R es el parámetro de rotación que es producto de la matriz de rotación de cada eje, donde el parámetro de rotación de cada eje ( ϕ , ω , θ ), el parámetro de traslación ( Tx , Ty , Tz ), la matriz es (4 x 4 matriz homogénea) de la siguiente manera:

ac34f3e553814a58bd034a747a5d8805.png

【2】Introducción al flujo de algoritmos y operadores relacionados

(1) El proceso del algoritmo consta principalmente de cinco partes:

        1. Extraiga los puntos de las esquinas de la placa de calibración.

        2. Precisión de esquinas de subpíxeles.

        3. Visualice los puntos de las esquinas.

        4. Calibración de la cámara.

        5. Error de cálculo (error de reproyección).

(2) Introducción a los operadores relacionados

1. Encuentra los puntos de las esquinas en el tablero de calibración del tablero de ajedrez.

bool findChessboardCorners(
    InputArray image, 
    Size patternSize, 
    OutputArray corners,
    int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE 
);

El primer parámetro es la imagen entrante, el tipo de imagen es 8UC1 y 8UC3.

El segundo parámetro es el número de puntos de las esquinas interiores en el tablero de calibración del tablero de ajedrez (cuántos en una fila, cuántos en una columna), por ejemplo

Size size(9,6)
//一行九个内角点,一列6个内角点

El tercer parámetro se utiliza para registrar las coordenadas de píxeles de los puntos de las esquinas que se van a buscar.

Su tipo de datos es vector <punto2f>, que almacena las coordenadas de los puntos de las esquinas de una imagen y define el almacenamiento de los puntos de las esquinas de múltiples placas de calibración.

vector<vector<punto2f>>

El cuarto parámetro:

CALIB_CB_ADAPTIVE_THRESH = 1, encuentre la placa de calibración a través del método de umbral adaptativo;
CALIB_CB_NORMALIZE_IMAGE = 2, si este indicador está configurado, use CV::equalizeHist() para normalizar la imagen antes de aplicar el umbral; CALIB_CB_FILTER_QUADS = 4, cuando la imagen de la placa de calibración tiene distorsión, la aplicación del cuadrilátero se limitará para evitar cuadriláteros incorrectos;
CALIB_CB_FAST_CHECK = 8, la imagen se escaneará rápidamente para garantizar que haya puntos de esquina en la imagen. Si no hay puntos de esquina, la imagen se omitirá directamente.

El valor de retorno de este operador es si el punto de esquina se encontró correctamente.

(Encontrar la placa de calibración de cuadrícula circular es similar a esto y aún no se presentará).

2. Precisión de esquinas de subpíxeles

Principalmente los dos operadores cv::cornerSubPix y find4QuadCornerSubpix tienen los mismos parámetros.

bool cv::find4QuadCornerSubpix  ( InputArray  img,  
  InputOutputArray  corners,  
  Size  region_size  
 ) 

img: La matriz Mat de entrada, preferiblemente una imagen en escala de grises de 8 bits, tiene una mayor eficiencia de detección;                                                    corners: El vector de coordenadas del punto de esquina inicial también se utiliza como salida de la posición de las coordenadas de subpíxeles, por lo que generalmente deben ser datos de punto flotante usando elementos Pointf2f/Point2drepresentados por vectores.
region_size: El tamaño de la ventana de búsqueda de puntos de esquina, el rango de vecindad considerado al optimizar las coordenadas.

3. Visualiza los puntos de las esquinas

El operador de visualización para puntos de esquina es drawChessboardCorners.

void cv::drawChessboardCorners  ( InputOutputArray  image,  
  Size  patternSize,  
  InputArray  corners,  
  bool  patternWasFound  
 )  

imagen: la imagen de destino donde se deben dibujar los puntos de las esquinas, debe ser una imagen en color de 8 bits.
patrónTamaño: El número de filas y columnas de puntos de esquina interiores en cada tablero de ajedrez calibrado.
esquinas: una matriz de coordenadas de esquina detectadas.
patrónWasFound: dibuje una bandera de estilo de punto de esquina para mostrar si se encuentra una placa de calibración completa.
Cuando patrónWasFound=true, conecte cada punto de esquina interior en secuencia.
Cuando patrónWasFound=false, la posición de la esquina se marca con un círculo (rojo).

4.Calibración de la cámara

calibrateCamera es el operador de calibración de la cámara. La función específica es la siguiente:

     double calibrateCamera(        
        InputArrayOfArrays objectPoints,        //输入:目标点位 的集合的集合
        InputArrayOfArrays imagePoints,         //输入:图像点位 的集合的集合
        Size imageSize,                         //输入:图像尺寸 
        InputOutputArray cameraMatrix,          //输出:相机内参矩阵(fx,fy,cx,cy)
        InputOutputArray distCoeffs,            //输出:畸变矫正多项式(k1,k2,p1,p2,k3...)
        OutputArrayOfArrays rvecs,              //输出:旋转矩阵 的集合
        OutputArrayOfArrays tvecs,              //输出:平移矩阵 的集合
        int flags = 0,                          //输入:对标定过程方法进行更精细的控制
        TermCriteria criteria =                 //输入:迭代参数
        TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON));

El primer parámetro, objectPoints, es un punto tridimensional en el sistema de coordenadas mundial. Al usarlo, debe ingresar un vector de vectores de puntos de coordenadas tridimensionales, es decir, vector<vector<Point3f>> object_points. Es necesario calcular (inicializar) las coordenadas mundiales de cada punto de esquina interior en función del tamaño de una única matriz en blanco y negro en el tablero de ajedrez.

El segundo parámetro imagePoints es el punto de coordenadas de la imagen correspondiente a cada punto de la esquina interior. Al igual que objectPoints, se debe ingresar una variable en forma de vector<vector<Point2f>> image_points_seq;

El tercer parámetro, imageSize, es el tamaño de píxel de la imagen. Este parámetro debe utilizarse al calcular los parámetros internos y la matriz de distorsión de la cámara;

El cuarto parámetro cameraMatrix es la matriz de parámetros internos de la cámara. Simplemente ingrese un Mat cameraMatrix, como Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0));

El quinto parámetro distCoeffs es la matriz de distorsión. Introduzca un Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0));

El sexto parámetro rvecs es el vector de rotación, se debe ingresar un vector de tipo Mat, es decir, vector<Mat>rvecs;

El séptimo parámetro tvecs es el vector de desplazamiento, que debería ser vector<Mat> tvecs como rvecs;

Los indicadores del octavo parámetro son el algoritmo utilizado durante la calibración. Existen los siguientes parámetros:

CV_CALIB_USE_INTRINSIC_GUESS: cuando se utiliza este parámetro, debe haber valores estimados de fx, fy, u0, v0 en la matriz cameraMatrix. De lo contrario, se inicializará el punto central de la imagen (u0, v0) y fx, fy se estimarán utilizando mínimos cuadrados. 
CV_CALIB_FIX_PRINCIPAL_POINT: Fija el punto del eje óptico al optimizar. Cuando se establece el parámetro CV_CALIB_USE_INTRINSIC_GUESS, el punto del eje óptico permanecerá en el centro o en algún valor de entrada. 
CV_CALIB_FIX_ASPECT_RATIO: corrige la relación de fx/fy y solo usa fy como variable para un cálculo óptimo. Cuando CV_CALIB_USE_INTRINSIC_GUESS no está configurado, se ignorarán fx y fy. En el cálculo sólo se utiliza la relación fx/fy. 
CV_CALIB_ZERO_TANGENT_DIST: establece los parámetros de distorsión tangencial (p1, p2) en cero. 
CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6: La distorsión radial correspondiente permanece sin cambios durante la optimización. 
CV_CALIB_RATIONAL_MODEL: Calcula tres parámetros de distorsión k4, k5, k6. Si no se establece, sólo se calculan los otros 5 parámetros de distorsión.

El noveno criterio de parámetro es la configuración óptima de la condición de terminación de iteración.

Antes de usar esta función para la operación de calibración, es necesario inicializar las coordenadas de posición del sistema de coordenadas espaciales de cada punto de esquina interior en el tablero de ajedrez. El resultado de la calibración es generar la matriz de parámetros interna de la cámara cameraMatrix, los cinco coeficientes de distorsión de la cámara. distCoeffs y cada imagen generan sus propios vectores de traducción y vectores de rotación.

Valor de retorno: error cuadrático medio de reproyección

5. Cálculo de errores

Error de reproyección projectPoints, el método para evaluar los resultados de la calibración es reproyectar los puntos tridimensionales en el espacio a través de los parámetros internos y externos obtenidos de la cámara, obtener las coordenadas de los nuevos puntos de proyección de los puntos tridimensionales en El espacio en la imagen y calcula las coordenadas de proyección y los subpíxeles. La desviación entre las coordenadas de los puntos de las esquinas. Cuanto menor sea la desviación, mejor será el resultado de la calibración.

void projectPoints( InputArray objectPoints,
                                 InputArray rvec, InputArray tvec,
                                 InputArray cameraMatrix, InputArray distCoeffs,
                                 OutputArray imagePoints,
                                 OutputArray jacobian=noArray(),
                                 double aspectRatio=0 );

El primer parámetro, objectPoints, son las coordenadas de puntos tridimensionales en el sistema de coordenadas de la cámara;

El segundo parámetro rvec es el vector de rotación y cada imagen tiene su propio vector de selección;

El tercer parámetro tvec es un vector de desplazamiento y cada imagen tiene su propio vector de traducción;

El cuarto parámetro cameraMatrix es la matriz de parámetros internos de la cámara obtenida;

El quinto parámetro distCoeffs es la matriz de distorsión de la cámara;

El sexto parámetro imagePoints es el punto de coordenadas en la imagen correspondiente a cada punto de esquina interior;

El séptimo parámetro jacobiano es el jacobiano;

El octavo parámetro relación de aspecto es un parámetro opcional relacionado con la unidad fotosensible del sensor de la cámara. Si se establece en un valor distinto de 0, la función predeterminada es un dx/dy fijo de la unidad fotosensible y la matriz jacobiana se ajustará en consecuencia;

【3】Código completo

#include<opencv2\opencv.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<iostream>
#include<fstream>
#include<io.h>
#include<string>

using namespace cv;
using namespace std;

const char* path = "E:\\乔大花进度\\11-24\\相机标定\\calibration2\\*.jpg";//标定板存放地址
string pic_path = "E:\\乔大花进度\\11-24\\相机标定\\calibration2\\"; 

int main(int argc, char** argv)
{
     //【1】 从文件夹中读取标定图片
	
	intptr_t handle;	//用于文件查找的句柄
	struct _finddata_t fileinfo;   //定义文件存储的结构体
	handle = _findfirst(path, &fileinfo); //用于第一次查找,handle记录查找结果,未找到返回-1;
	if (handle==-1)
	{
		return -1;		//未成功读取图片结束程序
	}

	if (fileinfo.attrib!= _A_SUBDIR)
	{
		printf("%s\n",fileinfo.name); //如果不是子文件夹则输出名字
	}

	ofstream outdata; //定义输出流
	outdata.open(pic_path + "list.txt",ios::trunc);//打开存放标定板名字的txt文件,并且清空其内容
	outdata << fileinfo.name << endl;
	outdata.close();
	while (!_findnext(handle,&fileinfo))//继续在文件夹下搜索标定板jpg格式文件,未找到返回-1,找到返回0;
	{
		ofstream outdata; //定义输出流
		if (fileinfo.attrib != _A_SUBDIR)//
			printf("%s\n", fileinfo.name);
		outdata.open(pic_path + "list.txt", ios::app);
		outdata << fileinfo.name << endl;
	}
	outdata.close();//关闭输出流
	_findclose(handle);//关闭查找句柄


	//定义vector用于存放图片名字;
	vector<string> pic_name;
	ifstream indata;
	indata.open(pic_path + "list.txt", ios::in);
	if (!indata)//未打开成功返回0
	{
		cout << "读取txt文件失败" << endl;
		return -1;
	}

	
	while (!indata.eof())
	{
		string str;
		getline(indata,str);
		if (str.length()>2)
		{
			pic_name.push_back(str);
		}

	}
	cout << "标定板照片的数量为" << pic_name.size() << endl;
	//【2】 依次读取每张照片,提取角点

	cout << "开始提取角点……" << endl;
	int image_nums = 0;  //图片数量
	Size image_size; //图片尺寸
	int points_per_row = 9;  //每行的内点数
	int points_per_col = 6;   //每列的内点数

	Size corner_size = Size(points_per_row, points_per_col); //标定板每行每列角点个数,共12*12个角点
	vector<Point2f> points_per_image;                            //缓存每幅图检测到的角点
	vector<vector<Point2f>> points_all_images;                   //用一个二维数组保存检测到的所有角点
	string image_file_name;                                          //声明一个文件名的字符串

	for (int i = 0; i < pic_name.size(); i++)
	{
		image_nums++;
		//读入图片
		Mat image_raw = imread(pic_path + pic_name[i]);
		if (image_nums == 1)
		{
			// cout<<"channels = "<<image_raw.channels()<<endl;
			// cout<<image_raw.type()<<endl;  //CV_8UC3
			image_size.width = image_raw.cols;  //图像的宽对应着列数
			image_size.height = image_raw.rows; //图像的高对应着行数
			cout << "image_size.width = " << image_size.width << endl;
			cout << "image_size.height = " << image_size.height << endl;
		}
		//角点检测
		Mat image_gray;                               
		cvtColor(image_raw, image_gray,COLOR_BGR2GRAY); //将BGR图转化为灰度图
														
		 //step1 提取角点,并且返回布尔值
		bool success = findChessboardCorners(image_gray, corner_size, points_per_image, CALIB_CB_FAST_CHECK);
		if (!success)
		{
			cout << pic_name[i]<<":图片未找到角点" << endl;
			continue;
		}
		else
		{
			//亚像素精确化(两种方法)
			//step2 亚像素角点
			find4QuadCornerSubpix(image_gray, points_per_image, Size(5, 5));
			// cornerSubPix(image_gray,points_per_image,Size(5,5));
			points_all_images.push_back(points_per_image); //保存亚像素角点
														   
			//step3 角点可视化,//在图中画出角点位置
			drawChessboardCorners(image_raw, corner_size, points_per_image, success); //将角点连线
			imshow("Camera calibration", image_raw);
			waitKey(0); //等待按键输入
			cout << "照片的序号为:" << image_nums << endl;
		}
	}

	//destroyAllWindows();
	int nums = points_all_images.size();
	cout << "处理标定照片的数量为:" << nums << endl;

	cout << "开始进行相机标定........." << endl;

	//【3】将标定板中二维像素点位转为三维空间点

	//开始相机标定
	Size2f block_size(0.023, 0.023);                            //每个小方格实际大小, 只会影响最后求解的平移向量t(标定板中每个小方块的实际大小)
	Mat camera_K(3, 3, CV_32FC1, Scalar::all(0));   //内参矩阵3*3
	Mat distCoeffs(1, 5, CV_32FC1, Scalar::all(0)); //畸变矩阵1*5
	vector<cv::Mat> rotationMat;                            //旋转矩阵
	vector<cv::Mat> translationMat;                         //平移矩阵
															//初始化角点三维坐标,从左到右,从上到下!!!
	vector<Point3f> points3D_per_image;
	for (int i = 0; i < corner_size.height; i++)
	{
		for (int j = 0; j < corner_size.width; j++)
		{
			points3D_per_image.push_back(Point3f(block_size.width * j, block_size.height * i, 0));
		}
	}
	//生成与图片数量相同的标定板照片向量,也就是一张拍摄照片和一张标准标定板照片对应
	vector<vector<Point3f>> points3D_all_images(nums, points3D_per_image);  //保存所有图像角点的三维坐标, z=0 

	int point_counts = corner_size.area(); //每张图片上角点个数
	cout << "标定板的角点数量为:" << point_counts << endl;

	for (int i = 0; i < points3D_per_image.size(); i++)
	{
		cout << points3D_per_image[i] << endl;
	}

	//【4】开始进行标定
	calibrateCamera(points3D_all_images, points_all_images, image_size, camera_K, distCoeffs, rotationMat, translationMat, 0);

	//【5】对标定结果进行评价,计算标定误差
	ofstream fout;
	//将标定出来的误差存到该文件下;
	fout.open(pic_path+"calibration.txt", ios::app);
	double total_err = 0.0;               //所有图像平均误差总和
	double err = 0.0;                     //每幅图像的平均误差
	vector<cv::Point2f> points_reproject; //重投影点
	cout << "\n\t每幅图像的标定误差:\n";
	fout << "每幅图像的标定误差:\n";
	for (int i = 0; i < nums; i++)
	{
		vector<cv::Point3f> points3D_per_image = points3D_all_images[i];//标准的标定板图像
		//通过之前标定得到的相机内外参,对三维点进行重投影
		cv::projectPoints(points3D_per_image, rotationMat[i], translationMat[i], camera_K, distCoeffs, points_reproject);
		//计算两者之间的误差,转为双通道,一个通道存储x,一个通道存储y
		vector<cv::Point2f> detect_points = points_all_images[i];  //提取到的图像角点
		cv::Mat detect_points_Mat = cv::Mat(1, detect_points.size(), CV_32FC2); //变为1*144的矩阵,2通道保存提取角点的像素坐标
		cv::Mat points_reproject_Mat = cv::Mat(1, points_reproject.size(), CV_32FC2);  //2通道保存投影角点的像素坐标
		for (int j = 0; j < detect_points.size(); j++)
		{
			detect_points_Mat.at<cv::Vec2f>(0, j) = cv::Vec2f(detect_points[j].x, detect_points[j].y);
			points_reproject_Mat.at<cv::Vec2f>(0, j) = cv::Vec2f(points_reproject[j].x, points_reproject[j].y);
		}

		err = cv::norm(points_reproject_Mat, detect_points_Mat, cv::NormTypes::NORM_L2);
		total_err += err /= point_counts;
		cout << "第" << i + 1 << "幅图像的平均误差为: " << err << "像素" << endl;
		fout << "第" << i + 1 << "幅图像的平均误差为: " << err << "像素" << endl;
	}
	cout << "总体平均误差为: " << total_err / nums << "像素" << endl;
	fout << "总体平均误差为: " << total_err / nums << "像素" << endl;
	cout << "评价完成!" << endl;

	//将标定结果写入txt文件
	Mat rotate_Mat = Mat(3, 3, CV_32FC1, cv::Scalar::all(0)); //保存旋转矩阵
	cout << "\n相机内参数矩阵:" << endl;
	cout << camera_K << endl << endl;
	fout << "\n相机内参数矩阵:" << endl;
	fout << camera_K << endl << endl;
	cout << "畸变系数:\n";
	cout << distCoeffs << endl << endl << endl;
	fout << "畸变系数:\n";
	fout << distCoeffs << endl << endl << endl;
	for (int i = 0; i < nums; i++)
	{
		cv::Rodrigues(rotationMat[i], rotate_Mat); //将旋转向量通过罗德里格斯公式转换为旋转矩阵
		fout << "第" << i + 1 << "幅图像的旋转矩阵为:" << endl;
		fout << rotate_Mat << endl;
		fout << "第" << i + 1 << "幅图像的平移向量为:" << endl;
		fout << translationMat[i] << endl
			<< endl;
	}
	fout << endl;
	fout.close();

	system("pause");
	return 0;


}

El código en ejecución muestra:

baf0f8cfcc594919987ab54b4f229f5c.png

 c5d745d47627416b984c74b1e16991b6.png

Supongo que te gusta

Origin blog.csdn.net/qiaodahua/article/details/128037910
Recomendado
Clasificación