单目标定(免费拿走不谢)

单目标定源代码
OpenCV版本4.0.0 Visual studio2017版本
如果遇到任何问题,或者有错误的地方,欢迎评论留言指正
本段代码亲测可用,直接复制即可
注意:有些路径是需要更改的,注释中已有说明
很多文章中的源码,不是收费,就是运行不成功,且注释较少,较难理解。我在这份代码中加了足够多的注释,希望这份代码能对和我一样刚学习标定的同学有所帮助!

#include <opencv2/core/core.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <stdio.h>
#include <iostream>

using namespace cv;
using namespace std;

vector< vector< Point3f > > object_points; //定义物点
vector< vector< Point2f > > image_points;//定义像点
vector< Point2f > corners;//定义角点(一张图片)

Mat img, gray;
Size im_size;

bool doesExist(const std::string& name) {//判断图像是否存在
    struct stat buffer;
    return (stat(name.c_str(), &buffer) == 0);
}

void setup_calibration(int board_width, int board_height, int num_imgs,
 float square_size, const char* imgs_directory, const char* imgs_filename,
 const char* extension)  {//将所有的物点坐标写入object_points,所有与之对应的像点都放入image_points中
    Size board_size = Size(board_width, board_height);//获得标定板尺寸,用于后续寻找角点,只有标定板的内角点是有效的角点,所以标定板最外侧一圈不参与计算
    int board_n = board_width * board_height;//获得格子数目
    
    for (int k = 1; k <= num_imgs; k++) {//遍历传入的每一张图像
  	char img_file[100]; //定义字符串存储路径的名字
  	sprintf_s(img_file, "%s%s%d.%s", imgs_directory, imgs_filename, k, extension);//格式化字符串用的,将img_file拼接成一段完整路径
  	//printf("%s\n", img_file);
  	if (!doesExist(img_file))//若文件不存在,则结束本次循环
   	continue;
  	img = imread(img_file, IMREAD_COLOR);//从路径中读取本次图片文件
  	//img = imread(img_file);
  	cv::cvtColor(img, gray, COLOR_BGR2GRAY);//转灰度
  	
  	bool found = false;
  	found = cv::findChessboardCorners(img, board_size, corners,
   	CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS);//该函数的功能就是判断图像内是否包含完整的棋盘图,如果能够检测完全,就把他们的角点坐标按顺序(逐行,从左到右)记录下来,并返回非0数true,否则返回false。使用自适应阈值转化为黑白图,CALIB_CB_FILTER_QUADS用于去除检测阶段检测到的错误方块
  	if (found)
  	{
   	    cornerSubPix(gray, corners, Size(5, 5), Size(-1, -1),
    	    TermCriteria(TermCriteria::EPS | TermCriteria::MAX_ITER, 30, 0.1));//进行亚像素精度的调整,获得亚像素级别的角点坐标
   	    drawChessboardCorners(gray, board_size, corners, found);//将每一个角点处做标记,此为物点对应的像点坐标值
  	}
  	
  	vector< Point3f > obj;
  	for (int i = 0; i < board_height; i++)//寻找每张图的物点坐标并存储在obj中
   	    for (int j = 0; j < board_width; j++)
    		obj.push_back(Point3f((float)j * square_size, (float)i * square_size, 0));//把一张图中寻找到的每一个角点坐标依次存入数组obj(vector<Point3f>)中

	if (found) {//将每张图片的角点坐标集中放在object_points中,存储所有点的坐标
   	    cout << k << ". Found corners!" << endl;
   	    image_points.push_back(corners);//把每张图提取的角点坐标依次都存入image_points中,此为像点坐标
   	    object_points.push_back(obj);//把存放每张图的所有角点坐标(obj内)依次存放到数组object_points(vetor<vector<3f>>)中,object_points中存放了所有的物点坐标
  	}
    }
}

double computeReprojectionErrors(const vector< vector< Point3f > >& objectPoints,
 const vector< vector< Point2f > >& imagePoints,
 const vector< Mat >& rvecs, const vector< Mat >& tvecs,
 const Mat& cameraMatrix, const Mat& distCoeffs) {//将标定结果代入计算误差
    vector< Point2f > imagePoints2;//一张图片上的二维角点坐标,用于存储三维点投影后的结果,与标定的到的像点进行对比
    int i, totalPoints = 0;
    double totalErr = 0, err;
    vector< float > perViewErrors;
    perViewErrors.resize(objectPoints.size());//大小为图片数量
    
    for (i = 0; i < (int)objectPoints.size(); ++i) {//遍历传入的每一张图片
  	projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);//求取三维点投影到二维平面的坐标
 	err = norm(Mat(imagePoints[i]), Mat(imagePoints2), NORM_L2); //求解对应点的范数,确定误差,传入标定的像点;重投影的像点;求范数类型;所有对应点距离的平方加和开根号
  	int n = (int)objectPoints[i].size();//第i张图片含有的角点数  
  	perViewErrors[i] = (float)std::sqrt(err * err / n);//第i张标定板测得的平均误差,第i张板的对应点距离平方相加/第i张图片角点个数
  	totalErr += err * err;//累计误差
  	totalPoints += n;//参与计算的有效角点坐标
    }
    return std::sqrt(totalErr / totalPoints);//最终得到所有对应点距离平方加和/总的角点数,为总的平均误差
}

