opencv(十):单目相机“三维重建”

注意

所谓单目相机三维重建并非得到了图片的深度信息!!!!
而是由棋盘确定新的空间坐标点,然后返回到像素坐标系中(即图片中),让人看起来像三维的!
效果如图


本文章设置的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()


你可能感兴趣的:(Opencv)