相对位姿估计
相对位姿估计
示意图

理论推导
离线数据库:
P的位置 P = [ X , Y , Z ] T P=[X,Y,Z]^{T} P=[X,Y,Z]T
相机内参 k 1 k_{1} k1
安卓手机:
相机内参 k 2 k_{2} k2
两个像素点位置 : p 1 和 p 2 p_1和p_2 p1和p2
公式一:
s 1 p 1 = K 1 P s_1p_1=K_1P s1p1=K1P s 2 p 2 = K 2 ( R P + t ) s_2p_2=K_2(RP+t) s2p2=K2(RP+t)
**公式二:**归一化平面上的坐标
x 1 = K 1 − 1 p 1 x_1=K_1^{-1}p_1 x1=K1−1p1 x 2 = K 2 − 1 p 2 x_2=K_2^{-1}p2 x2=K2−1p2
公式三:
x 2 = R x 1 + t x_2=Rx_1+t x2=Rx1+t
公式四:
t ^ x 2 = t ^ R x 1 \hat{t}x_2=\hat{t}Rx_1 t^x2=t^Rx1
公式五
x 2 T t ^ x 2 = x 2 T t ^ R x 1 x_2^{T}\hat{t}x_2=x_2^{T}\hat{t}Rx_1 x2Tt^x2=x2Tt^Rx1
x 2 T t ^ R x 1 = 0 x_2^{T}\hat{t}Rx_1=0 x2Tt^Rx1=0
公式六:
( K 2 − 1 p 2 ) T t ^ R K 1 − 1 p 1 (K_2^{-1}p_2)^{T}\hat{t}RK_1^{-1}p_1 (K2−1p2)Tt^RK1−1p1
结论:
本质矩阵: E = t ^ R E=\hat{t}R E=t^R ---------------------已知相机参数的情况下
基础矩阵: F = K 2 − T E K 1 − 1 F=K_2^{-T}EK_1^{-1} F=K2−TEK1−1 -----------未知相机参数的情况下
伪代码
input:image_src,k_src,image_dst,k_dst
output:R,t
1 feature_detect(image_src,image_dst)---->keypoints and deccriptors
2 feature_match(image_src,image_dst)---->matched_features
3 find_essentialmatrix(matched_keypoints,k_src,k_dst)----->essential_matrix
4 decompose_E(essentialmatrix)----->R,t
5 judge "left or right"
实现代码
import cv2
import numpy as npdef find_keypoints_and_descriptors(image):# 使用SIFT算法检测关键点和计算描述符sift = cv2.SIFT_create()keypoints, descriptors = sift.detectAndCompute(image, None)return keypoints, descriptorsdef match_keypoints(descriptors1, descriptors2):# 使用FLANN匹配器进行关键点匹配FLANN_INDEX_KDTREE = 0index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)search_params = dict(checks=50)flann = cv2.FlannBasedMatcher(index_params, search_params)matches = flann.knnMatch(descriptors1, descriptors2, k=2)# 保留良好的匹配good_matches = []for m, n in matches:if m.distance < 0.7 * n.distance:good_matches.append(m)return good_matchesdef estimate_relative_pose(keypoints1, keypoints2, good_matches, camera_matrix_src, camera_matrix_dst):# 提取匹配点对应的关键点src_pts = np.float32([keypoints1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)dst_pts = np.float32([keypoints2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)# 使用基础矩阵估计相机的相对位姿essential_matrix, _ = cv2.findEssentialMat(src_pts, dst_pts, camera_matrix_src, None, camera_matrix_dst, None, cv2.RANSAC, 0.999, 1.0)# 从基础矩阵中恢复旋转和平移矩阵_, R, t, _ = cv2.recoverPose(essential_matrix, src_pts, dst_pts, camera_matrix_src)return R, tdef determine_camera_direction(t, R):# 打印平移向量print(f"平移向量 t: {t}")# 计算旋转矩阵的欧拉角angles = cv2.Rodrigues(R)[0]yaw = np.arctan2(angles[1, 0], angles[0, 0]) * 180.0 / np.pi# 联合判断相机的方向if t[0] > 0 and yaw > 0:print("相机偏向右侧, 您应该向左转")elif t[0] < 0 and yaw < 0:print("相机偏向左侧,您应该向右转")elif t[0] > 0 and yaw < 0:print("相机偏向右侧, 但是角度偏向左")elif t[0] < 0 and yaw > 0:print("相机偏向左侧, 但是角度偏向右")else:print("相机方向正前方")print(f"X方向平移: {t[0]}, Y方向平移: {t[1]}, Z方向平移: {t[2]}")print(f"Yaw 角度: {yaw}")def main():# 加载两张图片image1 = cv2.imread('/media/k1928-3/028efb59-765e-462b-8aa6-085565fa80eb/hxy/biaoding/weiziguji/DJI_0273.JPG', cv2.IMREAD_GRAYSCALE)image2 = cv2.imread('/media/k1928-3/028efb59-765e-462b-8aa6-085565fa80eb/hxy/biaoding/weiziguji/phone/ori_right.jpg', cv2.IMREAD_GRAYSCALE)# 假设你已知相机内参----数据库相机fx_src = 4282.03fy_src = 2960.54cx_src = 844.20cy_src = 552.00camera_matrix_src = np.array([[fx_src, 0, cx_src],[0, fy_src, cy_src],[0, 0, 1]], dtype=float)# 手机相机fx_dst = 2934.52fy_dst = 2934.89cx_dst = 1466.29cy_dst = 2020.34camera_matrix_dst = np.array([[fx_dst, 0, cx_dst],[0, fy_dst, cy_dst],[0, 0, 1]], dtype=float)# 检测关键点和计算描述符keypoints1, descriptors1 = find_keypoints_and_descriptors(image1)keypoints2, descriptors2 = find_keypoints_and_descriptors(image2)# 匹配关键点good_matches = match_keypoints(descriptors1, descriptors2)# 估计相机的相对位姿R, t = estimate_relative_pose(keypoints1, keypoints2, good_matches, camera_matrix_src, camera_matrix_dst)# 联合判断相机的方向determine_camera_direction(t, R)if __name__ == "__main__":main()
##八点法
import cv2
import numpy as np
import timedef find_keypoints_and_descriptors(image):# 使用SIFT算法检测关键点和计算描述符sift = cv2.SIFT_create()keypoints, descriptors = sift.detectAndCompute(image, None)return keypoints, descriptorsdef match_keypoints(descriptors1, descriptors2):# 使用FLANN匹配器进行关键点匹配FLANN_INDEX_KDTREE = 0index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)search_params = dict(checks=50)flann = cv2.FlannBasedMatcher(index_params, search_params)matches = flann.knnMatch(descriptors1, descriptors2, k=2)# 保留良好的匹配good_matches = []for m, n in matches:if m.distance < 0.7 * n.distance:good_matches.append(m)return good_matchesdef estimate_relative_pose_eight_point(keypoints1, keypoints2, good_matches, camera_matrix_src, camera_matrix_dst):# 提取匹配点对应的关键点src_pts = np.float32([keypoints1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)dst_pts = np.float32([keypoints2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)# 计算基础矩阵fundamental_matrix, _ = cv2.findFundamentalMat(src_pts, dst_pts, cv2.FM_8POINT)# 计算本质矩阵essential_matrix = camera_matrix_dst.T @ fundamental_matrix @ camera_matrix_src# 从本质矩阵中恢复旋转和平移矩阵_, R, t, _ = cv2.recoverPose(essential_matrix, src_pts, dst_pts, camera_matrix_src)return R, tdef determine_camera_direction(t, R):# 打印平移向量print(f"平移向量 t: {t}")# 计算旋转矩阵的欧拉角angles = cv2.Rodrigues(R)[0]yaw = np.arctan2(angles[1, 0], angles[0, 0]) * 180.0 / np.pi# 联合判断相机的方向if t[0] > 0 and yaw > 0:print("人在走廊中轴线左侧, 手机摄像头角度偏右,您应该向右走,应将手机向左偏")elif t[0] < 0 and yaw < 0:print("人在走廊中轴线右侧,手机摄像头角度偏左,您应该向左走,应将手机向右偏")elif t[0] > 0 and yaw < 0:print("人在走廊中轴线左侧, 手机摄像头角度偏左,您应该向右走,应将手机向右偏")elif t[0] < 0 and yaw > 0:print("人在走廊中轴线右侧,手机摄像头角度偏右,您应该向左走,应将手机向左偏")else:print("相机方向正前方")print(f"X方向平移: {t[0]}, Y方向平移: {t[1]}, Z方向平移: {t[2]}")print(f"Yaw 角度: {yaw}")def main():# 加载两张图片image1 = cv2.imread('/media/k1928-3/028efb59-765e-462b-8aa6-085565fa80eb/hxy/biaoding/weiziguji/DJI_0273.JPG', cv2.IMREAD_GRAYSCALE)image2 = cv2.imread('/media/k1928-3/028efb59-765e-462b-8aa6-085565fa80eb/hxy/biaoding/weiziguji/phone/ori_right.jpg', cv2.IMREAD_GRAYSCALE)# 假设你已知相机内参----数据库相机fx_src = 4282.03fy_src = 2960.54cx_src = 844.20cy_src = 552.00camera_matrix_src = np.array([[fx_src, 0, cx_src],[0, fy_src, cy_src],[0, 0, 1]], dtype=float)# 手机相机fx_dst = 2934.52fy_dst = 2934.89cx_dst = 1466.29cy_dst = 2020.34camera_matrix_dst = np.array([[fx_dst, 0, cx_dst],[0, fy_dst, cy_dst],[0, 0, 1]], dtype=float)# 检测关键点和计算描述符keypoints1, descriptors1 = find_keypoints_and_descriptors(image1)keypoints2, descriptors2 = find_keypoints_and_descriptors(image2)# 匹配关键点good_matches = match_keypoints(descriptors1, descriptors2)# 记录开始时间start_time = time.time()# 使用八点法估计相机的相对位姿R, t = estimate_relative_pose_eight_point(keypoints1, keypoints2, good_matches, camera_matrix_src, camera_matrix_dst)# 联合判断相机的方向determine_camera_direction(t, R)# 记录结束时间并计算总时间end_time = time.time()elapsed_time = (end_time - start_time) * 1000 # 转换为毫秒print(f"求解位姿总耗时:{elapsed_time:.6f} 毫秒")if __name__ == "__main__":main()
注意:提取特征的时间是6s,特征匹配的时间是6秒,求解位姿的旋转和平移,所需要的时间不多
相关文章:
相对位姿估计
相对位姿估计 示意图 理论推导 离线数据库: P的位置 P [ X , Y , Z ] T P[X,Y,Z]^{T} P[X,Y,Z]T 相机内参 k 1 k_{1} k1 安卓手机: 相机内参 k 2 k_{2} k2 两个像素点位置 : p 1 和 p 2 p_1和p_2 p1和p2 公式一:…...
记一次 .NET某工业设计软件 崩溃分析
一:背景 1. 讲故事 前些天有位朋友找到我,说他的软件在客户那边不知道什么原因崩掉了,从windows事件日志看崩溃在 clr 里,让我能否帮忙定位下,dump 也抓到了,既然dump有了,接下来就上 windbg …...
2020 6.s081——Lab5:Lazy page allocation
再来是千年的千年 不变是眷恋的眷恋 飞越宇宙无极限 我们永不说再见 ——超兽武装 完整代码见:SnowLegend-star/6.s081 at lazy (github.com) Eliminate allocation from sbrk() (easy) 顾名思义,就是去掉sbrk()中调用growproc()的部分。1s完事儿。 Laz…...
华为认证学习笔记:生成树
以太网交换网络中为了进行链路备份,提高网络可靠性,通常会使用冗余链路。但是使用冗余链路会在交换网络上产生环路,引发广播风暴以及MAC地址表不稳定等故障现象,从而导致用户通信质量较差,甚至通信中断。为解决交换网络…...
leetcode 97.交错字符串
思路:LCS 其实也是同一个类型的题目,一般涉及到这种子序列的字符串问题的时候,状态的设置基本上都应该是以...结尾为状态的。这里同样,设置用dp[i][j]为s1,s2字符以i,j结尾能否拼接成s3[ij]。 那么,首先就…...
The Missing Semester ( Shell 工具和脚本 和 Vim)
管道符号 (1)管道符号 | 将前一个命令的输出作为下一个命令的输入 例如: 以下为 ./semester输出中提取包含 "Last-Modified" 的行并写入文件 last-modified.txt./semester | grep "Last-Modified" > ~/last-modif…...
【Uniapp微信小程序】自定义水印相机、微信小程序地点打卡相机
效果图 template 下方的image图片自行寻找替换! <template><view><camerav-if"!tempImagePath && cameraHeight ! 0":resolution"high":frame-size"large":device-position"device":flash"f…...
SimPO: Simple Preference Optimization with a Reference-Free Reward
https://github.com/princeton-nlp/SimPO 简单代码 class simpo(paddle.nn.Layer):def __init__(self):super(OrPoLoss, self).__init__()self.loss paddle.nn.CrossEntropyLoss()def forward(self,neg_logit, neg_lab, pos_logit, pos_lab,beta,gamma):neg_logit paddle.n…...
CDH6.3.2安装文档
前置环境: 操作系统: CentOS Linux release 7.7 java JDK : 1.8.0_231 1、准备工作 准备以下安装包: Cloudera Manager: cloudera-manager-agent-6.3.1-1466458.el7.x86_64.rpm cloudera-manager-daemons-6.3.1-1466458.el…...
Java实战入门:深入解析Java中的 `Arrays.sort()` 方法
文章目录 一、方法定义参数说明返回值 二、使用场景三、实现原理四、示例代码示例一:对整型数组排序示例二:对字符串数组排序示例三:对自定义对象数组排序 五、注意事项六、总结 在Java编程中,Arrays.sort() 方法是一个非常常用的…...
JavaScript的垃圾回收机制
No.内容链接1Openlayers 【入门教程】 - 【源代码示例300】 2Leaflet 【入门教程】 - 【源代码图文示例 150】 3Cesium 【入门教程】 - 【源代码图文示例200】 4MapboxGL【入门教程】 - 【源代码图文示例150】 5前端就业宝典 【面试题详细答案 1000】 文章目录 一、垃圾…...
小程序使用Canvas设置文字竖向排列
在需要使用的js页面引入js文件,传入对应参数即可 /** * 文本竖向排列 */ function drawTextVertical(context, text, x, y) {var arrText text.split();var arrWidth arrText.map(function (letter) {return 26; // 字体间距,需要自定义可以自己加参数,根据传入参数进行…...
GPT-4o:重塑人机交互的未来
一个愿意伫立在巨人肩膀上的农民...... 一、推出 在人工智能(AI)领域,自然语言处理(NLP)技术一直被视为连接人类与机器的桥梁。近年来,随着深度学习技术的快速发展,NLP领域迎来了前所未有的变革…...
大语言模型拆解——Tokenizer
1. 认识Tokenizer 1.1 为什么要有tokenizer? 计算机是无法理解人类语言的,它只会进行0和1的二进制计算。但是呢,大语言模型就是通过二进制计算,让你感觉计算机理解了人类语言。 举个例子:单1,双2&#x…...
Linux自动挂载服务autofs讲解
1.产生原因 2.配置文件讲解 总结:配置客户端,先构思好要挂载的目录如:/abc/cb 然后在autofs.master中编辑: /abc(要挂载的主目录) /etc/qwe(在这个文件里去找要挂载的副目录,这个名…...
堆结构知识点复习——玩转堆结构
前言:堆算是一种相对简单的数据结构, 本篇文章将详细的讲解堆中的知识点, 包括那些我们第一次学习堆的时候容易忽略的内容, 本篇文章会作为重点详细提到。 本篇内容适合已经学完C语言数组和函数部分的友友们观看。 目录 什么是堆 建堆算法…...
JS数据类型运算符标准库
目录 数据类型运算符标准库对象Object对象属性描述对象Array对象包装对象Boolean对象Number对象String对象Math对象Date对象...
单片机之从C语言基础到专家编程 - 4 C语言基础 - 4.13数组
C语言中,有一类数据结构,它可以存储一组相同类型的元素,并且可以通过索引访问这些元素,没错,这类数据结构就是数组。数组可以说是C语言中非常重要的数据结构之一了。使用数组可以是程序逻辑更加清晰,也更加…...
【码银送书第二十期】《游戏运营与出海实战:策略、方法与技巧》
市面上的游戏品种繁杂,琳琅满目,它们是如何在历史的长河中逐步演变成今天的模式的呢?接下来,我们先回顾游戏的发展史,然后按照时间轴来叙述游戏运营的兴起。 作者:艾小米 本文经机械工业出版社授权转载&a…...
String 类
目录: 一. 认识 String 类 二. String 类的基本用法 三. String对象的比较 四.字符串的不可变性 五. 认识 StringBuffer 和 StringBuilder 一. 认识 String 类: 在C语言中已经涉及到字符串了,但是在C语言中要表示字符串只能使用字符数组或者…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
