双目立体视觉: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文件中从节点有可能在从主节点拿到数据之前,主节点…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...
