python opencv计算F矩阵分解RT - MKT

news/2026/1/24 1:55:50/文章来源:https://www.cnblogs.com/gooutlook/p/19524433

 

 

 

 

import cv2
import numpy as np
from scipy.spatial.transform import Rotation as Rdef improved_decompose_homography():"""改进的单应性矩阵分解,处理尺度问题"""print("="*60)print("改进的单应性分解 - 处理尺度问题")print("="*60)# 生成更合理的测试数据np.random.seed(42)# 相机内参K = np.array([[800, 0, 320],[0, 800, 240],[0, 0, 1]], dtype=np.float32)# 平面参数n_true = np.array([0, 0, 1])  # 平面法向量 (z=0平面)d_true = 5.0  # 相机到平面的距离# 生成平面上的3D点n_points = 50X_plane = np.random.rand(n_points, 2) * 4 - 2X_plane = np.hstack([X_plane, np.zeros((n_points, 1))])  # z=0# 相机1位姿 (在平面正上方)R1 = np.eye(3)t1 = np.array([0, 0, d_true])  # 相机在(0,0,d)处# 相机2位姿 (有小的旋转和平移)# 小的旋转angle = 0.2  # 约11.5度axis = np.array([0.1, 0.2, 1.0])axis = axis / np.linalg.norm(axis)R2_true = R.from_rotvec(axis * angle).as_matrix()# 小的平移 (在平面内移动)t2_true = np.array([0.5, 0.2, 0])  # 主要在X方向移动print("真实参数:")print(f"平面法向量 n: {n_true}")print(f"平面距离 d: {d_true}")print(f"相机2旋转 R:\n{R2_true}")print(f"相机2平移 t: {t2_true}")print(f"平移长度: {np.linalg.norm(t2_true):.4f}")# 计算真实的单应性矩阵# H = K (R - t n^T/d) K^{-1}n = n_true.reshape(3, 1)H_true = K @ (R2_true - t2_true.reshape(3, 1) @ n.T / d_true) @ np.linalg.inv(K)H_true = H_true / H_true[2, 2]  # 归一化print(f"\n真实单应性矩阵 H:\n{H_true}")# 投影3D点到图像P1 = K @ np.hstack([R1, t1.reshape(3, 1)])P2 = K @ np.hstack([R2_true, (t1 + t2_true).reshape(3, 1)])  # 注意:t2是相对平移X_homo = np.hstack([X_plane, np.ones((n_points, 1))])pts1_homo = (P1 @ X_homo.T).Tpts2_homo = (P2 @ X_homo.T).Tpts1 = pts1_homo[:, :2] / pts1_homo[:, 2:3]pts2 = pts2_homo[:, :2] / pts2_homo[:, 2:3]# 添加适度的噪声noise_scale = 0.5pts1 += np.random.randn(*pts1.shape) * noise_scalepts2 += np.random.randn(*pts2.shape) * noise_scale# 从点估计单应性矩阵H_est, mask = cv2.findHomography(pts1, pts2, cv2.RANSAC, 3.0)mask = mask.flatten().astype(bool)pts1_inliers = pts1[mask]pts2_inliers = pts2[mask]print(f"\n估计的单应性矩阵 H:\n{H_est}")print(f"内点数量: {np.sum(mask)}/{n_points}")# 分解单应性矩阵print("\n" + "="*60)print("分解结果")print("="*60)num_solutions, rotations, translations, normals = cv2.decomposeHomographyMat(H_est, K, pts1_inliers, pts2_inliers)print(f"找到 {num_solutions} 个解")# 分析每个解best_solution = -1best_score = -1for i in range(num_solutions):R_i = rotations[i]t_i = translations[i]n_i = normals[i]# 计算分数score = evaluate_solution(R_i, t_i, n_i, pts1_inliers, pts2_inliers, K, d_true)# 转换为欧拉角便于理解euler = R.from_matrix(R_i).as_euler('xyz', degrees=True)print(f"\n解 {i+1}:")print(f"  欧拉角: [{euler[0]:.2f}, {euler[1]:.2f}, {euler[2]:.2f}]°")print(f"  平移向量: {t_i.flatten()}")print(f"  平移长度: {np.linalg.norm(t_i):.6f}")print(f"  法向量: {n_i.flatten()}")print(f"  评分: {score:.4f}")if score > best_score:best_score = scorebest_solution = iprint(f"\n最佳解: 解{best_solution+1}")# 提取最佳解R_best = rotations[best_solution]t_best = translations[best_solution]n_best = normals[best_solution]# 评估误差print("\n" + "="*60)print("误差分析")print("="*60)# 旋转误差R_error = np.linalg.norm(R_best - R2_true)# 平移方向误差(注意尺度)t_best_norm = t_best / np.linalg.norm(t_best)t_true_norm = t2_true / np.linalg.norm(t2_true)t_dir_error = np.arccos(np.clip(np.dot(t_best_norm.flatten(), t_true_norm), -1.0, 1.0))# 法向量误差n_error = np.arccos(np.clip(np.dot(n_best.flatten(), n_true), -1.0, 1.0))print(f"旋转误差: {R_error:.6f}")print(f"平移方向误差: {t_dir_error:.6f} rad ({np.degrees(t_dir_error):.2f}°)")print(f"法向量误差: {n_error:.6f} rad ({np.degrees(n_error):.2f}°)")# 尺度估计print("\n" + "="*60)print("尺度估计")print("="*60)# 从单应性估计尺度estimated_scale = estimate_scale_from_homography(R_best, t_best, n_best, pts1_inliers, pts2_inliers, K, d_true)print(f"估计的尺度因子: {estimated_scale:.6f}")# 如果有真实尺度,可以缩放平移向量if estimated_scale > 0:t_scaled = t_best * estimated_scaleprint(f"缩放后的平移: {t_scaled.flatten()}")print(f"真实平移: {t2_true}")t_scaled_error = np.linalg.norm(t_scaled.flatten() - t2_true)print(f"缩放后平移误差: {t_scaled_error:.6f}")return R_best, t_best, n_best, estimated_scaledef evaluate_solution(R, t, n, pts1, pts2, K, d=1.0):"""评估解的合理性使用多个标准:1. 正深度点数量2. 重投影误差3. 平面法向量的合理性"""n_points = len(pts1)# 1. 正深度检查P1 = K @ np.hstack([np.eye(3), np.zeros((3, 1))])P2 = K @ np.hstack([R, t])points_4d = cv2.triangulatePoints(P1, P2, pts1.T, pts2.T)points_3d = points_4d[:3] / points_4d[3]depths1 = points_3d[2, :]points_3d_cam2 = R @ points_3d + tdepths2 = points_3d_cam2[2, :]positive_mask = (depths1 > 0) & (depths2 > 0)positive_score = np.sum(positive_mask) / n_points# 2. 重投影误差H_estimated = compute_homography_from_pose(R, t, K, n, d)pts1_homo = np.hstack([pts1, np.ones((n_points, 1))])pts2_proj_homo = (H_estimated @ pts1_homo.T).Tpts2_proj = pts2_proj_homo[:, :2] / pts2_proj_homo[:, 2:3]reproj_errors = np.linalg.norm(pts2 - pts2_proj, axis=1)reproj_score = 1.0 / (np.mean(reproj_errors) + 1e-6)# 3. 法向量合理性(假设平面大致朝前)# 相机前方是z轴正方向camera_forward = np.array([0, 0, 1])n_dot = np.abs(np.dot(n.flatten(), camera_forward))# 综合评分total_score = positive_score * 0.5 + reproj_score * 0.3 + n_dot * 0.2return total_scoredef compute_homography_from_pose(R, t, K, n, d=1.0):"""从位姿计算单应性矩阵"""K_inv = np.linalg.inv(K)H = K @ (R - t @ n.T / d) @ K_invreturn H / H[2, 2]def estimate_scale_from_homography(R, t, n, pts1, pts2, K, d_prior=None):"""从单应性估计尺度方法:通过三角测量恢复3D点,然后估计平面距离"""n_points = len(pts1)if d_prior is None:d_prior = 1.0  # 先验距离# 创建投影矩阵P1 = K @ np.hstack([np.eye(3), np.zeros((3, 1))])P2 = K @ np.hstack([R, t])# 三角测量points_4d = cv2.triangulatePoints(P1, P2, pts1.T, pts2.T)points_3d = points_4d[:3] / points_4d[3]# 过滤无效点depths = points_3d[2, :]valid_mask = depths > 0points_3d_valid = points_3d[:, valid_mask]if len(points_3d_valid[0]) < 5:return 0.0# 估计平面距离# 对于平面上的点,满足 n·X = dn = n.flatten()estimated_d = np.median(np.dot(n, points_3d_valid))# 尺度因子是 estimated_d / d_priorif abs(d_prior) > 1e-6:scale = estimated_d / d_priorelse:scale = 1.0return scaledef test_planar_motion_scenarios():"""测试不同的平面运动场景"""print("="*60)print("不同平面运动场景测试")print("="*60)scenarios = [{'name': '纯旋转','R': R.from_euler('xyz', [10, 5, 15], degrees=True).as_matrix(),'t': np.array([0, 0, 0])},{'name': '平面内平移','R': np.eye(3),'t': np.array([0.5, 0.2, 0])},{'name': '旋转+小平移','R': R.from_euler('xyz', [5, 3, 8], degrees=True).as_matrix(),'t': np.array([0.3, 0.1, 0])},{'name': '大旋转','R': R.from_euler('xyz', [30, 15, 20], degrees=True).as_matrix(),'t': np.array([0.2, 0.1, 0])}]for scenario in scenarios:print(f"\n场景: {scenario['name']}")print("-"*40)test_scenario(scenario['R'], scenario['t'])def test_scenario(R_true, t_true):"""测试特定场景"""# 相机内参K = np.array([[800, 0, 320],[0, 800, 240],[0, 0, 1]])# 平面参数n_true = np.array([0, 0, 1])d_true = 5.0# 生成平面点n_points = 50X_plane = np.random.rand(n_points, 2) * 4 - 2X_plane = np.hstack([X_plane, np.zeros((n_points, 1))])# 相机位姿R1 = np.eye(3)t1 = np.array([0, 0, d_true])# 投影P1 = K @ np.hstack([R1, t1.reshape(3, 1)])P2 = K @ np.hstack([R_true, (t1 + t_true).reshape(3, 1)])X_homo = np.hstack([X_plane, np.ones((n_points, 1))])pts1_homo = (P1 @ X_homo.T).Tpts2_homo = (P2 @ X_homo.T).Tpts1 = pts1_homo[:, :2] / pts1_homo[:, 2:3]pts2 = pts2_homo[:, :2] / pts2_homo[:, 2:3]# 添加噪声pts1 += np.random.randn(*pts1.shape) * 0.5pts2 += np.random.randn(*pts2.shape) * 0.5# 估计单应性H, mask = cv2.findHomography(pts1, pts2, cv2.RANSAC, 3.0)mask = mask.flatten().astype(bool)pts1_inliers = pts1[mask]pts2_inliers = pts2[mask]# 分解num_solutions, rotations, translations, normals = cv2.decomposeHomographyMat(H, K, pts1_inliers, pts2_inliers)# 选择最佳解best_idx = 0best_score = -1for i in range(num_solutions):score = evaluate_solution(rotations[i], translations[i], normals[i], pts1_inliers, pts2_inliers, K, d_true)if score > best_score:best_score = scorebest_idx = iR_est = rotations[best_idx]t_est = translations[best_idx]n_est = normals[best_idx]# 计算误差R_error = np.linalg.norm(R_est - R_true)t_est_norm = t_est / np.linalg.norm(t_est)t_true_norm = t_true / (np.linalg.norm(t_true) + 1e-6)t_dir_error = np.arccos(np.clip(np.dot(t_est_norm.flatten(), t_true_norm), -1.0, 1.0))n_error = np.arccos(np.clip(np.dot(n_est.flatten(), n_true), -1.0, 1.0))print(f"  旋转误差: {R_error:.6f}")print(f"  平移方向误差: {np.degrees(t_dir_error):.2f}°")print(f"  法向量误差: {np.degrees(n_error):.2f}°")return R_est, t_est, n_estdef practical_advice():"""实用建议"""print("\n" + "="*60)print("单应性分解实用建议")print("="*60)advice = ["1. 单应性分解只能恢复单位平移,没有尺度信息","2. 平移尺度需要从其他来源获取:","   - 已知平面距离","   - 其他传感器(IMU、深度相机)","   - 多帧三角测量","3. 选择正确解的关键:","   - 检查正深度点数量","   - 比较重投影误差","   - 验证平面法向量的合理性","4. 适用场景:","   - 平面或近似平面场景","   - 相机纯旋转","   - 远距离场景(近似平面)","5. 局限性:","   - 非平面场景不适用","   - 需要足够的特征点","   - 对噪声敏感","6. 实际应用:","   - 增强现实中的平面跟踪","   - 文档扫描和校正","   - 图像拼接"]for line in advice:print(line)if __name__ == "__main__":print("改进的单应性矩阵分解分析")print("="*60)# 运行改进的分解R_best, t_best, n_best, scale = improved_decompose_homography()# 测试不同场景test_planar_motion_scenarios()# 提供实用建议practical_advice()

  

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

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

