本文共 12139 字,大约阅读时间需要 40 分钟。
import numpy as npimport cv2import glob# termination criteriacriteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)objp = np.zeros((6*7,3), np.float32)objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)# Arrays to store object points and image points from all the images.objpoints = [] # 3d point in real world spaceimgpoints = [] # 2d points in image plane.images = glob.glob('*.jpg')for fname in images: img = cv2.imread(fname) gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # Find the chess board corners ret, corners = cv2.findChessboardCorners(gray, (7,6),None) # If found, add object points, image points (after refining them) if ret == True: objpoints.append(objp) corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria) imgpoints.append(corners2) # Draw and display the corners img = cv2.drawChessboardCorners(img, (7,6), corners2,ret) cv2.imshow('img',img) cv2.waitKey(500)cv2.destroyAllWindows()
一副图像和被绘制在上边的图案:
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)
42.2.3 畸变校正 现在我们找到我们想要的东西了,我们可以找到一幅图像来对他进行校正了。OpenCV 提供了两种方法,我们都学习一下。不过在那之前我们可以使用从函数 cv2.getOptimalNewCameraMatrix() 得到的自由缩放系数对摄像机矩阵进行优化。如果缩放系数 alpha = 0,返回的非畸变图像会带有最少量的不想要的像素。它甚至有可能在图像角点去除一些像素。如果 alpha = 1,所有的像素都会被返回,还有一些黑图像。它还会返回一个 ROI 图像,我们可以用来对结果进行裁剪。 我们读取一个新的图像(left2.ipg)
img = cv2.imread('left12.jpg')h, w = img.shape[:2]newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))
使用 cv2.undistort() 这是最简单的方法。只需使用这个函数和上边得到的 ROI 对结果进行裁剪。
# undistortdst = cv2.undistort(img, mtx, dist, None, newcameramtx)# crop the imagex,y,w,h = roidst = dst[y:y+h, x:x+w]cv2.imwrite('calibresult.png',dst)
使用 remapping 这应该属于“曲线救国”了。首先我们要找到从畸变图像到非畸变图像的映射方程。再使用重映射方程。
# undistortmapx,mapy = cv2.initUndistortRectifyMap(mtx,dist,None,newcameramtx,(w,h),5)dst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)# crop the imagex,y,w,h = roidst = dst[y:y+h, x:x+w]cv2.imwrite('calibresult.png',dst)
这两中方法给出的结果是相同的。结果如下所示:
mean_error = 0for i in xrange(len(objpoints)): imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist) error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2) tot_error += errorprint "total error: ", mean_error/len(objpoints)
import cv2import numpy as npimport glob# Load previously saved datawith np.load('B.npz') as X: mtx, dist, _, _ = [X[i] for i in ('mtx','dist','rvecs','tvecs')]
现在我们来创建一个函数:draw,它的参数有棋盘上的角点(使用cv2.findChessboardCorners() 得到)和要绘制的 3D 坐标轴上的点。
def draw(img, corners, imgpts): corner = tuple(corners[0].ravel()) img = cv2.line(img, corner, tuple(imgpts[0].ravel()), (255,0,0), 5) img = cv2.line(img, corner, tuple(imgpts[1].ravel()), (0,255,0), 5) img = cv2.line(img, corner, tuple(imgpts[2].ravel()), (0,0,255), 5) return img
和前面一样,我们要设置终止条件,对象点(棋盘上的 3D 角点)和坐标轴点。3D 空间中的坐标轴点是为了绘制坐标轴。我们绘制的坐标轴的长度为3。所以 X 轴从(0,0,0)绘制到(3,0,0),Y 轴也是。Z 轴从(0,0,0)绘制到(0,0,-3)。负值表示它是朝着(垂直于)摄像机方向。
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)objp = np.zeros((6*7,3), np.float32)objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)axis = np.float32([[3,0,0], [0,3,0], [0,0,-3]]).reshape(-1,3)
很通常一样我们需要加载图像。搜寻 7x6 的格子,如果发现,我们就把它优化到亚像素级。然后使用函数:cv2.solvePnPRansac() 来计算旋转和变换。但我们有了变换矩阵之后,我们就可以利用它们将这些坐标轴点映射到图像平面中去。简单来说,我们在图像平面上找到了与 3D 空间中的点(3,0,0),(0,3,0),(0,0,3) 相对应的点。然后我们就可以使用我们的函数 draw() 从图像上的第一个角点开始绘制连接这些点的直线了。搞定!!!
for fname in glob.glob('left*.jpg'): img = cv2.imread(fname) gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, corners = cv2.findChessboardCorners(gray, (7,6),None) if ret == True: corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria) # Find the rotation and translation vectors. rvecs, tvecs, inliers = cv2.solvePnPRansac(objp, corners2, mtx, dist) # project 3D points to image plane imgpts, jac = cv2.projectPoints(axis, rvecs, tvecs, mtx, dist) img = draw(img,corners2,imgpts) cv2.imshow('img',img) k = cv2.waitKey(0) & 0xff if k == 's': cv2.imwrite(fname[:6]+'.png', img)cv2.destroyAllWindows()
结果如下,看到了吗,每条坐标轴的长度都是 3 个格子的长度。
def draw(img, corners, imgpts): imgpts = np.int32(imgpts).reshape(-1,2) # draw ground floor in green img = cv2.drawContours(img, [imgpts[:4]],-1,(0,255,0),-3) # draw pillars in blue color for i,j in zip(range(4),range(4,8)): img = cv2.line(img, tuple(imgpts[i]), tuple(imgpts[j]),(255),3) # draw top layer in red color img = cv2.drawContours(img, [imgpts[4:]],-1,(0,0,255),3) return img
修改后的坐标轴点。它们是 3D 空间中的一个立方体的 8 个角点:
axis = np.float32([[0,0,0], [0,3,0], [3,3,0], [3,0,0], [0,0,-3],[0,3,-3],[3,3,-3],[3,0,-3] ])
结果如下:
import cv2import numpy as npfrom matplotlib import pyplot as pltimg1 = cv2.imread('myleft.jpg',0) #queryimage # left imageimg2 = cv2.imread('myright.jpg',0) #trainimage # right imagesift = cv2.SIFT()# find the keypoints and descriptors with SIFTkp1, des1 = sift.detectAndCompute(img1,None)kp2, des2 = sift.detectAndCompute(img2,None)# FLANN parametersFLANN_INDEX_KDTREE = 0index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)search_params = dict(checks=50)flann = cv2.FlannBasedMatcher(index_params,search_params)matches = flann.knnMatch(des1,des2,k=2)good = []pts1 = []pts2 = []# ratio test as per Lowe's paperfor i,(m,n) in enumerate(matches): if m.distance < 0.8*n.distance: good.append(m) pts2.append(kp2[m.trainIdx].pt) pts1.append(kp1[m.queryIdx].pt)
现在得到了一个匹配点列表,我们就可以使用它来计算基础矩阵了。
pts1 = np.int32(pts1)pts2 = np.int32(pts2)F, mask = cv2.findFundamentalMat(pts1,pts2,cv2.FM_LMEDS)# We select only inlier pointspts1 = pts1[mask.ravel()==1]pts2 = pts2[mask.ravel()==1]
下一步我们要找到极线。我们会得到一个包含很多线的数组。所以我们要定义一个新的函数将这些线绘制到图像中。
def drawlines(img1,img2,lines,pts1,pts2): ''' img1 - image on which we draw the epilines for the points in img2 lines - corresponding epilines ''' r,c = img1.shape img1 = cv2.cvtColor(img1,cv2.COLOR_GRAY2BGR) img2 = cv2.cvtColor(img2,cv2.COLOR_GRAY2BGR) for r,pt1,pt2 in zip(lines,pts1,pts2): color = tuple(np.random.randint(0,255,3).tolist()) x0,y0 = map(int, [0, -r[2]/r[1] ]) x1,y1 = map(int, [c, -(r[2]+r[0]*c)/r[1] ]) img1 = cv2.line(img1, (x0,y0), (x1,y1), color,1) img1 = cv2.circle(img1,tuple(pt1),5,color,-1) img2 = cv2.circle(img2,tuple(pt2),5,color,-1) return img1,img2
现在我们两幅图像中计算并绘制极线。
# Find epilines corresponding to points in right image (second image) and# drawing its lines on left imagelines1 = cv2.computeCorrespondEpilines(pts2.reshape(-1,1,2), 2,F)lines1 = lines1.reshape(-1,3)img5,img6 = drawlines(img1,img2,lines1,pts1,pts2)# Find epilines corresponding to points in left image (first image) and# drawing its lines on right imagelines2 = cv2.computeCorrespondEpilines(pts1.reshape(-1,1,2), 1,F)lines2 = lines2.reshape(-1,3)img3,img4 = drawlines(img2,img1,lines2,pts2,pts1)plt.subplot(121),plt.imshow(img5)plt.subplot(122),plt.imshow(img3)plt.show()
下面是我得到的结果:
45.2 代码
下面的代码显示了构建深度图的简单过程。import numpy as npimport cv2from matplotlib import pyplot as pltimgL = cv2.imread('tsukuba_l.png',0)imgR = cv2.imread('tsukuba_r.png',0)stereo = cv2.createStereoBM(numDisparities=16, blockSize=15)disparity = stereo.compute(imgL,imgR)plt.imshow(disparity,'gray')plt.show()
下图左侧为原始图像,右侧为深度图像。如图所示,结果中有很大的噪音。
转载地址:http://igbab.baihongyu.com/