双目立体视觉:SAD算法
算法原理
SAD(Sum of absolute differences)是一种图像匹配算法。基本思想:差的绝对值之和。此算法常用于图像块匹配,将每个像素对应数值之差的绝对值求和,据此评估两个图像块的相似度。该算法快速、但并不精确,通常用于多级处理的初步筛选。
常见立体匹配算法流程
常见的立体匹配算法主要包括以下四步
匹配代价计算
代价聚合
视差计算或优化
视差改良
匹配代价计算常采用sad等方法,根据左右两幅图像上匹配点的像素之差的绝对值。
代价聚合常采用一个固定窗口,计算窗口内部的所有视差之和。
视差的计算最直观的方式是采用WTA(Winner Takes All)的方式,直接选取使得聚合代价最小的视差值。
BM算法概括
简单的理解立体匹配,在行对准的两幅图像中找到同一个点,或Reference图像中给定一点,在Target图像中搜索对应的点,如下图所示。


根据极线规则,上图左边图中红色像素点(x, y)到右边图中搜索匹配点。实际上,直接对一点来进行匹配,百分百会出现各种各样的问题,这个时候我们选择用一个固定窗口来替代一点,如下图所示。

这样做就隐含了一个假设,认为窗口内部视差值相同,但是,显然的,这种假设太过想当然,也使得算法实际效果不好。
BM算法,也常称为SAD(Sum of Absolute Differences)算法,是双目立体匹配中最基本的算法。
SAD基本理论
SAD算法由3步构成。
匹配代价计算
代价聚合
视差计算
Matching Cost Computation
SAD的匹配代价计算比较简单,Reference图像和Target图像像素直接相减加绝对值,即|IR(x,y)−IT(x+d,y)||IR(x,y)−IT(x+d,y)|。
视差空间(DSI)是一个三维矩阵,定义
[c(x,y,d)= | I_R(x,y)-I_T(x+d,y) | ] |
可以理解为Reference图像(x,y)(x,y)点,在搜索视差为dd时的代价。
Cost Aggeration
SAD的代价聚合就是将固定窗口FW(Fixed Window)内代价求和,直观理解如下图所示。

计算FW内视差视差为d时的聚合代价
[C(x,y,d)=\sum_{x\in S}|I_R(x,y)-I_T(x+d,y)|]
Disparity Computation
SAD的视差计算非常简单,采用WTA原则,对于给定的(x,y)(x,y),找使得C(x,y,d)C(x,y,d)最小的d,此d即可认为时该点的视差。

基本流程
输入:两幅图像,一幅Left-Image,一幅Right-Image且两幅图像已经校正实现行对准
对左图,依次扫描,选定一个锚点:
(1)设定SAD窗口的大小(下图灰色区域),left_image为开始匹配的位置,(p,q)以及在right_image中SAD窗口移动的范围D。
(2)在left_image图像中,确定待匹配的像素点的位置(x,y),并以此位置作为SAD窗口的锚点,用SAD窗口覆盖left_image中以(x,y)为锚点的区域regionl。
(3)在right_image图像中,选取匹配的开始点,位置为(m,n),并以该点作为SAD窗口的锚点,用SAD窗口去覆盖,在right_iamge中形成以(m,n)为锚点的图像区域regionr.
(4)定义differernce=regionr-regionl。计算difference中的和。
(5)在right_image图像中沿行方向移动SAD(移动次数为匹配的范围大小),重复步骤(3),(4),并将每次得到的difference记录在mat矩阵中。
(6)找到mat矩阵中difference最小的值,则其所在位置就是right_image和left_image的视差。

