全景图像畸变校正

news2024/10/6 8:25:04

1.简介

理想的相机基本上是小孔成像的,在小孔成像模型中,如果焦距一定,那么图像传感器像素平面的面积直接决定了相机视场角的大小,超过这个视场角范围的物体不会被镜头获取到。因此基于透镜成像原理的相机,视场角无法做到足够大,水平视场角一般小于140°。

但是在一些领域,比如气象科学,科技工作者需要对天空天象变化进行观测,需要有一种相机能将整个半球形天空一次性拍摄下来。而在安防监控领域,安保团队期望有一种相机能从俯视角度一次性拍摄整个监控区域。而为了实现这些目的,就需要相机具有水平180°甚至更大的视场角能力。

就在科研人员陷入苦苦思索的时候,这个时候仿生学义无反顾站了出来。科学家们发现鱼的眼睛在往上看的时候,可以看到水面上整个半球形空间。细究原因,科学家发现由于水的折射率比空气大,光线由空气进入水中后会发生折射,且折射角比入射角要小。同时随着入射角增加,折射角变小的程度也增加。基于这个特性,水面上180°半球形空间的物体就可以被扭曲、压缩到一个有限的成像平面上。
 

cc86a94cf53de5a2320bdda0e2a2dab5.png

虽然这样大大的增加了视场角,能够看到的角度更大,但是这样也产生了图像畸变的问题。

2.图像畸变

相机的成像过程实质上是坐标系的转换。首先空间中的点由 “世界坐标系” 转换到 “像机坐标系”,然后再将其投影到成像平面 ( 图像物理坐标系 ) ,最后再将成像平面上的数据转换到 图像像素坐标系。但是由于透镜制造精度以及组装工艺的偏差会引入畸变,导致原始图像的失真。镜头的畸变分为径向畸变和切向畸变两类。参见:

http://blog.csdn.net/dcrmg/article/details/52950141

http://blog.csdn.net/waeceo/article/details/50580808

由于切向畸变是由于组装工艺的偏差,所以我们大部分主要解决的是图像径向畸变的校正。

3.畸变校正

想要对图像进行畸变校正先要知道几个参数,分别是相机的内参,包括相机的焦距、成像中心和畸变参数;相机的外参,包括旋转矩阵和平移矩阵。如果你详细的知道相机的内参,那么图像校正就很容易,但大多数情况你是不知道的,这样就需要通过相机的标定来得到相机的内参和外参了。

现在最常用的方法是张正友标定法,不知道的去搜一下就了解了。具体的原理有能力的可以了解下,但最重要的还是知道如何使用就行了。

最常用的就是用棋盘格对相机参数进行标定,步骤如下:

1.需要一个棋盘格,精度越高越好,最好整个棋盘格都处于同一平面中,没有凹凸。

2.从多个视角拍摄棋盘格,棋盘格必须全部出现在图像中,最好能出现在图像的各个位置,比如一张图像中棋盘格出现在图像的左上角,一张出现在右上角。最好能够拍摄10~15张图像。

3.用opencv自带的包进行棋盘格角点的检测,得到世界坐标系下角点的坐标objpoint和像素坐标系下角点的像素坐标imgpoint。

4.利用objpoint和imgpoint来标定相机参数,得到相机的内参、畸变系数、旋转矩阵和平移矩阵。

5.利用得到的相机参数对图像进行畸变校正。

4.畸变校正代码

# coding:utf-8
import cv2
import numpy as np
import glob

# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 棋盘格模板规格
w = 9
h = 6
# 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵
objp = np.zeros((w * h, 3), np.float32)
objp[:, :2] = np.mgrid[0:w, 0:h].T.reshape(-1, 2)
# 储存棋盘格角点的世界坐标和图像坐标对
objpoints = []  # 在世界坐标系中的三维点
imgpoints = []  # 在图像平面的二维点

images = glob.glob('D:images\\*.jpg')
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 找到棋盘格角点
    ret, corners = cv2.findChessboardCorners(gray, (w, h), None)
    # 如果找到足够点对,将其存储起来
    if ret == True:
        cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        objpoints.append(objp)
        imgpoints.append(corners)
        # 将角点在图像上显示
        cv2.drawChessboardCorners(img, (w, h), corners, ret)
        cv2.imshow('findCorners', img)
        # cv2.imwrite('D:images\\grid_out.png', img)
        cv2.waitKey(1)
