从0到1搭建一套属于你自己的高精度实时结构光3D相机(9):二维误差扩散法下的1位深度正弦条纹生成算法

1. 写在前面

在前面的博客中,介绍了结构光系统模型与逆相机法投影仪标定。在这篇博客中,博主将介绍二维误差扩散法下的二值正弦条纹生成算法。完成本篇博客的学习内容后,你将收获二维误差扩散法下的二值正弦条纹生成算法与代码实践经验。

本系列博客的完整项目代码皆位于博主的Github项目SLMaster👈

https://github.com/Practice3DVision/SLMaster

动动你的小指头给个Star⭐并follow博主吧!你的支持是博主不懈的动力!

关注公众号,每天推送不迷路!更多技术博客在公众号与你分享!

在这里插入图片描述

扫一扫关注公众号

2. 简单二值化方法下的1位深度正弦条纹生成

首先我们需要知道为什么要采用1位深度的正弦条纹?因为8位深度的图案会降低投影仪的最高投影速率,如果我们想将投影仪的投影速率完全发挥出来,我们必须采用1位深度的图案。详情可以见博主博客从0到1搭建一套属于你自己的高精度实时结构光3D相机(1):硬件搭建

我们先简单了解一下8位正弦条纹图案是如何生成的。 N N N步相移中的第 n n n n = 0 , 2 , … , N − 1 n = 0, 2,\dots,N-1 n=0,2,,N1)步相移条纹的强度编码可用通过下式进行表示:

I n ( x , y ) = A ( x , y ) + B ( x , y ) c o s [ ϕ ( x , y ) + 2 π n N ] I_n(x, y) = A(x, y) + B(x, y)cos \left [ \phi(x, y) + \frac{2\pi n}{N} \right ] In(x,y)=A(x,y)+B(x,y)cos[ϕ(x,y)+N2πn]

式中, ( x , y ) (x, y) (x,y)为像素坐标系下点坐标, ϕ \phi ϕ包裹相位且有 ϕ = x T ∗ 2 π \phi=\frac{x}{T}*2\pi ϕ=Tx2π A A A平均强度 B B B调制强度。为了使得条纹对比度正弦性皆较好,一般令 A = B = 127.5 A = B = 127.5 A=B=127.5

8位深度的正弦条纹图案通过上式逐像素编码即可,博主不再赘述。回到我们的问题出发点,我们想生成1位深度的正弦条纹图案,并且它的投影效果越接近于8位深度的正弦条纹图案越好。因此,我们后续的1位深度正弦条纹生成算法都将基于8位深度正弦条纹生成算法展开。 简单的可以理解为模仿8位深度正弦图案,模仿的越像越好,那么我们就把它作为我们的学习模板

为了达到这个效果,我们能很容易的想到给个灰度阈值,大于该灰度阈值的像素就令它等于1,小于的则为0,这就是简单二值化下的1位深度正弦条纹生成算法了,它的强度编码可以用下式说明:

