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

算法效果图镇楼:

上一篇博客框架中,我们已经从最顶层的角度理清了整个算法的思路、框架、步骤,本篇开始我们就进入实质性的代码分析。

本篇的内容是PatchMatchStereo(后面简称PMS)的主类分析。

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

  • 主类 PatchMatchStereo
  • 公有函数
  • 私有函数
  • 成员变量

主类 PatchMatchStereo

主类,即PMS的实现类,我们以PatchMatchStereo 给类命名,

/*** \brief PatchMatch类*/
class PatchMatchStereo
{public:PatchMatchStereo();~PatchMatchStereo();
}

公有函数

PMS类的职责是匹配,所以设计 Match 成员函数为执行匹配的接口,给调用者调用,看注释便一目了然,传入图像,传出视差图,功能很清晰。

/**
* \brief 执行匹配
* \param img_left   输入,左影像数据指针,3通道
* \param img_right  输入,右影像数据指针,3通道
* \param disp_left  输出,左影像视差图指针,预先分配和影像等尺寸的内存空间
*/
bool Match(const uint8* img_left, const uint8* img_right, float32* disp_left);

为了匹配,它需要分配一些内存,预分配往往是提高效率的常规操作,可别总是需要的时候才分配,要记住内存分配那是要耗时的。举个例子,你需要一块和图像等大的内存块存储梯度,只要图像尺寸不变,你每次都是要那么大的内存块,完全没必要频繁的分配销毁、再分配销毁,一开始分配一块后就别还给系统了,自己拿着一直用一直爽!

因此设计 Initialize 初始化函数来给内部数组预分配内存;设计 Reset 函数在影像尺寸和算法参数修改时重新预分配。

/**
* \brief 类的初始化,完成一些内存的预分配、参数的预设置等
* \param width      输入,核线像对影像宽
* \param height     输入,核线像对影像高
* \param option     输入,PatchMatchStereo参数
*/
bool Initialize(const sint32& width, const sint32& height, const PMSOption& option);/**
* \brief 重设
* \param width      输入,核线像对影像宽
* \param height     输入,核线像对影像高
* \param option     输入,SemiGlobalMatching参数
*/
bool Reset(const uint32& width, const uint32& height, const PMSOption& option);

私有函数

以上只是上层的可开放接口,还有下层的算法步骤实现接口,它们是实现PMS各个步骤的一些子函数,对算法实现来说它们是真正的核心,根据PMS的步骤图,它们主要包括:

  1. 随机初始化 RandomInitialization
  2. 迭代传播 Propagation
  3. 一致性检查 LRCheck
  4. 视差填充 FillHolesInDispMap

还有一些其他的细枝末叶不用细说,例如计算梯度ComputeGradient、释放内存Release之类的,一看便懂。

它们统统归为私有函数,但调用者不一定关心算法的详细实现步骤,甚至可以完全隐藏它们。

private:/** \brief 随机初始化 */void RandomInitialization() const;/** \brief 计算灰度数据 */void ComputeGray() const;/** \brief 计算梯度数据 */void ComputeGradient() const;/** \brief 迭代传播 */void Propagation() const;/** \brief 一致性检查    */void LRCheck();/** \brief 视差图填充 */void FillHolesInDispMap();/** \brief 平面转换成视差 */void PlaneToDisparity() const;/** \brief 内存释放    */void Release();

成员变量

成员变量保存着算法需要在算法周期内完全持有的数据,数据是算法的内核,算法的运算过程便是在对数据不断的进行数学/逻辑运算及存取。

我们需要哪些数据呢?

  1. PMS算法参数
  2. 左右影像数据、尺寸等属性
  3. 影像的灰度、梯度数据(灰度是为了算梯度,梯度是为了算相似度)
  4. 聚合代价数据,存储像素的聚合代价值
  5. 视差图,存储像素的视差值
  6. 平面数据,存储像素的平面
  7. 误匹配像素,存储像素填充的对象

详见代码:

/** \brief PMS参数  */
PMSOption option_;/** \brief 影像宽     */
sint32 width_;/** \brief 影像高     */
sint32 height_;/** \brief 左影像数据  */
const uint8* img_left_;
/** \brief 右影像数据     */
const uint8* img_right_;/** \brief 左影像灰度数据   */
uint8* gray_left_;
/** \brief 右影像灰度数据   */
uint8* gray_right_;/** \brief 左影像梯度数据    */
PGradient* grad_left_;
/** \brief 右影像梯度数据   */
PGradient* grad_right_;/** \brief 左影像聚合代价数据  */
float32* cost_left_;
/** \brief 右影像聚合代价数据     */
float32* cost_right_;/** \brief 左影像视差图  */
float32* disp_left_;
/** \brief 右影像视差图   */
float32* disp_right_;/** \brief 左影像平面集  */
DisparityPlane* plane_left_;
/** \brief 右影像平面集   */
DisparityPlane* plane_right_;/** \brief 是否初始化标志 */
bool is_initialized_;/** \brief 误匹配区像素集 */
vector<pair<int, int>> mismatches_left_;
vector<pair<int, int>> mismatches_right_;