cv2.destroyAllWindows()

# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# print(mtx)
# print(dist)
# print(rvecs)
# print(tvecs)
# 去畸变
img2 = cv2.imread('D:images\\10.jpg')
h, w = img2.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))  # 自由比例参数
dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
# 根据前面ROI区域裁剪图片
# x,y,w,h = roi
# dst = dst[y:y+h, x:x+w]
cv2.imwrite('D:images\\grid_out.png', dst)

# 反投影误差
total_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
    total_error += error
print("total error: ", total_error / len(objpoints))

# 校正视频
cap = cv2.VideoCapture('D:video\\video.mp4')
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
fps = int(cap.get(cv2.CAP_PROP_FPS))
frame_size = (width, height)
video_writer = cv2.VideoWriter('D:video\\result2.mp4', cv2.VideoWriter_fourcc(*"mp4v"), fps, frame_size)
for frame_idx in range(int(cap.get(cv2.CAP_PROP_FRAME_COUNT))):
    ret, frame = cap.read()
    if ret:
      image_ = cv2.undistort(frame, mtx, dist, None, newcameramtx)
      cv2.imshow('jiaozheng', image_)
      # gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
      video_writer.write(image_)
    if cv2.waitKey(10) & 0xFF== ord('q'):
        break
cap.release()
# cv2.destroyALLWindows()

5.其他补充

如果没有标定板或者不方便使用标定板的情况,我们想要标定摄像机参数可以不用角点检测来得到objpoint,可以自己手动标记,最好是在图像中放一些标志物,最好是矩形表格状,如4x4。这样标定虽然有些误差但是也能有不错的效果。

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

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

相关文章

JAVA解析XML时的内存消耗问题

问题出现 最近一个项目中,有个需求功能是从外部传入的XML读取数据,然后写入到数据库中。 写完之后,有在本地电脑上的Tomcat跑了一下,正常读取到XML中的数据,并整理好后,插入了数据库保存了。但是线上运行的…

C语言文件操作复习回顾(2)

TIPS 文件的顺序读写:fgetc, fputc, fputs(一行字符串的输出\n注意一下), fgets(一行字符串的输入\n三特性),fprintf(格式化字符串的输出联想printf很简单),fscanf&…

pyspark 实验二,rdd编程

1.环境准备 start-all.sh启动Hadoop ./bin start-all.sh 启动spark 上传数据集 1.求该系总共多少学生 linessc.textFile("file:///home/data.txt") res lines.map(lambda x:x.split(",")).map(lambda x:x[0]) sumres.distinct() sum.cont() 2.求该系设置…

【MybatisPlus快速入门】—— 进阶入门

进阶篇 1.1 映射专题 Mybatis 框架之所以能够简化数据库操作,是因为他内部的映射机制,通过自动映射,进行数据的封装,我们只要符合映射规则,就可以快速高效的完成SQL操作的实现既然 MybatisPlus 是基于Mybatis 的增强…

程序员如何能提高自己的编程水平?

这些实用的小建议,能帮你迅速地提高编程水平: 不要做无意义的奋斗 拒绝喊口号和无意义的奋斗,包括但不限于: ①做了计划表却从未有执行的一天; ②每天都是最早来、最晚走,但是工作进度趋近于0&#xff1b…

ASP.NET Core MVC 从入门到精通之接化发(一)

随着技术的发展,ASP.NET Core MVC也推出了好长时间,经过不断的版本更新迭代,已经越来越完善,本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容,适用于初学者,在校毕业生&#xff0c…

PathCore:IAD文献解读

论文链接:[Towards Total Recall in Industrial Anomaly Detection]Towards Total Recall in Industrial Anomaly Detection :数据集, :标签 : 在ImageNet上预训练后的网络 第 张图 网络中第 层 1. Locall…

Sentinel学习笔记

Sentinel 官方文档: https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5 SpringCloud Alibaba: https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel 是什么…

丝滑的打包部署,一套带走~

以下文章来源于悟空聊架构 ,作者悟空聊架构 本文主要内容如下: 目录 一、背景 Docker打包部署方案 项目背景:新项目的后端框架是刚起步,搭建的是一套微服务框架,基础服务有网关 Gateway, Nacos 注册中心…

