格雷码是一种特殊的二进制码,在结构光三维视觉中,常常被用于编码。比起我们常见的二进制码,格雷码具有相邻数字的编码只有一位不同的优点,这个优点对于解码而言十分重要,可以减少光解码的错误率。下面我们可以看下如何对结构光用格雷码编码,并如何对编码的结构光进行解码。
以5位格雷码为例,5位格雷码可以对32个像素位置进行编码,由之前的文章可以知道,我们在计算结构光三维重建时,只需要对结构光图片的一个方向编码,以常见的列格雷码为例,如图所示是5位列格雷码编码图片集。
图中我们对每个像素点进行了格雷码编码,每一张图片都代表了格雷码的某一位,以图片第1列为例,其格雷码编码为00001,则前4张图片中第一列的的格雷码编码的条纹都是黑色,代表0,而最后一张图片第一列的格雷码编码是白色,代表1.
格雷码的解码很简单,只要把投影的格雷码结构光再还原回十进制数字,我们就能知道相机中的像素点(uc,vc)对应的是投影图片的哪一列(up)了。想要得到一个好的三维重建结果,主要是对相机捕捉到的结构光进行准确的二值化操作,使得相机图片中每个像素点都能够正确解码。
常见的二值化操作有很多,最简单的是设一个全局灰度阈值,对灰度值高于阈值的像素点置1,对灰度值低于阈值的像素点置0。 或者利用局部自适应阈值对图片进行二值化操作等·。常见的利用每个像素点周边灰度信息的二值化操作,往往不能够满足格雷码结构光解码的二值化需求。因为使用结构光的环境往往是未知且复杂的。比如同样的结构光光强照在黑色物体表面的亮度比照射在白色物体表面的亮度要低。这意味着同样的光条纹在不同物体上获取的灰度值不同。由于不能提前预知环境中的物体表面信息,仅仅靠简单的利用像素点及其周周围灰度值的分布得出该像素点当前是来自结构光的亮条纹还是暗条纹是及其不准确的。
虽然由于环境光,以及物体表面材质的原因,一幅图像中像素的亮度(灰度值)通常是不均匀的,无法直接利用一张图片呈现的灰度信息对结构光解码,但是我们可以利用结构光系列图片来帮助获取像素点当前是亮条纹还是暗条纹的信息。以上图的格雷码编码为例,一个5位的格雷码编码需要投影5张结构光图片,假设有一个编码为 11011的格雷码条纹打在物体表面上,,在连续投影的5张格雷码图片中,物体表面被编码照射的位置既经历过暗条纹(编码为0)又经历过亮条纹(编码为1)。不过由于每张格雷码编码图片的光源编码不同,而且结构光光源在物体表面上形成的漫反射不同,当结构光编码图片不一样时,即使是同样经历亮条纹照射,该位置的亮度(灰度值)也是不同的。总的来说,对于同一个位置,可以近似认为其被亮条纹照射到的亮度总是高于其被暗条纹照射到的亮度。那么对于一个像素点在一张图片中的二值化可以用如下方法。
首先找到像素点在系列格雷码图片中最大的灰度值,记为Imax,并找到该像素点在系列格雷码图片中最小的灰度值,记为Imin。对于每张图片,我们可以这样计算,
In = (I-Imin)/(Imax-Imin)
其中I是该像素点在当前图片下的亮度,In可以看做是被归一化(normalize)后的灰度值,显然In的取值范围是[0,1]的。由上述公式可以看出,如果该像素点当前经历的是暗条纹,其值会接近最小的灰度值,In接近0,反之,In会接近1, 于是我们可以设计一个阈值,比如0.5,即当In大于0.5时,我们认为其值是偏亮的,此时像素经历的是亮条纹编码,反之,如果In小于0.5时,该像素此时经历的是暗条纹编码。值得注意的是,这个方法对于编码全为0的点,或者编码全为1的点,会有影响,因为编码全0的点和编码全1的点不会同时经历明暗变化。解决方法是避开这个编码,或者额外投图片让所有编码位置都能经历全0或者全1的过程。
上述方法的前提是近似认为被亮条纹照射到的亮度总是高于该位置被暗条纹照射到的亮度。实际上这个假设成立的前提是物体间没有漫反射,以及投影投射的光之间不会互相干扰。在一些特殊的位置,是有可能物体在经历亮条纹时其亮度值比其经历暗条纹时要暗。因为每张格雷码图片总体的光分布是不一样的,如上图的第一张和最后一张,第一张光主要分布在右边半边,而最后一张条纹光很细,是明暗相间的。不一样的格雷码图片的光强分布,会造成物体间漫反射的光强不一样,导致其经历暗条纹时周围物体间漫反射到的光远大于结构光光源发出来的光。实际上这是有可能的,比如该像素点处于某个物体对的阴影部分,一般情况下这样的点是没有光变化的,因为结构光本身无法直射到该位置,但是因为其周边物体漫反射的光有可能导致这个位置的光强发生剧烈变化。出现该位置经历暗条纹时光强比经历亮条纹时还要强。为了解决这类特殊的点,论文[2] 给出其思路如下:
如图所示,我们可以对每幅格雷码编码的条纹做一个逆向图,把原来编码条纹中的1的位置变为0,0的位置成1。这样我们把每一幅编码图片变成了一对编码图. 我们可以通过比较一对编码图中每个像素的灰度差值来判断其值为0还是为1。这个很容易理解,因为如果编码是亮条纹,则其逆向编码是暗条纹,则图片上编码是亮条纹的时候对应的点比编码是暗条纹(逆向)的时候对应的像素点更亮,即灰度值更高。反之亦然, 这样就可以简单地对相机图片上的结构光条纹解码了。
这个思路很简单,对大部分相机图片上的点,这样的解码效果就足够好了。但是仍然不能解决上述提到的问题。对于部分点,其经历暗条纹时仍旧可能比经历亮条纹时灰度值要更高。于是论文【2】在上述思路上,再增加了一部分想法,如果我们能求出当前灰度值中可能的来自结构光直接光源的成分的比例,就可以帮助我们辨别出该点是否经历暗条纹或者亮条纹。具体计算规则如下:
其中p指像素坐标,Lp 是像素在格雷码系列图中灰度最大值,Lp-是像素在格雷码系列图中灰度最小值。Ld可以看作是该像素点来自直接光源的灰度值,Lg可以看作来自其他光源(物体间漫反射和环境光)等的光源的灰度值。对于大部分情况,显然来自投影光的光强较强,Ld>Lg。但是对于部分点Ld<Lg, 比如处于物体阴影处的点,又比如处于周围反光物体较多点(来自周围反光太强了)。这部分点就是我们要解决的点,通过下面的规则,可以很好的解决点的二值化问题:
对每个像素点p和其灰度值I,有如下二值化规则 其中m是一个比较小的常数阈值,I_inv是条纹结构光的逆向图。
- I<m 该点的二值化认为是不确定点
- Ld>Lg 且满足 I>I_inv 该点二值化为1
- Ld>Lg 且满足I <I_inv 该点二值化为0
- I<Ld 且满足I_inv>Lg 该点二值化为0
- I>Lg 且满足I_inv<Ld 该点二值化为1
不符合以上所有条件的点为不确定点
有了以上的二值化方法,格雷码的编码和解码都不是什么太大的问题,解码后可以根据笔者之前文章提供的三维数据计算方法得到较为准确的物体三维信息。
在文章的最后,笔者想讨论下为什么格雷码编码比一般的二进制编码要好。我们知道格雷码最大的优点是十进制相邻数字编码只相差一位。那为什么这样就比普通二进制编码好呢?我们知道,解码最容易出错的点,往往是黑白相间的边界点,相机拍摄到的黑白相间的边界点往往是一个过渡灰度,很容易导致解码错误,所以相邻的数字间位数差别越多,其黑白相间的变化越多,自然出错的概率也更大。对于这一点,其实有一个图片很直观感受到,如果我们对每个像素进行格雷码编码,那么无论多少位编码,在其编码的最后一张图片上,条纹通常是非常细的,如果是普通二进制编码,其最后一张细条纹图片是每个像素之间编码都不一样,对一幅图片从左到右呈现010101…的变化,而如果用格雷码编码则会呈现0110011…的变化,明显格雷码编码其投影条纹更粗,更不容易解码出错。
本文解码方法参考论文:
[1] High-accuracy, high-speed 3D structured light imaging techniques and potential applications to intelligent robotics
[2] Robust Pixel Classification for 3D Modeling with Structured Light
上述内容,如有侵犯版权,请联系作者,会自行删文。