高一高二的时候用了一年半设计过一套手持3D扫描系统,有数学模型和论文,还有数据处理软件。
下面贴点自己的设计呗~~ 之前还没有怎么宣传过,不混开源社区,其实做得不算非常完美毕竟是个人项目嘛,摸爬滚打
————————多图预警——————
V1.0图片(转盘式)
V1.0图片(转盘式)
V2.0图片(手持激光线扫描)
V2.0图片(手持激光线扫描)
————————分割线————————
整个系统的一个总体思路是
渲染的逆向工程 之前在论文里写了关于可变焦3D扫描的研究,包括物理设计与数学实现,已完成的部分的物理和编程实现。其主要流程是,在较暗环境中,用激光线(结构光)进行照射与拍照后要对数据进行处理。主要处理流程包括:
(1)对图片的采样计算获取2D点;
(2)从2D信息中生成3D顶点;
(3)标识点的识别分析并计算出当前照片相机的部分刚体变换信息;
(4)坐标变换;
(5)网格重构 。
运用了光学规律与定律、传统几何、线性代数、计算机三维图形学理论等来设计了一个从现实世界的物体转化为3D数字模型文件的系统,在理论上已经实现了一个手持、可变焦的3D扫描仪。物理实现上主要的传感器仅为重力传感器。
所以其实设计上,只用了单反,红色激光笔,重力传感器,还有一个标识结构。
外国在理论和实物上已有比较成熟的发展。其中已发展出了民用的扫描仪,美国3D打印机厂商MakerBot CEO布瑞·佩蒂斯(Bre
Pettis)在SXSW互动大会上展示了3D扫描仪的原型品Digitizer.此扫描仪的价格在1800美元左右,但这个是转盘式的扫描仪,虽然没有
手持扫描仪的方便,而且表面信息容易获取不全,但是和国内四五位数的价格比起有着较大的优势。而且相对与转盘式等扫描仪,手持扫描仪因为其更为一般化的算
法和技术,价格也是异常昂贵。
所以了喔= =想着自己开发玩玩。试一下嘛学学东西。
然后就一发不可收拾了。
我是做过两个版本的,V1.0是物体放在转盘式上扫描,这样会容易产生盲区,因为有些地方是拍不到而且是激光照不到的。停滞了一段时间学线性代数和计算机三维图形学之后,再继续开发第二版。
不说废话了。
一、局部空间(Local Space)
无论是哪个版本,第一个任务就是升维。但是很多人就会觉得奇怪,为什么从一张二维的图片可以凭空生成三维的坐标???嗯嗯嗯确实不可以凭空生成三维点,因为信息丢失了就不可能再找回来了。所以要生成一个三维点就需要更多的信息。
第一步,就是要生成局部坐标。我们这里定义的局部坐标,就是在相机参考系内,或者说,站在观察者的角度看问题测出来的坐标。一步步来嘛
来讲讲我设计的扫描结构(感觉市面也有类似的,有点启发的)。看俯视图,先要一台单反(其他也可以,我家刚好有单反,还能顺便做变焦扫描仪,平衡了扫描尺寸和精度的矛盾),然后放红色激光,在激光头的出口处摆一个玻璃柱,让激光线变为激光面(可以在墙上投出一条竖线),而且激光面平行于主光轴,一开始的时候是垂直于地面
下面是侧视图。
所以我设计就是激光面到主光轴的距离是确定而且是已知的,记为CamToLight,单位mm。
我们还要知道相机的参数如视场角,拍照的像素尺寸
事实上,照在墙上是直直的一条激光线,照在凹凸不平的物体上就不是了,起码在照片里面不是。
上图就是了,一条激光线,照到盒子上,拍照,pia,分成了两条。为什么会这样??怎么定量分析???
计算机图形学里有一个概念叫视截体,这是非广角Camera的一种理想模型
这说明了相机是进行透视投影成像的。
所以我们可以根据这个继续下去。
看俯视图。
AB:
主光轴所在直线上的线段,之后延伸为光心到虚拟背景板的距离,命名为“标准深度”(Standard Depth,瞎编)。实验证明这个量无物理意义,这个“标准深度”,即到虚拟(假想)背景板的距离,是可正可负的,所谓的背景板只是选用一种标准,是想象出来的,无论是否有真实的背景板。实际上采用多远的背景板,测量点到光心的距离都是不变的,变的只是测量的深度。
DF=BC:红激光线到主光轴的距离
CD:物体到背景板的距离,命名为深度
GH:视截面的长度,D在此视截面上。
IJ: 视截面的长度,C在此视截面上。
∠JIA:记作∠θ,可视角范围
然后我们把目光从世界空间转到屏幕空间。要结合成像才能推出公式。
P预:“基准线”在屏幕上的X坐标,这个基准线与图片横边垂直平分线的距离,与上文提到的“标准深度”有关,原因显而易见。
P实:实际成像点在屏幕上的X坐标
PictureHeight:照片纵向像素数
PictureWidth:照片横向像素数
算法基本思想(由偏移得高度):照片上偏移的比例等于实际偏移的比例
我们把上面公式编程成函数,名字叫GetDepthFromOffset(自己瞎编)。这条公式是第一部分的基础,作用是根据紅激光在成像上的偏移测出它到 ”背景板“的距离,相当于Z坐标。再结合成像的xy坐标,我们就可以算出坐标的xyz分量。(用左手系,y轴指向天,z轴往前插,x轴向右)
但是,xyz的实际三维坐标(局部空间)中还是要计算的。
LocalX:绝对值为CamToLight,符号由激光在主光轴的左右边决定。摄像机正交基x轴正方向朝着视角的右边,z轴是视向量。
LocalY:
实际上,Local Y坐标还和LocalZ坐标有关,所以实际计算的时候是先要计算Z坐标的。如图,
EF :长度为R,某一视截面的实际高度,方便起见我们还假设EF就是背景板
A:物体上的一个可视点
OB :标准深度,焦点到背景板的距离
AC:测得深度,值为z
ED/EF:这是一个比值,值为k,可以根据成像获得,即 (屏幕上端到A点成像的距离) / (屏幕高度),这个应该要用像素数去计算。
d:标准深度
z:到背景板的距离
LocalZ:值为StandardDepth - GetDepthFromOffset 。
事实上到这里,我们已经可以生成图像中像素点的局部三维坐标了。
二、提取激光线,生成二维点
看图应该也差不多明白了呗=。=设定一个颜色阈值,对每一行像素进行遍历,寻找颜色转折点,得到激光线的像素坐标区间。我就简单地对区间取个平均值,从而提取出每一行像素上激光线的大致X坐标。
三、世界变换
在计算机三维图形学里,有渲染流水线这一个说法(貌似叫render
pipeline?)。在进行三角形渲染之前都要进行顶点变换(Vertex
Transformation),有人叫这个是“顶点变换”三部曲,意思是通过WORLD,VIEW,PROJECTION三个矩阵把处于局部模型空间的
顶点转换到相机的正交投影空间,并且投影到“底片”上。这些东西偶像们
@Milo Yip@叛逆者 不知道是多少年前就在玩的。(我在想这个思路还是挺有趣的)
“渲染”(render)做的事是从三维数据生成图片。而3D扫描是一个
逆向工程(Reverse Engineering),所以我们可以借鉴渲染管线,进行一个逆向变换,转到世界空间中。这个世界空间是唯一的确定的。
为什么要确定一个世界坐标系????因为我们拍的图片分析后生成的顶点只有相对于相机参考系(局部空间)而言的坐标,而且理论上每张图片相机的位置,姿态角都不一样,所以才要对每张图片的所有顶点进行对应的变换,整合到一个坐标系下。
假设我们知道相机的姿态角(用重力传感器测,用Yaw-Pitch-Roll角度系统),和世界坐标,我们就可以用一个
仿射矩阵去实现变换。
用的是列向量,下标别太在意,和欧拉角混用一下。用这个刚体变换矩阵就可以实现顶点的整合。
四、标志点——光学解算,实现手持的重要一步
前面说:
假设我们知道相机的姿态角(用重力传感器测,用Yaw-Pitch-Roll角度系统),和世界坐标,我们就可以用一个仿射矩阵去实现变换。
其实有些量并不是那么好测。例如光心的世界坐标(就是相机位置),如果用加速度传感器,时间久了容易会有积累误差。(倒是我以前根本没想到,用加速度传感器可能可以用PID控制去减少误差,不过不是很会)。
所以那个时候设计了一个光学校正算法。基本思路是,充分利用已有的信息(图片中标志点的2D像素坐标,相机参数如视场角,重力传感器测出的俯仰角和翻滚
角)去除相机翻滚角和俯仰角的影响,预测出没有俯仰角和翻滚角时标志点的相对位置,从而得出相机的水平角(航向角)和世界坐标。需要有的物理结构是一个正方形框框,四个顶点分别放置一个颜色的灯。理想状态如下:
利用灯光颜色就可以确定哪个点对应哪个点。但是有个缺点就是OABC四个点必须全部都拍到(而且我没考虑干扰,去噪,因为根本不会= =所以用3DSMAX渲染出理想图片之后反过来用我的软件处理)
我们可以定义O为世界坐标系原点,OB为世界X轴,OA为世界Y轴。
此标识结构的作用有几个:
1,提供成像信息用以计算摄像机等效光心(摄像机位置)的世界坐标;
2,提供信息重力传感器不能测得的水平角(航向角);一开始我们要去除翻滚角影响。
不过实际上在屏幕空间旋转一下就校正好了~~= =爽(其实就是绕主光轴Z轴旋转一下)
接下来,我们利用图片上标志点的2D像素坐标在3D空间中引出一条“屏幕射线”(跟渲染引擎里的Picking射线类似)。
然后我们经过一系列巧妙的纠结,可以求出相机位置到两个标志点的距离。然后 向量+距离 就可以求出O,B两个点在这个空间下的局部坐标。绕X轴旋转一下便可以去除翻滚角影响。
在这个空间下,相机是相当于只有航向角的影响,在这你会知道相机到四个标志点OABC的相对坐标。所以简单的变换一下,绕Y轴旋转一下,对齐一下,就能得到相对坐标和航向角。
然后其实完整实现到这里就可以得到3D点云了。(不理会去噪这些问题= =)(图像分析不大会,激光线提取的不好,加点其他误差,搞得有点噪点)
五、网格重构(Mesh Reconstruction)
额,这种三维散乱点云的重构是一个大课题。有成熟算法不过也看不懂T T。 我就说说的我的小想法= =适合拓补简单,就是球那样的结构,胖乎乎的点云。
先把顶点投影到单位球上
像世界地图那样展开。
到这个带边界平面上。
最后进行2D的Delaunay三角剖分,得到三角形数据和邻接数据,最后映射回三维空间。
六、曲面细分
呵呵我连重构都没搞好细什么分
七、计算相机视场角
这个工序在顺序上是应该在流程的第一步。单反做取景器真的是大土豪= =。那就顺便用上单反镜头可以变焦这个功能。
然而在编程上怎么实现呢= =???因为你又没有佳能厂商的支持,做不了一个实时更新视角或焦距到电脑的自动化系统。所以用了一个小hack,在jpg文件中提取"EXIF“信息,引用百度百科
Exif是一种图像文件格式,它的数据存储与JPEG格式是完全相同的。实际上Exif格式就是在JPEG格式头部插入了数码照片的信息,包括拍摄时的光圈、快门、白平衡、ISO、焦距、日期时间等各种和拍摄条件以及相机品牌、型号、色彩编码、拍摄时录制的声音以及GPS全球定位系统数据、缩略图等。
你看,有焦距!!然后就可以拿焦距来计算视角,之后就可以生成三维点,然后继续下去
***不过还是一言难尽,传了论文到百度网盘,无聊可以看看= =
3D扫描.pdf_免费高速下载———————分割线——————
V2.0的扫描系统也是烂烂的,反而是V1.0我实现从扫描到打印(不过噪点还是多)。然而,
v2.0还没有集成自动化系统,别看我,我不会
(; ̄Д ̄)
在
开发算法和软件的时候都是用3dsmax渲染的理想图片来喂给我的程序处理。研究的时候在得不到正确的结果时真的是万分纠结,因为根本不知道是算法、还是
编程、还是实验数据、还是实验参数出了问题,可能是这里漏了个符号,那里居然没有用临时变量,局部Y坐标公式错了5次,标识点处理仰角的竖直边居然不平行
于屏幕竖直边,维基的欧拉角矩阵看得一头雾水,变换不对时又不知道是变换错了还是标识点识别错了或者是计算错了,实验时设定数据想当然想到一个以为和真实
情况等价的结构但是却并不等价......排错非常困难,当时也是够拼的。
编程,我都是用VB实现的呵呵呵呵呵呵呵呵,渲染引擎是支持vb的TrueVision3D。
纪念Vb时代:
高一做的游戏 还是VB党 心酸
————————————————————
那次去面对一堆Princeton,standford清华复旦的教授答辩真是免费好好地练了一把英语,虽然最后也有点跪在了英语上。第二天的清华的面试
也没有什么鸟用,成绩太差,教授们面面相觑。所以这个项目在升学上并没有什么鸟用,但是却让我学到了很多东西,很让我第一次接触到“学术”。
真心觉得科研是非常难的,而且很辛苦。所以我觉得我以后能做个工程师已经是非常满意了。
成绩不稳,高考跪惨了。今天录取短信发来了,靠着可怜的自招加分进了华南理工大学,也不错,起码不用出省= =
现在在做DirectX11的渲染引擎,不知道够不够能力T T。大学好好学吧。