所谓单目相机三维重建并非得到了图片的深度信息!!!!
而是由棋盘确定新的空间坐标点,然后返回到像素坐标系中(即图片中),让人看起来像三维的!
效果如图
本文章设置的xyz轴遵从右手定则
需要提前对相机进行标定,得到内参和畸变参数
标定的内参和畸变参数方法点击这里
import cv2
import numpy as np
import glob
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
if __name__ == '__main__':
checker = [8, 11] #设置角点个数,这里采用9*12的棋盘格,所以由8*11个角点
distance = 30 #设置棋盘格每一个小块的距离,这里是30mm
with np.load('D:\\ML\\Project_python\\my_code\\video_and_img\\checkerboard1.npz') as X:
#传入标定的结果,即内参和畸变参数
mtx, dist = [X[i] for i in ('mtx', 'dist')]
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
#建立世界坐标点
objp = np.zeros((checker[0]*checker[1], 3), np.float32)#建立8*11*1的0矩阵
objp[:, :2] = distance * np.mgrid[0:checker[0], 0:checker[1]].T.reshape(-1, 2)#将分成8*11的网格,不要z轴
axis = np.float32([[5*distance, 0, 0], [0, 5*distance, 0], [0, 0, 5*distance]]).reshape(-1, 3)
#设置每个坐标系,x,y,z在空间中占5个distance的长度
img = cv2.imread('D:\\ML\\Project_python\\my_code\\video_and_img\\two_plate\\camera0\\plate_field.jpg')
#这张图片需要是所标定的相机拍的棋盘格照片
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (checker[0], checker[1]), None)
# 精确查找角点
if ret == True:
corners2 = cv2.cornerSubPix(gray, corners, (11,11), (-1, -1), criteria)
# 精确查找角点
_, rvecs, tvecs, inliers = cv2.solvePnPRansac(objp, corners2, mtx, dist)
# rvecs 返回的是旋转矩阵,但是需要经过罗德里格斯变换才能称为我们熟悉的矩阵形式
# tvecs 返回的是平移矩阵
imgpts, jac = cv2.projectPoints(axis, rvecs, tvecs, mtx, dist)
# imgpts,坐标系点返回到像素坐标中的位置
img = draw(img, corners2, imgpts)
cv2.imshow('img', img)
k = cv2.waitKey()
cv2.destroyAllWindows()
与上面的原理一样,只需要修改axis就可以了,因为构成一个坐标轴,另外需要4个点就行了,而方框需要8个点
import cv2
import numpy as np
import glob
# 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
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
if __name__ == '__main__':
checker = [8, 11] #设置角点个数,这里采用9*12的棋盘格,所以由8*11个角点
distance = 30 #设置棋盘格每一个小块的距离,这里是30mm
length = 10 #设置一个坐标轴或者一个正方体变成占几个棋盘
with np.load('D:\\ML\\Project_python\\my_code\\video_and_img\\checkerboard1.npz') as X:
mtx, dist = [X[i] for i in ('mtx', 'dist')]
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
#建立世界坐标点
objp = np.zeros((checker[0]*checker[1], 3), np.float32)#建立8*11*1的0矩阵
objp[:, :2] = distance * np.mgrid[0:checker[0], 0:checker[1]].T.reshape(-1, 2)#将分成8*11的网格,不要z轴
# 由于opencv默认z轴遵守右手定则,所以棋盘格向下是z轴,做起正方体就不直观了,所以采用-length的形式,采用-z的形式
axis = distance * np.float32([[0, 0, 0],
[0, length, 0],
[length, length, 0],
[length, 0, 0],
[0, 0, -length],
[0, length, -length],
[length, length, -length],
[length, 0, -length]])
#axis = np.float32([[5*distance, 0, 0], [0, 5*distance, 0], [0, 0, 5*distance]]).reshape(-1, 3)
#设置每个坐标系,x,y,z在空间中占5个distance的长度
img = cv2.imread('D:\\ML\\Project_python\\my_code\\video_and_img\\two_plate\\camera0\\plate_field.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (checker[0], checker[1]), None)
# 精确查找角点
if ret == True:
corners2 = cv2.cornerSubPix(gray, corners, (11,11), (-1, -1), criteria)
# 精确查找角点
_, rvecs, tvecs, inliers = cv2.solvePnPRansac(objp, corners2, mtx, dist)
# rvecs 返回的是旋转矩阵,但是需要经过罗德里格斯变换才能称为我们熟悉的矩阵形式
# tvecs 返回的是平移矩阵
imgpts, jac = cv2.projectPoints(axis, rvecs, tvecs, mtx, dist)
# imgpts,坐标系点返回到像素坐标中的位置
img = draw(img, corners2, imgpts)
cv2.imshow('img', img)
k = cv2.waitKey()
cv2.destroyAllWindows()