【投影仪标定】-【摄像机投影仪三维形貌测量(附C++代码)】-【2】

转于:https://blog.csdn.net/tama1204/article/details/88707482

 版权声明:若有转载、转移代码等涉及版权操作请标明出处【963597034@qq.com】 https://blog.csdn.net/tama1204/article/details/88707482
上一篇文章贴了关于相机标定的代码,这篇文章 贴投影仪标定代码。

我的投影仪标定方法很简单,先把投影仪看成一个逆相机,也满足针孔模型,也引入畸变参数。可以完全把投影仪当成相机对待。那么只需要得到世界坐标系上某些点的坐标,和与之对应的投影图片的图像坐标,计算出他们之间的单应性矩阵,进而求出内外参。

标定步骤:

(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 <fstream>
#include <iostream>
#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<Point2f>> & ROI2Image_points_seq)
{
    ifstream fin(FILENAME); /* 标定所用图像文件的路径 */
    vector<Point2f> 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<Point2f>> & IMAGE_POINTS,
                        vector<vector<Point2f>> & ROI2Image_points_seq,
                        vector<vector<Point2f>> & WORLD_POINTS,
                        vector<Mat> & HOMOGRAPHY_ALLImage,
                        vector<vector<Point3f>> & 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<Point3f>> & Projector_Object_points,
                    vector<vector<Point2f>> & 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<Point2f>> & ROI2Image_points_seq 保存检测到的所有角点坐标(已转换到全局坐标)
----------------------------------------------------------*/
 
bool projectorFindROICorners(const string & FILENAME, Size &BOARDSIZE, vector<vector<Point2f>> & 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<Point2f>> & IMAGE_POINTS,
                            vector<vector<Point2f>> & ROI2Image_points_seq,
                            vector<vector<Point2f>> & WORLD_POINTS,
                            vector<Mat> & HOMOGRAPHY_ALLImage,
                            vector<vector<Point3f>> & 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<Point3f>> & Projector_Object_points,
                    vector<vector<Point2f>> & Projector_Image_points,
                    const Size & PROJECT_IMAGE_SIZE,
                    const Size & PROJECT_BOARD_SIZE,
                    const Size & PROJECT_GridWidthANDHeigh,
                    const Size & EdgePixel);
 
#endif // !PROJECTCALIB_H_
 
 
--------------------- 
作者:催人乳 
来源:CSDN 
原文:https://blog.csdn.net/tama1204/article/details/88707482 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值