光流方向以及 remap 重映射的理解

news2024/10/6 8:35:24

Date: 2023-09-07


省流:光流法计算prev 到next 的flow,之后flow (加上当前位置坐标)生成flow_map,利用flow_map 和OpenCV remap 函数,可以将next remap 得到 prev,即remap 后一帧得到前一帧图像。

疑问

一直以来我都有个疑问,光流(optical flow)中存储的是 next 相对于 prev 的平移量(dx,dy)。但是为什么使用光流生成的 map(以下简称 flow_map)表进行 remap 操作的时候,却是用 next+flow_map 得到 prev?平移量不是 prev 到 next 的平移量吗?不应该是 prev+flow_map 得到next 吗?

光流值的含义

坐标原点在图像左上角,假设在 prev 图像上有一个像素点为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),在 next 图像上对应的像素点为 ( x 2 , y 2 ) (x_2,y_2) (x2,y2) ,那么位移表示为 ( d x , d y ) = ( x 2 , y 2 ) − ( x 1 , y 1 ) (dx,dy)=(x_2,y_2)-(x_1,y_1) (dx,dy)=(x2,y2)(x1,y1)
prev 图像

next 图像

图上整体是往左上平移的,也就是如果原点在左上角,那么dx 和dy 都为负数。

inst = cv2.optflow.createOptFlow_DeepFlow()
flow = inst.calc(input_image_gray, output_image_gray, None)

下面是 flow 里面的值,确实也都是负数

接下来我们用 flow_map 来进行重映射。

remap 重映射

在利用 flow 进行映射之前我们需要了解两个概念,前向映射后向映射

前向映射(Forward Mapping)和后向映射(Inverse Mapping)是在计算机图形学和计算机视觉领域中用于处理图像和几何变换的两种不同方法。

  1. 前向映射(Forward Mapping)

    • 前向映射是一种直接的方法,它将输入图像中的每个像素通过变换映射到输出图像中的相应位置。
    • 在前向映射中,我们遍历输入图像中的每个像素,将其通过变换计算出在输出图像中的位置,然后将像素值复制到该位置。
    • 前向映射的优点是简单直观,但在某些情况下可能会导致输出图像中的某些位置没有被填充或者有重叠。
  2. 后向映射(Inverse Mapping)

    • 后向映射是一种反向的方法,它将输出图像中的每个像素通过逆变换映射到输入图像中的相应位置。
    • 在后向映射中,我们遍历输出图像中的每个像素,通过逆变换计算出它在输入图像中的位置,并从输入图像中取得对应位置的像素值。
    • 后向映射的优点是可以确保输出图像中的每个位置都有对应的像素值,避免了前向映射中的缺失或重叠问题。

选择使用前向映射还是后向映射取决于具体的应用需求和变换操作。前向映射通常用于简单的几何变换,而后向映射通常用于复杂的非线性变换或需要确保像素一对一映射的情况。每种方法都有其优点和限制,根据具体情况选择合适的映射方法是重要的。

以上引用内容由 ChatGPT 回答得出。那么 remap 是属于哪种映射方式?知道了它属于哪种映射方式有助于我们理解映射过程。虽然ChatGPT 给出的答案是 remap 函数是前向映射,但按照我的理解它似乎是后向映射。

remap 是前向映射还是后向映射?

我做了这样一个实验:
生成一个(x,y) 都是(100,100)的map 表,如果remap 是前向映射,那么遍历原图中所有的坐标点,并把它的像素映射到目标图上(100,100) 的位置,那么目标图像将会是一个只在(100,100)处有值,其他部分为黑色(没有值)的图像。
而实际测试上生成的目标图是个纯色的图像。它更符合后向映射的描述,即遍历目标图每个像素点,从原图上取值填充过去,对于这个map 表中所有元素都是(100,100)的映射来说,目标图上所有像素取值来源相同,就应该是个纯色图像。

鉴于 remap 函数可能是后向映射,我们不可能用 prev+flow_map 生成的map 表得到 next。反而是 next+flow_map 生成的map 表有可得到prev。
下面是一段将图像按照 flow 中表示的位移进行 remap 的代码,内含用 flow 生成 flow_map 的操作。

