双目立体视觉: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文件中从节点有可能在从主节点拿到数据之前,主节点…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...

echarts使用graphic强行给图增加一个边框(边框根据自己的图形大小设置)- 适用于无法使用dom的样式
pdf-lib https://blog.csdn.net/Shi_haoliu/article/details/148157624?spm1001.2014.3001.5501 为了完成在pdf中导出echarts图,如果边框加在dom上面,pdf-lib导出svg的时候并不会导出边框,所以只能在echarts图上面加边框 grid的边框是在图里…...

Redis上篇--知识点总结
Redis上篇–解析 本文大部分知识整理自网上,在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库,Redis 的键值对中的 key 就是字符串对象,而 val…...