投影仪标定全局单应矩阵

标定步骤:

(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:投影仪的图像的尺寸(如:1024
762)
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值