针孔相机模型是计算机视觉中广泛使用的相机模型。对于大多数应用来说,针孔照相机模型简单,并且具有足够的精神度。该相机从一个小孔采集射到暗箱内部的光线。在针孔照相机模型中,在光线投影到图像平面之前,从唯一一个点经过,也就是照相机中心。
照相机矩阵可以分解为:
这里,K是内参矩阵。[I|0]是外参矩阵
内参矩阵:
这里,图像平面和照相机中心间的距离为焦距 f 。当像素数组在传感器上偏斜的时候,需要用到倾斜参数 s ,也就是畸变参数。在大多数情况下,s 可以设置为 0,也就是说:
(x0 , y0 )就是光线坐标轴和图像平面的交点。因为光心通常在图像的中心,并且图像的坐标是从左上角开始计算的,所以光心的坐标接近于图像宽度和高度的一半。在这强调的是,唯一未知的变量是焦距 f 。
标定照相机是指计算出该照相机的内参数。同步标定内部参数和外部参数,一般包括两种策略:
光学标定: 利用已知的几何信息(如定长棋盘格)实现参数求解。
自标定: 在静态场景中利用 structure from motion估算参数。
通过空间中已知坐标的(特征)点 (Xi,Yi,Zi),以及它们在图像中的对应坐标 (ui,vi),直接估算 11 个待求解的内部和外部参数。
采用万能的最小二乘法解决上述问题!
增强现实是将物体和相应信息放置在图像数据上的一系列操作的总称。
实现增强现实我们需要用到PyGame和PyOpenGL两个工具包。
PyGame是非常流行的游戏开发工具包,它可以简单的处理现实窗口、输入设备、事件,以及其他内容。PyGame是开源的,可以从https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyopengl 下载
也可以直接pip install pygame安装。
PyOpenGL是OpenGL图形编程的Python绑定接口。OpenGL可以安装在几乎所有的系统上,并且具有很好的图形性能。OpenGL具有跨平台性,能够在不同的操作系统之间工作。我们可以在https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyopengl 下载
找到适合自己python版本的下载,我的是python3.6 的64位的。
然后把下载的.whl文件放到安装的python下lib文件下的site-packages文件夹下。
这样OpenGL和OpGame就安装好了。
生成视频:
#-*- coding=utf-8 -*-
import cv2
import numpy as np
def stevideo(fps,StevideoPath,OutputPituer):
#0为默认摄像头的编号
cap = cv2.VideoCapture(0)
sz = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
# 第三个参数则是镜头快慢的(即为帧数)
out = cv2.VideoWriter(StevideoPath, fourcc,fps,sz)
print ("OK!!!!!!")
while True:
ret,frame = cap.read()
if ret == True:
frame = cv2.flip(frame, 1)
a = out.write(frame)
cv2.imshow("frame", frame)
cv2.imwrite(OutputPituer, frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
break
cap.release()
out.release()
cv2.destroyAllWindows()
对生成的视频进行sift特征处理
import math
import pickle
from pylab import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import pygame, pygame.image
from pygame.locals import *
from PCV.geometry import homography, camera
from PCV.localdescriptors import sift
import cv2
def makesift(setin,loadvideo,output):
# compute features
sift.process_image(setin+'.jpg', setin+'.sift')
cap = cv2.VideoCapture(loadvideo)
i = 1
while(cap.isOpened()):
ret, frame = cap.read()
cv2.imwrite(output+str(i)+".jpg",frame)
cv2.imwrite(output+str(i)+".bmp",frame)
sift.process_image(output+str(i)+".jpg", output+str(i)+'.sift',)
cap.release()
cv2.destroyAllWindows()
图像增强现实实现:
先是得到了单应性矩阵,该单应性矩阵将一幅图像中的标记物上的点映射到另一幅图像中的的对应点。我们定义相应的三维坐标系,使标记物在X-Y平面上(Z=0),原点在标记物的某位置上。图像增强需要用到OpenGL和OpenGame,如果还没有下载的需要先下载。
import math
import pickle
from pylab import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import pygame, pygame.image
from pygame.locals import *
from PCV.geometry import homography, camera
from PCV.localdescriptors import sift
import cv2
def cube_points(c, wid):
""" Creates a list of points for plotting
a cube with plot. (the first 5 points are
the bottom square, some sides repeated). """
p = []
# bottom
p.append([c[0]-wid, c[1]-wid, c[2]-wid])
p.append([c[0]-wid, c[1]+wid, c[2]-wid])
p.append([c[0]+wid, c[1]+wid, c[2]-wid])
p.append([c[0]+wid, c[1]-wid, c[2]-wid])
p.append([c[0]-wid, c[1]-wid, c[2]-wid]) #same as first to close plot
# top
p.append([c[0]-wid, c[1]-wid, c[2]+wid])
p.append([c[0]-wid, c[1]+wid, c[2]+wid])
p.append([c[0]+wid, c[1]+wid, c[2]+wid])
p.append([c[0]+wid, c[1]-wid, c[2]+wid])
p.append([c[0]-wid, c[1]-wid, c[2]+wid]) #same as first to close plot
# vertical sides
p.append([c[0]-wid, c[1]-wid, c[2]+wid])
p.append([c[0]-wid, c[1]+wid, c[2]+wid])
p.append([c[0]-wid, c[1]+wid, c[2]-wid])
p.append([c[0]+wid, c[1]+wid, c[2]-wid])
p.append([c[0]+wid, c[1]+wid, c[2]+wid])
p.append([c[0]+wid, c[1]-wid, c[2]+wid])
p.append([c[0]+wid, c[1]-wid, c[2]-wid])
return array(p).T
def my_calibration(sz):
row, col = sz
fx = 758*col/640
fy = 752*row/480
K = diag([fx, fy, 1])
K[0, 2] = 0.5*col
K[1, 2] = 0.5*row
return K
def set_projection_from_camera(K):
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
fx = K[0,0]
fy = K[1,1]
fovy = 2*math.atan(0.5*height/fy)*180/math.pi
aspect = (width*fy)/(height*fx)
near = 0.1
far = 100.0
gluPerspective(fovy,aspect,near,far)
glViewport(0,0,width,height)
def set_modelview_from_camera(Rt):
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
Rx = np.array([[1,0,0],[0,0,-1],[0,1,0]])
R = Rt[:,:3]
U,S,V = np.linalg.svd(R)
R = np.dot(U,V)
R[0,:] = -R[0,:]
t = Rt[:,3]
M = np.eye(4)
M[:3,:3] = np.dot(R,Rx)
M[:3,3] = t
M = M.T
m = M.flatten()
glLoadMatrixf(m)
def draw_background(imname):
bg_image = pygame.image.load(imname).convert()
bg_data = pygame.image.tostring(bg_image,"RGBX",1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D,glGenTextures(1))
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,width,height,0,GL_RGBA,GL_UNSIGNED_BYTE,bg_data)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST)
glBegin(GL_QUADS)
glTexCoord2f(0.0,0.0); glVertex3f(-1.0,-1.0,-1.0)
glTexCoord2f(1.0,0.0); glVertex3f( 1.0,-1.0,-1.0)
glTexCoord2f(1.0,1.0); glVertex3f( 1.0, 1.0,-1.0)
glTexCoord2f(0.0,1.0); glVertex3f(-1.0, 1.0,-1.0)
glEnd()
glDeleteTextures(1)
def draw_teapot(size):
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glEnable(GL_DEPTH_TEST)
glClear(GL_DEPTH_BUFFER_BIT)
glMaterialfv(GL_FRONT,GL_AMBIENT,[0,0,0,0])
glMaterialfv(GL_FRONT,GL_DIFFUSE,[0.5,0.0,0.0,0.0])
glMaterialfv(GL_FRONT,GL_SPECULAR,[0.7,0.6,0.6,0.0])
glMaterialf(GL_FRONT,GL_SHININESS,0.25*128.0)
glutSolidTeapot(size)
def main(mm,barch,path,output):
l0, d0 = sift.read_features_from_file('im0.sift')
while mm<=barch :
l1, d1 = sift.read_features_from_file(path+str(mm)+'.sift')
matches = sift.match_twosided(d0, d1)
if sum(matches)<=1:
mm=mm+1
continue
try:
pygame.init()
win=pygame.display.set_mode((width,height),OPENGL | DOUBLEBUF)
pygame.display.set_caption("OpenGL AR demo")
ndx = matches.nonzero()[0]
fp = homography.make_homog(l0[ndx, :2].T)
ndx2 = [int(matches[i]) for i in ndx]
tp = homography.make_homog(l1[ndx2, :2].T)
model = homography.RansacModel()
H, inliers = homography.H_from_ransac(fp, tp, model)
K = my_calibration((640, 480))
cam1 = camera.Camera(hstack((K, dot(K, array([[0], [0], [-1]])))))
box = cube_points([0, 0, 0.1], 0.1)
box_cam1 = cam1.project(homography.make_homog(box[:, :5]))
box_trans = homography.normalize(dot(H,box_cam1))
cam2 = camera.Camera(dot(H, cam1.P))
A = dot(linalg.inv(K), cam2.P[:, :3])
A = array([A[:, 0], A[:, 1], cross(A[:, 0], A[:, 1])]).T
cam2.P[:, :3] = dot(K, A)
Rt=dot(linalg.inv(K),cam2.P)
draw_background(path+str(mm)+'.bmp')
set_projection_from_camera(K)
set_modelview_from_camera(Rt)
draw_teapot(0.05)
#pygame.image.save(win, output+str(mm)+'.jpg')
pygame.image.save(win,"test2//im"+str(mm)+'.jpg')
pygame.display.flip()
mm=mm+1
print (setin+str(mm)+'.sift ok!!!')
except ValueError:
print ('im'+str(mm)+" Error!!!")
mm=mm+1
continue
# width,height = 640,480
# mm = 2
# path = 'test/im'
# barch = 600
# setin = 'test/im'
# output = 'test2/im'
# main(mm,barch,path,setin,output)
函数*set_projection_from_camera(K)是将照相机参数转换成OpenGL中的投影矩阵,glMatrixMode(GL_PROJECTION) 将工作矩阵设置GL_PROJECTION,glLoadIdentity()将该矩阵设置为单位矩阵,这是重置矩阵的一般操作。然后,根据图像的高度、照相机的焦距以及纵横比,计算出视图中的垂直场。OPenGL的投影同样具有近距离和远距离的裁剪平面来限制场景拍摄的深度范围。我们设置近深度为一个小的数值,使得照相机能包含最近的物体,而远深度设置为一个大的数值,使用GLU的实用函数gluPerspective()来设置投影矩阵,将整个图像定义为视图部分。用glLoadMatrixf()函数的一个选项来定义一个完全的投影矩阵。
函数set_modelview_from_camera(Rt)*实现如何获得移除标定矩阵后的针孔照相机矩阵,并创建一个模拟视图;在这,我们首先切换到GL_MODELVIEW矩阵,然后重置矩阵。然后,由于需要旋转该物体,所以我们创建一个90度的旋转矩阵。由于估计照相机矩阵时,可能会出现错误或噪声干扰,所以我们确保照相机矩阵的旋转部分确实是个旋转矩阵。由于OpenGL中的坐标系有点不一样,所以我们将x轴翻转。
*draw_background(imname)函数中,首先使用P有Game中的一些函数来载入一幅图像,将其他序列化为能够在PyOpenGL中使用的原始字符串表示。然后重置模拟视图,清除颜色和深度缓存。
函数draw_teapot(size)*中,前两行激活了灯光效果和一盏灯。*glEnable()*函数用来激活OpenGL的一些特性。这些特性是使用大写常量来定义的。
接下来的就是深度测试被激活,使物体按照其深度表示出来,然后清理深度缓存。
# -*- coding: UTF-8 -*-
import os
import cv2
# 图片合成视频
def image2video(path,size,file_path):
filelist = os.listdir(path)#获取该目录下的所有文件名
fps = 24
fourcc = cv2.VideoWriter_fourcc(*'XVID')
video = cv2.VideoWriter( file_path, fourcc, fps, size )
for item in filelist:
if item.endswith('.jpg'):#判断图片后缀是否是.jpg
item = path + '/' + item
img = cv2.imread(item)#使用opencv读取图像,直接返回numpy.ndarray 对象,通道顺序为BGR ,注意是BGR,通道值默认范围0-255。
video.write(img)#把图片写进视频
print (item)
video.release()#释放
#image2video('F:\\Desktop\\data\\video_cut\\2',(1920,1080),'i.avi')#调用函数(图片路径,分辨率,要保存的视频名称)
# path = 'test2'
# image2video(path,(640,480),'test.avi')
结果展示: