返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >OpenCV相机标定的全过程记录
  • 874
分享到

OpenCV相机标定的全过程记录

2024-04-02 19:04:59 874人浏览 安东尼
摘要

目录一、OpenCV标定的几个常用函数findChessboardCorners()棋盘格角点检测cv::drawChessboardCorners()棋盘格角点的绘制find4Qu

一、OpenCV标定的几个常用函数

findChessboardCorners() 棋盘格角点检测

bool findChessboardCorners( InputArray image, 
                                Size patternSize, 
                                OutputArray corners,
                                int flags = CALIB_CB_ADAPTIVE_THRESH + 
                                CALIB_CB_NORMALIZE_IMAGE );

第一个参数是输入的棋盘格图像(可以是8位单通道或三通道图像);

第二个参数是棋盘格内部的角点的行列数(注意:不是棋盘格的行列数,如棋盘格的行列数分别为4、8,而内部角点的行列数分别是3、7,因此这里应该指定为cv::Size(3, 7));

第三个参数是检测到的棋盘格角点,类型为std::vectorcv::Point2f。

第四个参数flag,用于指定在检测棋盘格角点的过程中所应用的一种或多种过滤方法,可以使用下面的一种或多种,如果都是用则使用OR:

  • cv::CALIB_CB_ADAPTIVE_THRESH:使用自适应阈值将图像转化成二值图像
  • cv::CALIB_CB_NORMALIZE_IMAGE:归一化图像灰度系数(用直方图均衡化或者自适应阈值)
  • cv::CALIB_CB_FILTER_QUADS:在轮廓提取阶段,使用附加条件排除错误的假设
  • cv::CALIB_CV_FAST_CHECK:快速检测

cv::drawChessboardCorners() 棋盘格角点的绘制

drawChessboardCorners( InputOutputArray image, 
                           Size patternSize,
                           InputArray corners, 
                           bool patternWasFound );
  • image为8-bit,三通道图像
  • patternSize,每一行每一列的角
  • corners,已经检测到的角
  • patternWasFound,findChessboardCorners的返回值

find4QuadCornerSubpix() 对粗提取的角点进行精确化

find4QuadCornerSubpix( InputArray img, 
                           InputOutputArray corners, 
                           Size region_size );
  • image源图像
  • corners,提供角点的初始坐标
  • region_size: 搜索窗口的一般尺寸

cornerSubPix() 亚像素检测

void cornerSubPix( InputArray image, 
                       InputOutputArray corners,
                       Size winSize, 
                       Size zeroZone,
                       TermCriteria criteria );
  • image源图像
  • corners,提供角点的初始坐标,返回更加精确的点
  • winSize,搜索窗口的一般尺寸,如果winSize=Size(5,5),则search windows为11*11
  • winSize,死区的一般尺寸,用来避免自相关矩阵的奇点,(-1,-1)表示没有死区
  • criteria,控制迭代次数和精度

calibrateCamera() 求解摄像机的内在参数和外在参数

double calibrateCamera( InputArrayOfArrays objectPoints,
                            InputArrayOfArrays imagePoints,
                            Size imageSize,
                            InputOutputArray cameraMatrix, 
                            InputOutputArray distCoeffs,
                            OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
                            int flags = 0, 
                            TermCriteria criteria = TermCriteria(TermCriteria::COUNT + 
                            TermCriteria::EPS, 30, DBL_EPSILON) );

objectPoints,世界坐标,用vector<vector>,输入x,y坐标,z坐标为0

imagePoints,图像坐标,vector<vector>

imageSize,图像的大小用于初始化标定摄像机的image的size

cameraMatrix,内参数矩阵

distCoeffs,畸变矩阵

rvecs,位移向量

tvecs,旋转向量

flags,可以组合:

CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,将包含有效的fx,fy,cx,cy的估计值的内参矩阵cameraMatrix,作为初始值输入,然后函数对其做进一步优化。如果不使用这个参数,用图像的中心点初始化光轴点坐标(cx, cy),使用最小二乘估算出fx,fy(这种求法好像和张正友的论文不一样,不知道为何要这样处理)。注意,如果已知内部参数(内参矩阵和畸变系数),就不需要使用这个函数来估计外参,可以使用solvepnp()函数计算外参数矩阵。

CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点,光轴点将保持为图像的中心点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,保持为输入的值。

CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当
CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx和fy的实际输入值将会被忽略,只有fx/fy的比值被计算和使用。

CV_CALIB_ZERO_TANGENT_DIST:切向畸变系数(P1,P2)被设置为零并保持为零。

CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:对应的径向畸变系数在优化中保持不变。如果设置了CV_CALIB_USE_INTRINSIC_GUESS参数,就从提供的畸变系数矩阵中得到。否则,设置为0。

CV_CALIB_RATIONAL_MODEL(理想模型):启用畸变k4,k5,k6三个畸变参数。使标定函数使用有理模型,返回8个系数。如果没有设置,则只计算其它5个畸变参数。

CALIB_THIN_PRISM_MODEL (薄棱镜畸变模型):启用畸变系数S1、S2、S3和S4。使标定函数使用薄棱柱模型并返回12个系数。如果不设置标志,则函数计算并返回只有5个失真系数。

CALIB_FIX_S1_S2_S3_S4 :优化过程中不改变薄棱镜畸变系数S1、S2、S3、S4。如果cv_calib_use_intrinsic_guess设置,使用提供的畸变系数矩阵中的值。否则,设置为0。

CALIB_TILTED_MODEL (倾斜模型):启用畸变系数tauX and tauY。标定函数使用倾斜传感器模型并返回14个系数。如果不设置标志,则函数计算并返回只有5个失真系数。

CALIB_FIX_TAUX_TAUY :在优化过程中,倾斜传感器模型的系数不被改变。如果cv_calib_use_intrinsic_guess设置,从提供的畸变系数矩阵中得到。否则,设置为0。

initUndistortRectifyMap() 计算畸变参数

void initUndistortRectifyMap(InputArray cameraMatrix, 
                                InputArray distCoeffs, 
                                InputArray R, 
                                InputArray newCameraMatrix, 
                                Size size, 
                                int m1type, 
                                OutputArray map1, 
                                OutputArray map2)
  • cameraMatrix,摄像机内参数矩阵
  • distCoeffs, 摄像机的5个畸变系数,(k1,k2,p1,p2[,k3[,k4,k5,k6]])
  • R,在客观空间中的转换对象
  • newCameraMatrix,新的3*3的浮点型矩矩阵
  • size,为失真图像的大小
  • m1type,第一个输出的map,类型为CV_32FC1或CV_16SC2
  • map1,x映射函数
  • map2,y映射函数

二、绘制棋盘格,拍摄照片

这里自己画一个棋盘格用作标定,长度为1280像素,宽490像素,横向10方格,纵向7方格

std_cb = Vision::makeCheckerboard(1280, 490, 10, 7, 0, 
(char *)"../blizzard/res/calibration/std_cb.png");

效果如图

Vision是我个人创建的视觉类,可以用来绘制标准的棋盘格。

头文件vision.h

//
// Created by czh on 18-10-16.
//
#ifndef OPENGL_PRO_VISION_H
#define OPENGL_PRO_VISION_H

#include "opencv2/opencv.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imGCodecs/imgcodecs.hpp>

#include "iOStream"

class Vision {
public:
    static cv::Mat read(std::string file_path, int flags = cv::IMREAD_ANYCOLOR | cv::IMREAD_ANYDEPTH);

    static cv::Mat write(std::string file_path, int flags = cv::IMREAD_ANYCOLOR | cv::IMREAD_ANYDEPTH);

    static void dispConfig(cv::Mat img);

    static cv::Mat makeCheckerboard(int bkgWidth, int bkgHeight, int sqXnum, int sqYnum = 0, int borderThickness = 0, char *savePath = NULL);
private:

};
#endif //OPENGL_PRO_VISION_H

源文件vision.cpp

//
// Created by czh on 18-10-16.
//

#include "vision.h"
#include "string.h"

using namespace std;
using namespace cv;

const char *findName(const char *ch) {
    const char *name = strrchr(ch, '/');
    return ++name;
}

