python图像处理笔记-八-针孔照相机模型与照相机标定
参考教材:
- python计算机视觉编程
- 视觉SLAM十四讲,从理论到实践
针孔照相机模型
针孔摄像机模型(有时称作摄影照相机模型),是计算机视觉中广泛应用的照相机模型。原因是:
这个名字来源于一种简单的照相机,他利用小孔成像原理进行成像,换句话说就是:在光线投影到图像平面前,从唯一一个点经过,这个经过的点就叫做:照相机中心,记做C,如下图所示:
(这张图来自于他人博客:https://blog.csdn.net/Dujing2019/article/details/91691202)
照相机矩阵
\right]K=⎣⎡af00sf0cxcy1⎦⎤
三维点的投影
from scipy import linalg
from numpy import *
from pylab import *class Camera(object):"""表示针孔照相机的类"""def __init__(self, P):"""初始化照相机模型:param P: P = K[R| t]"""self.P = Pself.K = None # 标定矩阵self.R = None # 旋转self.t = None # 平移self.c = None # 照相机中心def project(self, X):"""返回投影到图像视图的点:param X:X (4*n数组)的投影点:return:并且进行坐标归一化"""x = dot(self.P, X)for i in range(3):x[i] /= x[2]return xif __name__ == "__main__":# 载入点points = loadtxt("house.p3d")# 交换维度以读取points = points.swapaxes(0,1)# 齐次坐标points = vstack((points,ones(points.shape[1])))# 设置照相机参数P = hstack((eye(3), array([[0],[0],[-10]])))cam = Camera(P)x = cam.project(points)# 绘制投影figure()plot(x[0], x[1], 'k.')show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
为了研究照相机的移动会如何改变投影效果,我们围绕一个随机的三维向量进行增量旋转的投影:
from scipy import linalg
import numpy as np
import matplotlib.pyplot as pltclass Camera(object):"""表示针孔照相机的类"""def __init__(self, P):"""初始化照相机模型:param P: P = K[R| t]"""self.P = Pself.K = None # 标定矩阵self.R = None # 旋转self.t = None # 平移self.c = None # 照相机中心def project(self, X):"""返回投影到图像视图的点:param X:X (4*n数组)的投影点:return:并且进行坐标归一化"""x = np.dot(self.P, X)for i in range(3):x[i] /= x[2]return xdef rotationMatrix(self, a):"""用于描述围绕a轴旋转的三维旋转矩阵:param a: 旋转轴"""R = np.eye(4)R[:3, :3] = linalg.expm([[0, -a[2], a[1]], [a[2], 0, -a[0]], [-a[1], a[0], 0]])return Rif __name__ == "__main__":# 载入点points = np.loadtxt("house.p3d")# 交换维度以读取points = points.swapaxes(0,1)# 齐次坐标points = np.vstack((points,np.ones(points.shape[1])))# 设置照相机参数P = np.hstack((np.eye(3), np.array([[0],[0],[-10]])))cam = Camera(P)x = cam.project(points)# 绘制投影plt.figure()plt.plot(x[0], x[1], 'k.')plt.show()# 创建变换r = 0.05 * np.random.random(3)rot = cam.rotationMatrix(r)# 旋转矩阵和投影plt.figure()for t in range(100):cam.P = np.dot(cam.P, rot)x = cam.project(points)plt.plot(x[0], x[1], 'k.')plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
plt.figure()
for t in range(20):cam.P = np.dot(cam.P, rot)x = cam.project(points)plt.plot(x[0], x[1], 'k.')
plt.show()
相当于我们每次都画,但是都不show,最后再一起show出来就可以了。
照相机矩阵的分解
from scipy import linalg
import numpy as np
import matplotlib.pyplot as pltclass Camera(object):"""表示针孔照相机的类"""def __init__(self, P):"""初始化照相机模型:param P: P = K[R| t]"""self.P = Pself.K = None # 标定矩阵self.R = None # 旋转self.t = None # 平移self.c = None # 照相机中心def project(self, X):"""返回投影到图像视图的点:param X:X (4*n数组)的投影点:return:并且进行坐标归一化"""x = np.dot(self.P, X)for i in range(3):x[i] /= x[2]return xdef rotationMatrix(self, a):"""用于描述围绕a轴旋转的三维旋转矩阵:param a: 旋转轴"""R = np.eye(4)R[:3, :3] = linalg.expm([[0, -a[2], a[1]], [a[2], 0, -a[0]], [-a[1], a[0], 0]])return Rdef factor(self):"""将矩阵分解为kRt,P = K[R|t]:return:KRt"""# 分解前半部分K, R = linalg.rq(self.P[:,:3])# 将K的对角线元素设为正值T = np.diag(np.sign(np.diag(K)))if(linalg.det(T)<0):T[1, 1] *= -1self.K = np.dot(K, T)self.R = np.dot(T, R)self.t = np.dot(linalg.inv(self.K), self.P[:, 3])return self.K, self.R, self.tif __name__ == "__main__":# 载入点points = np.loadtxt("house.p3d")# 交换维度以读取points = points.swapaxes(0,1)# 齐次坐标points = np.vstack((points,np.ones(points.shape[1])))# 设置照相机参数P = np.hstack((np.eye(3), np.array([[0],[0],[-10]])))cam = Camera(P)x = cam.project(points)# 绘制投影plt.figure()plt.plot(x[0], x[1], 'k.')plt.show()# 创建变换r = 0.05 * np.random.random(3)rot = cam.rotationMatrix(r)# 旋转矩阵和投影plt.figure()for t in range(20):cam.P = np.dot(cam.P, rot)x = cam.project(points)plt.plot(x[0], x[1], 'k.')plt.show()# 测试新写的函数K = np.array([[1000, 0, 500], [0, 1000, 300], [0, 0, 1]])tmp = cam.rotationMatrix([0, 0, 1])[:3, :3]Rt = np.hstack((tmp, np.array([[50], [40], [30]])))cam = Camera(np.dot(K, Rt))print(K,Rt)print(cam.factor())
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
你可以观测到,结果与输入并不相同,这是由于为了避免符号的二义性而导致的。
计算中心
照相机的中心是一个三维点,满足约束:P C = 0 PC= 0PC=0,又因为P = K [ R ∣ t ] P = K[R|t]P=K[R∣t],所以有:
KaTeX parse error: No such environment: align at position 9: \begin{̲a̲l̲i̲g̲n̲}̲ K [R|t] C = K…
实现起来就很简单了,当然我们也可以看出来照相机的中心和照相机的标定矩阵是无关的:
def center(self):"""计算、返回摄像机的中心"""if(self.c is not None):return self.cself.factor()self.c = -np.dot(self.R.T, self.t)return self.c
相机标定(张正友标定法)
张正友标定法是一种介于传统标定方法和自标定方法之间的一种简易标定方法。它的标定方法简单易做,只需要我们打印一张棋盘格就可以了。并且在标定过程中只需要将平面模板按任意角度在摄像机前放置即可。
研究的坐标系
其中图像坐标系和像素坐标系非常相似,图像坐标系的原点是相机光轴与成像平面的角点,单位是毫米,而像素坐标系的原点是图像的左上角,像素坐标系的单位是像素。
世界坐标系到相机坐标系的变换
[ X C Y C Z C 1 ] = = [ R t 0 1 ] [ X w Y w Z w 1 ] \left[
XCYCZC1XCYCZC1
\right] = = \left[
R0t1Rt01
\right] \left[
XwYwZw1XwYwZw1
\right]⎣⎢⎢⎡XCYCZC1⎦⎥⎥⎤==[R0t1]⎣⎢⎢⎡XwYwZw1⎦⎥⎥⎤
这个就很简单了,就是一个旋转平移
相机坐标系下的点转换到图像坐标系下
x = f Z c X c x = \frac{f}{Z_c}X_cx=ZcfXc
y = f Z c Y c y = \frac{f}{Z_c}Y_cy=ZcfYc
这个也非常的简单,我们可以用矩阵的形式来进行表达:
[ x y 1 ] = . [ f Z C 0 0 0 0 f Z C 0 0 0 0 f Z C 0 ] [ X C Y C Z C 1 ] \left[
xy1xy1
\right] =. \left[
fZC000fZC000fZC000fZC0000fZC0000fZC0
\right] \left[
XCYCZC1XCYCZC1
\right]⎣⎡xy1⎦⎤=.⎣⎢⎡ZCf000ZCf000ZCf000⎦⎥⎤⎣⎢⎢⎡XCYCZC1⎦⎥⎥⎤
图像坐标系到像素坐标系的变换
u = c x + x f x u = c_x+xf_xu=cx+xfx
v = c y + y f y v =c_y+yf_yv=cy+yfy
[ u v 1 ] = . [ f x 0 c x 0 f y c y 0 0 1 ] [ x y 1 ] \left[
uv1uv1
\right] = . \left[
fx000fy0cxcy1fx0cx0fycy001
\right] \left[
xy1xy1
\right]⎣⎡uv1⎦⎤=.⎣⎡fx000fy0cxcy1⎦⎤⎣⎡xy1⎦⎤
相机畸变模型
想要知道是枕型畸变的话,那么离中心点越远,偏移量越大,反之则反。
标定了什么?
- 内参:
- f x , f y f_x,f_yfx,fy:相机焦距
- c x , c y c_x,c_ycx,cy:图像中心点
- k 1 , k 2 , k 3 , p 1 , p 2 k_1,k_2,k_3,p_1,p_2k1,k2,k3,p1,p2:畸变参数
- 外参:
- R t RtRt:反映了相机和世界坐标的关系
标定任务
标定原理
当然,在满足这个条件的时候,我们就有如下公式:
s [ u v 1 ] = . A [ r 1 r 2 r 3 t ] [ X Y 0 1 ] s \left[
现在我们可以把式子写为:
s m ~ = A [ r 1 r 2 t ] M ~ s\widetilde{m}= A \left[
\right]H=A[r1r2t]这里的H代表内参和外参的乘积构成的矩阵。我们可以通过反推单应性变换将H求出,那么现在的问题在于,如何通过已知的H反求出内参和外参。
我们知道,内参、外参、H之间是知二推一的关系,所以我们就想,如果能将内参求出来,那么就可以很自然的知道外参了。
[ h 1 h 2 h 3 ] = λ A [ r 1 r 2 r t ] \left[
\right][h1h2h3]=λA[r1r2rt]
有以下约束条件:
- r 1 , r 2 r_1,r_2r1,r2正交,也就是r 1 r 2 = 0 r_1r_2=0r1r2=0。因为r 1 , r 2 r_1,r_2r1,r2是分别绕x , y x,yx,y轴得到的,而x , y x,yx,y轴垂直z zz轴。
- 旋转向量的模为1,也就是说∣ r 1 ∣ , ∣ r 2 ∣ = 1 |r_1|,|r_2| = 1∣r1∣,∣r2∣=1,这是因为旋转不改变尺度。
根据这两个约束条件,经过数学变换,可以得到:
h 1 T A − T A − 1 h 2 = 0 h_1^TA^{-T}A^{-1}h_2 = 0h1TA−TA−1h2=0
可以看出来B是一个对称矩阵,所以B的有效元素只有六个:
b = [ B 11 B 12 B 22 B 13 B 23 B 33 ] T b = \left[
B11B12B22B13B23B33B11B12B22B13B23B33
hi1hj1hi1hj2+hi2hj1hi2hj2hi3hj2+hi2hj3hi3hj3hi1hj1hi1hj2+hi2hj1hi2hj2hi3hj2+hi2hj3hi3hj3
标定工具
理想状态下圆形的检测精度较高,因为它能避免特征检测时候的错误。也就是说:中心拟合精度高。但是圆形却存在着偏心差,如下图:
标定的摄像机位置和数量
python图像处理笔记-八-针孔照相机模型与照相机标定相关推荐
- python图像处理笔记-十二-图像聚类
python图像处理笔记-十二-图像聚类 学习内容 这一章主要在学习的是聚类算法以及其在图像算法中的应用,主要学习的聚类方法有: KMeans 层次聚类 谱聚类 并将使用他们对字母数据及进行聚类处理, ...
- Python图像处理笔记——卷积
Python图像处理--卷积 一.什么是卷积? 1. 数学定义 2. 引入库 3. python实现对图像的卷积 二.相关与卷积 1. 相关的定义 2. Python实现 扩展阅读 一.什么是卷积? ...
- Python学习笔记(八)
Python基础总结 Python基础总结 Python简介 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.目前分为Python2.X和Python3.X两个版本,且Python ...
- python学习笔记(IO模型)
1.IO模型介绍: io模型一般有五种: * blocking IO * nonblocking IO * IO multiplexing * s ...
- [Python图像处理] 十八.图像锐化与边缘检测之Scharr算子、Canny算子和LOG算子
该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门.OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子.图像增强技术.图像分割等,后期结合深度学习研究图像识别 ...
- 【懒懒的Python学习笔记八】
面向对象编程是最有效的编程方法之一,在面向对象编程中,你编写表示现实世界中事物和情景的类,并基于这些类来创建对象.使用类来创建对象被称为实例化. 创建和使用类 使用类可以模拟任何东西.下面的实例编写一 ...
- Python学习笔记(八)爬虫基础(正则和编解码)
知识点 正则 正则匹配url,引用re库,将需要匹配的字段用(.*?)来匹配,可以匹配任何字符串.如果有换行,可以用如下方式解决: 1. ([\s\S]*?) 2. re.findall(reg,ht ...
- python学习笔记一——鸭子模型
1.鸭子模型 在程序设计中,鸭子类型(duck typing)是动态类型的一种风格.在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合&qu ...
- python图像处理笔记(六):手动获取坐标标注图像
引言 之前的两篇文章有提到图片标注,但一个是用yolo算法给识别到的图像加框,一个是根据霍夫变换做直线与圆的检测,本篇想总结一下根据鼠标点击来对图像做一些填充处理的方式. opencv鼠标事件 介绍的 ...
- Python图像处理笔记——傅里叶变换
文章目录 一.前言 二.傅里叶变换在图像中的应用 0. 本文用到的库 1. 图像的傅里叶变换和逆变换 2. 高斯模糊 3. 傅里叶变换频域滤波 (1)低通滤波 (2)高通滤波 (3)带通滤波 一.前言 ...
最新文章
- 大数据开发实战:Hive优化实战2-大表join小表优化
- AjaxControlTookit中的AutoCompleteExtender位置错位问题 ListSearchExtender不支持中文的问题...
- 5.2.3 OS之I/O设备的分配与回收(DCT-COCT-CHCT-SDT)
- element table 表格设置max-height 没有出现滚动条,多渲染了一列。
- matlab深度学习_matlab使用贝叶斯优化的深度学习
- Confluence 6 自定义 Decorator 模板的宏和针对高级用户
- Pv6报头结构以及与IPv4的比较
- zookeeper的集群配置
- unix network programming volume 2 interprocess communications second edition环境搭建出错的处理...
- 剑指offer面试题[31]-连续数组的最大和
- 2020 CTF暑假夏令营培训Day2 密码学Crypto 部分笔记
- 【代码优化】坚持使用Override注解
- DBeaver执行SQL脚本
- 专业wifi测试软件,专业的WiFi检测工具有哪些?
- excel公式编辑器_巧用Excel制作炫酷聚光灯效果,数据查看太方便了
- vue拍照功能PC+手机需要的可以看一下
- AI萃取的5G咖啡,只有华为能调出这个味道
- Nginx源码完全注释(1)ngx_alloc.h / ngx_alloc.c
- 已通过认证的微信公众号名字可以改吗?
- Android开发之最新Android Studio推送代码到最新GitHub教程 | Android Studio绑定GitHub | AS令牌登录GitHub | 创建GitHub令牌