OpenCV+相机校准和3D重建

相机校准至少需要10个测试图案,所需的重要输入数据是3D现实世界点集以及图像中这些点的相应2D坐标。3D点称为对象点,而2D图像点称为图像点。

准备工作

除了棋盘,我们还可以使用圆形网格。 在这种情况下,我们必须使用函数cv.findCirclesGrid()来找到模式。 较少的图像足以使用圆形网格执行相机校准。
一旦找到拐角,就可以使用cv.cornerSubPix()来提高其精度。我们还可以使用cv.drawChessboardCorners()绘制图案。

import numpy as np
import cv2 as cv
import glob
# 终止条件
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 准备对象点, 如 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
# 用于存储所有图像的对象点和图像点的数组。
objpoints = [] # 真实世界中的3d点
imgpoints = [] # 图像中的2d点
images = glob.glob('*.jpg')
for fname in images:img = cv.imread(fname)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 找到棋盘角落
ret, corners = cv.findChessboardCorners(gray, (7,6), None)
# 如果找到,添加对象点,图像点(细化之后)
if ret == True:objpoints.append(objp)
corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria)
imgpoints.append(corners)
# 绘制并显示拐角
cv.drawChessboardCorners(img, (7,6), corners2, ret)
cv.imshow('img', img)
cv.waitKey(500)
cv.destroyAllWindows()

校准

现在我们有了目标点和图像点,现在可以进行校准了。我们可以使用函数cv.calibrateCamera()返回相机矩阵,失真系数,旋转和平移矢量等。

ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints,
gray.shape[::-1], None, None)

不失真

现在,我们可以拍摄图像并对其进行扭曲。OpenCV提供了两种方法来执行此操作。但是,首先,我们可以使用cv.getOptimalNewCameraMatrix()基于自由缩放参数来优化相机矩阵。如果缩放参数alpha = 0,则返回具有最少不需要像素的未失真图像。因此,它甚至可能会删除图像角落的一些像素。如果alpha = 1,则所有像素都保留有一些额外的黑色图像。此函数还返回可用于裁剪结果的图像ROI。

使用cv.undistort()

这是最简单的方法。只需调用该函数并使用上面获得的ROI裁剪结果即可。

使用remapping

该方式有点困难。首先,找到从扭曲图像到未扭曲图像的映射函数。然后使用重映射功能。

img = cv.imread('left12.jpg')
h, w = img.shape[:2]
newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
# 1
dst = cv.undistort(img, mtx, dist, None, newcameramtx)
# 剪裁图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult.png', dst)
# 2
mapx, mapy = cv.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w,h), 5)
dst = cv.remap(img, mapx, mapy, cv.INTER_LINEAR)
# 裁剪图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult.png', dst)

参数:重投影误差

重投影误差可以很好地估计找到的参数的精确程度。重投影误差越接近零,我们发现的参数越准确。给定固有,失真,旋转和平移矩阵,我们必须首先使用cv.projectPoints()将对象点转换为图像点。然后,我们可以计算出通过变换得到的绝对值和拐角发现算法之间的绝对值范数。为了找到平均误差,我们计算为所有校准图像计算的误差的算术平均值。

mean_error = 0
for i in xrange(len(objpoints)):
imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)/len(imgpoints2)
mean_error += error
print( "total error: {}".format(mean_error/len(objpoints)) )

姿态估计

先优化。然后使用函数cv.solvePnPRansac()计算旋转和平移。一旦有了这些变换矩阵,就可以使用它们将轴点投影到图像平面上。

for fname in glob.glob('left*.jpg'):
img = cv.imread(fname)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret, corners = cv.findChessboardCorners(gray, (7,6),None)
if ret == True:
corners2 = cv.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
# 找到旋转和平移矢量。
ret,rvecs, tvecs = cv.solvePnP(objp, corners2, mtx, dist)
# 将3D点投影到图像平面
imgpts, jac = cv.projectPoints(axis, rvecs, tvecs, mtx, dist)
img = draw(img,corners2,imgpts)
cv.imshow('img',img)
k = cv.waitKey(0) & 0xFF
if k == ord('s'):
cv.imwrite(fname[:6]+'.png', img)
cv.destroyAllWindows()

