欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于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;
}

鱼眼镜头拍摄原图
基于OpnecCV的鱼眼镜头校正
校正效果图
基于OpnecCV的鱼眼镜头校正

在进行鱼眼镜头校正时,采集的校正图像一定要多角度拍摄,确保各个方向各个角度都有棋盘格靶标,而且棋盘格靶标占整个图片2/3左右比较合适。