传统图像处理之相机模型

 博主简介

博主是一名大二学生,主攻人工智能研究。感谢让我们在CSDN相遇,博主致力于在这里分享关于人工智能,c++,Python,爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主,博主会继续更新的,如果有错误之处,大家可以指正。

专栏简介:   本专栏主要研究计算机视觉,涉及算法,案例实践,网络模型等知识。包括一些常用的数据处理算法,也会介绍很多的Python第三方库。如果需要,点击这里订阅专栏   。

给大家分享一个我很喜欢的一句话:“每天多努力一点,不为别的,只为日后,能够多一些选择,选择舒心的日子,选择自己喜欢的人!”


目录

相机模型

针孔相机模型

射影几何

 透镜

透镜畸变

光流

稀疏光流

跟踪


前面我们讲解了如何对图像进行美化,包括简单的打光、美白、去噪和稍显复杂的直方图均衡化、图像金字塔。那么这一章我们将二维的图像与现实生活中的物体联系起来。现实生活中的物体和图像有两个明显的不同之处:①物体是三维的,图像十二位的。②物体是运动的,图象是静止的。为了解决第一个不同,需要从三维物体采集二维图像,这往往是一个几何过程,在计算机视觉中称为相机模型。为了解决第二个不同,我们使用光流算法来进行解决。所以,本节的学习任务就是光流算法的应用。

相机模型

针孔相机模型

小孔成像,这应该是个不陌生的话题。初中时候的研究课题,今天我们介绍的第一个相机模型就是针孔相机模型。

①.相机平面,我们将相机看成一个平面,远处的光线经过此平面时被折射或反射,当然一个相机模型可能存在多个相机平面(多个透镜)。

②.图像平面,物体聚焦成像的平面。

③.相机焦距,远处平行光经过相机模型后汇聚于一点,此点与相机平面的距离称为焦距,显然焦距可以是负的。

④.投影中心,物体发出的所有光线汇聚的点。

针孔相机模型很简单,在相机平面只有一个点可以通过光线。 对于理想的针孔相机,焦距是图像到针孔平面的距离。

 假设针孔相机模型焦距为f,物体高度为H,物体离针孔平面距离为x,则成像的高度h,可用公式来表示:

h=f*\frac{H}{x}

假设物体上某个点的坐标为(X,Y,Z),其中Z为物体到小孔的距离,则物体经过相机模型后对应的成像点的坐标为(f*X/Z,f*Y/Z),如果考虑到相机模型并非真正的平面,而是具有一定的厚度,以及焦距并非是均匀的,那么对应成像点的坐标为(f*X/Z+Cx,fy*Y/Z+Cy)

针孔相机模型是最简单的相机模型,那么为什么在现实生活中这种相机并不存在呢?这是因为前面所讲的,物体所发出的光线或者说反射的光线,只有一条能够穿过小孔,就会显得非常暗,做过实验的应该就知道。所以需要很长时间的曝光才能得到足够亮度的图像。

射影几何

其实真正的相机模型就是将物理世界中的点(X,Y,Z)映射到平面上的点(x,y)处,显然在物理世界中的点在成像平面上有且仅有一个点与之对应,而成像平面上的点可能对应物理世界中的多个点。这种映射也叫射影变换。

采用射影变换,我们可以将点(X,Y,Z)转为点(x,y),首先将点(x,y)转为齐次坐标:(x,y,1)。则射影变换可以用一个3x3的矩阵M表示,满足(x,y,1)=Mx(X,Y,Z)。例如针孔摄像头:

\frac{1}{Z}\begin{bmatrix} f_{x} &0&c_{x}\\ 0 &f_{y} &c_{y}\\ 0&0 &1 \end{bmatrix}

在OPenCV中,齐次坐标和非齐次坐标的转换非常简单,使用函数cv2.converPointsToHomogeneous和cv2.convertPointsFromHomogeneous即可。参数为输入和输出向量。

import numpy as np
import cv2
#设置坐标
point=np.array([[1,2]])
#转为齐次坐标
pointth=cv2.convertPointsToHomogeneous(point)
#转为非齐次坐标
pointfth=cv2.convertPointsFromHomogeneous(pointth)
#输出
print('齐次坐标',pointth)
print('非齐次坐标',pointfth)

 透镜

透镜的工作原理是使得同一点发出的不同光线,在经过透镜后汇聚到同一点,或者更加发散。透镜成像公式非常简单,假设物距为u,焦距为f,则像距v:

\frac{1}{u}+\frac{1}{v}=\frac{1}{f}

透镜一般分为凸透镜和凹透镜,其均不是真正的球面。凸透镜中间厚,两边薄。 物体放在焦距之外,对光线有汇聚作用,可在凸透镜另一侧成倒立的实像。物体放在焦距之内,在凸透镜的另一侧成倒立放大的虚像。凹凸镜则是中间薄,两边厚,对光线有发散作用,所成的像为缩小的虚像。

