相机标定的原理及实现

相机标定的原理及实现本文参考文档:原理部分:https://blog.csdn.net/honyniu/article/details/51004397代码部分:https://www.cnblogs.com/wildbloom/p/8320351.html;https://blog.csdn.net/firemicrocosm/article/details/48594897#1、相机标定的原理摄像…

大家好,又见面了,我是你们的朋友全栈君。

本文参考文档:
原理部分:https://blog.csdn.net/honyniu/article/details/51004397
代码部分:https://www.cnblogs.com/wildbloom/p/8320351.htmlhttps://blog.csdn.net/firemicrocosm/article/details/48594897#

1、相机标定的原理

摄像机标定(Camera calibration)简单来说是从世界坐标系转换为相机坐标系,再由相机坐标系转换为图像坐标系的过程,也就是求最终的投影矩阵P的过程。

世界坐标系(world coordinate system):用户定义的三维世界的坐标系,为了描述目标物在真实世界里的位置而被引入。单位为m。

相机坐标系(camera coordinate system):在相机上建立的坐标系,为了从相机的角度描述物体位置而定义,作为沟通世界坐标系和图像/像素坐标系的中间一环。单位为m。

图像坐标系(image coordinate system):为了描述成像过程中物体从相机坐标系到图像坐标系的投影透射关系而引入,方便进一步得到像素坐标系下的坐标。 单位为m。

从世界坐标系到相机坐标系:

这一步是三维点到三维点的转换,包括R,t(相机外参)等参数;
在这里插入图片描述

相机坐标系转换为图像坐标系:

这一步是三维点到二维点的转换,包括K(相机内参)等参数;在这里插入图片描述
在这里插入图片描述
根据上述的关系图可以推导出下面的变换公式:
在这里插入图片描述在这里插入图片描述

像主点的偏移:

在这里插入图片描述
可以推出:
在这里插入图片描述

内参矩阵K:

在这里插入图片描述

外参矩阵[R丨t]:

表示三个方向的偏转:
在这里插入图片描述

投影矩阵P

(在这里可以认为旋转矩阵 R 为单位矩阵 I,平移矩阵 t 都为0):
在这里插入图片描述
根据上述变换最终可以得到一个投影矩阵P的公式为:
在这里插入图片描述
总结一下公式大致如下:
在这里插入图片描述

畸变参数

在几何光学和阴极射线管显示中,畸变是对直线投影的一种偏移。简单来说直线投影是场景内的一条直线投影到图片上也保持为一条直线。那畸变简单来说就是一条直线投影到图片上不能保持为一条直线了,这是一种光学畸变。畸变一般可以分为两大类,包括径向畸变和切向畸变。主要的一般径向畸变有时也会有轻微的切向畸变。

2、相机标定的实现

相机标定的目的:获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的选择和平移矩阵),内参和外参系数可以对之后相机拍摄的图像就进行矫正,得到畸变相对很小的图像。

相机标定的输入:标定图像上所有内角点的图像坐标,标定板图像上所有内角点的空间三维坐标(一般情况下假定图像位于Z=0平面上)。

相机标定的输出:摄像机的内参、外参系数。

这三个基础的问题就决定了使用Opencv实现张正友法标定相机的标定流程、标定结果评价以及使用标定结果矫正原始图像的完整流程:

  1. 准备标定图片
  2. 对每一张标定图片,提取角点信息
  3. 对每一张标定图片,进一步提取亚像素角点信息
  4. 在棋盘标定图上绘制找到的内角点(非必须,仅为了显示)
  5. 相机标定
  6. 对标定结果进行评价
  7. 查看标定效果——利用标定结果对棋盘图进行矫正

我准备的是下图这种格子数为7×5,内角点为6×4的棋盘格图片,手机型号为荣耀PLAY。
在这里插入图片描述
将它放在一个平面上从不同角度拍摄10张照片作为实验素材。
代码如下:

import cv2
import numpy as np
import glob

# 设置寻找亚像素角点的参数,采用的停止准则是最大循环次数30和最大误差容限0.001
criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001)

# 获取标定板角点的位置
objp = np.zeros((4 * 6, 3), np.float32)
objp[:, :2] = np.mgrid[0:6, 0:4].T.reshape(-1, 2)  # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y

obj_points = []  # 存储3D点
img_points = []  # 存储2D点

