热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

教程:使用iPhone相机和openCV来完成3D重建(第二部分)

本文为AI研习社编译的技术博客,原标题:Tutorial:Stereo3DreconstructionwithopenCVusinganiPhonecamera.PartII.作者
教程:使用iPhone相机和openCV来完成3D重建(第二部分)

本文为 AI 研习社编译的技术博客,原标题 :

Tutorial: Stereo 3D reconstruction with openCV using an iPhone camera. Part II.

作者 | Omar Padierna

翻译 | Disillusion、yaya牙牙、AIfresher

校对 | Disillusion     审核 | 邓普斯•杰弗     整理 | 菠萝妹

原文链接:

https://medium.com/@omar.ps16/stereo-3d-reconstruction-with-opencv-using-an-iphone-camera-part-ii-77754b58bfe0

查看第一部分,请点击:第一部分

教程:使用iPhone相机和openCV来完成3D重建(第二部分)

欢迎来到关于立体重建三部曲的第2部。在本节中,我们将讨论如何校准您的相机。

就像之前所提到的,照相机的镜头使你拍的照片失真。这在三维重建中是很麻烦的,所以我们需要纠正这个问题。在校正之前,我们需要知道我们所使用的相机的内部参数。 

有时这些参数是未知的,但幸运的是,OpenCV有一个专门针对这个的算法, 我们可以应用该算法开始我们的3D重建。

在整个设计过程中,我们会用到Python 3.7.1, OpenCV 3.4.4. 还有一些python第三方库: Numpy, Glob,tqdm和Pillow。因此,首先确保这些工具都已经安装好。

OpenCV中摄像机的标定过程是让计算机用棋盘图形扫描一幅图像,用不同的图像多次识别内部的角。 

教程:使用iPhone相机和openCV来完成3D重建(第二部分)

OpenCV提供的模型校准示例  

大多数关于相机校准的教程都是关于网络摄像机或其他摄像机的,因此它们是为分析每一个单独的帧(或多个帧)而定制的。

在我们的情况下,我们想校准的是手机摄像头,所以我们不能这样做。为了让计算机正确地校准手机摄像头,它需要几个相同模式的例子。

在处理视频流时,我们可以分析用户在摄像机前移动时的每一帧,直到移动的模型被多次检测到(这通常是一个任意的测量,但一般是至少有10次检测)。

因为我们不处理视频流,所以我们必须为相同的模型拍多张照片。

  步骤1. 得到一个棋盘图案.

前往这个链接,把这个棋盘图案打印在一张纸上。为求完美结果,确保每个正方形都是30毫米长(尽管这不是特别重要)。 

  步骤2. 把棋盘图案挂在白色的墙上.

当我运行这个算法时,我注意到背景中的东西越多,计算相机矩阵的时间就越长。所以我用了一面白色的墙,把棋盘图案贴在上面。确保它是完全平坦的。

教程:使用iPhone相机和openCV来完成3D重建(第二部分)

根据棋盘图案重新装饰房子  

  步骤3. 拍几张图案的照片.

这一步很重要,确保你拍的照片有很好的光照,并且图案是从不同的角度拍摄的。还要确保图案位于屏幕的不同部分。  

如果你只拍摄居中的照片,可能会发生错误的校准。确保你的照片有很多变化。  

教程:使用iPhone相机和openCV来完成3D重建(第二部分)

这是一个很好的关于如何拍摄图案的例子。来自乌特卡什·西纳。  

在Utkarsh Sinah的博客(AI shack)中可以找到关于相机校准和如何捕捉图案的丰富资源。如果你对C++校准相机的方法感兴趣,你应该去看看。

需要指出的是,并不是所有的图片都适合检测模式。而你事先很难知道哪些照片会起作用,因此拍尽可能多的照片是一个好主意。我拍了64张。

  步骤4:让我们开始代码部分

一旦你拍了足够多的照片,是时候写一些代码了(记住整个代码都在这里)。第一步是选择棋盘大小。虽然大小完全是任意的,但建议您选择一个不对称的大小(即矩形,而不是正方形)。此处,我选择了7X5。

import cv2
import numpy as np
import glob
from tqdm import tqdm
import PIL.ExifTags
import PIL.Image

#============================================
# Camera calibration
#============================================

#Define size of chessboard target.

chessboard_size = (7,5)

第二步是定义一个网格来存储所有点。存储的点需要是有序的,如:(0,0,0),(1,0,0),(2,0,0)….,(6,5,0)

#Define arrays to save detected points
obj_points = [] #3D points in real world space
img_points = [] #3D points in image plane

#Prepare grid and points to display

objp = np.zeros((np.prod(chessboard_size),3),dtype=np.float32)

objp[:,:2] = np.mgrid[0:chessboard_size[0],
0:chessboard_size[1]].T.reshape(-1,2)

因为我们要处理几个图像,所以我们可以使用glob迭代地打开它们。此外,由于OpenCv中的角点检测算法需要一些时间来处理,因此我们可以用tqdm包裹我们的循环,以了解距离处理上一个图像已经有多长时间了,还剩下多少图像没有处理。

#read images

calibration_paths = glob.glob('./calibration_images/*')

#Iterate over images to find intrinsic matrix
for image_path in tqdm(calibration_paths):

#Load image
image = cv2.imread(image_path)
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
print("Image loaded, Analizying...")
#find chessboard corners
ret,corners = cv2.findChessboardCorners(gray_image,
chessboard_size, None)

if ret == True:
 print("Chessboard detected!")
 print(image_path)
 #define criteria for subpixel accuracy
 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER,
30, 0.001)
 #refine corner location (to subpixel accuracy) based on criteria.
 cv2.cornerSubPix(gray_image, corners, (5,5), (-1,-1), criteria)
 obj_points.append(objp)
 img_points.append(corners)