cv::Mat Vision::read(std::string file_path, int flags) {
    printf("#Vision read\n");
    cv::Mat img;
    img = cv::imread(file_path, flags);
    if (img.data == NULL) {
        printf("\tError:vision read\n");
    } else {
        dispConfig(img);
    }
    return img;
}

void Vision::dispConfig(cv::Mat img) {
    printf("\tpixel:%d*%d, channels:%d\n", img.size().width, img.size().height, img.channels());
}

cv::Mat Vision::makeCheckerboard(int bkgWidth, int bkgHeight, int sqXnum, int sqYnum, int thickNum, char *savePath) {
    if(sqYnum == 0){
        sqYnum = sqXnum;
    }
    if(savePath == NULL){
        char *defaultPath = (char *)"../res/calibration/maths.png";
        savePath = defaultPath;
    }
    int checkboardX = 0;//棋盘x坐标
    int checkboardY = 0;//棋盘y坐标
    int xLen = bkgWidth / sqXnum;//x方格长度
    int yLen = bkgHeight / sqYnum;//y方格长度
    cv::Mat img(bkgHeight + thickNum * 2, bkgWidth + thickNum * 2, CV_8UC4, cv::Scalar(0, 255, 255, 255));
    for (int i = 0; i < img.rows; i++) {

        for (int j = 0; j < img.cols; j++) {

            if (i < thickNum || i >= thickNum + bkgHeight || j < thickNum || j >= thickNum + bkgWidth) {
                img.at<Vec<uchar, 4>>(i, j) = cv::Scalar(0, 0, 0, 255);
                continue;
            }
            checkboardX = j - thickNum;
            checkboardY = i - thickNum;
            if (checkboardY / yLen % 2 == 0) {
                if ((checkboardX) / xLen % 2 == 0) {
                    img.at<Vec<uchar, 4>>(i, j) = cv::Scalar(255, 255, 255, 255);
                } else {
                    img.at<Vec<uchar, 4>>(i, j) = cv::Scalar(0, 0, 0, 255);
                }
            }
            else{
                if ((checkboardX) / xLen % 2 != 0) {
                    img.at<Vec<uchar, 4>>(i, j) = cv::Scalar(255, 255, 255, 255);
                } else {
                    img.at<Vec<uchar, 4>>(i, j) = cv::Scalar(0, 0, 0, 255);
                }
            }
        }
    }
    imwrite(savePath, img);    //保存生成的图片
    printf("#makeCheckerboard %d*%d\n", bkgWidth + thickNum, bkgHeight + thickNum);
    return img;
}

用A4纸打印棋盘格,相机拍摄照片。

我偷懒,拿了别人的标定照片

三、相机标定

下面是相机标定代码

