相机造成的失真类型

如何找到相机的内在和外在特性

如何基于这些特性来消除图像失真

基础

一些针孔相机会对图像造成严重失真。两种主要的畸变是径向畸变和切向畸变。

径向变形会使直线看起来是弯曲的。点离图像中心越远,径向失真就越大。例如,下面显示了一个图像,其中棋盘的两个边缘用红线标记。但是,你可以看到棋盘的边界不是一条直线,与红线不匹配。所有预期的直线都凸起了。



径向畸变可以表示如下:

类似地,由于图像拍摄透镜没有完全平行于成像平面对准,所以发生切向失真。因此,图像中的某些区域看起来可能比预期的更近。切向畸变的量可以表示如下:

简而言之,我们需要找到五个参数,即失真系数,由下式给出:



除此之外,我们还需要一些其他信息,比如相机的内在和外在参数。固有参数是特定于相机的。它们包括像焦距(fx,fy)和光学中心(cx,cy)这样的信息。焦距和光学中心可用于创建相机矩阵,该矩阵可用于消除特定相机镜头造成的失真。相机矩阵对于特定的相机是唯一的,因此一旦计算出来,它就可以在同一相机拍摄的其他图像上重复使用。它表示为3x3矩阵。



外部参数对应于将3D点的坐标转换为坐标系的旋转和平移矢量。

对于立体应用,需要首先校正这些失真。为了找到这些参数,我们必须提供一些定义良好的模式(例如棋盘)的样本图像。我们找到了一些我们已经知道相对位置的特定点(例如棋盘上的直角)。我们知道这些点在现实世界空间中的坐标,也知道图像中的坐标。所以我们可以求解失真系数。为了获得更好的结果,我们至少需要10个测试模式。

如上所述,我们需要至少10个测试模式来进行相机校准。OpenCV附带了一些棋盘的图像(请参阅samples/data/left01.jpg–left14.jpg),因此我们将使用这些图像。考虑一个棋盘的图像。相机校准所需的重要输入数据是3D真实世界点的集合以及图像中这些点的对应2D坐标。2D图像点是可以的,我们可以很容易地从图像中找到。(这些图像点是棋盘中两个黑色方块相互接触的位置)

那么来自真实世界空间的3D点呢?这些图像是从静态相机中拍摄的,棋盘被放置在不同的位置和方向。所以我们需要知道(X,Y,Z)值。但为了简单起见,我们可以说棋盘在XY平面上保持静止(所以Z=0总是),相机也相应地移动了。这种考虑有助于我们只找到X,Y值。现在,对于X,Y值,我们可以简单地将点传递为(0,0),(1,0),(2,0)。。。其表示点的位置。在这种情况下,我们得到的结果将以棋盘正方形的大小为尺度。但是,如果我们知道正方形的大小(比如30毫米),我们可以将值传递为(0,0)、(30,0),(60,0)、。因此,我们得到了以毫米为单位的结果。(在这种情况下,我们不知道正方形的大小,因为我们没有拍摄这些图像,所以我们以正方形的大小通过)。

3D点称为对象点,2D图像点称为图像点。

Setup

因此,要在棋盘中找到图案,我们可以使用函数cv.findChessboardCorners()。我们还需要传递我们正在寻找的图案类型,如8x8网格、5x5网格等。在本例中,我们使用7x6网格。(通常棋盘有8x8个方格和7x7个内角)。它返回角点和retval,如果获得图案,该值将为True。这些角将按顺序(从左到右,从上到下)放置

Note

此功能可能无法在所有图像中找到所需的图案。因此,一个好的选择是编写代码,这样,它启动相机并检查每一帧所需的模式。获得图案后,找到角落并将其存储在列表中。此外,在阅读下一帧之前提供一些间隔,以便我们可以将棋盘调整到不同的方向。继续此过程,直到获得所需数量的良好图案。即使在这里提供的例子中,我们也不确定给出的14个图像中有多少是好的。因此,我们必须阅读所有的图像,只拍摄好的图像。

我们可以使用圆形网格来代替棋盘。在这种情况下,我们必须使用函数cv.findCirclesGrid()来查找模式。较少的图像足以使用圆形网格执行相机校准。

