文章目录

  • 前言
  • 一、光学畸变是什么?
  • 二、校准步骤
    • 1.标定
    • 2.校准
    • 3.矫正
    • 4.重投影误差分析
  • 总结

前言

  开始练习opencv了,对于立体应用方面,这些畸变现象首先需要解决。所以第一个处理的问题就是对于光学畸变的照片进行畸变矫正。


一、光学畸变是什么?

  一些相机会有严重的图像畸变的问题。其中径向畸变和切向畸变是两种主要的畸变现象。径向畸变使得直线变得弯曲。切向畸变使得离图像中心点越远的点看上去更远。
径向畸变:

径向畸变可表示为如下公式:

切向畸变:

切向畸变可表示为如下公式:

简而言之,我们需要找到上面的五个参数,其被称为畸变系数,由下式给出:

  除此之外,我们还需要一些其他信息,像是相机的固有属性和可变属性。固有属性是每个相机的特有属性。其中包括像是焦距(fx,fy)和光心(Cx,Cy)。焦距和光心可以被用于创建相机矩阵,用于消除相机镜头特有属性造成的畸变。每个相机的相机矩阵都是独一无二的,所以一旦我们计算出来,便可以在同一相机所拍摄的其他图像上重复使用。其表示为 3×3 的矩阵:

  如果想要获取更好的畸变系数,我们需要至少 10 个测试图像。图片可以使用opencv自带案例(如何下载opencv自带案例,可以参考另一篇文章。)

二、校准步骤

相机校准所需要的重要输入数据便是 3D 真实世界点的集合以及在图像中这些点所对应的 2D 坐标。3D 点被称作对象点,2D 点被称作图像点。
(1)我们可以轻易从这些图像中寻找到 2D 图像点。(这些图像点是棋盘中两个黑色块相交的位置)。
(2)图像于同一相机静止拍摄,其中的棋盘放置于不同的位置与方向。为了简单起见,我们可以说棋盘在 XY 平面保持静止,(所以 Z 恒等于 0 )而相机是移动物件。现在对于 X,Y 的值,我们可以简单地传递像是(0,0), (1,0), (2,0), … 之类的点用于表示点的位置。在此之下,我们得到的结果将是棋盘方块相对的大小。但是如果我们知道棋盘方块的大小,(大约 30mm),我们便可以传递像(0,0), (30,0), (60,0), …这样的值。因此,我们的到的结果也是 mm 为单位的。(因为我们没有拍摄这些图像,我们不知道方块尺寸,所以我们将方块尺寸作为参数传入。

1.标定

  测量相机焦距和便宜主要的原理是张正友标定法,测量畸变参数是Brown算法。所以为了寻找到棋盘上的图案,我们可以使用一个函数,cv.findChessboardCorners()。该标定函数的一个输入参数是像点坐标,即在摄像机成像平面上对应角点相对于摄像机坐标系的二维坐标。而获得像点坐标的函数第一步就是找到角点坐标,函数是findChessboardCorners(image,patternSize,corners,flags = None)

cv.findChessboardCorners参数:
  image:输入原始的棋盘板图像。该图像必须是一张8位的灰度图或色彩图。
  patternSize:(w,h),棋盘上每一排和每一列的内角数。w=棋盘板一行上黑白块的数量-1,h=棋盘板一列上黑白块的数量-1,例如:10x6的棋盘板,则(w,h)=(9,5)。
  corners:array,检测到的角点的输出数组。
  flags:int,不同的操作标记,能够为0或者下述值的组合:
    CALIB_CB_ADAPTIVE_THRESH 使用自适应阈值法把图像转换为黑白图,而不是使用一个固定的阈值。
    CALIB_CB_NORMALIZE_IMAGE 在利用固定阈值或自适应阈值法二值化图像之前,利用直方图均衡化图像。
    CALIB_CB_FILTER_QUADS 使用额外的标准(如轮廓面积,周长,正方形形状)来过滤掉在轮廓检索阶段提取的假四边形。
    CALIB_CB_FAST_CHECK 对图像运行一个快速检查机制以查找棋盘板的角点,如果没有找到角点则返回一个快捷提醒。当没有观察到棋盘时,可以极大地加快在退化条件下的调用。
cv.cornerSubPix参数:
  image:输入图像
  corners:输入角点的初始坐标以及精准化后的坐标用于输出。
  winSize:搜索窗口边长的一半,例如如果winSize=Size(5,5),则一个大小为的搜索窗口将被使用。
  zeroZone:搜索区域中间的dead region边长的一半,有时用于避免自相关矩阵的奇异性。如果值设为(-1,-1)则表示没有这个区域。
  criteria:角点精准化迭代过程的终止条件。也就是当迭代次数超过criteria.maxCount,或者角点位置变化小于criteria.epsilon时,停止迭代过程。

import numpy as np
import cv2 as cv
import glob# 终止标准
#当任何上面的条件满足就停止迭代
#cv2.TERM_CRITERIA_EPS - 如果满足了指定准确度,epsilon就停止算法迭代。
#cv2.TERM_CRITERIA_MAX_ITER - 在指定次数的迭代后就停止算法。
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(r'D:\test\venv\data\*.jpg')for fname in images:img = cv.imread(fname)gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) #从RBG和BGR颜色空间转换到灰度空间# 找到棋盘上所有的角点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()