def cv_warp(src, flow):
    # 生成网格点坐标矩阵
    h, w = flow.shape[:2]
    warp_grid_x, warp_grid_y = np.meshgrid(np.linspace(0, w - 1, w), np.linspace(0, h - 1, h))
    # 因为flow 里面存储的是dx dy, 而remap 需要的是点到点的映射,所以需要加上x,y 坐标,也就是上面生成的网格点坐标矩阵
    # axis=-1 的作用是将分开的grid_x(x0,x1,...) 和grid_y(y0,y2,...) 合并成(x0,y0),(x1,y1),... 的形式
    flow_inv = flow + np.stack((warp_grid_x, warp_grid_y), axis=-1)
    flow_inv = flow_inv.astype(np.float32)
    warped = cv2.remap(src, flow_inv, None, cv2.INTER_CUBIC)
    return warped

我们将 next 和 flow 输入到上面的函数,得到如下结果:

它基本上可以和 prev 的内容对齐。
如果我们想要从 prev remap 得到 next,则需要将 flow 的值取反再生成map 表。

remap 函数真正的映射关系

OpenCV 官方文档对于 remap 函数的解释中有这么一个公式:
d s t ( x , y ) = s r c ( m a p x ( x , y ) , m a p y ( x , y ) ) dst(x,y)=src(mapx(x,y),mapy(x,y)) dst(x,y)=src(mapx(x,y),mapy(x,y))
我们实际带入数据来理解一下这公式。先来看dst 图像中的第(0,0) 个像素,假设map_x(0,0) 中存储的是10,map_y(0,0) 存储的是10,那么它表示的是目标图像上第(0,0) 位置的像素值,需要在源图像上第(10,10) 中取得(不考虑插值)。你会发现这个像素从原图像的(10,10) 移动到了目标图像的(0,0),它是往左上角移动了,但实际你计算光流的话这个点的(dx,dy)=(0,0)-(10,10) 是(-10,-10),map 表中存储的是(10,10),但是flow 中存的是(-10,-10),有一点反直觉对不对?
现在我们回头看看我最初的疑问是不是合理的 “平移量不是 prev 到 next 的平移量吗?不应该是 prev+flow_map 得到next 吗?” flow 中村的是 prev 到 next 的平移量这个理解没问题,问题在于(dx,dy) 的坐标。当我们把光流的意义用下面的表达式表示 ( d x , d y ) = n e x t ( x , y ) − p r e v ( x ′ , y ′ ) (dx,dy) = next(x,y)-prev(x',y') (dx,dy)=next(x,y)prev(x,y)你会发现flow 中每一点的偏移量,是表示的next 图像中对应坐标中的平移量,所以当你试图用flow_map 对prev 做remap 时,其实是将当前的平移量应用到了另一个点上,这显然是错误的。 而next+flow_map 进行remap 才是合理的,同样带入数据,目标图上(0,0)的像素,要在源图像(此时为 next)的(-10,-10)取得,这表示像素向右下角移动了,和前面src 变换成dst 移动的方向相反,于是dst 还原成了src。

光流的可视化

光流的可视化方法不唯一,可视化方法不同,则相同的颜色/亮度会表示的含义不同。
有些方法用亮度表示位移大小,有些方法用饱和度表示位移大小,也有可能相同的位移方向在不同的转换方法中表现为不同颜色。
个人认为不必过分追求统一的表示方法,与人交流时提前明确表示方法即可。
(下面光流图和上面计算光流的输入图不是同一组)

OpenCV 官方文档中的可视化方法:OpenCV: Optical Flow
csdn 上某博主的可视化方法:光流介绍以及FlowNet学习笔记_光流可视化

参考链接

  1. 一文搞懂光流 光流的生成,可视化以及映射(warp)_光流可视化_深山里的小白羊的博客-CSDN博客
  2. opencv光流预测和remap重映射函数使用-腾讯云开发者社区-腾讯云 (tencent.com)
  3. c++ - OpenCV warping image based on calcOpticalFlowFarneback - Stack Overflow
  4. python - How do I use OpenCV’s remap function? - Stack Overflow
    这个链接里有个答案比较详细的解释了他/她对 remap 映射的理解,以及remap选择这种映射的原因。
  5. OpenCV: Geometric Image Transformations(remap)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1456679.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spring boot学习第十三篇:使用jsonrpc