一旦我们找到了角,我们可以使用cv.canerSubPix()来提高它们的准确性。我们也可以使用cv.drawChessbordCorners()来绘制图案。所有这些步骤都包含在下面的代码中:

点击查看代码
import numpy as np
import cv2 as cv
import glob
# termination criteria
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# prepare object points, like (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)
# Arrays to store object points and image points from all the images.
objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.
images = glob.glob('*.jpg')
for fname in images:
img = cv.imread(fname)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# Find the chess board corners
ret, corners = cv.findChessboardCorners(gray, (7,6), None)
# If found, add object points, image points (after refining them)
if ret == True:
objpoints.append(objp)
corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria)
imgpoints.append(corners2)
# Draw and display the corners
cv.drawChessboardCorners(img, (7,6), corners2, ret)
cv.imshow('img', img)
cv.waitKey(500)
cv.destroyAllWindows()

一张画有图案的图片如下所示:



校准

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

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

Undistortion

现在,我们可以拍摄一张图片并不侵权。OpenCV提供了两种方法。然而,首先,我们可以使用cv.getOptimalNewCameraMatrix()基于自由缩放参数来细化相机矩阵。如果缩放参数alpha=0,它将返回具有最小不需要像素的未失真图像。因此,它甚至可以去除图像角落的一些像素。如果alpha=1,则所有像素都会保留一些额外的黑色图像。此函数还返回可用于裁剪结果的图像ROI。

因此,我们拍摄一张新的图像(在本例中为left12.jpg。这是本章的第一张图像)

点击查看代码
img = cv.imread('left12.jpg')
h, w = img.shape[:2]
newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))

1. Using cv.undistort()
这是最简单的方法。只需调用函数并使用上面获得的ROI来裁剪结果。

点击查看代码
# undistort
dst = cv.undistort(img, mtx, dist, None, newcameramtx)
# crop the image
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult.png', dst)

**2.Using remapping**
这条路有点难。首先,找到一个从失真图像到未失真图像的映射函数。然后使用重映射函数

点击查看代码
# undistort
mapx, mapy = cv.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w,h), 5)
dst = cv.remap(img, mapx, mapy, cv.INTER_LINEAR)
# crop the image
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult.png', dst)

尽管如此,两种方法都给出了相同的结果。结果如下:

你可以从结果中看到,所有的边都是直的。

现在,您可以使用NumPy中的写入函数(np.savez、np.savetxt等)存储相机矩阵和失真系数,以备将来使用。

重投影误差

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