对极几何

首先我们需要在两个图像之间找到尽可能多的匹配项,以找到基本矩阵。为此,我们将SIFT描述符与基于FLANN的匹配器和比率测试结合使用。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img1 = cv.imread('myleft.jpg',0) #索引图像 # left image
img2 = cv.imread('myright.jpg',0) #训练图像 # right image
sift = cv.SIFT()
# 使用SIFT查找关键点和描述符
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
# FLANN 参数
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)
flann = cv.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)
good = []
pts1 = []
pts2 = []
# 根据Lowe的论文进行比率测试
for i,(m,n) in enumerate(matches):
if m.distance < 0.8*n.distance:
good.append(m)
pts2.append(kp2[m.trainIdx].pt)
pts1.append(kp1[m.queryIdx].pt)

现在,我们获得了两张图片的最佳匹配列表。 让我们找到基本面矩阵。

pts1 = np.int32(pts1)
pts2 = np.int32(pts2)
F, mask = cv.findFundamentalMat(pts1,pts2,cv.FM_LMEDS)
# 我们只选择内点
pts1 = pts1[mask.ravel()==1]
pts2 = pts2[mask.ravel()==1]

接下来,我们找到Epilines。在第二张图像上绘制与第一张图像中的点相对应的Epilines。因此,在这里提到正确的图像很重要。我们得到了一行线。因此,我们定义了一个新功能来在图像上绘制这些线条。

def drawlines(img1,img2,lines,pts1,pts2):
''' img1 - 我们在img2相应位置绘制极点生成的图像
lines - 对应的极点 '''
r,c = img1.shape
img1 = cv.cvtColor(img1,cv.COLOR_GRAY2BGR)
img2 = cv.cvtColor(img2,cv.COLOR_GRAY2BGR)
for r,pt1,pt2 in zip(lines,pts1,pts2):
color = tuple(np.random.randint(0,255,3).tolist())
x0,y0 = map(int, [0, -r[2]/r[1] ])
x1,y1 = map(int, [c, -(r[2]+r[0]*c)/r[1] ])
img1 = cv.line(img1, (x0,y0), (x1,y1), color,1)
img1 = cv.circle(img1,tuple(pt1),5,color,-1)
img2 = cv.circle(img2,tuple(pt2),5,color,-1)
return img1,img2

现在,我们在两个图像中都找到了Epiline并将其绘制。

# 在右图(第二张图)中找到与点相对应的极点,然后在左图绘制极线
lines1 = cv.computeCorrespondEpilines(pts2.reshape(-1,1,2), 2,F)
lines1 = lines1.reshape(-1,3)
img5,img6 = drawlines(img1,img2,lines1,pts1,pts2)
# 在左图(第一张图)中找到与点相对应的Epilines,然后在正确的图像上绘制极线
lines2 = cv.computeCorrespondEpilines(pts1.reshape(-1,1,2), 1,F)
lines2 = lines2.reshape(-1,3)
img3,img4 = drawlines(img2,img1,lines2,pts2,pts1)
plt.subplot(121),plt.imshow(img5)
plt.subplot(122),plt.imshow(img3)
plt.show()

立体图像的深度图

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
imgL = cv.imread('tsukuba_l.png',0)
imgR = cv.imread('tsukuba_r.png',0)
stereo = cv.StereoBM_create(numDisparities=16, blockSize=15)
disparity = stereo.compute(imgL,imgR)
plt.imshow(disparity,'gray')
plt.show()