为什么stm32gpio引脚的翻转速度最大只有18Mhz

(1). GPIO 引脚速度:GPIO_Speed_2MHz (10MHz, 50MHz) ; 又称输出驱动电路的响应速度:(芯片内部在I/O口的输出部分安排了多个响应速度不同的输出驱动电路,用户可以根据自己的需要选择合适的驱动电路,通过选择速度来选择…

史上最全测试开发工具视频教程详解(含自动化、性能、接口、抓包)

目录 一、UI自动化测试工具 1. uiautomator2 2. Appium 3. ATX-Test 4. Airtest 5. ATXServer2 6. STF 7. Appetizer 二、APP稳定性测试工具 8. UICrawler 9. Maxim 10. AppCrawler 三、APP性能测试工具 11. SoloPi 12. GT 四、抓包工具 13. AnyProxy …

pytorch安装教程(二)

一直用的pytorch1.2,有点老了,想换个新版本,换成了pytorch2.0. torch安装 安装过程最重要的就是cuda、cudnn的版本和pytorch对应。 因为要在GPU上跑代码。 删除老旧torch 我用的软件是anaconda,因为可以创建虚拟环境。 步骤&…

LAZADA将缩短履约时效,卖家发货倍感压力

Lazada的跨境卖家们,恐怕又要头疼了。 近日, Lazada官方宣布,为了提升消费者体验,平台将调整商家履约订单时效。从2023年5月4日起生成的订单履约时效将有所更新。 具体而言,内地、香港和Laz Go Global的履约节点为“点…

Qt Quick - MessageDialog 消息提示框

MessageDialog 使用总结一、概述二、使用1. 例子一2. 例子二三、常用属性一、概述 MessageDialog 最基本的用例是弹出警告框。它还允许用户根据启用的按钮以各种方式进行响应。对话框最初是不可见的。你需要首先按需设置属性,然后将visible设置为true或调用open()。…

FIFO设计笔记(双口RAM、同步FIFO、异步FIFO)Verilog及仿真

文章目录0、前言0.1、FIFO0.2、FIFO与RAM1、异步双口RAM1.1、原理1.2、Verilog代码1.3、tb仿真2、FIFO设计前瞻知识2.1、格雷码2.1.1、二进制转格雷码Verilog代码tb仿真2.1.2、格雷码转二进制Verilog代码tb仿真2.2、独热码3、同步FIFO3.1、同步FIFO设计原理3.1.1、设计原理3.1.…

SpringBoot中操作Redis通过所有可能的key查询存在的key并解析为对象实体的通用方法

场景 SpringBoot中操作Redis的特殊操作-批量查询(通过key的集合批量查杜绝模糊搜索)、查询并解析对象list: SpringBoot中操作Redis的特殊操作-批量查询(通过key的集合批量查杜绝模糊搜索)、查询并解析对象list_霸道流氓气质的博客-CSDN博客 在上面讲操作redis中特…

【未来已来】人人都说GPT,人人都怕GPT,人人都用GPT

文章目录前言一、GPT是什么?二、当GPT和AI遇到摄影总结前言 ChatGPT是由美国OpenAI研发的能够通过自然语言驱动的人工智能技术工具,因为它强大的执行力和任务处理能力,一经亮相就引起了极大的关注。与之类似,在图像智能生成方面&…

Java 自学 - 接口与继承 接口

设计 Java 的接口 在设计 LOL 的时候,进攻类英雄有两种,一种是进行物理系攻击,一种是进行魔法系攻击 这时候,就可以使用接口来实现这个效果。 接口就像是一种约定,我们约定某些英雄是物理系英雄,那么他们…

交接机的基本原理

第七章:交接机的基本原理 在网络中传输数据时需要遵循一些标准,以太网协议定义了数据帧在以太网上的传输标准,了解以太网协议是充分理解数据链路层通信的基础。以太网交换机是实现数据链路层通信的主要设备,了解以太网交换机的工作…

抽象轻松web

不管是求最大值,还是最小值,无论是整数还是小数,数据类型是不是一样的 它们的本质上都是判断,在判断的基础上不断的变换,增加判断条件,增加判断过程罢了 判断需要两个本质 1 :两个以上的元素…