点击查看代码
mean_error = 0
for i in range(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.calibrateCamera的更多相关文章

  1. 我的复杂的OpenCV编译之路(OpenCV3.1.0 + VS2010 + Win7)

    教程:www.cnblogs.com/jliangqiu2016/p/5597501.html 这里主要记载我编译遇到的错误及解决方法. OpenCV3.1软件下载:https://sourcefor ...

  2. 相机标定问题-Matlab & Py-Opencv

    一.相机标定基本理论 1.相机成像系统介绍 图中总共有4个坐标系: 图像坐标系:Op    坐标表示方法(u,v)                 Unit:Dots(个) 成像坐标系:Oi      ...

  3. 基于OpenCV做“三维重建”(2)--封装标定过程

    既然已经能够找到了标定点,那么下边的工作就是使用标定结果了.[这本书在这里的内容组织让人莫名其妙]但是通过阅读代码能够很方便地串起来. /*------------------------------ ...

  4. 【双目备课】《学习OpenCV第18章》相机模型与标定整编

    一.相机模型 针孔模型.在这个简单模型中,想象光线是从场景或一个很远的物体发射过来的,但只有一条光线从该场景中的任意特定点进入针孔. 我们将这个图像进行抽象,就能够得到这样的结果: 其中,f为像到针孔 ...

  5. 车道线识别/Opencv/传统方法

    车道检测(Advanced Lane Finding Project) 实现步骤: 使用提供的一组棋盘格图片计算相机校正矩阵(camera calibration matrix)和失真系数(disto ...

  6. 采用QHD分辨率使用kinect2_calibration,完成QHD图像校正

    //.................................................................................//采用QHD分辨率使用kinec ...

  7. Camera Calibration 相机标定:Opencv应用方法

    本系列文章由 @YhL_Leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/49427383 Opencv中Camer ...

  8. OpenCV-Python 相机校准 | 四十九

    目标 在本节中,我们将学习 由相机引起的失真类型, 如何找到相机的固有和非固有特性 如何根据这些特性使图像不失真 基础 一些针孔相机会给图像带来明显的失真.两种主要的变形是径向变形和切向变形. 径向变 ...

  9. [OpenCV实战]50 用OpenCV制作低成本立体相机

    本文主要讲述利用OpenCV制作低成本立体相机以及如何使用OpenCV创建3D视频,准确来说是模仿双目立体相机,我们通常说立体相机一般是指双目立体相机,就是带两个摄像头的那种(目就是指眼睛,双目就是两 ...

  10. [OpenCV实战]38 基于OpenCV的相机标定

    文章目录 1 什么是相机标定? 2 图像形成几何学 2.1 设定 2.1.1 世界坐标系 2.1.2 相机坐标系 2.1.3 图像坐标系 2.2 图像形成方法总结 3 基于OpenCV的相机标定原理 ...

随机推荐

  1. 「实操」结合图数据库、图算法、机器学习、GNN 实现一个推荐系统

    本文是一个基于 NebulaGraph 上图算法.图数据库.机器学习.GNN 的推荐系统方法综述,大部分介绍的方法提供了 Playground 供大家学习. 基本概念 推荐系统诞生的初衷是解决互联网时 ...

  2. Java static 关键字的使用 小练习

    1 package com.bytezreo.statictest2; 2 3 /** 4 * 5 * @Description static 关键字的使用 小练习 6 * @author Bytez ...

  3. 冲击900亿美元估值!邀约路演、秘密交表的Shein上市有望

    双十一的狂欢刚刚结束,Shein即将赴美上市的消息又在电商圈里投下一枚重磅炸弹. 继被媒体曝光其寻求900亿美金估值后,最新的消息称其已邀请投资人参与路演,且已秘密完成交表.这个神秘的中国独角兽,离敲 ...

  4. Redis集群Cluster

    Redis Cluster 是社区版推出的 Redis 分布式集群解决方案,主要解决 Redis 分布式方面的需求,比如,当遇到单机内存,并发和流量等瓶颈的时候,Redis Cluster 能起到很好 ...

  5. 获取一段时间内,以月/季度为单位,第N天在各个月/季度是几几年几月几号

    /** * 获取一段时间内(可跨年),以季度为单位,第N天在各个季度是几月几号 * @param $sTime 时间戳 * @param $eTime 时间戳 * @param $number 第N天 ...

  6. Python基础之程序与用户交互

    [一]Python基础之程序与用户交互 [一]程序如何与用户交互 用户通过input命令在窗口内与输入就可以让用户和窗口进行交流 input接受的所有数据类型都是 str 类型 username = ...

  7. 海词 dict.cn 有 词义饼状分布图 和 词性饼状分布图 - 词典推荐

    海词 dict.cn 有 词义饼状分布图 和 词性饼状分布图 http://dict.cn/like

  8. SelectZenEmpty 下拉框 支持 最大长度 超出... vue 组件

    <template> <Select v-model="innerValue" :disabled="disabled" :clearable ...

  9. C++字符串编码转换

    C++中字符串有很多种类,详情参考C++中的字符串类型.本文主要以string类型为例,讲一下字符串的编码,选择string主要是因为: byte是字符串二进制编码的最小结构,字符串本质上就是一个by ...

  10. 文心一言 VS 讯飞星火 VS chatgpt (216)-- 算法导论16.2 3题

    三.假定在 0-1 背包问题中,商品的重量递增序与价值递减序完全一样.设计一个高效算法求此背包问题的变形的最优解,证明你的算法是正确的.如果要写代码,请用go语言. 文心一言: 在0-1背包问题中,如 ...