cv::imwrite("../blizzard/res/calibration/cb_source.png", cb_source);

    printf("#Start scan corner\n");
    cv::Mat img;
    std::vector<cv::Point2f> image_points;
    std::vector<std::vector<cv::Point2f>> image_points_seq; 
    if (cv::findChessboardCorners(cb_source, cv::Size(aqXnum, aqYnum), image_points, 0) == 0) {
        printf("#Error: Corners not find ");
        return 0;
    } else {
        cvtColor(cb_source, img, CV_RGBA2GRAY);
        cv::imwrite("../blizzard/res/calibration/cb_gray.png", img);
        //find4QuadCornerSubpix(img, image_points, cv::Size(5, 5));

        cv::cornerSubPix(img, image_points, cv::Size(11, 11), cv::Size(-1, -1),
                         cv::TermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 30, 0.01));

        image_points_seq.push_back(image_points);

        cv::Mat cb_corner;
        cb_corner = cb_source.clone();
        drawChessboardCorners(cb_corner, cv::Size(aqXnum, aqYnum), image_points, true);
        cv::imwrite("../blizzard/res/calibration/cb_corner.png", cb_corner);
    }

    printf("#Start calibrate\n");
    cv::Size square_size = cv::Size(14.2222, 12);
    std::vector<std::vector<cv::Point3f>> object_points; 
    cv::Mat cameraMatrix = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0)); 
    cv::Mat distCoeffs = cv::Mat(1, 5, CV_32FC1, cv::Scalar::all(0)); 
    std::vector<cv::Mat> tvecsMat;  
    std::vector<cv::Mat> rvecsMat;  

    std::vector<cv::Point3f> realPoint;
    for (int i = 0; i < aqYnum; i++) {
        for (int j = 0; j < aqXnum; j++) {
            cv::Point3f tempPoint;
            
            tempPoint.x = i * square_size.width;
            tempPoint.y = j * square_size.height;
            tempPoint.z = 0;
            realPoint.push_back(tempPoint);
        }
    }
    object_points.push_back(realPoint);

    printf("#objectPoints: %ld\n", sizeof(object_points[0]));
    std::cout << object_points[0] << std::endl;

    printf("#image_points: %ld\n", sizeof(image_points_seq[0]));
    std::cout << image_points << std::endl;

    printf("#image size\n");
    std::cout << SCREEN_WIDTH << "*" << SCREEN_HEIGHT << std::endl;

    cv::calibrateCamera(object_points, image_points_seq, cb_source.size(), cameraMatrix, distCoeffs, rvecsMat, tvecsMat,
                        CV_CALIB_FIX_K3);

    std::cout << "tvecsMat:\n" << tvecsMat[0] << std::endl;
    std::cout << "rvecsMat:\n" << rvecsMat[0] << std::endl;

    std::cout << "#cameraMatrix:\n" << cameraMatrix << std::endl;
    std::cout << "#distCoeffs:\n" << distCoeffs << std::endl;

四、对图片进行校正

	cv::Mat cb_final;

    cv::Mat mapx = cv::Mat(cb_source.size(), CV_32FC1);
    cv::Mat mapy = cv::Mat(cb_source.size(), CV_32FC1);
    cv::Mat R = cv::Mat::eye(3, 3, CV_32F);
    //initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cv::Mat(), cb_source.size(), CV_32FC1,
    //                        mapx, mapy);
    //cv::remap(cb_source, cb_final, mapx, mapy, cv::INTER_LINEAR);

    undistort(cb_source, cb_final, cameraMatrix, distCoeffs);
    
    cv::imwrite("../blizzard/res/calibration/cb_final.png", cb_final);

1.校正前的图片

2.校正后的图片

总结 

到此这篇关于OpenCV相机标定的文章就介绍到这了,更多相关OpenCV相机标定内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: OpenCV相机标定的全过程记录

