下载源码,点击进入: Github – PatchMatchStereo 算法效果图镇楼: 上一篇博客框架中,我们已经从最顶层的角度理清了整个算法的思路、框架、步骤,本篇开始我们就进入实质性的代码分析。 本篇的内容是PatchMatchStereo(后面简称PMS)的主类分析。 主类,即PMS的实现类,我们以PatchMatchStereo 给类命名, PMS类的职责是匹配,所以设计 Match 成员函数为执行匹配的接口,给调用者调用,看注释便一目了然,传入图像,传出视差图,功能很清晰。 为了匹配,它需要分配一些内存,预分配往往是提高效率的常规操作,可别总是需要的时候才分配,要记住内存分配那是要耗时的。举个例子,你需要一块和图像等大的内存块存储梯度,只要图像尺寸不变,你每次都是要那么大的内存块,完全没必要频繁的分配销毁、再分配销毁,一开始分配一块后就别还给系统了,自己拿着一直用一直爽! 因此设计 Initialize 初始化函数来给内部数组预分配内存;设计 Reset 函数在影像尺寸和算法参数修改时重新预分配。 以上只是上层的可开放接口,还有下层的算法步骤实现接口,它们是实现PMS各个步骤的一些子函数,对算法实现来说它们是真正的核心,根据PMS的步骤图,它们主要包括: 还有一些其他的细枝末叶不用细说,例如计算梯度ComputeGradient、释放内存Release之类的,一看便懂。 它们统统归为私有函数,但调用者不一定关心算法的详细实现步骤,甚至可以完全隐藏它们。 成员变量保存着算法需要在算法周期内完全持有的数据,数据是算法的内核,算法的运算过程便是在对数据不断的进行数学/逻辑运算及存取。 我们需要哪些数据呢? 详见代码: 需要关注的是,成员变量的类型中,除了一些基础类型(sint32、float32之类的),还有几个陌生的类型: 它们三个是代码里自定义的类型,定义成结构体那自然是为了方便,它们都放在文件 pms_types.h 中,我们看看它们的具体定义: PMSOption结构体,它的成员是PMS算法的所有参数,调用者可以通过改变这些参数来让算法得到不同的结果,不同的数据也会对应着不同的参数,参数的存在让算法变得更灵活自由。 梯度结构体,保存着 视差平面是一个较为核心的结构体,贯穿全代码,它可以通过视差和法线来构建,并包含以下功能: 将视差平面设计成一个结构体会增加代码的可读性,因为代码中会频繁的获取像素的视差、较频繁的获取平面的法线,把他们都写成一个函数,让代码更加简洁和易懂。 好了同学们,本篇就到这吧,虽然篇幅较长,但是似乎文字并不多,对着代码来看,我想不会占用多少时间,咱们下篇来解读算法的具体实现代码,博主还会做一些实验,借助实验图来帮助大家加深理解。 同学们拜拜! 博主简介: 主方向立体匹配、三维重建 2019年获测绘科技进步一等奖(省部级) 爱三维,爱,爱开源 个人微信: 喜欢博主的文章不妨关注一下博主的博客,感谢!
欢迎同学们在Github项目里讨论,如果觉得博主代码质量不错,给颗小星星,以及Follow Me!感激不尽!
【码上实战】【立体匹配系列】经典PatchMatch: (2)主类
主类 PatchMatchStereo
/** * brief PatchMatch类 */ class PatchMatchStereo { public: PatchMatchStereo(); ~PatchMatchStereo(); }
公有函数
/** * 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);
/** * 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);
私有函数
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();
成员变量
/** 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_;
/** brief PMS参数结构体 */ struct PMSOption { sint32 patch_size; // patch尺寸,局部窗口为 patch_size*patch_size sint32 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 Window bool 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两个方向的梯度值,代码里采用的是Sobel这类带方向的边缘提取算法,所以梯度有两个维度。/** * brief 梯度结构体 */ struct PGradient { sint16 x, y; PGradient() : x(0), y(0) {} PGradient(sint16 _x, sint16 _y) { x = _x; y = _y; } };
/** * 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; } };
Ethan Li 李迎松
武汉大学 摄影测量与遥感专业博士
GitHub: https://github.com/ethan-li-coding
邮箱:ethan.li.whu@gmail.com
欢迎交流!
博客主页:https://blog.csdn.net/rs_lys
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算