结果受到高度噪声的污染。通过调整numDisparities和blockSize的值,可以获得更好的结果。
参数:

  • texture_threshold:过滤出纹理不足以进行可靠匹配
  • 区域斑点范围和大小:基于块的匹配器通常会在对象边界附近产生“斑点”,其中匹配窗口捕获一侧的前景和背景
  • 在另一场景中,匹配器似乎还在桌子上投影的纹理中找到小的虚假匹配项。为了消除这些伪像,我们使用由speckle_size和speckle_range参数控制的散斑滤镜对视差图像进行后处理。speckle_size是将视差斑点排除为“斑点”的像素数。speckle_range控制必须将值差异视为同一对象的一部分的程度
  • 视差数量:滑动窗口的像素数。它越大,可见深度的范围就越大,但是需要更多的计算
  • min_disparity:从开始搜索的左像素的x位置开始的偏移量
  • uniqueness_ratio:另一个后过滤步骤。如果最佳匹配视差不足够好于搜索范围中的所有其他视差,则将像素滤出。如果texture_threshold和斑点过滤仍在
  • 通过虚假匹配,则可以尝试进行调整
  • prefilter_size和prefilter_cap:预过滤阶段,可标准化图像亮度并增强纹理,以准备块匹配
  • 通常,你不需要调整这些

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

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

相关文章

行业洞察:分布式云如何助力媒体与娱乐业实现创新与增长?

过去数年&#xff0c;流媒体经历了蓬勃的发展过程&#xff0c;观众可以根据喜好收看自己所喜爱的节目内容&#xff0c;并希望在全球范围内访问内容。 繁荣的市场让媒体和娱乐行业的 IT 领导者们竞相发力&#xff0c;用更短的时间去创造互动且令人难忘的内容体验&#xff0c;力求…

20.有效的括号(LeetCode)

思路&#xff1a;用栈的后进先出的特性&#xff0c;来完成题目的要求 因为C有库&#xff0c;可以直接用&#xff0c;而C语言没有&#xff0c;所以我们直接把写好的栈拷贝上来用。 首先&#xff0c;完成框架的搭建 其次&#xff0c;再实现循环内的部分。1.左括号入栈 2.右括…

react函数式组件props形式子向父传参