本文链接: https://lsjlt.com/news/141860.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • OpenCV相机标定的全过程记录
    目录一、OpenCV标定的几个常用函数findChessboardCorners()棋盘格角点检测cv::drawChessboardCorners()棋盘格角点的绘制find4Qu...
    99+
    2024-04-02
  • OpenCV实现相机标定
    本文实例为大家分享了OpenCV实现相机标定的具体代码,供大家参考,具体内容如下 一、相机与针孔相机模型 1.相机模型 现代科技加持下的相机已经成为制造精密设计巧妙的消费品,相机的光...
    99+
    2024-04-02
  • OpenCV实现相机标定板
    本文实例为大家分享了OpenCV实现相机标定板的具体代码,供大家参考,具体内容如下 1.代码实现 #include <opencv.hpp> #include "h...
    99+
    2024-04-02
  • Qt Creator配置opencv环境的全过程记录
    首先需要下载好相应的opencv+控件文件夹(注意不要有中文和空格): 到文件夹下的x86/bin 目录中拷贝所有的.dll文件(建议选择按类型排序,否则有可能拷贝漏了) 复制到...
    99+
    2024-04-02
  • OpenCV怎么实现相机标定板
    这篇文章主要介绍OpenCV怎么实现相机标定板,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!具体内容如下1.代码实现#include <opencv.hpp>#include &quo...
    99+
    2023-06-14
  • OpenCV实现相机标定示例详解
    目录环境准备相机标定棋盘格图片实时显示相机的画面在线标定实时显示相机画面,按键保存能检测到角点的棋盘格图片离线标定畸变矫正环境准备 vs2015+opencv4.10安装与配置 相机...
    99+
    2024-04-02
  • Python OpenCV 单目相机标定、坐标转换相关代码(包括鱼眼相机)
    前言   本文不讲原理,只关注代码,有很多博客是讲原理的,但是代码最多到畸变矫正就结束了,实际上就是到 OpenCV 官方示例涉及的部分。   在官方示例中使用黑白棋盘格求解了相机的内外参和畸变系数,...
    99+
    2023-09-23
    python opencv 计算机视觉
  • Flutter构建自定义Widgets的全过程记录
    目录一.组合widget实现二.通过自定义CustomPainter实现widgets三.饼状图piechart.dart代码展示四.实际效果图,eg:附:Flutter中父widg...
    99+
    2024-04-02
  • 自定义WPF分页控件的全过程记录
    一、分页控件功能说明# 实现如上图所示的分页控件,需要实现一下几个功能: 可以设置每页能够展示的最大列数(例如每页8列、每页16列等等)。 加载的数组总数量超过...
    99+
    2024-04-02
  • Python Apex YOLO V5 6.2 目标检测 全过程记录
    博文目录 文章目录 效果展示工程源码环境准备第一阶段 使用自带模型 实现实时目标检测屏幕截图封装 详见 toolkit.py 中 Capture 类目标检测封装 详见 toolkit.py 中...
    99+
    2023-08-31
    python 目标检测
  • springboot打war包的全过程记录
    目录为什么要把SpringBoot打成war包springboot打war包分步指南总结为什么要把SpringBoot打成war包 正常情况下SpringBoot项目是以jar包的形...
    99+
    2024-04-02
  • opencv摄像机标定的实现
    原图 矫正后 我新建了个jz的文件夹放相机矫正所需要拍摄的图片,如下:共12张   # coding:utf-8 import cv2 import nu...
    99+
    2024-04-02
  • shiro拦截认证的全过程记录
    目录概述shrio 拦截认证全过程总结概述 Shiro是apache旗下一个开源安全框架(http://shiro.apache.org/),它将软件系统的安全认证相关的功能抽取出来...
    99+
    2024-04-02
  • iOS动态更换Icon的全过程记录
    iOS 动态更换Icon 动态切换 App 的 icon 这个需求,在上一家公司做一款定制 App 时遇到过一次,这次领导说可能需要做,就又做了一次。虽然不是什么很难的知识点,这里...
    99+
    2022-06-02
    ios icon 动态
  • Vue导出Excel功能的全过程记录
    目录1.前端主导流程: 2. 插件使用及初始化 2.1  借助vue-admin中提供的方法。2.2  安装插件依赖。2.3  回调函数内容如下3.对后...
    99+
    2024-04-02
  • Python解析JSON对象的全过程记录
    前言 本章节我们将为大家介绍如何使用 Python 语言来编码和解码 JSON 对象。 json处理模块的主要任务,是将一个JSON对象,转换成Python数据类型数据进行处理,或者...
    99+
    2024-04-02
  • vite构建vue3项目的全过程记录
    目录环境准备创建项目启动总结环境准备 安装最新版本 @vuejs/app yarn global add @vue/cli # OR npm install -g @vue/cli ...
    99+
    2023-01-16
    vite搭建vue3 vue3+vite vue构建
  • MySQL DeadLock故障排查全过程记录
    【作者】 刘博:携程技术保障中心数据库高级经理,主要关注Sql server和Mysql的运维和故障处理。 【环境】 版本号:5.6.21 隔离级别:REPEATABLE READ 【问题描述】 接...
    99+
    2024-04-02
  • SpringBoot创建RSocket服务器的全过程记录
    前言 在微服务的多样化世界中,HTTP是代理到代理通信中无可争议的领导者。它成熟,无处不在。但在某些情况下,HTTP请求-响应可能很麻烦。如果您需要传统请求-响应之外的通信模式,如f...
    99+
    2024-04-02
  • Nginx反向代理配置的全过程记录
    一、准备工作 Linux系统安装Tomcat,使用默认端口8080,启动Tomcat服务器 可以正常访问 接下来想要通过Nginx反向代理,转发请求到Tomcat服务器。对外暴露...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作