透镜畸变

现实中是不存在完美的透镜的,所以我们把真实的透镜与完美透镜的差异叫做透镜畸变。畸变导致的结果是远处的平行光经过透镜后不再汇聚于一点。透镜畸变分为径向畸变和切向畸变,前者由于透镜的厚度和形状不均匀导致,而后者往往是由于相机 在组装过程中造成的。

径向畸变往往在透镜的边缘附近产生,对应不完美的透镜,远离中心处的光线比靠近透镜中心处的光线更加弯曲,导致“鱼眼”效应。

径向畸变很小,对于一般的相机,如果要对径向畸变进行矫正,我们可以在r=0处进行泰勒展开,然后对成像位置x进行矫正:

x=x*(1+c_{1}r^{2}+c_{2}r^{4})

切向畸变往往由于透镜平面与成像平面不平行导致。切向畸变 导致的结果是物象比例失真。

 

 修正:

x=x*(1+c_{1}r^{2}+c_{2}r^{4})

当然,除了这些,还有其他的畸变种类,不过相比径向和切向畸变,对结果影响较小。所以一般不处理,但是好的摄像机肯定会处理。

光流

在现实生活中,物体和相机一般都是运动的,这也导致了很多的图象是视频类的,为了追踪物体或相机,研究者提出了光流的概念。光流将匹配不同图像中的相同像素,光流的理想输出是两幅图像中每个像素的速度关联,也叫做位移向量。光流分为稠密光流和稀疏光流,区别在于,前者匹配一幅图像中的所有点,而后者只匹配一部分点,这些点一般都是容易跟踪的点,例如角点。由于稀疏光流的成本比稠密光流低,所以常使用稀疏光流。

稀疏光流

 稀疏光流算法也叫Lucas-Kanade稀疏光流算法,他计算围绕某些特殊点的局部窗口信息。缺点是当两幅图之间对应点移动过大时,无法在局部窗口中找到,这也导致了金字塔Lucas-Kanade算法的发展。

此算法基于3个基本假设。

①.亮度恒定,认为在整个场景中的平均亮度不变或者说像素的值不变,知识位置改变了。

②.运动缓慢,灵符图像之间的运动幅度很小。

③.空间一致性,对于同一物体,在两幅图像中的大小和形状应该基本一致。 

首先用I(x,t)来表示t时刻,在x位置的像素,显然,由于物体与相机的移动,x也是与时间有关的函数x(t),那么第一个假设就是I(x,t)对时间的偏导数为0.即

\frac{dI}{dt}=0

由第二个条件可的,可以认为在短时间内,物体速度基本是不变的。 

\frac{dI}{dt}=\frac{\partial I}{\partial x}\frac{\partial x}{\partial t}+\frac{\partial I}{\partial t}

于是就得到一维情况下的光流速度方程为:

v=-\frac{I_{t}}{I_{x}}

如果上述的条件均不满足,那么我们该怎么做?就可以直接使用迭代的方法(也叫牛顿法)求解,每次使用上一次的速度作为下一次的初值,然后重复计算,直至收敛。一般5次左右即可结束。

二维的情况和一维类似,将速度分解为x方向上的分量v_{x}与y方向上的v_{y},于是有;

I_{x}v_{x}+I_{y}v_{y}+I_{t}=0

这里需要使用最后一个假设,即窗口内的像素的移动的规律是相同的。

假设使用3*3的窗口,在当前像素周围3x3窗口内计算其运动:

\begin{bmatrix} I_{x}(p1) &I_{y}(p1) \\ ... ... &...... \\ I_{x}(p9)&I_{y}(p9) \end{bmatrix}\begin{bmatrix} v_{x}\\ v_{y} \end{bmatrix}+\begin{bmatrix} I_{t}(p1)\\ .... \\ I_{t}(p9) \end{bmatrix}=0

现在有了一个约束方程,只要像素不是恰好处于图像的边缘位置,就可以对其进行求解。使用最小二乘法:

min||A\begin{bmatrix} v_{x}\\ v_{y} \end{bmatrix}+b||

\begin{bmatrix} v_{x}\\ v_{y} \end{bmatrix}=-(A^{T}A)^{-1}A^{T}b

于是局部光流速度问题就解决了,但是当发生剧烈的运动需要尺寸变大时,效果就会很明显变差,所以我们使用金字塔Lucas-Kannade算法,首席那解决顶层光流,然后将其值作为下一层问题的初值,以此类推。由于不同层之间的图像的比例不同,所以即使每层都是用较小的窗口,也可以追踪到更长、更快的运动。利用函数cv2.calcOpticalFlowPyrLK()。

preving:前一帧图像。

nexting:后一帧图像。

prevpts:前一帧图像像素;

nextpts:后一帧图像像素;

status:是否发生相应的特征。

err:错误度量;