2.校准

现在我们拥有了对象点与图像点,我们便可以准备开始校准了。我们使用函数返回相机矩阵,畸变系数,旋转和平移向量等等。
cv.calibrateCamera:
  ret也就是retval,表示的是重投影误差;
  mtx是相机的内参矩阵;
  dist表述的相机畸变参数;
  rvecs表示标定棋盘格世界坐标系到相机坐标系的旋转参数:rotation vectors,需要进行罗德里格斯转换;
  tvecs表示translation vectors,主要是平移参数。
代码如下 :

#通过多个视角的2D/3D对应,求解出该相机的内参数和每一个视角的外参数
ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

3.矫正

(1)使用cv.getOptimalNewCameraMatrix()基于自由缩放参数来优化相机矩阵。如果缩放参数alpha = 0,则返回具有最少不需要像素的未失真图像。因此,它甚至可能会删除图像角落的一些像素。如果alpha = 1,则所有像素都保留有一些额外的黑色图像。此函数还返回可用于裁剪结果的图像ROI。
注:
  矩阵中,[0]表示行数,[1]表示列数
  img.shape[:2]取彩色图片的长、宽
  img.shape[:3]取彩色图片的长、宽、通道
  img.shape[0]图像的垂直尺寸(高度)
  img.shape[1]图像的水平尺寸(宽度)
  img.shape[2]图像的通道数

img = cv.imread('left12.jpg') #使用一张新图像做处理
h, w = img.shape[:2]
newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))

OpenCV提供了两种方法来对拍摄图像进行扭曲:

  1. 使用cv.undistort()
#有时不需要矫正整个图像,而仅仅计算图像中特定点的位置,这是可以使用undistortPoints函数
dst = cv.undistort(img, mtx, dist, None, newcameramtx) # 输入原图 输出矫正后的图像  内参矩阵 畸变系数
#剪裁图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult1.png', dst)

  1. 使用initUndistortRectifyMap()+remap()
    cv.initUndistortRectifyMap:用于计算无畸变和修正转换关系。
      cameraMatrix:相机矩阵
      distCoeffs:相机的畸变系数
      R:可选的修正变换矩阵,是个3*3的矩阵。通过stereoRectify计算得来的R1或R2可以放在这里。如果这个矩阵是空的,就假设为单位矩阵。在cvInitUndistortMap中,R被认为是单位矩阵。
      newCameraMatrix:新的相机矩阵
      size:未畸变的图像尺寸。
      m1type:第一个输出的映射的类型,可以为 CV_32FC1, CV_32FC2或CV_16SC2
    cv.remap:输入图像中各个像素按照一定的规则映射到另外一张图像的对应位置上去,形成一张新的图像。
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('calibresult2.png', dst)


两张照片似乎并无明显差异


4.重投影误差分析

重投影误差可以很好地估计找到的参数的精确程度。重投影误差越接近零,我们发现的参数越准确。
首先,使用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 += errorprint(error)
print( "total error: {}".format(mean_error/len(objpoints)) )

总结

刚刚入门,还是一直在参考各种大佬的文章。文章大部分是参考了极客笔记的《OpenCV-Python 相机校准和消除畸变》,大佬的笔记非常好,强烈推荐!还有一部分是opencv官方中文手册以及其他介绍opencv用法的文章!