在这个循环里,所有的魔法都在发生。加载图像后,我们必须将先其转换为灰度图,然后使用findchessboardcorners算法。

此算法将返回检测到的角点和一个名为ret的标志而且如果算法能够检测到模式,则返回true。

为了提高标定算法的精度,将角点位置细化到亚像素精度。在这种情况下,我们必须定义定位所需的标准。

我们采用了如果的标准定义:标准=(类型、迭代次数、精度)。在这个例子中,我们告诉算法我们关心迭代次数和精度(cv2.term_criteria_eps+cv2.term_criteria_max_iter),我们选择了30次迭代,精度为0.001。

cv2.cornerSubPix是一种专注于重新定位点的算法。它接收图像、角点、窗口大小、零区域和实际条件作为输入。窗口大小是搜索区域。

关注这个算法不是很重要,我只是决定对参数进行评论,因为大多数教程只是对这个算法进行了润色。如果想要了解有关其工作原理的更多信息,请查看此处。

分析完所有图片后,我们运行cv2.calibratecamera算法。这是输出相机参数的算法。该算法返回摄像机矩阵(k)畸变系数(dist)和旋转和平移矢量(rvecs和tvecs)。

#Calibrate camera
ret, K, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points,gray_image.shape[::-1], None, None)

#Save parameters into numpy file
np.save("./camera_params/ret", ret)
np.save("./camera_params/K", K)
np.save("./camera_params/dist", dist)
np.save("./camera_params/rvecs", rvecs)
np.save("./camera_params/tvecs", tvecs)

注意,我们将这些值保存到不同的numpy文件中。我之所以选择这样做是因为实用性。由于运行这个脚本需要一段时间,所以每次我们想要重建某个东西时都要执行它是不方便的(也是不必要的)。

因此,只需将所有内容保存到一个numpy文件中,并在以后加载就更容易了。为什么是numpy而不是xml或json?因为对于numpy文件,不需要解析数据。

要进行三维重建,我们真正关心的是3个参数:相机矩阵、畸变系数和焦距。焦距可以从相机矩阵中推导出来。

尽管如此,为了学习,我决定决定从EXIF数据中包含的图像中获取焦距信息。相机手机的焦距信息是保存在EXIF数据中的。

#Get exif data in order to get focal length.
exif_img = PIL.Image.open(calibration_paths[0])

exif_data = {
PIL.ExifTags.TAGS[k]:v
for k, v in exif_img._getexif().items()
if k in PIL.ExifTags.TAGS}

#Get focal length in tuple form
focal_length_exif = exif_data['FocalLength']

#Get focal length in decimal form
focal_length = focal_length_exif[0]/focal_length_exif[1]

np.save("./camera_params/FocalLength", focal_length)

Exif 数据可以用pillow解析成字典形式,但是,键值将以数字形式给出,这也是我们必须使用ExifTags模块才能将其转换为可读形式的原因。

最后,我们需要有一种方法来测量我们的校准有多精确。我们有两种方法可以做到这一点。视觉方式和数字方式。

数值方法包括计算投影点的总误差。如果您注意到,在脚本的开头,我们声明了两个数组,对象点(3d点)和图像点(2d点)。

这些是在校准过程中反复获得的。这里的目标是使用在校准循环中计算的旋转和平移向量将三维点投影到二维平面中。然后将这些新投影点(脚本中称为img_points2)与在计算循环中获得的图像点进行比较。

然后我们计算每个点的误差,得到平均值。此错误应尽可能接近0。在我的例子中,误差是0.44,比随机稍好。

视觉上的方法是对相机拍摄的图像(最好是显示一些曲线扭曲的模式之一)做画面扭曲消除。目标是用算法消除透镜畸变,如果它做得正确,那么你就有了一个好的标定。

教程:使用iPhone相机和openCV来完成3D重建(第二部分)

Utkarsh Sinah做的画面扭曲消除结果。由Ai Shack的Utkarsh Sinah提供。

如果您的错误太高,请确保您检测到棋盘至少10次,并确保图片是不同的。

我也必须警告你,你必须要有耐心,尤其是当涉及到大图像时。在我的例子中,校准算法需要1.5小时才能完成。

一旦标定完成,您就可以计算出视差图了,这是我们将在下一部分讨论的主题。

再见!

想要继续查看该篇文章相关链接和参考文献?

长按链接点击打开或点击底部【教程:使用iPhone相机和openCV来完成3D重建(第二部分)】:

https://ai.yanxishe.com/page/TextTranslation/1413

查看第一部分,请点这里

AI研习社每日更新精彩内容,观看更多精彩内容:雷锋网雷锋网(公众号:雷锋网)雷锋网

命名实体识别(NER)综述

杰出数据科学家的关键技能是什么?

初学者怎样使用Keras进行迁移学习

如果你想学数据科学,这 7 类资源千万不能错过

等你来译:

深度学习目标检测算法综述

一文教你如何用PyTorch构建 Faster RCNN

高级DQNs:利用深度强化学习玩吃豆人游戏

用于深度强化学习的结构化控制网络 (ICML 论文讲解)



教程:使用iPhone相机和openCV来完成3D重建(第二部分)


推荐阅读
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 使用正则表达式爬取36Kr网站首页新闻的操作步骤和代码示例
    本文介绍了使用正则表达式来爬取36Kr网站首页所有新闻的操作步骤和代码示例。通过访问网站、查找关键词、编写代码等步骤,可以获取到网站首页的新闻数据。代码示例使用Python编写,并使用正则表达式来提取所需的数据。详细的操作步骤和代码示例可以参考本文内容。 ... [详细]
author-avatar
嘟嘟仔2286768
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有