父组件中定义 子组件中触发回调传值 import { useState } from "react"; function Son(params) {const [count, setCount] useState(0);function handleClick() {console.log(params, paramsparamsparamsparamsparamsparams);params.onClick(111)setCount(count 1…

Skywalking流程分析_3(服务的准备、启动、关闭)

前文将SkyWalkingAgent.premain中的&#xff1a; SnifferConfigInitializer.initializeCoreConfig(agentArgs)pluginFinder new PluginFinder(new PluginBootstrap().loadPlugins())这两个方法分析完毕&#xff0c;下面继续分析premain方法其余部分 创建byteBuddy final By…

Rust-使用dotenvy加载和使用环境变量

系统的开发&#xff0c;测试和部署离不开环境变量&#xff0c;今天分享在Rust的系统开发中&#xff0c;使用dotenvy来读取和使用环境变量。 安装 cargo add dotenvy dotenv_codegen 加载环境变量 use dotenvy::dotenv;fn main() {dotenv().expect(".env不存在");…

TensorFlow学习笔记--(3)张量的常用运算函数

损失函数及求偏导 通过 tf.GradientTape 函数来指定损失函数的变量以及表达式 最后通过 gradient(%损失函数%,%偏导对象%) 来获取求偏导的结果 独热编码 给出一组特征值 来对图像进行分类 可以用独热编码 0的概率是第0种 1的概率是第1种 0的概率是第二种 tf.one_hot(%某标签…

Python高级语法----使用Python进行模式匹配与元组解包

文章目录 1. 模式匹配的新特性2. 高级元组解包技巧3. 数据类的匹配与应用1. 模式匹配的新特性 Python自3.10版本起引入了结构化模式匹配的新特性,这是一种强大的工具,允许开发者用更清晰、更直观的方式处理数据结构。模式匹配类似于其他编程语言中的switch-case语句,但它更…

Rust逆向学习 (4)

Reverse for Struct Rust中的结构体是一个重要的内容&#xff0c;由于Rust中没有类的概念&#xff0c;因此其他编程语言中的封装、继承、多态与Rust中的表现都有较大差异。 我们使用参考书中的一个示例开始进行分析。 Struct 初始化 struct User {username: String,email: …

Kubernetes介绍

Kubernetes介绍 1.应用部署方式演变 在部署应用程序的方式上&#xff0c;主要经历了三个时代&#xff1a; 传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其它技术的参与 缺点&#xff1a;不能为应用程序…

利用uni-app 开发的iOS app 发布到App Store全流程

1.0.3 20200927 更新官方对应用审核流程的状态。 注&#xff1a;最新审核后续将同步社区另一篇记录 AppStore 审核被拒原因记录及解决措施 &#xff1a;苹果开发上架常见问题 | appuploader使用教程 1.0.2 20200925 新增首次驳回拒绝邮件解决措施。 1.0.1 20200922 首次…

目标检测问题总结

目标检测问题总结 目标检测二阶段和一阶段的核心区别目标检测二阶段比一阶段的算法精度高的原因1. 正负样本不平衡2.样本的不一致性 如何解决目标检测中遮挡问题如何解决动态目标检测FPN的作用如何解决训练数据样本过少的问题IOU代码实现NMS代码实现NMS的改进思路 目标检测二阶…

C //例 7.13 有一个3*4的矩阵,求所有元素中的最大值。

C程序设计 &#xff08;第四版&#xff09; 谭浩强 例 7.13 例 7.13 有一个3*4的矩阵&#xff0c;求所有元素中的最大值。 IDE工具&#xff1a;VS2010 Note: 使用不同的IDE工具可能有部分差异。 代码块 方法&#xff1a;使用指针、动态分配内存 #include <stdio.h> …

设计模式-观察者模式(Observer)

设计模式-观察者模式&#xff08;Observer&#xff09; 一、观察者模式概述1.1 什么是观察者模式1.2 简单实现观察者模式1.3 使用观察者模式的注意事项 二、观察者模式的用途三、观察者模式实现方式3.1 使用接口实现观察者模式3.2 使用抽象类和具体子类实现观察者模式3.3 使用 …

Rust语言基础:从Hello World开始

大家好&#xff0c;我是[lincyang]。 我们将一起探索Rust语言的基础&#xff0c;从最经典的程序入手——“Hello, World!”。 Rust简介 Rust是一种系统编程语言&#xff0c;由Mozilla赞助开发&#xff0c;旨在提供内存安全、并发性和实用性。它的设计思想强调安全性和性能&…

CSS怎么选择除了第一个子元素外的其余同级子元素

使用 CSS 的:not()伪类选择器和:nth-child()伪类选择器 要通过CSS的代码选择某一个元素的除了第一个子元素外的其余的跟第一个子元素同级的子元素&#xff0c;可以结合使用CSS的:not()伪类选择器和:nth-child()伪类选择器进行选择。大致的语法如下&#xff1a; .parent > …

【EI会议征稿】第七届结构工程与工业建筑国际学术会议(ICSEIA 2024)

第七届结构工程与工业建筑国际学术会议&#xff08;ICSEIA 2024&#xff09; 2024 7th International Conference on Structural Engineering and Industrial Architecture 随着城市化进程的不断深入&#xff0c;建筑领域的需求也在优化、调整。结构工程的发展依旧受到重视&am…

c语言-数据结构-链表分割

链表分割实际上是给定一个值&#xff0c;遍历链表把链表中小于该值的节点与大于该值的节点分开&#xff0c;一般是将小于该值的节点放到链表的前面部分&#xff0c;大于该值的节点放在链表的后面部分。 链表分割示意图如下&#xff1a; 思路&#xff1a; 首先创建两条带哨兵位节…

CSDN每日一题学习训练——Java版(字符串相乘、子集、删除链表的倒数第 N 个结点)

版本说明 当前版本号[20231112]。 版本修改说明20231112初版 目录 文章目录 版本说明目录字符串相乘题目解题思路代码思路补充说明参考代码 子集题目解题思路代码思路参考代码 删除链表的倒数第 N 个结点题目解题思路代码思路参考代码 字符串相乘 题目 给定两个以字符串形…

【Linux】第十六站:进程地址空间

文章目录 一、程序地址空间1.内存的分布2.static修饰后为什么不会被释放3.一个奇怪的现象 二、进程地址空间1.前面现象的原因2.地址空间究竟是什么&#xff1f;3.为什么要有进程地址空间4.页表5.什么叫进程&#xff1f;6.进程具有独立性。为什么&#xff1f;怎么做到呢&#xf…