下载完整源码,点击进入: https://github.com/ethan-li-coding/PatchMatchStereo
欢迎同学们在Github项目里讨论,如果觉得博主代码质量不错,右上角star一下!感谢!

算法效果图镇楼:

上一篇博客主类中,我们介绍了算法的主类:PatchMatchStereo,基本上大家对头文件已经了解了,从本篇开始,我们进入算法的具体实现部分。

在第一篇编码教学博客框架中,我们有一张算法的架构图(下图),从图中我们可以清晰的看到算法的几个步骤,而第一个步骤,就是视差平面的随机初始化,本篇的内容也就是它!

【码上实战】【立体匹配系列】经典PatchMatch: (3)随机初始化

  • 视差平面的随机方法
  • 代码解析
  • 实验图

视差平面的随机方法

我们知道,一个空间平面,你可以用参数来表达: z = a x + b y + c z = ax+by+c z=ax+by+c,同时你也可以用平面上一个点 p ( x , y , z ) p(x,y,z) p(x,y,z)以及平面法线 n ⃗ ( n x , n y , n z ) \vec{n}(n_x,n_y,n_z) n (nx​,ny​,nz​)来表示: p l = ( p , n ⃗ ) pl=(p,\vec{n}) pl=(p,n )。在PMS中, z z z就代表视差 d d d,

如果选择第一种表达法,由于 a 、 b 、 c a、b、c a、b、c是无边界的,可以随机到任何值,因此 d d d就是无边界的,没有范围约束,这样显然不合适(要是随机个99999.9,那可怎么玩!)。

第二种表达法则可以限制 d d d的范围,用户可以设定视差范围为 ( d m i n , d m a x ) (d_{min},d_{max}) (dmin​,dmax​),对像素 ( x , y ) (x,y) (x,y),随机数生成器可在视差范围内随机视差值 d d d组成平面上点 p ( x , y , d ) p(x,y,d) p(x,y,d)。再随机一个单位法向量组成平面法线 n ⃗ ( n x , n y , n z ) \vec{n}(n_x,n_y,n_z) n (nx​,ny​,nz​)。这样平面可以表达成: p l = ( p , n ⃗ ) pl=(p,\vec{n}) pl=(p,n ),再换算成第一种方式,便于后面计算: a = − n x n z a=-\frac{n_x}{n_z} a=−nz​nx​​, b = − n y n z b=-\frac{n_y}{n_z} b=−nz​ny​​、 c = n x x + n y y + n z d n z c=\frac{n_xx+n_yy+n_zd}{n_z} c=nz​nx​x+ny​y+nz​d​。

代码解析

原理看懂了,我们再来看代码,先回顾下上篇关于视差平面的结构体定义:


/*** \brief 视差平面*/
struct DisparityPlane {PVector3f p;DisparityPlane() = default;DisparityPlane(const float32& x,const float32& y,const float32& z) {p.x = x; p.y = y; p.z = z;}DisparityPlane(const sint32& x, const sint32& y, const PVector3f& n, const float32& d) {p.x = -n.x / n.z;p.y = -n.y / n.z;p.z = (n.x * x + n.y * y + n.z * d) / n.z;}/*** \brief 获取该平面下像素(x,y)的视差* \param x       像素x坐标* \param y     像素y坐标* \return 像素(x,y)的视差*/float32 to_disparity(const sint32& x,const sint32& y) const{return p.dot(PVector3f(float32(x), float32(y), 1.0f));}/** \brief 获取平面的法线 */PVector3f to_normal() const{PVector3f n(p.x, p.y, -1.0f);n.normalize();return n;}/*** \brief 将视差平面转换到另一视图* 假设左视图平面方程为 d = a_p*xl + b_p*yl + c_p* 左右视图满足:(1) xr = xl - d_p; (2) yr = yl; (3) 视差符号相反(本代码左视差为正值,右视差为负值)* 代入左视图视差平面方程就可得到右视图坐标系下的平面方程: d = -a_p*xr - b_p*yr - (c_p+a_p*d_p)* 右至左同理* \param x        像素x坐标* \param y     像素y坐标* \return 转换后的平面*/DisparityPlane to_another_view(const sint32& x, const sint32& y) const{const float32 d = to_disparity(x, y);return { -p.x, -p.y, -p.z - p.x * d };}// operator ==bool operator==(const DisparityPlane& v) const {return p == v.p;}// operator !=bool operator!=(const DisparityPlane& v) const {return p != v.p;}
};