相关文章

科研论文提取难?MinerU+LaTeX_OCR部署实战案例

科研论文提取难&#xff1f;MinerULaTeX_OCR部署实战案例 科研人员每天面对大量PDF格式的论文&#xff0c;但真正能“读懂”它们的工具却不多。多栏排版、嵌套表格、复杂公式、矢量图混排——这些在人类眼里一目了然的内容&#xff0c;对传统PDF解析工具来说却是连环陷阱。复制…

大面积修复卡顿?fft npainting lama性能优化建议

大面积修复卡顿&#xff1f;FFT NPainting LAMA性能优化建议 在使用 FFT NPainting LAMA 进行图像大面积修复时&#xff0c;不少用户反馈&#xff1a;标注一大片区域后点击“ 开始修复”&#xff0c;界面长时间卡在“执行推理…”状态&#xff0c;等待30秒甚至超过1分钟仍无响…

亲自动手试了Qwen-Image-2512,AI修图竟如此简单

亲自动手试了Qwen-Image-2512&#xff0c;AI修图竟如此简单 你有没有过这样的经历&#xff1a;刚拍完一张风景照&#xff0c;却发现右下角有个碍眼的水印&#xff1b;辛苦做的产品图&#xff0c;客户临时要求把LOGO换成新版本&#xff1b;或者想给老照片换背景&#xff0c;又不…