因为我学习spring boot会有很多内容&#xff0c;所以和spring boot有关的文章使用的pom.xml和application.yml这2个文件的内容就全列出来&#xff0c;不用再单独抽出来了&#xff0c;单独抽出来有点麻烦也不方便学习。 1、pom.xml文件内容如下&#xff1a; <?xml version…

[深度学习] 卷积神经网络“卷“在哪里?

​ &#x1f308; 博客个人主页&#xff1a;Chris在Coding &#x1f3a5; 本文所属专栏&#xff1a;[深度学习] ❤️ 热门学习专栏&#xff1a;[Linux学习] ⏰ 我们仍在旅途 目录 1.卷积的定义 2.卷积的"卷"在哪里 3.什么又是卷积神…

【HTML】SVG实现炫酷的描边动画

前沿 今天闲来无事&#xff0c;看到Antfu大佬的个性签名&#xff0c;觉得还是非常炫酷的&#xff0c;于是也想要搞一个自己的个性签名用来装饰自己的门面&#xff0c;不过由于手写的签名太丑了&#xff0c;遂放弃。于是尝试理解原理&#xff0c;深入研究此等密法&#xff0c;终…

读写分离的利器——MySQL Proxy

0 引言 MySQL Proxy是一个位于客户端和MySQL服务器端之间的程序&#xff0c;通过它可以实现监听和管理客户端与MySQL服务器端之间的通信&#xff0c;最大的作用是实现数据库的读写分离&#xff0c;从而达到负载均衡的目的。 MySQL Proxy的常用用途包括负载平衡、故障分析、查…

纯前端低代码平台demo,vue框架,nodejs,简单的pm2纯前端部署实践

文章目录 目录结构说明本地运行项目启动后的页面demo前端部署打包pm2nginx 后话 前段时间开发了一个纯前端的低代码平台demo&#xff0c;vue框架&#xff0c;nodejs&#xff0c;pm2纯前端部署实践。为此记录一下开发过程以及各方面遇到的问题&#xff0c;并作说明。 表单用了若…

74VHC4040使用方法

74VHC4040芯片是一款12位异步二进制计数器 Q:数据输出引脚。 CP:计数引脚。当CP引脚接收到下降沿&#xff08;从高电平电平变为低电平&#xff09;时&#xff0c;计数器的输出Q会加一。MR:复位输入。当MR引脚接收到高电平时&#xff0c;计数器的所有输出都会变为低电平&#…

特征提取匹配方案不止SuperPoint

局部特征匹配在计算机视觉领域广泛应用&#xff0c;涵盖图像检索、3D重建和目标识别等领域。然而&#xff0c;由于视点和光照变化等因素&#xff0c;改进匹配的准确性和鲁棒性仍然面临挑战。近年来&#xff0c;深度学习模型的引入引发了对局部特征匹配技术的广泛探索。这些方法…

MoonBit新增functional for loop控制流支持

1. 增加functional for loop控制流支持 与传统的命令式for loop 不同&#xff0c;循环变量是不可变的。这样的设计将来也容易抽取出来做形式化验证&#xff1a; fn init {for i 0; i < 5; i i 1 {debug(i)// i i 4 error: The variable i is not mutable.} }输出&am…

基于物联网的工业企业能耗监控系统

上海安科瑞电气股份有限公司 胡冠楠 咨询家&#xff1a;“Acrelhgn”&#xff0c;了解更多产品资讯 摘要&#xff1a;针对工业制造企业存在高能耗、高污染的问题&#xff0c;提出了一种用于工业企业能耗监控的多层级网络系统。本系统可完成企业内部电力、水资源以及燃气等能源…

【内网穿透】如何在小米4A中刷OpenWRT并实现公网访问本地路由器

文章目录 前言1. 安装Python和需要的库2. 使用 OpenWRTInvasion 破解路由器3. 备份当前分区并刷入新的Breed4. 安装cpolar内网穿透4.1 注册账号4.2 下载cpolar客户端4.3 登录cpolar web ui管理界面4.4 创建公网地址 5. 固定公网地址访问 前言 OpenWRT是一个高度模块化、高度自…