opencv-python光学畸变校准相关推荐

  1. 透镜畸变和畸变校准(OpenCV)

    透镜畸变和畸变校准(OpenCV) 透镜畸变 理想的相机模型应该是小孔成像模型,但由于透镜制造和相机镜头安装等因素,必然会导致畸变的存在.畸变主要分为径向畸变.切向畸变和薄棱镜畸变. 1.径向畸变 径 ...

  2. 【ZED】从零开始使用ZED相机(五):Opencv+Python实现相机标定(双目)

    引言 同样Opencv+Python实现双目相机的标定,单目标定详见[ZED]从零开始使用ZED相机(五):Opencv+Python实现相机标定(单目) 1 cv2.stereoCalibrate ...

  3. OpenCV+python:Canny边缘检测算法

    1,边缘处理 图像边缘信息主要集中在高频段,通常说图像锐化或检测边缘,实质就是高频滤波.我们知道微分运算是求信号的变化率,具有加强高频分量的作用. 在空域运算中来说,对图像的锐化就是计算微分.由于数字 ...

  4. OpenCV Python在计算机视觉中的应用

    OpenCV Python教程 在这篇文章中,我们将使用Python中的OpenCv来涵盖计算机视觉的各个方面.OpenCV长期以来一直是软件开发的重要组成部分. 什么是计算机视觉? 我们考虑一个场景 ...

  5. OpenCV Python教程(2、图像元素的访问、通道分离与合并)

    OpenCV Python教程之图像元素的访问.通道分离与合并 转载请详细注明原作者及出处,谢谢! 访问像素 像素的访问和访问numpy中ndarray的方法完全一样,灰度图为: [python] v ...

  6. python中import cv2遇到的错误及安装方法_独家利用OpenCV,Python和Ubidots来构建行人计数器程序(附代码amp;解析)...

    作者:Jose Garcia 翻译:吴振东 校对:张一豪 本文约4000字,建议阅读14分钟. 本文将利用OpenCV,Python和Ubidots来编写一个行人计数器程序,并对代码进行了较为详细的讲 ...

  7. 如何把OpenCV Python获取的图像传递到C层处理

    原文:https://blog.csdn.net/yushulx/article/details/52788051 用OpenCV Python来开发,如果想要用到一些C/C++的图像处理库,就需要创 ...

  8. openCV—Python(6)—— 图像算数与逻辑运算

    openCV-Python(6)-- 图像算数与逻辑运算 一.函数简介 1.add-图像矩阵相加 函数原型:add(src1, src2, dst=None, mask=None, dtype=Non ...

  9. opencv python 图像去噪

    opencv python 图像去噪 文章目录: https://blog.csdn.net/Annihilation7/article/details/82718470 https://segmen ...

最新文章

  1. 90后斯坦福博士论文登Science封面!AI算法准确预测RNA三维结构
  2. 如何证明你的性能测试结果可信?
  3. idea中package和directory的区别
  4. 深度学习(一)深度学习学习资料
  5. nyoj-483--Nightmare--BFS+允许回头
  6. 显微镜下的大明内容_平凡故事展现炮火下人性光辉,李少红《解放·终局营救》创作全解...
  7. mac php7.0 yaf 安装,MAC MAMP PRO PHP YAF 安装
  8. 高铁车厢女童突然抽搐 护士为防其咬舌将手指放口中
  9. JS面试之对象(2)
  10. Oracle expdp 导出
  11. 基于JAVA+SpringMVC+Mybatis+MYSQL的体育器材管理系统
  12. Microsoft AJAX Library对 String的扩展
  13. 软件测试流程改进的几点看法
  14. 用AI「驯服」人类幼崽,手头有娃的可以试试
  15. 坐标中国|中国速度,挑战极限驱动发展“快车”
  16. charles+安卓模拟器采集豆果美食app
  17. Backstepping(反步法)控制初学讲解
  18. 计算机常用英语(键盘符号),计算机常用英语..docx
  19. 怎样才能使你的Mac桌面干净整洁?
  20. c语言进阶编程 培训,大学C语言进阶编程实例.doc

热门文章

  1. 8583报文的使用和解析
  2. Android音频系统学习一:基本概念
  3. 匆匆那年,写给过去年轻的自己 @2014
  4. This may indicate that the storage must be wiped and the GlusterFS nodes must be reset
  5. OpenCV实现动态人脸识别(第三讲)
  6. 保序回归Isotonic Regression
  7. 无线蓝牙运动耳机什么牌子好、好用的运动蓝牙耳机推荐
  8. ols回归结果分析表python_OLS回归结果Python中coef的VIF
  9. python三位数组合有哪些_python组合无重复三位数的实例
  10. 用idea启动项目后桌面上自动生成三个log文件