为什么Qwen3-4B部署慢?镜像免配置优化教程提升启动效率

为什么Qwen3-4B部署慢&#xff1f;镜像免配置优化教程提升启动效率 1. 真实体验&#xff1a;从点击部署到能用&#xff0c;等了整整7分23秒 你是不是也遇到过这样的情况——在镜像平台点下“一键部署”Qwen3-4B-Instruct-2507&#xff0c;然后盯着进度条发呆&#xff1a;模型…

FSMN VAD社区贡献指南:如何参与二次开发

FSMN VAD社区贡献指南&#xff1a;如何参与二次开发 1. 为什么FSMN VAD值得你投入时间参与开发&#xff1f; FSMN VAD是阿里达摩院FunASR项目中开源的语音活动检测&#xff08;Voice Activity Detection&#xff09;模型&#xff0c;以轻量、高精度、低延迟著称。它仅1.7MB大…

Llama3-8B对话体验最佳实践:Open-WebUI参数调优部署教程

Llama3-8B对话体验最佳实践&#xff1a;Open-WebUI参数调优部署教程 1. 为什么选Llama3-8B&#xff1f;轻量与能力的黄金平衡点 你是不是也遇到过这些情况&#xff1a;想本地跑个大模型&#xff0c;但显卡只有RTX 3060&#xff0c;装完Llama3-70B直接爆显存&#xff1b;或者试…