winsize:窗口尺寸;

maxlevel:最大深度;

criteria:算法结束条件;


import numpy as np
import cv2
#打开一个视频
cap=cv2.VideoCapture('F:\Image\\test.mp4')
ret,frame0=cap.read()
#转换为灰度图
gray0=cv2.cvtColor(frame0,cv2.COLOR_BGR2GRAY)
#获取图像中的角点
p0=cv2.goodFeaturesToTrack(gray0,mask=None,maxCorners=50,
                           qualityLevel=0.5,minDistance=5,blockSize=5)
mask=np.zeros_like(frame0)
while(True):
    ret,frame=cap.read()
    gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
    #计算光流
    p1,st,err=cv2.calcOpticalFlowPyrLK(gray0,gray,p0,None,winSize=(10,10),maxLevel=5,criteria=(cv2.TERM_CRITERIA_EPS,10,0.03))
    #选取跟踪点
    new=p1[st==1]
    old=p0[st==1]
    #画出轨迹
    for i,(new,old) in enumerate(zip(new,old)):
        #坐标值
        a,b=new.ravel()
        c,d=old.ravel() 
        #划轨迹曲线
        mask=cv2.line(mask,(int(a),int(b)),(int(c),int(d)),[0,0,255],2)
        frame=cv2.circle(frame,(int(a),int(b)),5,[0,0,255],-1)
    img=cv2.add(frame,mask)
    #显示图像
    cv2.imshow('frame',img)
    #按esc建退出
    k=cv2.waitKey(30) & 0xff
    if k==27:
        break
    #更新上一层的图像和追踪点
    gray0=gray.copy()
    p0=new.reshape(-1,1,2)
    #释放资源
cap.release()

稠密光流的另一个重要算法就是DualTV-L模型。首先定义能量损失,指的是前后帧强度的差:

E(x,u)=(I_{t+1}(x+u)-I_{t}(x))^{2}+a^{2}||\bigtriangledown u||^{2}

其中,I_{t}(x)表示t时刻图像在x处的强度,a是相对影响的权重系数,u是能量流向量。原先的Horn-Schunck算法在所有可能的流场中找到使E(x,u)最小的u,可以使用拉格朗日乘子法的到。而DualTV-L模型对能量公式进行了改进,将平方和变为直接求和。

E(x,u)=|I_{t+1}(x+u)-I_t(x)|+\lambda |\bigtriangledown u|

如此做的好处就是局部梯度收到的惩罚较小,在不连续的问题上效果更好。

在OPenCV中使用cv2.optflow.DualTVL1OpticalFlow_create()函数创建DualTV-L光流,然后使用flow.calc()计算光流。参数说明:

lambda:权重系数。

nscales:金字塔尺读数。

epsilon:停止准则。

跟踪

视频跟踪其实应用的非常广泛,比如监控跟拍,无人视频监控等,更高端点的就是战斗机的显示头盔,会跟踪敌方战机,为驾驶员提供帮助。这些功能的实现,都离不开算法。这里就简单的介绍一些算法。后面我会专门介绍跟踪算法。

跟踪主要分为两大类:生成模型和判别模型。目前的流行是判别模型:

经典的判别模型有Struck和TLD两种,原理非常简单。将钨锑矿作为正样本,将背景作为负样本,然后使用机器学习模型进行分类训练,之后将训练后的模型应用在后续帧上即可,相当于在每一帧做一个目标检测。

例如,行人检测用HOG特征+SVM分类器,Struck算法用到了类haar特征+SVM分类器。

生成类算法模型有卡尔曼滤波、粒子滤波和mean-shift等,其在当前帧对目标区域进行区域建模,然后在下一帧寻找与模型最相似的区域,这就与前面面讲的光流有关系了。

说到跟踪算法,就要提一下相关滤波。最经典的就是CSK、KCF、DCF、CN等算法。他们的具体工作方法和前面讲到的高斯滤波很相似,简单地说就是一个单层的卷积神经网络。

例如,我来一个分析;

假设原图像为f,卷积核为h,则输出图像为:

g=f*h;

两边同时进行傅里叶变换:

G=FH

最小二乘法优化:

min\sum(F_{i}H-G_{i})^{2}

求导得:

H=\frac{\sum F_{i}\bigotimes G_{i}}{\sum F_{i}\bigotimes F_{i}}

CSK,KCF算法将原本的单通道线性卷积(MOSSE)拓展为了Kernel卷积,并且加入正则化项,防止过度拟合。另外,KCF和CSK的基础上拓展了很多多通道的HOG特征,CN算法在CSK的基础上拓展了多通道颜色的特征。HOG是梯度特征,而CN是颜色特征,两者恰好互补。

好了,今天的内容就到这里了,拜拜了你嘞!

点赞加关注不迷路

猜你喜欢

转载自blog.csdn.net/qq_59931372/article/details/129571754