images = glob.glob("image4/*.jpg")
i=0;
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    size = gray.shape[::-1]
    ret, corners = cv2.findChessboardCorners(gray, (6, 4), None)
    #print(corners)

    if ret:

        obj_points.append(objp)

        corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria)  # 在原角点的基础上寻找亚像素角点
        #print(corners2)
        if [corners2]:
            img_points.append(corners2)
        else:
            img_points.append(corners)

        cv2.drawChessboardCorners(img, (6, 4), corners, ret)  # 记住,OpenCV的绘制函数一般无返回值
        i+=1;
        cv2.imwrite('conimg'+str(i)+'.jpg', img)
        cv2.waitKey(1500)

print(len(img_points))
cv2.destroyAllWindows()

# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None)

print("ret:", ret)
print("mtx:\n", mtx) # 内参数矩阵
print("dist:\n", dist)  # 畸变系数   distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print("rvecs:\n", rvecs)  # 旋转向量  # 外参数
print("tvecs:\n", tvecs ) # 平移向量  # 外参数

print("-----------------------------------------------------")

img = cv2.imread(images[2])
h, w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))#显示更大范围的图片(正常重映射之后会删掉一部分图像)
print (newcameramtx)
print("------------------使用undistort函数-------------------")
dst = cv2.undistort(img,mtx,dist,None,newcameramtx)
x,y,w,h = roi
dst1 = dst[y:y+h,x:x+w]
cv2.imwrite('calibresult3.jpg', dst1)
print ("方法一:dst的大小为:", dst1.shape)

代码中的9、10、21、35行中的角点数根据自己的棋盘格大小进行更改。
实验分为两步,第一步主要是对每张图片进行角点的提取,结果如下:
在这里插入图片描述
第二步就是实现相机的标定,运行结果如下:

mtx为内参数矩阵,dist为畸变系数:
在这里插入图片描述
rvecs为旋转向量:
其中mtx为内参数矩阵,dist为畸变系数
tvecs为平移向量:
在这里插入图片描述
畸变矫正:
在这里插入图片描述
矫正前后的图片
在这里插入图片描述
在这里插入图片描述

总结

通过标定得到荣耀PLAY的后置摄像头的内置参数矩阵为:
在这里插入图片描述
矫正后的图和原图通过对比发现差距并不大,只是在宽度上有一点拉伸(肉眼能看出的差距只有图片右上角的白字因为图片的拉伸而消失了),原因应该是原图基本上没有出现明显的畸变。(应该是现在的手机相机比较厉害吧…)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/140209.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • kali的Ettercap(0.8.3.1)GUI界面中文化[通俗易懂]

    kali的Ettercap(0.8.3.1)GUI界面中文化[通俗易懂]前言:最近在学kali,学到了dns劫持,上的课老师也是讲了一部分功能,我在网上想找一下别的功能,却发现这个Ettercap居然是20年新版,并没有什么详细信息,我就心血来潮写这篇笔记,让自己以后在复习的时候可以更快的回顾,发出来也让大家一起进步。 启动方法 kali的终端中输入ettercap-G 启动界面 GUI界面 中间人攻击选项 其他选项 Targets Currenttargets Selecttargets

  • c语言中(int)什么意思_c语言中怎么用int转化char

    c语言中(int)什么意思_c语言中怎么用int转化char1、int;int是C++关键字,表示整型,其大小是32位有符号整型,表示的范围是-2,147,483,648 到2,147,483,647;在声明和定义变量时使用,它表示的意思是所声明或所定义的变量为整型变量。如果其用于函数参数时,其传递方向为值传递,即只能将实参的值传递给形参,而不能将形参的值传递给实参。例如:通过这种方式去进行交换两个数是无法达到目的的。例子1:#include&…

  • BZOJ3995:[SDOI2015]道路修建(线段树)[通俗易懂]

    BZOJ3995:[SDOI2015]道路修建(线段树)

  • 通俗理解线性回归(一)

    通俗理解线性回归(一)0什么是回归?假设线性回归是个黑盒子,那按照程序员的思维来说,这个黑盒子就是个函数,然后呢,我们只要往这个函数传一些参数作为输入,就能得到一个结果作为输出。那回归是什么意思呢?其实说白了,就是这个黑盒子输出的结果是个连续的值。如果输出不是个连续值而是个离散值那就叫分类。那什么叫做连续值呢?非常简单,举个栗子:比如我告诉你我这里有间房子,这间房子有40平,在地铁口,然后你来猜一猜我的房子总共值…

  • 简单工厂模式

    简单工厂模式

  • 【js】Input事件

    【js】Input事件InputEvent常用事件触发的先后顺序如下:1keydown2keypress3textInput4input5keyupkeydown,keyup1全部浏览器支持2当用户按下/释放键盘上的任意键时触发3event.keyCode,返回键盘上按键对应的特定键码(兼容性:分号在Firefox,Opera上返回的是ASC

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号