需要关注的是,成员变量的类型中,除了一些基础类型(sint32、float32之类的),还有几个陌生的类型:

  1. PMSOption,PMS的参数结构体
  2. PGradient,梯度结构体
  3. DisparityPlane,视差平面结构体

它们三个是代码里自定义的类型,定义成结构体那自然是为了方便,它们都放在文件 pms_types.h 中,我们看看它们的具体定义:

PMSOption结构体,它的成员是PMS算法的所有参数,调用者可以通过改变这些参数来让算法得到不同的结果,不同的数据也会对应着不同的参数,参数的存在让算法变得更灵活自由。

/** \brief PMS参数结构体 */
struct PMSOption {sint32    patch_size;         // patch尺寸,局部窗口为 patch_size*patch_sizesint32  min_disparity;     // 最小视差sint32   max_disparity;      // 最大视差float32  gamma;              // gamma 权值因子float32    alpha;              // alpha 相似度平衡因子float32 tau_col;            // tau for color    相似度计算颜色空间的绝对差的下截断阈值float32  tau_grad;           // tau for gradient 相似度计算梯度空间的绝对差下截断阈值sint32    num_iters;          // 传播迭代次数bool   is_check_lr;        // 是否检查左右一致性float32 lrcheck_thres;      // 左右一致性约束阈值bool    is_fill_holes;      // 是否填充视差空洞bool is_fource_fpw;      // 是否强制为Frontal-Parallel Windowbool is_integer_disp;    // 是否为整像素视差PMSOption() : patch_size(35), min_disparity(0), max_disparity(64), gamma(10.0f), alpha(0.9f), tau_col(10.0f),tau_grad(2.0f), num_iters(3),is_check_lr(false),lrcheck_thres(0),is_fill_holes(false), is_fource_fpw(false), is_integer_disp(false) { }
};

梯度结构体,保存着 x / y x/y x/y两个方向的梯度值,代码里采用的是Sobel这类带方向的边缘提取算法,所以梯度有两个维度。

/*** \brief 梯度结构体*/
struct PGradient {sint16 x, y;PGradient() : x(0), y(0) {}PGradient(sint16 _x, sint16 _y) {x = _x; y = _y;}
};

视差平面是一个较为核心的结构体,贯穿全代码,它可以通过视差和法线来构建,并包含以下功能:

  1. 获取像素(x,y)的视差
  2. 获取平面法线
  3. 在两个视图中相互转换

将视差平面设计成一个结构体会增加代码的可读性,因为代码中会频繁的获取像素的视差、较频繁的获取平面的法线,把他们都写成一个函数,让代码更加简洁和易懂。

/*** \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;}
};

好了同学们,本篇就到这吧,虽然篇幅较长,但是似乎文字并不多,对着代码来看,我想不会占用多少时间,咱们下篇来解读算法的具体实现代码,博主还会做一些实验,借助实验图来帮助大家加深理解。

同学们拜拜!

下载完整源码,点击进入: 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://blog.csdn.net/rs_lys

【码上实战】【立体匹配系列】经典PatchMatch: (2)主类相关推荐

  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: (6)后处理

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

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

    下载完整源码,点击进入: 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. 【TX2】英伟达Nvidia TX2连接蓝牙设备
  2. css中position的几个值
  3. 使用detours实现劫持
  4. android json.out,Android 之 json数据的解析(jsonReader)
  5. Postgresql之split_part()切割函数,取最后一部分
  6. OpenCV4每日一练day4:Mat类的创建、赋值、读取
  7. SQL计算两个日期之间的工作天数
  8. html5圆形提交按钮样式,HTML5 SVG带圆形进度条动画的提交按钮特效
  9. 判断两字符串是否为逆序
  10. 51nod 1577 线性基
  11. opencv 2 归一化函数normalize详解
  12. 订单系统需求分析说明
  13. matlab中表示矩阵的转置,注意Matlab中的矩阵转置(转)
  14. 一图看懂| 人工智能知识体系大全
  15. 安卓手机格式化怎么弄_安卓手机怎样进入格式化?
  16. 自动驾驶测绘资质的信息安全要求,真的来了
  17. ETAP仿真时移相变压器的设置
  18. 搭建Linux 编程工具-萝卜青菜各有所爱
  19. Linux解决Ubuntu中vi编辑方向键不能移动光标的问题
  20. 【CodeM初赛B轮】A 贪心

热门文章

  1. 达梦数据库JDBC连接池断开自动重连设置
  2. Python 安装requests库
  3. spring boot 微信 支付宝 集成 开源jm-pay sdk
  4. 记录idea整合git 创建分支 合并分支提交
  5. Call to undefined function bcmath()的解决方法
  6. ClownFish是什么?
  7. 信息安全服务资质-CCRC证书如何正确使用?
  8. Elasticsearch查询参数介绍
  9. Power BI(十一)Power Pivot常用DAX函数
  10. 水货手机需要注意的问题