OpenCV开发笔记(七十五):相机标定矫正中使用remap重映射进行畸变矫正

若该文为原创文章,转载请注明原文出处

本文章博客地址:https://blog.csdn.net/qq21497936/article/details/136293833

各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门)

OpenCV开发专栏(点击传送门)

上一篇:《OpenCV开发笔记(七十四):OpenCV3.4.1+ffmpeg3.4.8交叉编译移植到海思平台Hi35xx平台

下一篇:持续补充中...

前言

相机标定,重映射可以进行插值映射从而矫正图像,这是一种方法,也有矩阵映射方法,本篇使用重映射方式解说畸变矫正的计算原理。

Demo

横向纵向区域固定拉伸:

横向纵向拉伸:

右下角拉伸:

相机畸变矫正

标定相机需要做两件事:

  • 纠正畸变的影响
  • 根据图像重构三位场景

纠正畸变的影响

Opencv提供了可以直接使用的矫正算法,即通过输入原始图像和由函数cv::calibrateCamera()得到的畸变系数,生成校正后的图像。(注意:这里可使用用cv::undistort()使用该算法直接完成所需任务,也可以使用函数cv::iniitUndistorRectifyMap()和cv::remap()来更有效的处理。

矫正映射remap(畸变映射)

当进行图像矫正时,必须指定输入图像的每个像素在输出图像中移动到的位置,成为"矫正映射"(畸变映射)。

双通道浮点数表示方式

N x M的矩阵A中,重映射由双通道浮点数的N x M的矩阵B表示,对于图像A中的任意一点aPoint(i, j),映射为b1Point(i', j')和b2Point(i', j'),在A中假设i=2,j=3,那么(假设重映射之后4.5,5.5)在B1中b1Point(i', j')值为4.5,b2Point(i', j')值为5.5,由于坐标是浮点数,那么需要插值得到整数位置以及中间过渡的区域颜色(平滑处理)。

双矩阵浮点数表示方式

双矩阵浮点数表示,N x M的矩阵A中,重映射由一对N x M的矩阵B和C描述,这里所有的N x M矩阵都是单通道浮点矩阵,在A中的点aPoint(i, j),重映射矩阵B中的点bPoint(i,j)存储了重映射后的i' (映射后的i坐标), 重映射矩阵C中的点cPoint(i,j)存储了重映射后的j'(映射后的j坐标)。

定点表示方式

映射由双通道有符号整数矩阵(即CV_16SC2类型)表示。该方式与双通道浮点数表示方式相同,但使用此格式要快得多(笔者理解:由浮点数插值改为整数插值,会要快一些,但是肯定双通道浮点数的表示方式图像效果会稍微好一些)。

remap核心关键

在于得到插值的坐标系来映射新位置的x和y位置,要渐近等,所以本方法的核心关键在于得到标定后的矩阵,得到映射矩阵的方式可以自己写算法,也可以使用其他方式,后续文章继续深入这块。

函数原型

cpp 复制代码
void remap( InputArray src,
          OutputArray dst,
          InputArray map1,
          InputArray map2,
          int interpolation,
          int borderMode = BORDER_CONSTANT,
          const Scalar& borderValue = Scalar());
  • 参数一:InputArray类型的src,一般为cv::Mat;
  • 参数二:OutputArray类型的dst,目标图像。它的大小与map1相同,类型与src相同。
  • 参数三:InputArray类型的map1,它有两种可能的表示对象:表示点(x,y)的第一个映射或者表示CV_16SC2 , CV_32FC1 或CV_32FC2类型的x值。
  • 参数四:InputArray类型的map2,它也有两种可能的表示对象,而且他是根据map1来确定表示哪种对象。若map1表示点(x,y)时,这个参数不代表任何值,否则,表示CV_16UC1 , rCV_32FC1类型的y值(第二个值)。
  • 参数五:int类型的interpolation,使用的插值方法;
  • 参数六:int类型的borderMode,边界处理方式;
  • 参数七 :Scalar类型的borderValue,重映射后,离群点的背景,需要broderMode设置为BORDER_CONSTRANT时才有效。(离群点:当图片大小为400x300,那么对应的map1和map2范围为0~399、0~299,小于0或者大于299的则为离散点,使用该颜色填充);

Demo源码

cpp 复制代码
void OpenCVManager::testRemap2()
{
    std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/25.jpg";

    // 步骤一:读取文件
    cv::Mat srcMat = cv::imread(srcFilePath);
    // 缩放一下
    int width = 400;
    int height = 400;
    cv::resize(srcMat, srcMat, cv::Size(width, height));
    // 步骤二:映射矩阵
    cv::Mat mapX;
    cv::Mat mapY;
    mapX.create(srcMat.size(), CV_32FC1);
    mapY.create(srcMat.size(), CV_32FC1);
    // 算法:这里400x400,将0~100放大至0~200,将100~400映射为200~400
    // 算法:这里400x400,将0~100放大至0~200,将100~400映射为200~400
#if 0
    for(int row = 0; row < srcMat.rows; row++)
    {
        for(int col = 0; col < srcMat.cols; col++)
        {
//            if(true)
            if(col < 200)
            {
                mapX.at<float>(row, col) = static_cast<float>(col * 1.0f / 2);
            }else{
                mapX.at<float>(row, col) = static_cast<float>(100 + (col - 200) * 1.0f / 2 * 3);
            }
//            if(true)
            if(row < 200)
            {
                mapY.at<float>(row, col) = static_cast<float>(row * 1.0f / 2);
            }else{
                mapY.at<float>(row, col) = static_cast<float>(100 + (row - 200) * 1.0f / 2 * 3);
            }
        }
    }
#endif
#if 0
    for(int row = 0; row < srcMat.rows; row++)
    {
        for(int col = 0; col < srcMat.cols; col++)
        {
            // 这里是 0~200 缩放为 0~100 缩小                               // 比例系数
            if(col == 0)
            {
                mapX.at<float>(row, col) = static_cast<float>(col);
            }else if(col < 200)
            {
                mapX.at<float>(row, col) = static_cast<float>(col * 1.0f / 2 * (col * 1.0f / 199));
            }else{
                mapX.at<float>(row, col) = static_cast<float>(col * 1.0f / 2 * (col * 1.0f / 199));
            }
            if(row == 0)
            {
                mapY.at<float>(row, col) = static_cast<float>(row);
            }else if(row < 200)
            {
                mapY.at<float>(row, col) = static_cast<float>(row * 1.0f / 2 * (row * 1.0f / 199));
            }else{
                mapY.at<float>(row, col) = static_cast<float>(row * 1.0f / 2 * (row * 1.0f / 199));
            }
        }
    }
#endif
#if 1
    for(int row = 0; row < srcMat.rows; row++)
    {
        for(int col = 0; col < srcMat.cols; col++)
        {
            //                                                             比例系数 0~1.0(400~800)/400
            mapX.at<float>(row, col) = static_cast<float>(col * ((col + 1 + 400) * 1.0f / 800));
            mapY.at<float>(row, col) = static_cast<float>(row * ((row + 1 + 400) * 1.0f / 800));
        }
    }
#endif

    cv::Mat dstMat;
    cv::remap(srcMat,
              dstMat,
              mapX,
              mapY,
              CV_INTER_LINEAR,
              cv::BORDER_CONSTANT,
              cv::Scalar(255, 0, 0));

    cv::imshow("1", srcMat);
    cv::imshow(_windowTitle.toStdString(), dstMat);
    cv::waitKey(0);
}

对应工程模板v1.66.0

上一篇:《OpenCV开发笔记(七十四):OpenCV3.4.1+ffmpeg3.4.8交叉编译移植到海思平台Hi35xx平台

下一篇:持续补充中...

本文章博客地址:https://blog.csdn.net/qq21497936/article/details/136293833

相关推荐
小俊俊的博客6 天前
使用Opencv对监控相机进行内参标定记录
opencv·相机标定
mjlsuccess1 个月前
红外相机和RGB相机外参标定 - 无需标定板方案
相机标定·红外和可见光相机标定·thermal和rgb相机标定
亦枫Leonlew1 个月前
三维测量与建模笔记 - 3 Python Opencv实现相机标定
笔记·python·opencv·相机标定
亦枫Leonlew1 个月前
三维测量与建模笔记 - 3.3 张正友标定法
笔记·相机标定·三维重建·张正友标定法
路明呦呦2 个月前
【双目视觉标定】——3面结构光相机标定实践(获取相机内参)~未完待续
计算机视觉·相机标定
路明呦呦2 个月前
【双目视觉标定】——1原理与实践
计算机视觉·相机标定
1037号森林里一段干木头2 个月前
相机外参与相机位姿深度理解
数学·计算机视觉·slam·相机标定
charlee443 个月前
一次实践:给自己的手机摄像头进行相机标定
opencv·相机标定·张正友标定法
404detective4 个月前
Matlab R2022b使用Camera Calibrator工具箱张正友标定法进行相机标定附带标定前后对比代码
matlab·相机标定·camera·深度相机·张正友标定法·calibrator·棋盘法
长沙红胖子Qt4 个月前
项目实战:Qt+Opencv相机标定工具v1.3.0(支持打开摄像头、视频文件和网络地址,支持标定过程查看、删除和动态评价误差率,支持追加标定等等)
qt·opencv·相机标定·畸变矫正·动态标定过程