标定步骤:
(1)相机和投影仪摆好角度,将打印棋盘贴到白板上。
(2)相机拍摄仅带有打印棋盘的白板,图片命名为“camera1.jpg”;再将投影仪打开,将棋盘图片投射到白板上,相机拍摄投射的棋盘图片,命名为“projector.jpg”;
(3)移动白板,改变其相对相机-投影仪的相对位置,重复(2)步骤,不过命名.2jpg;
(4)拍摄十组图片后,将cameraX.jpg保存在“cameraCalibdata.txt”,将projectorX。jpg保存到“projectCalibdata.txt”;
(5)程序开始–》结束
#include “cameraCalib.h”
#include “projectCalib.h”
using namespace cv;
using namespace std;
int main()
{
Size BoardSize = Size(13, 6); /存储打印棋盘标定板的行数和列数/
Size Square_Size = Size(10, 10); /存储打印棋盘标定板的高度和宽度/
Size ProjectBoardSize = Size(13, 6); /存储投影仪投射的棋盘的行数和列数/
Size ProjectImageSize = Size(1280, 720); /存储投影仪投射的图片的像素行列数/
Size Project_GridWidthANDHeigh = Size(80, 80); /投影仪的图像的棋盘的高度和宽度(像素表示)/
Size EdgePixel = Size(160, 160); /投影仪的图像的边缘多余的像素/
string ProjectFilename("projectCalibdata.txt");
string CameraFileName("cameraCalibdata.txt"); /*提取图片信息的txt*/
vector<vector<Point2f>> Project_ProjectROIPoints;/*投影仪投射的棋盘被摄像机捕捉后得到的角点*/
vector<vector<Point2f>> Project_Image_Points; /*投影仪的图像的角点*/
vector<vector<Point2f>> Camera_ImagePoints; /* 相机标定时保存检测到的所有图片的所有角点 */
vector<vector<Point2f>> Camera_WorldPoints; /*相机标定时所有角点对应的世界坐标[Xw,Yw]*/
vector<vector<Point3f>> Project_Object_Points;
/*投影仪投射的棋盘的真实世界坐标[ Xw , Yw , Zw ]*/
vector<Mat> Homography_AllImage; /*相机标定时图像坐标与世界坐标之间的单应性矩阵*/
/*摄像机标定*/
cameraCalib(CameraFileName, BoardSize, Camera_ImagePoints);
/*投影仪标定*/
cout << "-----开始投影机标定-------" << endl;
if (0 == projectorFindROICorners(ProjectFilename, ProjectBoardSize, Project_ProjectROIPoints))
{
cout << "角点检测失败" << endl;
}
projectCameraTOWorld(Camera_ImagePoints, Project_ProjectROIPoints,Camera_WorldPoints,Homography_AllImage,
Project_Object_Points, BoardSize, Square_Size);
system("pause");
ProjectorCalib(Project_Object_Points, Project_Image_Points, ProjectImageSize, ProjectBoardSize, Project_GridWidthANDHeigh, EdgePixel);
waitKey(0);
return 0;
}
#include “projectCalib.h”
#include
#include
#include “opencv2/calib3d/calib3d.hpp”
#include “opencv2/imgproc/imgproc.hpp”
Rect g_rectangle;//用于存储矩形框的信息
bool g_bDrawingBox = false;//鼠标是否按下的标志
bool stopDrawingFlag = false;/停止取图标志位/
Mat tempROI; /用于保存ROI图像/
RNG g_rng(12345);//RNG是随机数生成器
bool projectorFindROICorners(const string & FILENAME,Size &BOARDSIZE, vector<vector> & ROI2Image_points_seq)
{
ifstream fin(FILENAME); /* 标定所用图像文件的路径 /
vector ROI_points_buf; / 缓存每幅图像上检测到的角点坐标 */
int cornersROI_count = 0;/每幅roi图像上检测到的角点坐标个数/
string filename;
int projector_image_count = 0;/*图像的数量*/
/*---------------------------------检测角点坐标---------------------------------*/
while(getline(fin, filename))
{
projector_image_count++;/*图像读取个数+1*/
cout << "image_cout:-->" << projector_image_count << endl;
cout << " " << filename << endl;
Mat srcImage = imread(filename);/*读取原图像*/
Mat tempImage; /*用于画图的图像*/
namedWindow(filename,0);
while (!stopDrawingFlag)
{
setMouseCallback(filename, on_MouseHandle, (void*)&srcImage);
srcImage.copyTo(tempImage);//每次都用srcImg覆盖一下画面,当鼠标弹起时才会改变srcImg
if (g_bDrawingBox)
DrawRectangle(tempImage, g_rectangle);
imshow(filename, tempImage);
if (waitKey(10) == 27) break;
}
stopDrawingFlag = 0;
waitKey(500);//暂停0.5S
cvDestroyWindow("ROI");
const char* str_filename = filename.c_str();
cvDestroyWindow(str_filename);
cout << "-------------第 " << projector_image_count << "张图片ROI提取完成,开始角点检测--------------" << endl;
if (0 == findChessboardCorners(tempROI, BOARDSIZE, ROI_points_buf, CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK))
{
cout << "can not find chessboard corners!\n"; //找不到角点
waitKey(0);
return false;
//exit(1);
}
else
{
Mat view_grayImage;
cvtColor(tempROI, view_grayImage, CV_RGB2GRAY);
cornerSubPix(view_grayImage, ROI_points_buf, Size(5, 5), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
drawChessboardCorners(view_grayImage, BOARDSIZE, ROI_points_buf, false); //用于在图片中标记角点
imshow("Projector Calibration", view_grayImage);//显示图片
waitKey(500);//暂停0.5S
cornersROI_count = ROI_points_buf.size();
cout << "----cornersROI_count = " << cornersROI_count << endl;
for (int i = 0; i < cornersROI_count; i++)
{
ROI_points_buf[i].x += g_rectangle.x;
ROI_points_buf[i].y += g_rectangle.y;/*对ROI图像的角点进行补全*/
//cout << ROI_points_buf[i].x << " , " << ROI_points_buf[i].y << endl;
}
ROI2Image_points_seq.push_back(ROI_points_buf);/*进栈保存*/
}
cout << "--------------第 " << projector_image_count << "张图片角点检测完成--------------" << endl;
}
int total = ROI2Image_points_seq.size();/*成功提取角点图片的个数*/
cout << "----总图片数: " << total<<"提取角点完成----" << endl;
/*------------------------------------------------------------------------------*/
return true;
}
void projectCameraTOWorld(vector<vector> & IMAGE_POINTS,
vector<vector> & ROI2Image_points_seq,
vector<vector> & WORLD_POINTS,
vector & HOMOGRAPHY_ALLImage,
vector<vector> & WORLD_POINTS_Homogeneous,Size & BOARD_SIZE,Size & SQUARE_PRINT_SIZE)
{
/对WORLD_POINTS进行初始化/
for (int i = 0; i < IMAGE_POINTS.size(); i++)
{
vector<Point2f> tempImagePoints;
for (int j = 0; j<BOARD_SIZE.height; j++)
{
for (int k = 0; k<BOARD_SIZE.width; k++)
{
Point2f realPoint;
/* 假设标定板放在世界坐标系中z=0的平面上 */
realPoint.x = k * SQUARE_PRINT_SIZE.width;
realPoint.y = j * SQUARE_PRINT_SIZE.height;
tempImagePoints.push_back(realPoint);
}
}
WORLD_POINTS.push_back(tempImagePoints);
}
//system("pause");
/*计算所有的单应性矩阵*/
for (int t = 0; t < IMAGE_POINTS.size(); t++)
{
Mat h(3,3,CV_32FC1);
h = findHomography(IMAGE_POINTS[t], WORLD_POINTS[t]);
HOMOGRAPHY_ALLImage.push_back(h);
}
/*将计算出来的WORLD_POINTS变为[X , Y , Z ],其中Z=0*/
for (int t = 0; t < ROI2Image_points_seq.size(); t++)
{
vector<Point3f> EveryImagePoints;
for (int j = 0; j < ROI2Image_points_seq[t].size(); j++)
{
Mat temProppoint=Mat(3, 1, CV_32FC1, Scalar(0));
Mat temCrappoint=Mat(3, 1, CV_32FC1, Scalar(0));
Mat homo = Mat(3, 3, CV_32FC1, Scalar(0));
Mat homo2 = Mat(3, 3, CV_32FC1, Scalar(0));
Point3f realPoint;
float Szc;
temCrappoint.at<float>(0, 0) = ROI2Image_points_seq[t][j].x;
temCrappoint.at<float>(1, 0) = ROI2Image_points_seq[t][j].y;
temCrappoint.at<float>(2, 0) = 1.0;
//HOMOGRAPHY_ALLImage[t].convertTo(HOMOGRAPHY_ALLImage[t], CV_32FC1);
//temCrappoint.convertTo(temCrappoint, CV_32FC1);
homo = HOMOGRAPHY_ALLImage[t].clone();
homo.convertTo(homo2, CV_32FC1);
temProppoint = homo2 * temCrappoint;
realPoint.z = temProppoint.at<float>(2, 0);
Szc = realPoint.z;
realPoint.x = temProppoint.at<float>(0, 0) / Szc;
realPoint.y = temProppoint.at<float>(1, 0) / Szc;
realPoint.z = 0.0;
EveryImagePoints.push_back(realPoint);
}
WORLD_POINTS_Homogeneous.push_back(EveryImagePoints);
}
cout << "投影仪投射棋盘的角点的世界坐标计算完成" << endl;
}
void ProjectorCalib(vector<vector> & Projector_Object_points,
vector<vector> & Projector_Image_points,
const Size & PROJECT_IMAGE_SIZE,
const Size & PROJECT_BOARD_SIZE,
const Size & PROJECT_GridWidthANDHeigh,
const Size & EdgePixel)
{
/---------------------------------标定程序-------------------------------------/
ofstream fout("projectCaliberation_result.txt");
int image_count = Projector_Object_points.size();
Mat projector_IntMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 投影仪内参数矩阵 */
Mat projector_distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0)); /* 投影仪的5个畸变系数:k1,k2,p1,p2,k3 */
vector<Mat> projector_rvecsMat; /* 每幅图像的旋转向量 */
vector<Mat> projector_tvecsMat; /* 每幅图像的平移向量 */
/*初始化投影仪图片的二维坐标*/
for (int i = 0; i < image_count; i++)
{
vector<Point2f> tempImages;
for (int j = 0; j < PROJECT_BOARD_SIZE.height; j++)
{
for (int k = 0; k < PROJECT_BOARD_SIZE.width; k++)
{
Point2f tempPoints;
tempPoints.x = k * PROJECT_GridWidthANDHeigh.width + EdgePixel.width;
tempPoints.y = j * PROJECT_GridWidthANDHeigh.height + EdgePixel.height;
tempImages.push_back(tempPoints);
}
}
Projector_Image_points.push_back(tempImages);
}
/*开始标定*/
cout << Projector_Object_points.size()<<"----"<< Projector_Object_points[0].size() << endl;
cout << Projector_Image_points.size() << "----" << Projector_Image_points[0].size() <<endl;
system("pause");
calibrateCamera(Projector_Object_points,
Projector_Image_points,
PROJECT_IMAGE_SIZE,
projector_IntMatrix,
projector_distCoeffs,
projector_rvecsMat,
projector_tvecsMat, 0);
/*标定完成*/
Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */
fout << "内参数矩阵:" << endl;
fout << projector_IntMatrix << endl << endl;
fout << "畸变系数:\n";
fout << projector_distCoeffs << endl << endl << endl;
for (int i = 0; i<image_count; i++)
{
fout << "第" << i + 1 << "幅图像的旋转向量:" << endl;
fout << projector_rvecsMat[i] << endl;
/* 将旋转向量转换为相对应的旋转矩阵 */
Rodrigues(projector_rvecsMat[i], rotation_matrix);
fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;
fout << rotation_matrix << endl;
fout << "第" << i + 1 << "幅图像的平移向量:" << endl;
fout << projector_tvecsMat[i] << endl << endl;
}
std::cout << "投影仪完成标定" << endl;
/*---------------------------------------------------------------------------------*/
}
void on_MouseHandle(int event, int x, int y, int flags, void* param)
{
Mat& image = (Mat)param;//先从空指针转换为Mat指针再解引用
switch (event)
{
case EVENT_MOUSEMOVE:
{
if (g_bDrawingBox)
{
g_rectangle.width = x - g_rectangle.x;
g_rectangle.height = y - g_rectangle.y;
}
}
break;
case EVENT_LBUTTONDOWN:
{
cvDestroyWindow("ROI");
g_bDrawingBox = true;
g_rectangle = Rect(x, y, 0, 0);//记录起始点
}
break;
case EVENT_LBUTTONUP:
{
g_bDrawingBox = false;
if (g_rectangle.width < 0)
{
g_rectangle.x += g_rectangle.width;
g_rectangle.width *= -1;
}
if (g_rectangle.height < 0)
{
g_rectangle.y += g_rectangle.height;
g_rectangle.height *= -1;
}
tempROI = image(Rect(g_rectangle.x, g_rectangle.y, g_rectangle.width, g_rectangle.height));
namedWindow("ROI");
imshow("ROI", tempROI);
//DrawRectangle(image, g_rectangle);//鼠标松开后可以在固定显示一个框
}
break;
case EVENT_RBUTTONDOWN:
{
stopDrawingFlag = true;/*停止取图*/
}
}
}
void DrawRectangle(Mat& img, Rect box)
{
rectangle(img, box.tl(), box.br(), Scalar(g_rng.uniform(0, 255),
g_rng.uniform(0, 255), g_rng.uniform(0, 255)));
}
#ifndef PROJECTCALIB_H_
#define PROJECTCALIB_H_
#include “opencv2/highgui/highgui.hpp”
using namespace cv;
using namespace std;
void on_MouseHandle(int event, int x, int y, int flags, void* param);
void DrawRectangle(Mat& img, Rect box);
/--------------------------------------------------------
projectorFindROICorners投影仪投影图片 ,选取ROI函数
FILENAME:图片来源
BOARDSIZE:投影仪投射棋盘的行数和列数
vector<vector> & ROI2Image_points_seq 保存检测到的所有角点坐标(已转换到全局坐标)
----------------------------------------------------------/
bool projectorFindROICorners(const string & FILENAME, Size &BOARDSIZE, vector<vector> & ROI2Image_points_seq);
/--------------------------------------------------------
projectCameraTOWorld 计算由二维图像坐标变为三维世界坐标[ u , v , 1 ]–>[ Xw , Yw , Zw , 1 ]的单应性矩阵,
IMAGE_POINTS 打印棋盘格的图像坐标
ROI2Image_points_seq:投影仪投射的棋盘的在摄像机图像中的坐标
WORLD_POINTS 对应的世界坐标
HOMOGRAPHY_ALLImage 图像坐标与世界坐标之间的单应性矩阵
WORLD_POINTS_Homogeneous 投影仪投射的棋盘的真实世界坐标
BOARD_SIZE 打印棋盘的行数和列数
SQUARE_PRINT_SIZE 打印棋盘棋盘格的高度和宽度
----------------------------------------------------------/
void projectCameraTOWorld(vector<vector> & IMAGE_POINTS,
vector<vector> & ROI2Image_points_seq,
vector<vector> & WORLD_POINTS,
vector & HOMOGRAPHY_ALLImage,
vector<vector> & WORLD_POINTS_Homogeneous, Size & BOARD_SIZE, Size & SQUARE_PRINT_SIZE);
/--------------------------------------------------------
ProjectorCalib 投影仪标定函数
Projector_Object_points:投影仪投射的棋盘的真实世界坐标(输入不能为空)
Projector_Image_points:投影仪的图像的角点(输入为空,函数内部计算)
PROJECT_IMAGE_SIZE:投影仪的图像的尺寸(如:1024762)
PROJECT_BOARD_SIZE:投影仪的图像的棋盘的行数列数;
PROJECT_GridWidthANDHeigh:投影仪的图像的棋盘的高度和宽度
EdgePixel:投影仪的图像的边缘多余的像素
----------------------------------------------------------*/
void ProjectorCalib(vector<vector> & Projector_Object_points,
vector<vector> & Projector_Image_points,
const Size & PROJECT_IMAGE_SIZE,
const Size & PROJECT_BOARD_SIZE,
const Size & PROJECT_GridWidthANDHeigh,
const Size & EdgePixel);
#endif // !PROJECTCALIB_H_
————————————————
版权声明:本文为CSDN博主「催人乳」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tama1204/article/details/88707482