代码实现
#include "opencv2/opencv.hpp"class SAD
{
public:SAD() :winSize(7), DSR(30) {}SAD(int _winSize, int _DSR) :winSize(_winSize), DSR(_DSR) {}cv::Mat computerSAD(cv::Mat& L, cv::Mat& R); //计算SAD
private:int winSize; //卷积核的尺寸int DSR; //视差搜索范围};cv::Mat SAD::computerSAD(cv::Mat& L, cv::Mat& R)
{int Height = L.rows;int Width = L.cols;cv::Mat Kernel_L(cv::Size(winSize, winSize), CV_8U, cv::Scalar::all(0));cv::Mat Kernel_R(cv::Size(winSize, winSize), CV_8U, cv::Scalar::all(0));cv::Mat Disparity(Height, Width, CV_8U, cv::Scalar(0)); //视差图for (int i = 0; i < Width - winSize; i++){for (int j = 0; j < Height - winSize; j++){Kernel_L = L(cv::Rect(i, j, winSize, winSize));cv::Mat MM(1, DSR, CV_32F, cv::Scalar(0)); //MM是一个1行DSR列的图像(矩阵)for (int k = 0; k < DSR; k++){int x = i - k; //为什么是i-k参见我上面的叙述if (x >= 0){Kernel_R = R(cv::Rect(x, j, winSize, winSize));cv::Mat Dif;cv::absdiff(Kernel_L, Kernel_R, Dif);//cv::Scalar ADD = sum(Dif);float a = ADD[0];//a为视差为k是相应窗口的像素差值的绝对值之和MM.at<float>(k) = a;//将a赋给MM的第k列,因为从0开始搜索,遍历结束后MM每一列为视差为列序号时对应的SAD值,我们取其最小即可std::cout << "i,j: " << i << ", " << j << "; MM " << MM << std::endl;}}cv::Point minLoc; //point数据类型为二维点对象,有横纵xy两个坐标double min = 0.0;cv::minMaxLoc(MM, &min, NULL, &minLoc, NULL);//返回MM最小值的坐标int loc = minLoc.x;//取最小值坐标的横坐标x值,即为对应的列序号,也就是相应的视差值//int loc=DSR-loc;Disparity.at<char>(j, i) = loc * 16;//*16只是为了方便显示}double rate = double(i) / (Width);//cout << "已完成" << setprecision(2) << rate * 100 << "%" << endl; //处理进度}return Disparity;
}int main()
{cv::Mat Img_L = cv::imread("SAD\\left_0.jpg", 0);cv::Mat Img_R = cv::imread("SAD\\right_0.jpg", 0);cv::Mat Disparity; //视差图//SAD mySAD;SAD mySAD(7, 30);Disparity = mySAD.computerSAD(Img_L, Img_R);cv::imshow("Img_L", Img_L);cv::imshow("Img_R", Img_R);cv::imshow("Disparity", Disparity);cv::waitKey();return -1;
}
备注:
用SAD算法可以得出左右图像的视差,进一步处理就可以得到深度图,深度与视差成反比的关系。我们做个实验:将手指头放在离眼睛不同距离的位置,并轮换睁、闭左右眼,可以发现手指在不同距离的位置,视觉差也不同,且距离越近,视差越大,其中距离的远近就是深度了。并且可以观察到,用左眼看手指时,手指在你眼中的靠右位置,而用右眼看时,手指在你眼中靠左的位置。假设两只眼分别看到的视野一样大。若用(x,y)表示左眼视图中某个位置的坐标,那么相应的该位置右眼视图的坐标应该为(x-d,y),其中d就是视差。这时(x,y)和(x-d,y)就是最佳匹配点。但是实际情况我们并不知道d是多少。SAD算法就给出了如何求视差d.
SAD算法:我们按视差搜索范围从0开始搜索,找到左右图像最匹配的点,对应的视差值就确定了。如何确定最佳匹配点呢?试想一下,如果视差为0,也就是左右图像一样,那么这个点上下左右区域对应的点都应该相同,所以像素相减后都为0,由于视差的存在(简单理解为从不同的角度看物体,由于光照的影响像素值也会发生改变),该点上下左右区域的像素值不会完全相等,但是我们依然可以利用这个思想,设定一个小窗口,在左右两幅图中计算其像素值差的绝对值之和。根据极线约束覆盖右图像像素点,假如视差搜索范围为0-50,那么就会得到51个结果。若在某个视差值d下该绝对值之和最小,那么d就为该中心点对应的视差。再由视差与深度的关系就可以得到深度图。
https://jiweibo.github.io/StereoBM/
相关文章:

双目立体视觉:SAD算法
算法原理SAD(Sum of absolute differences)是一种图像匹配算法。基本思想:差的绝对值之和。此算法常用于图像块匹配,将每个像素对应数值之差的绝对值求和,据此评估两个图像块的相似度。该算法快速、但并不精确,通常用于多级处理的…...

海外问卷调查答题技巧,纯干货分享,新手小白看过来
海外问卷调查为什么别人赚得盆满钵满而我却连通过都不行?是不是经常有人发出这种疑问,东哥作为一个结交过很多做问卷调查行业的跨境人士,也了解到很多做这一行的去答题的时候都是掌握一定技巧的,而不是去乱答。今天东哥就来说说国…...

【NGINX入门指北】Nginx Web 架构实验
Nginx Web 架构实验 文章目录Nginx Web 架构实验一、动态网站结构二、LNMP 动态网站环境部署三、fastcgi & php-fpm:四、php-fpm初始化配置五、Nginx Location、六、Nginx Rewrite七、CA&HTTPS八、Nginx 的平滑升级一、动态网站结构 资源 资源文件识别——…...
rtt-nano移植
nano其他功能移植 添加finsh组件打开宏实现rt_hw_console_getchar函数添加finsh组件到工程总结问题1. 移植到stm32G0过程中出现Undefined symbol rt_hw_interrupt_disable (referred from clock.o)??2. “implict declaration of function ‘ ‘ is invalid in c99??3. 关于…...
cnn+transformer
好的,下面是使用 Transformer 加 CNN 实现语义分割的代码,使用的数据集是 Semantic Segmentation Drone Dataset。 首先,我们需要导入必要的 Python 库和模块。我们将使用 PyTorch 深度学习框架来实现模型: #python import torch import torch.nn as nn import torch.nn.fu…...

Python fileinput模块:逐行读取多个文件
前面章节中,我们学会了使用 open() 和 read()(或者 readline()、readlines() )组合,来读取单个文件中的数据。但在某些场景中,可能需要读取多个文件的数据,这种情况下,再使用这个组合࿰…...

Vue3路由传参
vue3路由和vue2差别不是很大,不过在传参形式上略有改变 在Vue3中使用路由必须引入 useRouter 和 useRoute import { useRoute, useRouter } from vue-routerconst Router useRouter() //跳转const Route useRoute() //获取到值 同Vue2一样,query使用p…...
用户管理——认证功能JWT和Session
目录用户认证功能的技术选型JWT和Session的区别基于JWT和Session的认证流程基于JWT的认证流程基于Session的认证流程基于JWT和Session的认证的优缺点基于JWT和Session的认证的安全性基于JWT和Session的认证的性能分析基于JWT的一次性和无法废弃基于JWT和Session的认证的续签选择…...
hashlib — 加密哈希算法
hashlib — 加密哈希算法 1.概述 加密可以保护消息的安全,以便验证它们的准确性并且使它们受保护不被拦截。 Python 的加密方式支持包括利用像 MD5 和 SHA 这样的标准算法对消息内容产生签名的 hashlib 和验证消息没有在传输过程中被改变的 hmac hashlib 哈希库模…...
四喜临门选股预警源码指标
{四喜临门选股预警} AP1:CROSS(MA(C,5),MA(C,10)); RSV:(CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9))*100; K:SMA(RSV,3,1); D:SMA(K,3,1); AP2:CROSS(K,D); DIFF:EMA(CLOSE,12) - EMA(CLOSE,26); DEA:EMA(DIFF,9); AP3:CROSS(DIFF,DEA); AP4:CROSS(MA(V,5),MA(V,10)); GYTJ1:…...
Kotlin新手教程五(扩展)
一、扩展 在Kotlin中可以给一个类添加一个新的方法而不用继承该类或者使用设计模式,这样的方法称为扩展。 1.扩展函数 声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。 下面代码为 MutableList 添加一个swap 函数&am…...