它的成员只有一个:

PVector3f p;

PVector3f是一个三维矢量结构体,在pms_types.h文件中同样有定义,这种最底层的结构体我们不细说,大家自己了解下,明白它有 x , y , z x,y,z x,y,z三个分量就好。在视差平面结构体中,成员变量 p p p就代表视差平面,它的三个分量 x , y , z x,y,z x,y,z代表视差平面的三个参数 a , b , c a,b,c a,b,c。

按照上面的说法,我们就是先随机一个视差 d d d,再随机一个单位法向量 n ⃗ \vec{n} n ,通过它俩构造视差平面。咱们看代码:


void PatchMatchStereo::RandomInitialization() const
{const sint32 width = width_;const sint32 height = height_;if (width <= 0 || height <= 0 ||disp_left_ == nullptr || disp_right_ == nullptr ||plane_left_ == nullptr || plane_right_ == nullptr) {return;}const auto& option = option_;const sint32 min_disparity = option.min_disparity;const sint32 max_disparity = option.max_disparity;// 随机数生成器std::random_device rd;std::mt19937 gen(rd());const std::uniform_real_distribution<float32> rand_d(static_cast<float32>(min_disparity), static_cast<float32>(max_disparity));const std::uniform_real_distribution<float32> rand_n(-1.0f, 1.0f);for (int k = 0; k < 2; k++) {auto* disp_ptr = k == 0 ? disp_left_ : disp_right_;auto* plane_ptr = k == 0 ? plane_left_ : plane_right_;sint32 sign = (k == 0) ? 1:-1;for (sint32 y = 0; y < height; y++) {for (sint32 x = 0; x < width; x++) {const sint32 p = y * width + x;// 随机视差值float32 disp = sign * rand_d(gen);if (option.is_integer_disp) {disp = static_cast<float32>(round(disp));}disp_ptr[p] = disp;// 随机法向量PVector3f norm;if (!option.is_fource_fpw) {norm.x = rand_n(gen);norm.y = rand_n(gen);float32 z = rand_n(gen);while (z == 0.0f) {z = rand_n(gen);}norm.z = z;norm.normalize();}else {norm.x = 0.0f; norm.y = 0.0f; norm.z = 1.0f;}// 计算视差平面plane_ptr[p] = DisparityPlane(x, y, norm, disp);}}}
}

前面几句代码是一些变量、参数的获取,自不必说。

首先来看看这个来自于std标准库的随机数生成器uniform_real_distribution。顾名思义,它是一个均匀的实随机数生成器,它的构造参数是一个范围,这意味着它可以在给定的范围内进行均匀随机,这个太适合我们的随机过程了,我们对视差范围内的视差都没有偏好,都是等概率随机,随意均匀随机数生成器很适合,后面我将视差分布图示给大家看看,看是不是均匀分布的。

接下来,我们看看给这个随机数生成器的范围。给视差随机生成器的范围是设定的最小最大视差,这没什么疑问。给法线的是{-1.0,1.0},这点其实也可以给其他值,最后我们会对随机的矢量进行单位化,所以范围长度不讲究,也可以是{-0.5,0.5},但要中心对称,你不能给个{-2.0,1.0}之类的范围。

最后,我们来看逐像素的随机过程。因为我们要随机两张图,所以我用计数n来控制左右图像,k=0是随机左图,k=1随机右图。

随机的方法我上面也描述了,首先随机视差 d d d:

// 随机视差值
float32 disp = sign * rand_d(gen);
if (option.is_integer_disp) {disp = static_cast<float32>(round(disp));
}
disp_ptr[p] = disp;

rand_d(gen)是随机器生成的视差值,disp是存储到图像视差数组里的值。这里有个sign,在左图的时候它是1,在右图的时候它是-1,这是因为代码中有一个设定:左图视差值和右图视差值互为相反数。这样的设定是为了让左影像当左视图进行传播及让右影像当左视图进行传播时,都满足 d = x 1 − x 2 d=x_1-x_2 d=x1​−x2​,使得即使图像对调都完全复用传播类,不用顾忌符号的问题。

option.is_integer_disp是PMS的一个参数,指定是否是整像素视差值,如果是整像素视差值,就把随机的小数转换为整数。

其次,随机法线 n ⃗ \vec{n} n 。