告别手动输入!用cv_resnet18_ocr-detection自动识别发票内容

告别手动输入&#xff01;用cv_resnet18_ocr-detection自动识别发票内容 在财务、行政和中小企业日常运营中&#xff0c;发票处理是高频却低效的重复劳动&#xff1a;一张张扫描、截图、再逐字录入系统&#xff0c;平均耗时3-5分钟/张&#xff0c;出错率高&#xff0c;月底扎堆…

有源蜂鸣器PWM频率配置:完整指南

以下是对您提供的博文《有源蜂鸣器PWM频率配置&#xff1a;完整技术分析指南》的 深度润色与专业重构版本 。本次优化严格遵循您的全部要求&#xff1a; ✅ 彻底去除AI腔调与模板化结构&#xff08;如“引言/概述/总结”等机械分节&#xff09; ✅ 所有内容以 真实嵌入式工…

Qwen3-4B部署教程:3步完成GPU算力适配,支持256K长文本处理

Qwen3-4B部署教程&#xff1a;3步完成GPU算力适配&#xff0c;支持256K长文本处理 1. 这个模型到底能做什么&#xff1f; 你可能已经听说过Qwen系列&#xff0c;但Qwen3-4B-Instruct-2507不是简单升级——它是一次面向真实工作流的深度打磨。这不是一个“参数堆出来”的模型&…

Llama3与Qwen-Image多模态对比:文本生成vs图像生成实战评测

Llama3与Qwen-Image多模态对比&#xff1a;文本生成vs图像生成实战评测 在AI应用落地过程中&#xff0c;一个常被忽略的关键事实是&#xff1a;文本模型和图像模型根本不是同一类工具——就像锤子和画笔&#xff0c;各自擅长的领域截然不同。但很多人仍习惯把Llama3和Qwen-Ima…