int main(int argc, char const** argv) {
    int board_width, board_height, num_imgs;//标定板的宽度、高度、图像数量
    float square_size;//标定板格子尺寸
    //char* imgs_directory;//文件存储路径
    //char* imgs_filename;//文件名
    //char* out_file;
    char* extension;  	
    
    setup_calibration(9, 6, 27, 0.02423, "C:/Users/26839/Desktop/right/", "right", "jpg");//传入棋盘格标定板的参数,得到所有的物点坐标和对应的像点坐标,存放在数组object_pointd和image_ponts中。这里需要根据自己的实际图片存储路径,名称格式改
    //像点的坐标根据物点的坐标,用角点提取算法即可获得 
    
    printf("Starting Calibration\n");
    Mat K;//内参矩阵(3*3) 
    Mat D;//畸变矩阵(1*5) 
    vector< Mat > rvecs, tvecs;//旋转向量和位移向量,用于存放相机和每一个标定板的外参矩阵(旋转+平移)
    int flag = 0;//标定函数中所采用的模型
    flag |= CALIB_FIX_K4; 
    flag |= CALIB_FIX_K5; 
    //calibrateCamera(object_points, image_points, img.size(), K, D, rvecs, tvecs, flag);
    calibrateCamera(object_points, image_points, img.size(), K, D, rvecs, tvecs, flag );//传入物点、像点、图像尺寸、内参矩阵、畸变矩阵、旋转向量、平移向量、flag标志位,进行标定
    
    /*此段为矫正畸变,可以考虑优化
    Mat dst;
    for (int i = 1; i <= 30; i++) {
  	char img_file[100]; //定义字符串存储路径的名字
  	sprintf_s(img_file, "%s%s%d.%s", "C:/Users/26839/Desktop/calibR/", "right", i, "jpg");//格式化字符串用的,将img_file拼接成一段完整路径
  	if (!doesExist(img_file))//若文件不存在,则结束本次循环
            continue;
  	img = imread(img_file, IMREAD_COLOR);//从路径中读取本次图片文件
  	undistort(img, dst, K, D);//校正畸变
  	imwrite(format("C:/Users/26839/Desktop/camera301/right%d.jpg",i), dst);//将矫正畸变后的图像存放到目标路径下
  	//imshow("img", img);
  	//imshow("dst", dst);
  	//waitKey(1000);  
    }
    */
    
    cout << "Calibration error: " << computeReprojectionErrors(object_points, image_points, rvecs, tvecs, K, D) << endl;//输出投影的误差
    //const string out_file = "D:/test_calibration.yml";
    FileStorage fs("D:/calib/right_calibration.yml", FileStorage::WRITE);//存储标定结果,这里可以选择自己的存放路径
    fs << "K" << K;//存放内参矩阵
    fs << "D" << D;//存放畸变矩阵
    fs << "board_width" << 9;//存放标定板长度信息
    fs << "board_height" << 6;//存放标定板宽度信息
    fs << "square_size" << 0.02423;//存放标定板格子尺寸信息
    printf("Done Calibration\n");
    
    return 0;
}
  • 11
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Halcon相机标定代码.hdev 1.初始化 for example: Full image (640*480) Subsampling (320*240) ImageWidth 640 320 ImageHeight 480 240 2.标定板初始化 CaltabName := 'caltab_30mm.descr'//标定板描述文件 set_calib_data_calib_object (CalibDataID, 0, CaltabName) 3.创建数据模型 create_calib_data ('calibration_object', 1, 1, CalibDataID) 4.获取标定图片 相机拍摄不同位姿下图片8-15张,拍摄图片时标定板尽量覆盖整个视场(标定板要根据工作距离、视场大小定制);拍摄图片上的圆直径不得小于10个像素 5.加载所有图像,寻找标定板区域,确定圆心,将结果加载到组元中 for I := 1 to NumImages by 1 ... acquire image ... find_caltab (Image, Caltab, CaltabName, SizeGauss, MarkThresh, MinDiamMarks) find_marks_and_pose (Image, Caltab, CaltabName, StartCamPar, StartThresh, \ DeltaThresh, MinThresh, Alpha, MinContLength, MaxDiamMarks, RCoord, CCoord, StartPose) set_calib_data_observ_points (CalibDataID, 0, 0, I, RCoord, CCoord, 'all', StartPose) endfor 下面将Halcon中提取目标点的大致原理说一下: 首先find_caltab 算子对图像高斯滤波(核大小为SizeGauss),接着阈值分割(与之大小为MarkThresh)将标定板的区域找出来, find_marks_and_pose 算子对区域中的圆进行分割,找到圆的个数,周长,坐标位置等应该和标定板描述文件中的一致,否则会自动调整StartThresh,使得StartThresh按照DeltaThresh步长减小到MinThresh,知道找到准确的圆心。 6.有了所有图像中的圆心就可以标定了 calibrate_cameras (CalibDataID, Errors) 返回平均投影误差Errors

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值