// 随机法向量
PVector3f norm;
if (!option.is_fource_fpw) {norm.x = rand_n(gen);norm.y = rand_n(gen);float32 z = rand_n(gen);while (z == 0.0f) {z = rand_n(gen);}norm.z = z;norm.normalize();
}
else {norm.x = 0.0f; norm.y = 0.0f; norm.z = 1.0f;
}

代码很简单,法线的每个分量都随机一个-1.0到1.0的小数值,最后对法线做一个单位化。这里需要注意的是法线的z分量不能为0,我们从上面的公式中也能看到,计算平面的参数的时候,n_z是分母,所以不能为0。代码的处理方式就是判断下是否为0,如果是就再次随机一个值,直到不为0(这很容易满足)。

option.is_fource_fpw是PMS的一个参数,指定是否是前端窗口模型(Fronto-parallel windows,这个模型我在PMS理论介绍第一篇介绍的很详细了,参见PMS:Slanted support windows倾斜支持窗模型)。在这个模型下,视差平面是固定的,且垂直于相机坐标系的Z轴,法线恒定为(0,0,1)。

最后,用视差和法线构造视差平面:

// 计算视差平面
plane_ptr[p] = DisparityPlane(x, y, norm, disp);

实验图

文字这么多,该来点图片解解乏了。我们在文件PatchMatchStereo.cpp中Match函数的实现体内,将迭代传播、一致性检查、视差填充步骤都注释掉,只保留了随机初始化,如下代码段:

bool PatchMatchStereo::Match(const uint8* img_left, const uint8* img_right, float32* disp_left)
{// 随机初始化RandomInitialization();// 计算灰度图//ComputeGray();// 计算梯度图//ComputeGradient();// 迭代传播//Propagation();// 平面转换成视差PlaneToDisparity();// 左右一致性检查if (option_.is_check_lr) {// 一致性检查//  LRCheck();}// 视差填充if (option_.is_fill_holes) {//    FillHolesInDispMap();}// 输出视差图if (disp_left && disp_left_) {memcpy(disp_left, disp_left_, height_ * width_ * sizeof(float32));}return true;
}

运行程序,左右视差图结果如下:

左影像 右影像 左视差图 右视差图

这就是随机初始化的结果,就是一团随机的麻点图,啥也看不出来,哈哈哈。可别说我代码有问题,原文也是如此:

后经多次迭代传播,才能得到最后的好效果视差图,放心,后面我会展示的。

我们再把视差图导入PhotoShop看下值分布:

去除随机波动影响,分布还是非常均匀的,这就保证了视差(法线)范围内的等概率随机,公平公正。

好了,本篇就到这吧,下一篇带给大家的是PMS的代价计算器Cost Computer,敬请期待!

下载完整源码,点击进入: https://github.com/ethan-li-coding/PatchMatchStereo.git
欢迎同学们在Github项目里讨论,如果觉得博主代码质量不错,给颗小星星,Follow 我!感激不尽!

码上实战系列

【码上实战】【立体匹配系列】经典PatchMatch: (1)框架
【码上实战】【立体匹配系列】经典PatchMatch: (2)主类
【码上实战】【立体匹配系列】经典PatchMatch: (3)随机初始化
【码上实战】【立体匹配系列】经典PatchMatch: (4)代价计算
【码上实战】【立体匹配系列】经典PatchMatch: (5)迭代传播
【码上实战】【立体匹配系列】经典PatchMatch: (6)后处理

博主简介:
Ethan Li 李迎松(知乎:李迎松)
武汉大学 摄影测量与遥感专业博士

主方向立体匹配、三维重建

2019年获测绘科技进步一等奖(省部级)

爱三维,爱分享,爱开源
GitHub: https://github.com/ethan-li-coding
邮箱:ethan.li.whu@gmail.com

个人微信:

欢迎交流!

关注博主不迷路,感谢!
博客主页:https://ethanli.blog.csdn.net