NewBie-image-Exp0.1生成失败?数据类型冲突修复全流程指南

NewBie-image-Exp0.1生成失败&#xff1f;数据类型冲突修复全流程指南 你是不是刚打开NewBie-image-Exp0.1镜像&#xff0c;运行python test.py后却只看到一串红色报错&#xff1f; 最常见的就是这行&#xff1a;TypeError: float object cannot be interpreted as an integer&…

Qwen3-4B-Instruct响应不一致?温度参数调优实战指南

Qwen3-4B-Instruct响应不一致&#xff1f;温度参数调优实战指南 1. 为什么你总感觉Qwen3-4B-Instruct“忽冷忽热” 你是不是也遇到过这些情况&#xff1a; 同一个提示词&#xff0c;第一次生成逻辑清晰、条理分明&#xff1b;第二次却答非所问、自相矛盾&#xff1b;让它写一…

NewBie-image-Exp0.1降本部署案例:节省环境配置时间90%实操手册

NewBie-image-Exp0.1降本部署案例&#xff1a;节省环境配置时间90%实操手册 你是不是也经历过——为了跑通一个动漫生成模型&#xff0c;花整整两天配环境&#xff1a;装CUDA版本对不上、PyTorch和Diffusers版本冲突、源码报错找不到原因、模型权重下到一半断连……最后发现&a…

FSMN-VAD部署全流程:从环境配置到Web界面调用详细步骤

FSMN-VAD部署全流程&#xff1a;从环境配置到Web界面调用详细步骤 1. 这不是“语音识别”&#xff0c;而是更底层的“听觉开关” 你有没有遇到过这样的问题&#xff1a;一段5分钟的会议录音&#xff0c;真正说话的时间可能只有2分半&#xff0c;中间夹杂着大量咳嗽、翻纸、键…

实测分享:我用Open-AutoGLM做了这些神奇操作

实测分享&#xff1a;我用Open-AutoGLM做了这些神奇操作 摘要&#xff1a;这不是一篇理论堆砌的教程&#xff0c;而是一份真实、有温度、带细节的实测手记。我用Open-AutoGLM在真实手机上完成了12个日常任务&#xff0c;从点外卖到跨平台同步消息&#xff0c;全程记录卡点、惊喜…

YOLOE功能测评:文本/视觉/无提示三种模式对比

YOLOE功能测评&#xff1a;文本/视觉/无提示三种模式对比 你有没有遇到过这样的场景&#xff1a;在工业质检现场&#xff0c;突然要识别一种从未标注过的缺陷类型&#xff1b;在智能仓储中&#xff0c;客户临时要求新增“可折叠快递箱”这一类别&#xff1b;又或者在科研图像分…

深入解析电感的作用与电源稳定性关系

以下是对您原文的 深度润色与专业重构版博文 ,严格遵循您的全部要求: ✅ 彻底去除AI痕迹,语言自然如资深工程师口吻; ✅ 打破“引言-概述-总结”模板,以真实工程痛点切入、层层递进; ✅ 所有技术点均融合于逻辑流中,无生硬分节,标题生动贴切; ✅ 关键参数、公式…

开发者必看:GPEN人像增强镜像一键部署实操手册

开发者必看&#xff1a;GPEN人像增强镜像一键部署实操手册 你是否遇到过这样的问题&#xff1a;手头有一张模糊、有噪点、带压缩痕迹的人像照片&#xff0c;想快速修复却卡在环境配置上&#xff1f;装CUDA版本不对、PyTorch和numpy版本冲突、face检测模型下载失败……折腾两小…

GPEN更新日志解读:20260104版本新增功能实战演示

GPEN更新日志解读&#xff1a;20260104版本新增功能实战演示 1. 这次更新到底带来了什么&#xff1f; 你可能已经注意到&#xff0c;GPEN图像肖像增强工具在2026年1月4日悄悄完成了一次重要升级。这次不是小修小补&#xff0c;而是围绕真实用户反馈做的深度打磨——它变得更聪…

小白必看:用GPEN镜像快速实现人脸修复实战

小白必看&#xff1a;用GPEN镜像快速实现人脸修复实战 你有没有遇到过这些情况&#xff1a;翻出老照片&#xff0c;却发现人脸模糊、有噪点、甚至缺损&#xff1b;客户发来一张低分辨率证件照&#xff0c;却要求输出高清印刷级人像&#xff1b;社交媒体上想发一张精致自拍&…