QT入门Containers之Widget、Frame
目录 一、QWidget界面相关 1、布局介绍 2、基本界面属性 3、特殊属性 二、QFrame 三、Demo展示 此文为作者原创,创作不易,转载请标明出处! 一、QWidget界面相关 1、布局介绍 为什么将QWidget容器放在第一个,因为目前使用过…...

数据结构与算法基础-学习-12-线性表之顺序队
一、个人理解队列是线性表的衍生之一,具有先进先出的特性,在队尾进行插入操作,在队头进行删除操作。队列的存储结构分为两个大类,一种是顺序队,就是用数组实现。另一种就是链队,使用链表实现。顺序队存在真…...
Python 字典(Dictionary)小窍门
字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值 key:value 对用冒号 : 分割,每个键值对之间用逗号 , 分割,整个字典包括在花括号 {} 中 ,格式如下所示:d {key1 : value1, key2 : value2 }注意:dict …...

知识图谱构建技术综述
摘要 *知识图谱为实现语义化智能搜索以及知识互联打下了基础,。, *随着知识的发展,传统的基于模板和规则构建的知识图谱已经被深度学习所替代。 知识组织得原则中:知识的充分性、有序性和标准化规则。深度学习的效果在很大程度上…...

环境变量和进程地址空间
目录 环境变量: env:显示所有的环境变量: echo $环境变量名表示查看环境变量的值 理解环境变量: getenv:显示环境变量的值 export set命令:显示所有变量 unset取消变量: pwd:当…...

【数据结构】栈和队列
目录 一、栈 1、栈的定义 2、栈的模拟实现(顺序栈) 1、创建一个顺序结构的栈 2、实现压栈方法(push) 3、模拟实现pop方法(出栈) 4、模拟实现peek(查看) 5、测试上述方法 3、栈的应用场景 1、改变元…...

sql复习(视图、Top-N分析、其他数据库对象)
一、视图view 1.视图定义 视图是一种虚表。 视图建立在已有表的基础上, 视图赖以建立的这些表称为基表。 向视图提供数据内容的语句为 SELECT 语句, 可以将视图理解为存储起来的 SELECT 语句。 视图向用户提供基表数据的另一种表现形式。 2.使用视图的好处 控制数据访问 简…...

2023年私募股权基金研究报告
第一章 概况 PE是私募,也即私募投资基金,是指以非公开发行方式向合格投资者募集的,投资于股票、股权、债券、期货、期权、基金份额及投资合同约定的其他投资标的(如艺术品、红酒等)的投资基金,简称私募基金…...
Redis单点故障+红锁原理
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、Redis单点故障二、红锁原理三、Redission实现了红锁一、Redis单点故障 单台redis容易出单点故障采用集群,获取到锁之后数据持久化到rdb,aof文件中从节点有可能在从主节点拿到数据之前,主节点…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...