linux基础学习(10):基本权限与相关命令

1.基本权限 用ls -l查看当前目录文件时&#xff0c;可以看到文件的基本权限 其由10位组成&#xff0c;其中&#xff1a; 第1位&#xff1a;代表文件类型。 - d lbc普通文件目录文件软链接文件块设备文件&#xff0c;也就是硬盘等存储设备的文件字符设备文件&#xff0c;是鼠…

迈向AI时代:掌握Python编程与ChatGPT的强强联手

文章目录 一、ChatGPT与Python编程的结合二、利用ChatGPT学习Python编程的优势三、如何使用ChatGPT学习Python编程四、学习技巧与建议《码上行动&#xff1a;用ChatGPT学会Python编程》特色内容简介作者简介目录获取方式 随着人工智能技术的飞速发展&#xff0c;编程已经成为了…

如何在三维地球上加载obj、fbx、ifc、dae、3ds、gltf/glb模型?

通过以下方法可以在三维地球上加载obj、fbx、ifc、dae、3ds、gltf/glb模型。 方法/步骤 下载三维地图浏览器 http://www.geosaas.com/download/map3dbrowser.exe&#xff0c;安装完成后桌面上出现”三维地图浏览器“图标。 2、双击桌面图标打开”三维地图浏览器“ 3、点击“…

element-ui 中合并表格的总结

合并表头 <el-table :span-method"objectSpanMethod" :header-cell-style"headerMethod"/>// 合并表头headerMethod6({ row, cloumn, rowIndex, columnIndex }) {// row数组对应的是每一列row[0].colSpan 0row[1].colSpan 2if (columnIndex 0) {r…

基础antdesign的业务型 短时间控件封装(复制即可使用)

{/* startFieldName 开始时间标识 endFieldName 结束时间标识 label 同form lable rules 是否开启规则校验 默认开启 detailData 详情数据&#xff0c;用于编辑回显 dateRange 限制结束时间的范围 例如&#xff1a;开始时间选择了 2024-02-05 &#xff0c;加上 dateRange3 后 只…

【Jvm】性能调优(下)线上问题排查思路汇总

文章目录 前言性能调优&#xff08;上&#xff09;线上问题排查工具汇总JVM调优&#xff08;中&#xff09;Java中不得不了解的OOM Error 一.JVM参数1.参数分类2.非稳定参数&#xff08;-XX&#xff09;说明3.查询JVM默认参数及运行时生效参数4.常用参数5.GC日志相关参数6.发生…

linux基础命令和示例

redis在go语言中的使用 以下说明以读者有redis基础的前提下进行 未学习redis的可以到b站1小时浅学redis了解大概&#xff0c;学会如何使用 【GeekHour】一小时Redis教程_哔哩哔哩_bilibili 以下开发环境以windows为测试环境&#xff0c;旨在练习redis在go语言中的使用 red…

并查集,真好用,一次AC不是梦!

文章目录 &#x1f680;前言&#x1f680;并查集&#x1f680;并查集的两个优化✈️路径压缩✈️按秩合并 &#x1f680;并查集代码模板 &#x1f680;前言 大家好啊&#xff01;今天阿辉来给大家介绍一种简洁而优雅的数据结构——并查集&#xff0c;不知道各位是否了解它&…

IC会员卡写编号,写印刷卡号,卡面卡号,加密D3读卡器R330读卡器会员卡系统

IC卡作为会员卡来使用&#xff0c;比磁条卡安全耐用 游戏厅会员卡、酒店会员卡、美容养生会所会员卡等 IC卡片印刷好以后&#xff0c;要把卡面印刷编号写入到卡片中&#xff0c;才可以在会员卡系统上使用 用以下软件可以批量写入 写入以后&#xff0c;我读取卡片内的数据 把数…

旧衣回收小程序开发,市场发展潜力巨大

在当下快节奏生活下&#xff0c;人们的闲置衣物越来越多&#xff0c;为了减少浪费&#xff0c;旧衣回收成成为了大众的选择。旧衣回收能够降低人们的损失&#xff0c;减少我国资源浪费&#xff0c;又能循环利用&#xff0c;是我国一个环保商业模式。 “互联网旧衣回收”推动市场…