g ( x , y ) = { 0 , g ( x , y ) < C 1 , g ( x , y ) ≥ C g(x, y) = \begin{cases} 0, \quad g(x, y) < C \\1 , \quad g(x, y) \ge C \end{cases} g(x,y)={0,g(x,y)<C1,g(x,y)C

式中, C C C为二值化阈值,一般令 C = 127.5 C=127.5 C=127.5。我们来看看经过简单二值化之后的1位深度正弦条纹效果,如图1所示:

在这里插入图片描述

图1. 简单二值化算法所生成的二值正弦条纹效果

是不是觉得很简单呢,但简单也有简单的代价,我们来看看它有什么不好地方。但在这之前,我们先来谈谈离焦投影,毕竟,我们需要通过数学建模的方式来模拟离焦投影后的条纹效果,所以我们得先知道离焦投影的数学模型。

离焦投影说白了就是焦距变了,从而将图像模糊以产生更多的灰阶。由于焦距变化而导致的模糊可以用二维高斯卷积来建模,它的密度函数为:

g ( x , y ) = 1 2 π σ 2 e − ( x 2 + y 2 ) 2 σ 2 g(x,y)=\frac{1}{2\pi\sigma^2}e^{-\frac{(x^2+y^2)}{2\sigma^2}} g(x,y)=2πσ21e2σ2(x2+y2)

式中, σ \sigma σ为标准差,在图像中可以将其简单的认为是高斯模糊半径,因此, σ \sigma σ越大图像越模糊。博主在这里给出以下有关 σ \sigma σ的结论:

  • σ \sigma σ越大,振幅越小,像素灰度值变化越平滑

振幅对应于图像的对比度(即振幅越大对比度越高);像素灰度值变化越平滑则其正弦性越好

因此,如果我们想要获得较好的条纹效果,那么就应该是采用较小的 σ \sigma σ,同时投影的条纹正弦性也比较好。通常,把 σ \sigma σ称作离焦核大小。

数学建模有了,那我们来模糊一下简单二值化下生成的1位深度正弦条纹的离焦投影效果吧!

如图2所示,可以看到简单二值化所生成的二值正弦条纹仍然需要较大的离焦核(大概31~41之间)以产生接近8位深度正弦条纹的效果。此外,似乎它的对比度也并不是很好!因此,我们需要对其进行改进!

在这里插入图片描述

图2. 简单二值化在不同离焦核下的效果

3. 二维误差扩散法下的1位深度正弦条纹生成

二维误差扩散法实质上是传统图像处理中的一种半色调处理技术,它通过将图像的灰阶范围进行缩小,其工作原理是在缩小灰阶的过程中,逐像素将误差扩散至邻近像素,使得图像与原图像接近。

博主在这里主要介绍FloydSteinberg所提出的误差扩散算法,这也是论文中表述应用最多的,它的卷积核形式为:

1 16 [ − # 7 3 5 1 ] \frac{1}{16}\begin{bmatrix}- &\# & 7\\3 & 5 &1 \end{bmatrix} 161[3#571]

式中,符号 − - 表示已被处理的像素(对其不进行任何操作), # \# #表示当前正在处理的像素。简单来说,我们的误差 e r r o r error error可以定义为:

e r r o r = g 1 b i t ( x , y ) − g 8 b i t ( x , y ) error = g_{1bit}(x,y) - g_{8bit}(x, y) error=g1bit(x,y)g8bit(x,y)

式中, g 1 b i t ( x , y ) g_{1bit}(x,y) g1bit(x,y)是通过简单二值化得到的灰度值。然后将这个误差按照卷积核所描述的形式扩散至其它像素,例如当前像素的右边像素扩散过程为:

g ( x + 1 , y ) = g ( x + 1 , y ) + e r r o r ∗ 7 16 g(x + 1,y) = g(x + 1,y)+error*\frac{7}{16} g(x+1,y)=g(x+1,y)+error167

其它的则与之同理。最后,再次采用简单二值化将经过误差扩散后的图片进行二值化。我们来看看它的具体效果,如图3所示。

在这里插入图片描述

图3. 二维误差扩散法效果(左:8位深度,右:1位深度)

是不是很有欺骗感!我们来看看它的离焦效果如何,如图4所示。

在这里插入图片描述

图4. 二维误差扩散法在不同离焦核下的效果

效果显著啊!仅仅在离焦核为5的状态下就差不多拉满了!更关键的是,它的对比度还高!

4. 代码实践与实验

其实想想原理就这么点内容,代码当然也很简单啦!它位于defocusMethod.hpp中的twoDimensionErrorExpand方法,让我们来看看如何的吧!

void twoDimensionErrorExpand(cv::Mat& img) {
    CV_Assert(!img.empty() && img.type() == CV_8UC1);

    const int rows = img.rows;
    const int cols = img.cols;
    //简单二值化阈值
    const float threshod = 127.5;
    //二维误差扩散法系数
    const float efficient[4] = { 7.0f / 16.0f, 5.0f / 16.0f, 3.0f / 16.0f, 1.0f / 16.0f};

    for (int i = 0; i < img.rows; ++i) {
            auto imgPtr = img.ptr(i);
            for (int j = 0; j < cols; ++j) {
            	//简单二值化并计算误差
                const int error = imgPtr[j] - (imgPtr[j] < threshod ? 0 : 255);
                //将误差扩散出去
                if (j + 1 < cols) {
                    imgPtr[j + 1] += efficient[0] * error;
                }

                if (i + 1 < rows) {
                    img.ptr(i + 1)[j] += efficient[1] * error;
                    if (j - 1 >= 0) {
                        img.ptr(i + 1)[j - 1] += efficient[2] * error;
                    }
                    if (j + 1 < cols) {
                        img.ptr(i + 1)[j + 1] += efficient[3] * error;
                    }
                }
            }
        }
	//再次将经过误差扩散后的图像简单二值化
    cv::parallel_for_(cv::Range(0, img.rows), [&](const cv::Range& range) {
        for (int i = range.start; i < range.end; ++i) {
            auto imgPtr = img.ptr(i);
            for (int j = 0; j < cols; ++j) {
                imgPtr[j] = imgPtr[j] < threshod ? 0 : 1;
            }
        }
    });
}

是不是很简单!我们来用博主编写的SLMaster软件生成一下,如图5所示。

在这里插入图片描述

图5. 使用SLMaster软件编码条纹

5. 总结

在这篇博客中,博主介绍了二维误差扩散法下的1位深度正弦条纹生成算法 ,在下一小节中,博主将开始介绍基于绝对相位的立体匹配方法。

本系列文章将持续更新,如果有等不及想要获得代码的小伙伴,可访问博主Github中的SLMaster项目,动动你的小手指,follow and star⭐!你们的关注是博主持续的动力!

Github:https://github.com/Practice3DVision
QQ群:229441078

公众号:实战3D视觉

在这里插入图片描述

扫一扫关注公众号

  • 39
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值