【码上实战】【立体匹配系列】经典PatchMatch: (3)随机初始化相关推荐

  1. 【码上实战】【立体匹配系列】经典SGM:(2)代价计算

    码上教学系列 [码上实战][立体匹配系列]经典SGM:(1)框架与类设计 [码上实战][立体匹配系列]经典SGM:(2)代价计算 [码上实战][立体匹配系列]经典SGM:(3)代价聚合 [码上实战][ ...

  2. 【码上实战】【立体匹配系列】经典SGM:(4)代价聚合2

    昔人已乘黄鹤去,此地空余黄鹤楼. 2020对武汉.对中国.对世界来说是异常艰难的一年.武汉壮士扼腕,封一城而救一国,引得八方救援,举国抗疫.中国人在灾难面前总是空前团结,勇往直前!中华民族几千年来从未 ...

  3. 【码上实战】【立体匹配系列】经典PatchMatch: (1)框架

    下载完整源码,点击进入: https://github.com/ethan-li-coding/PatchMatchStereo 欢迎同学们在Github项目里讨论,如果觉得博主代码质量不错,右上角s ...

  4. 【码上实战】【立体匹配系列】经典PatchMatch: (4)代价计算

    下载完整源码,点击进入: https://github.com/ethan-li-coding/PatchMatchStereo 欢迎同学们在Github项目里讨论,如果觉得博主代码质量不错,右上角s ...

  5. 【码上实战】【立体匹配系列】经典PatchMatch: (2)主类

    下载完整源码,点击进入: https://github.com/ethan-li-coding/PatchMatchStereo 欢迎同学们在Github项目里讨论,如果觉得博主代码质量不错,右上角s ...

  6. 【码上实战】【立体匹配系列】经典PatchMatch: (6)后处理

    下载完整源码,点击进入: https://github.com/ethan-li-coding/PatchMatchStereo 欢迎同学们在Github项目里讨论,如果觉得博主代码质量不错,右上角s ...

  7. 【码上实战】【立体匹配系列】经典AD-Census: (1)框架

    下载AD-Census完整源码,点击进入: https://github.com/ethan-li-coding/AD-Census 欢迎同学们在Github项目里讨论,如果觉得博主代码质量不错,右上 ...

  8. 【码上实战】【立体匹配系列】经典AD-Census: (6)多步骤视差优化

    同学们好久不见! 下载完整源码,点击进入: https://github.com/ethan-li-coding/AD-Census 欢迎同学们在Github项目里讨论! 在实战的上一篇,我们对AD- ...

  9. 【码上实战】【立体匹配系列】经典SGM:(5)视差优化

    千呼万唤始出来,犹抱琵琶半遮面. 抱歉让大家久等,最近事儿繁多,导致更新推迟,实在抱歉. 码上教学系列 [码上实战][立体匹配系列]经典SGM:(1)框架与类设计 [码上实战][立体匹配系列]经典SG ...

最新文章

  1. Kafka万亿级消息实战
  2. UVa10905 - Children's Game(贪心算法)
  3. android调频收音机代码,android 收音机 FM 驱动 hal层 框架层以及应用层代码
  4. java中interrupt,interrupted和isInterrupted的区别
  5. Linux下shell脚本指定程序运行时长
  6. Tensorflow源码解析2 -- 前后端连接的桥梁 - Session 1
  7. Linux学习总结(51)——25个Linux服务器安全小贴士
  8. 检验多重共线性matlab_异方差太难?检验通不过?横截面分析难题的十大暴击!...
  9. Oracle日期类型转换格式
  10. dedecms flag标签属性
  11. 北京交通大学计算机学院复试名单,北京交通大学计算机与信息技术学院考研复试分数线复试通知复试名单...
  12. Faster RER-CNN 论文笔记
  13. 北大清华联手开设通用人工智能实验班,「顶级AI科学家」朱松纯领衔
  14. 正则表达式re.S与re.M的区别
  15. iphone输入法换行_iphone打字怎么换行?iphone输入法换行教程
  16. unity开发 HTC vive手柄控制 手柄上各个按钮的点击事件
  17. 解决bug 起止时间相同 搜索不到相关数据
  18. 统计字符串中所包含的各个英文小写字符及其对应的数量。
  19. 百度鹰眼轨迹后台的移植
  20. LeetCode算法刷题目录 (Java)

热门文章

  1. UOJ#553【UNR #4】己酸集合【计算几何(圆内数点→半平面数点)】
  2. 网摘某大学生给专栏作家连岳写了一份信:
  3. 第2话 Mesh对象的属性和threejs中的两种相机
  4. [附源码]SSM计算机毕业设计闲置物品交易管理系统JAVA
  5. Jupyter notebook 安装、美化、自定义路径启动
  6. 威联通nas的ipv4+ipv6双栈https证书ddns折腾记录
  7. C# 斐波那契数列 递归算法_2018Oct
  8. 斐波那契数列的递归和循环算法
  9. 将yuv422二进制数据保存为JPG图片
  10. TFRecord简介,原理分析,代码实现?