基于OpnecCV的鱼眼镜头校正
程序员文章站
2022-04-17 22:05:49
...
UINT CFisheyeCorrectionDlg::StartLineAcq(LPVOID lpParam)
{
ofstream fout("caliberation_result.txt"); /** 保存定标结果的文件 **/
/**********************************************************************
读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化
*********************************************************************/
pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(_T("开始提取角点…"));
int image_count=36; /**** 图像数量 ****/
bool bImageFindCorner[36]={false}; //对应图像是否找到角点
//Size board_size = Size(26,26); /**** 定标板上每行、列的角点数 ****/
Size board_size = Size(8,8); /**** 定标板上每行、列的角点数 ****/
vector<Point2f> corners; /**** 缓存每幅图像上检测到的角点 ****/
vector<vector<Point2f>> corners_Seq; /**** 保存检测到的所有角点 ****/
vector<Mat> image_Seq;
int successImageNum = 0; /**** 成功提取角点的棋盘图数量 ****/
int count = 0;
for( int i = 0; i != image_count ; i++)
{
char ch_sf[20];
itoa(i+1,ch_sf,10);
string s_status;
s_status=s_status+"正在提取第"+ch_sf+"张图角点...";
CString C_Status;
C_Status=s_status.c_str();
pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(C_Status);
cout<<"Frame #"<<i+1<<"..."<<endl;
string imageFileName;
std::stringstream StrStm;
StrStm<<i+1;
StrStm>>imageFileName;
imageFileName += ".bmp";
//cv::Mat image = imread("F:\\采集小靶标\\1.bmp");
cv::Mat image = imread(imageFileName);
/* 提取角点 */
Mat imageGray;
cvtColor(image, imageGray , CV_RGB2GRAY);//转换成灰度图
//imwrite("D:\\666.bmp", imageGray);
//查找角点
//bool patternfound = findChessboardCorners(imageGray, board_size, corners,+ CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK);
bool patternfound = findChessboardCorners(imageGray, board_size, corners,CALIB_CB_FAST_CHECK);
if (!patternfound)
{
//AfxMessageBox(_T("没有发现角点坐标!"));
//continue;
//exit(1);
char ch_sf[20];
itoa(i+1,ch_sf,10);
string s_status;
s_status=s_status+"第"+ch_sf+"张图,没有发现角点坐标!";
CString C_Status;
C_Status=s_status.c_str();
pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(C_Status);
AfxMessageBox(C_Status);
bImageFindCorner[i]=false; //没有找到角点
}
else
{
bImageFindCorner[i]=true; //找到角点
/* 亚像素精确化 */
cornerSubPix(imageGray, corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
/* 绘制检测到的角点并保存 */
Mat imageTemp = image.clone();
for (int j = 0; j < corners.size(); j++)
{
circle( imageTemp, corners[j], 10, Scalar(0,0,255), 2, 8, 0);
}
string imageFileName;
std::stringstream StrStm;
StrStm<<i+1;
StrStm>>imageFileName;
imageFileName += "_corner.jpg";
imwrite(imageFileName,imageTemp);
cout<<"Frame corner#"<<i+1<<"...end"<<endl;
count = count + corners.size();
successImageNum = successImageNum + 1;
corners_Seq.push_back(corners);
//#########################################
char ch_sf[20];
itoa(i+1,ch_sf,10);
string s_status;
s_status=s_status+"第"+ch_sf+"张图,角点提取完成!";
CString C_Status;
C_Status=s_status.c_str();
pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(C_Status);
}
image_Seq.push_back(image);
}
pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(_T("角点提取完成!"));
//AfxMessageBox(_T("角点提取完成!"));
/************************************************************************
摄像机定标
*************************************************************************/
pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(_T("开始定标…"));
//AfxMessageBox(_T("开始定标………………"));
Size square_size = Size(150,150); //标定板尺寸??
vector<vector<Point3f>> object_Points; /**** 保存定标板上角点的三维坐标 ****/
Mat image_points = Mat(1, count, CV_32FC2, Scalar::all(0)); /***** 保存提取的所有角点 *****/
vector<int> point_counts;
/* 初始化定标板上角点的三维坐标 */
for (int t = 0; t<successImageNum; t++)
{
vector<Point3f> tempPointSet;
for (int i = 0; i<board_size.height; i++)
{
for (int j = 0; j<board_size.width; j++)
{
/* 假设定标板放在世界坐标系中z=0的平面上 */
Point3f tempPoint;
tempPoint.x = i*square_size.width;
tempPoint.y = j*square_size.height;
tempPoint.z = 0;
tempPointSet.push_back(tempPoint);
}
}
object_Points.push_back(tempPointSet);
}
for (int i = 0; i< successImageNum; i++)
{
point_counts.push_back(board_size.width*board_size.height);
}
/* 开始定标 */
Size image_size = image_Seq[0].size();
//cv::Matx33d intrinsic_matrix; /***** 摄像机内参数矩阵 ****/
//cv::Vec4d distortion_coeffs; /* 摄像机的4个畸变系数:k1,k2,k3,k4*/
//std::vector<cv::Vec3d> rotation_vectors; /* 每幅图像的旋转向量 */
//std::vector<cv::Vec3d> translation_vectors; /* 每幅图像的平移向量 */
int flags = 0;
flags |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
flags |= cv::fisheye::CALIB_CHECK_COND;
flags |= cv::fisheye::CALIB_FIX_SKEW;
//cv::TermCriteria(3, 20, 1e-6)
//cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 100, DBL_EPSILON)
double rms =fisheye::calibrate(object_Points, corners_Seq, image_size, intrinsic_matrix, distortion_coeffs, rotation_vectors, translation_vectors, flags,cv::TermCriteria(3, 20, 1e-6));
pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(_T("标定完成!"));
//AfxMessageBox(_T("标定完成!"));
/**********************************************************************
对定标结果进行评价
**********************************************************************/
//cout<<"开始评价定标结果………………"<<endl;
double total_err = 0.0; /* 所有图像的平均误差的总和 */
double err = 0.0; /* 每幅图像的平均误差 */
vector<Point2f> image_points2; /**** 保存重新计算得到的投影点 ****/
//cout<<"每幅图像的定标误差:"<<endl;
//cout<<"每幅图像的定标误差:"<<endl<<endl;
float f_total_err=0; //总体误差
for (int i=0; i<image_count; i++)
{
if (bImageFindCorner[i])//找到角点再执行相关计算
{
vector<Point3f> tempPointSet = object_Points[i];
/**** 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 ****/
fisheye::projectPoints(tempPointSet, image_points2, rotation_vectors[i], translation_vectors[i], intrinsic_matrix, distortion_coeffs);
/* 计算新的投影点和旧的投影点之间的误差*/
vector<Point2f> tempImagePoint = corners_Seq[i];
Mat tempImagePointMat = Mat(1,tempImagePoint.size(),CV_32FC2);
Mat image_points2Mat = Mat(1,image_points2.size(), CV_32FC2);
for (size_t i = 0 ; i != tempImagePoint.size(); i++)
{
image_points2Mat.at<Vec2f>(0,i) = Vec2f(image_points2[i].x, image_points2[i].y);
tempImagePointMat.at<Vec2f>(0,i) = Vec2f(tempImagePoint[i].x, tempImagePoint[i].y);
}
err = norm(image_points2Mat, tempImagePointMat, NORM_INF);//求取最大值
float f_err=0;
f_err=err;
total_err += err/= point_counts[i];
f_total_err+=f_err;
//cout<<"第"<<i+1<<"幅图像的平均误差:"<<err<<"像素"<<endl;
fout<<"第"<<i+1<<"幅图像的最大误差:"<<f_err<<"像素"<<endl;
}
}
//cout<<"总体平均误差:"<<total_err/successImageNum<<"像素"<<endl;
//fout<<"总体平均误差:"<<total_err/successImageNum<<"像素"<<endl<<endl;
fout<<"总体最大误差均值:"<<f_total_err/successImageNum<<"像素"<<endl<<endl;
//cout<<"评价完成!"<<endl;
/*********************************************************************
保存定标结果
**********************************************************************/
//cout<<"开始保存定标结果………………"<<endl;
pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(_T("开始保存标定结果…"));
Mat rotation_matrix = Mat(3,3,CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */
fout<<"相机内参数矩阵:"<<endl;
fout<<intrinsic_matrix<<endl;
fout<<"畸变系数:\n";
fout<<distortion_coeffs<<endl;
for (int i=0; i<image_count; i++)
{
if (bImageFindCorner[i])//找到角点的图参与相关计算
{
fout<<"第"<<i+1<<"幅图像的旋转向量:"<<endl;
fout<<rotation_vectors[i]<<endl;
/* 将旋转向量转换为相对应的旋转矩阵 */
Rodrigues(rotation_vectors[i],rotation_matrix);
fout<<"第"<<i+1<<"幅图像的旋转矩阵:"<<endl;
fout<<rotation_matrix<<endl;
fout<<"第"<<i+1<<"幅图像的平移向量:"<<endl;
fout<<translation_vectors[i]<<endl;
}
}
pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(_T("标定结果保存完成!"));
//cout<<"完成保存"<<endl;
fout<<endl;
/*********************************************************************
显示定标结果
***********************************************************************/
Mat mapx = Mat(image_size,CV_32FC1);
Mat mapy = Mat(image_size,CV_32FC1);
Mat R = Mat::eye(3,3,CV_32F);
//cout<<"保存矫正图像"<<endl;
pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(_T("开始保存矫正图像..."));
for (int i = 0 ; i != image_count ; i++)
{
if (bImageFindCorner[i])//找到角点的图参与相关计算
{
cout<<"Frame #"<<i+1<<"..."<<endl;
Mat newCameraMatrix = Mat(3,3,CV_32FC1,Scalar::all(0));
fisheye::initUndistortRectifyMap(intrinsic_matrix,distortion_coeffs,R,intrinsic_matrix,image_size,CV_32FC1,mapx,mapy);
Mat t = image_Seq[i].clone();
cv::remap(image_Seq[i],t,mapx, mapy, INTER_LINEAR);
string imageFileName;
std::stringstream StrStm;
StrStm<<i+1;
StrStm>>imageFileName;
imageFileName += "_d.jpg";
imwrite(imageFileName,t);
}
}
pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(_T("矫正图像保存结束!"));
//cout<<"保存结束"<<endl;
/**********************************************************************
测试一张图片
*********************************************************************/
//if (1)
//{
// cout<<"TestImage ..."<<endl;
// Mat newCameraMatrix = Mat(3,3,CV_32FC1,Scalar::all(0));
// Mat testImage = imread("F:\\9-19新靶标26x24\\1.bmp",1);
// fisheye::initUndistortRectifyMap(intrinsic_matrix,distortion_coeffs,R,intrinsic_matrix,image_size,CV_32FC1,mapx,mapy);
// Mat t = testImage.clone();
// cv::remap(testImage,t,mapx, mapy, INTER_LINEAR);
// imwrite("TestOutput.jpg",t);
// cout<<"保存结束"<<endl;
//}
//AfxMessageBox(_T("校正完成!"));
return 0;
}
鱼眼镜头拍摄原图
校正效果图
在进行鱼眼镜头校正时,采集的校正图像一定要多角度拍摄,确保各个方向各个角度都有棋盘格靶标,而且棋盘格靶标占整个图片2/3左右比较合适。
下一篇: 昨天去侄子家作客