目标
在本节中,
- 我们将学习如何利用 calib3d 模块在图像中创建一些 3D 效果。
基础知识
这将是一个小的章节。在上一节相机标定中,你已经找到了相机矩阵、畸变系数等。给定一个图案图像,我们可以利用以上信息来计算它的姿态,或者物体在空间中的位置,例如它是如何旋转的,它是如何位移的等等。对于平面物体,我们可以假设 Z=0,这样,问题就变成了相机如何放置在空间中才能看到我们的图案图像。所以,如果我们知道物体在空间中的位置,我们就可以在其中绘制一些二维图来模拟三维效果。让我们看看如何做到这一点。
我们的问题是,我们想在棋盘格的第一个角上绘制我们的三维坐标轴(X、Y、Z 轴)。X 轴为蓝色,Y 轴为绿色,Z 轴为红色。因此,实际上,Z 轴应该感觉垂直于我们的棋盘平面。
首先,让我们从之前的标定结果中加载相机矩阵和畸变系数。
import numpy as np
import cv2 as cv
import glob
with np.load('B.npz') as X
mtx, dist, _, _ = [X[i] for i in ('mtx','dist','rvecs','tvecs')]
现在让我们创建一个函数 draw,它接收棋盘格中的角点(使用 cv.findChessboardCorners() 获取)和 轴点 来绘制三维轴。
def draw(img, corners, imgpts)
corner = tuple(corners[0].ravel().astype("int32"))
imgpts = imgpts.astype("int32")
img =
cv.line(img, corner, tuple(imgpts[0].ravel()), (255,0,0), 5)
img =
cv.line(img, corner, tuple(imgpts[1].ravel()), (0,255,0), 5)
img =
cv.line(img, corner, tuple(imgpts[2].ravel()), (0,0,255), 5)
return img
void line(InputOutputArray img, Point pt1, Point pt2, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)
绘制连接两点的线段。
然后像之前的例子一样,我们创建终止条件、物体点(棋盘格中角点的 3D 点)和轴点。轴点是用于绘制轴的三维空间中的点。我们绘制长度为 3 的轴(单位将以棋盘方格大小为单位,因为我们基于该大小进行标定)。因此,我们的 X 轴从 (0,0,0) 绘制到 (3,0,0),Y 轴也一样。对于 Z 轴,它从 (0,0,0) 绘制到 (0,0,-3)。负号表示它朝向相机绘制。
criteria = (cv.TERM_CRITERIA_EPS + cv.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 网格。如果找到,我们用子角点像素对其进行细化。然后,为了计算旋转和平移,我们使用函数 cv.solvePnPRansac()。一旦我们得到这些变换矩阵,我们就使用它们将我们的 轴点 投影到图像平面。简单来说,我们找到对应于三维空间中 (3,0,0)、(0,3,0)、(0,0,3) 中每一个点的图像平面上的点。一旦我们得到它们,我们就使用我们的 generateImage() 函数从第一个角点到这些点中的每一个点绘制线条。完成了!!!
for fname in glob.glob('left*.jpg')
if ret == True
ret,rvecs, tvecs =
cv.solvePnP(objp, corners2, mtx, dist)
img = draw(img,corners2,imgpts)
if k == ord('s')
void projectPoints(InputArray objectPoints, InputArray rvec, InputArray tvec, InputArray cameraMatrix, InputArray distCoeffs, OutputArray imagePoints, OutputArray jacobian=noArray(), double aspectRatio=0)
将 3D 点投影到图像平面。
bool solvePnP(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess=false, int flags=SOLVEPNP_ITERATIVE)
根据 3D-2D 点对应关系查找物体姿态。
bool findChessboardCorners(InputArray image, Size patternSize, OutputArray corners, int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE)
查找棋盘格内角点的坐标。
void imshow(const String &winname, InputArray mat)
在指定的窗口中显示图像。
int waitKey(int delay=0)
等待按键按下。
void destroyAllWindows()
销毁所有 HighGUI 窗口。
CV_EXPORTS_W bool imwrite(const String &filename, InputArray img, const std::vector< int > ¶ms=std::vector< int >())
将图像保存到指定文件。
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
从文件加载图像。
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0, AlgorithmHint hint=cv::ALGO_HINT_DEFAULT)
将图像从一个颜色空间转换为另一个颜色空间。
void cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria)
细化角点位置。
请参见下面的部分结果。注意每个轴的长度都是 3 个方格。
图像
渲染立方体
如果要绘制立方体,请修改 generateImage() 函数和轴点,如下所示。
修改后的 generateImage() 函数
def draw(img, corners, imgpts)
imgpts = np.int32(imgpts).reshape(-1,2)
for i,j in zip(range(4),range(4,8))
img =
cv.line(img, tuple(imgpts[i]), tuple(imgpts[j]),(255),3)
return img
void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar &color, int thickness=1, int lineType=LINE_8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point())
绘制轮廓线或填充轮廓。
修改后的轴点。它们是三维空间中立方体的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] ])
请看下面的结果。
图像
如果您对图形学、增强现实等感兴趣,可以使用OpenGL渲染更复杂的图形。