重现3D photography on your desk
番茄炒蛋
这学期选修了计算机视觉,作为结课大作业,选了这个用移动的影子来重建三维物体形状的题目。最终的效果非常好惨不忍睹。大佬的论文在此3D photography。当然论文也是非常老了。
#所需要的工具一览
- 首先是摄像机的标定。标定法网上有很多,关键在于明白各个矩阵的含义。
-
Z c [ x y 1 ] = [ 1 d x 0 u 0 0 1 d y v 0 0 0 1 ] [ f 0 0 0 0 f 0 0 0 0 1 0 ] [ R T o t 1 ] [ X Y Z 1 ] Z_c\begin{bmatrix} x\\ y\\ 1\\ \end{bmatrix}=\begin{bmatrix} \frac{1}{dx}&0&u_0\\ 0&\frac{1}{dy}&v_0\\ 0&0&1 \end{bmatrix}\begin{bmatrix} f&0&0&0\\ 0&f&0&0\\ 0&0&1&0 \end{bmatrix}\begin{bmatrix} R&T\\ o^t&1\\ \end{bmatrix}\begin{bmatrix} X\\ Y\\ Z\\ 1\\ \end{bmatrix} Zc⎣⎡xy1⎦⎤=⎣⎡dx1000dy10u0v01⎦⎤⎣⎡f000f0001000⎦⎤[RotT1]⎣⎢⎢⎡XYZ1⎦⎥⎥⎤
- 将世界坐标系转换到相机坐标系。也叫摄像机的外参数矩阵。如果摄像机移动了,那这个矩阵就会发生变化。在本例中,摄像机、灯和物体都保持静止。对应的matlab标定参数的RotationMatrices和TranslationVectors,合成RT的话
\begin{bmatrix}R&T\\ 0^T& 1\end{bmatrix}
。 - 内参矩阵用于将相机坐标系到图像坐标系,进一步可得到像素坐标系。对应matlab中的IntrinsicMatrix,注意使用上面的公式的话,需要转置,如果再opencv进行编程,这里内参矩阵也同样需要转置之后才是对的。
- 考虑到用于标定时的照片和用于阴影扫描重建使用的照片大小可能不一样,比如重建并不需要这么多像素点,需要裁剪,那么久需要调整 u 0 , v 0 u_0,v_0 u0,v0这两个参数。这两个参数近似于图像长宽的一半。
- 标定完成后得到一个 ( X , Y , Z ) → ( u , v ) ∗ Z c (X,Y,Z)\rightarrow(u,v)*Z_c (X,Y,Z)→(u,v)∗Zc的关系。RT矩阵是3x4的也就意味着从右到左可以直接计算,但是从左到右还缺一个方程。仔细想一想, ( X , Y , Z ) (X,Y,Z) (X,Y,Z)这个点投影通过了透镜到达了相机平面是一个点,但是由这个点出发却是空间中的一条直线,当然需要一个约束条件。
-
- 图像二值化,找到阴影的边缘
- 阴影和光源共同构成了一个平面 ∏ ( t ) \prod(t) ∏(t),如果知道这个平面的参数,那么另外一个约束就找到了。当然图像上每个点对应的 ∏ ( t ) \prod(t) ∏(t)都不同,需要先找到这个对应关系。
- 如图,我设定阈值为110.
- 注意如果光源不是理想光源,这里的阴影可能会有平滑,更加难以区别。
- 注意不对图片处以滤波,以免平滑边缘。但对时间意义上可以滤波。
- 这里我们找两条图像上水平的参考线,高度随便取,但要求被测物体不能和这两条线重合。需要找到 t 0 t_0 t0时刻经过两条参考线的哪一个点,后面我们会用到。
- 标定光源坐标
- 如图,用一只铅笔,阴影底端和阴影顶端的 ( u 0 , v 0 ) , ( u 1 , v 1 ) (u_0,v_0),(u_1,v_1) (u0,v0),(u1,v1)可以在图片中用Photoshop手动定位出来。当然用直线检测然后定位也是可以。这两点都在桌面上,z坐标为零。可以解出x,y坐标,再利用铅笔的长度得到一条直线,有两张照片就能由交点得到光源的坐标。
- 平面的参数
- 同样, 参考线上的点由于在桌面上,x,y坐标可以直接解出来。三个点就可以得到平面的方程。这里只列出公式,相信一眼就能看懂。
- a ( x − x 1 ) + b ( y − y 1 ) + c ( z − z 1 ) = 0 a = ( y 2 − y 1 ) ( z 3 − z 1 ) − ( y 3 − y 1 ) ( z 2 − z 1 ) b = ( z 2 − z 1 ) ( x 3 − x 1 ) − ( z 3 − z 1 ) ( x 2 − x 1 ) c = ( x 2 − x 1 ) ( y 3 − y 1 ) − ( x 3 − x 1 ) ( y 2 − y 1 ) \begin{aligned} &a(x-x_1)+b(y-y_1)+c(z-z_1)=0\\ &a=(y_2-y_1)(z_3-z_1)-(y_3-y_1)(z_2-z_1)\\ &b=(z_2-z_1)(x_3-x_1)-(z_3-z_1)(x_2-x_1)\\ &c=(x_2-x_1)(y_3-y_1)-(x_3-x_1)(y_2-y_1) \end{aligned} a(x−x1)+b(y−y1)+c(z−z1)=0a=(y2−y1)(z3−z1)−(y3−y1)(z2−z1)b=(z2−z1)(x3−x1)−(z3−z1)(x2−x1)c=(x2−x1)(y3−y1)−(x3−x1)(y2−y1)
- 求解任意一点的
(
x
,
y
,
z
)
(x,y,z)
(x,y,z)
- 准备工作都已经做完。剩下的就是联立求解方程了。然后solve解完符号方程然后subs代入的过程实在是太慢。100*100都要花1个小时。考虑到这是一个线性方程组,可以化成 a x = b ax=b ax=b的形式,然后求逆相乘就完了。同样这里贴个结果。
- − d 1 = a 1 X + b 1 Y + c 1 Z − Z c u − d 2 = a 2 X + b 2 y + c 2 Z − Z c v − d 3 = a 3 x + b 3 y + c 3 Z − Z c m = a X + b Y + c Z ,    m = a x 1 + b y 1 + c z 1 \begin{aligned} -d_1&=a_1X+b_1Y+c_1Z-Z_cu\\ -d_2&=a_2X+b_2y+c_2Z-Z_cv\\ -d_3&=a_3x+b_3y+c_3Z-Z_c\\ m&=aX+bY+cZ,\;m=ax_1+by_1+cz_1\\ \end{aligned} −d1−d2−d3m=a1X+b1Y+c1Z−Zcu=a2X+b2y+c2Z−Zcv=a3x+b3y+c3Z−Zc=aX+bY+cZ,m=ax1+by1+cz1
- 左右两张深度图的合成
- 因为物体本身的阴影是不带有深度信息的,所以会带来干扰。将灯光放在左右两侧,分别进行深度图重建。 然后利用方差信息进行融合,方差大的权值小。这个后面有时间在做
最终的效果
后续的工作
效果不太好,由于使用的不是点光源,估计是找边缘的问题。后面可以改进。matlab速度依旧太慢,可以使用matlab与c\Fortran混合编程。一种是matlab输出数据到mat文件,然后c读入进行处理写到txt文件,matlab显示。另外一种是用matlab的mex。我选择的是后面一种,效果还行,速度快了不少。这些内容不少以后有空再写。搬砖了…