生成黑白棋盘标定图和单目相机标定(一)(python+opencv实现)
学习记录。
事实上很早就接触过视觉定位这东西,但是到现在才返回头学习一下相机的标定,真是可耻啊!我把想法和过程记录一下。
相机成像
相机的成像原理——小孔成像
然而,在实际由于设计工艺问题、相机安装环境或物体摆放位置等影响,会照成成像与实际图像不一样的现象。
由于设计工艺照成的影响是无法改变的事实,所以这将是相机的内参;
由环境或安装方式照成的影响是可以改变的,这就是相机的外参。
在https://blog.csdn.net/aoulun/article/details/78768570中详细介绍了相机成像原理,相机内、外参数是什么。这里为了保证记录的完整型,把成像平面的像素坐标与实际物体的世界坐标公式写下来。
1.红框就是相机外参,R为旋转矩阵,T为平移向量;如果相机镜头和物体平面平行(室内定位中,有一种基于视觉的室内定位,定位方式就是在移动的小车上安装单目相机,在屋顶安装各种可识别的标签,相机的光轴一直与屋顶是垂直的),在这种情况下,旋转矩阵可以看作是单位向量及R=E,而平移向量T=0。
2.蓝框就是相机的内参,相机的内参从出厂后就被固定了。
f:相机的焦距
(u0,v0):像平面的投影中心点
dx,dy:就是单位像素对应实际距离
Zc:可以认为相机镜头到成像物体的垂直距离
对这部分我就不赘述了,在麻呱智能 的文章中介绍的特别详细,我就不班门弄斧了。
生成棋盘标定图
创建自定义的棋盘标定图,这个没啥要说的,就是调用了opencv的画矩形框的函数,代码如下:
#生成想要的标定图,大小自定义
import cv2
import sys#读入一张空白图片,该图片最好和你想要标定的相机分辨率一致
image = cv2.imread('C:\\Users\\wlx\\Documents\\py_study\\camera calibration\\white.jpg')
#设置图片上黑白方格
dpi = 96 #dpi自己电脑上一英寸显示的像素个数
cm_to_inch = 0.3937 #1cm = 0.3937inch
square_length = 1.5 #黑白方格边长1.5cm
x_nums = 10 #x方向画10个方格
y_nums = 8 #y方向画8个方格
square_pixel = int(square_length * cm_to_inch * dpi) #方格边长的像素
#为了把方格图像放在纸张的中间,设定起始坐标
x0 = 40
y0 = 16#画方格
def DrawSquare():flag = -1 #颜色转变标志#一行一行的画for i in range(y_nums):#每画一行先换一次颜色flag = 0 - flagfor j in range(x_nums):if flag > 0:color = [0,0,0] #黑色方格else:color = [255,255,255]#调用opencv中的画方框函数cv2.rectangle(image,(x0 + j*square_pixel,y0 + i*square_pixel),(x0 + j*square_pixel+square_pixel,y0 + i*square_pixel+square_pixel),color,-1)flag = 0 - flag#保存图像cv2.imwrite('chess_map.jpg',image)#判断本程序是独立运行还是被调用
if __name__ == '__main__':DrawSquare()
上面的代码可以生成自己想要的棋盘标定图,修改x_nums和y_nums参数的值,就能获得任意数量的黑白格子。实现原理就是在图像的某一点开始,先画一个黑色方格(或者白色),画完后将起点坐标和终点坐标都向右移动方格边长的距离,然后改变颜色再画一个方格,依次类推,画完一行后,就转战到第二行,直到全部完成。
注意:在图像上坐标是这样的
cv2.rectangle(img,pt1,pt2,color,thickness=None,line_type=None,shift=None)
img:图像,要画图的图像
pt1:方格的起点坐标(x0,y0)
pt2:方格的终点坐标(x1,y1)
color:方框的颜色
thickness:方框线的宽度(像素),当值为负数时,填充方框
line_type:方框线的样式
采集标定图
我采用离线标定,先把不同角度的标定图采集保存下来,然后再开始标定。下面是采集的代码
import cv2
import os#标定图像保存路径
photo_path = "C:\\Users\\wlx\\Documents\\py_study\\camera calibration\\image"
#创建路径
def CreateFolder(path):#去除首位空格del_path_space = path.strip()#去除尾部'\'del_path_tail = del_path_space.rstrip('\\')#判读输入路径是否已存在isexists = os.path.exists(del_path_tail)if not isexists:os.makedirs(del_path_tail)return Trueelse:return False
#获取不同角度的标定图像
def gain_photo(photo_path):# 检查输入路径是否存在——不存在就创建CreateFolder(photo_path)#开启摄像头video = cv2.VideoCapture(0)#显示窗口名称photo_window = 'calibration'#保存的标定图像名称以数量命名photo_num = 0while video.isOpened():ok,frame = video.read() #读一帧的图像if not ok:breakelse:cv2.imshow(photo_window,frame)key = cv2.waitKey(10)#按键盘‘A’保存图像if key & 0xFF == ord('a'):photo_num += 1photo_name = photo_path + '\\' + str(photo_num) + '.jpg'cv2.imwrite(photo_name,frame)print('create photo is :',photo_name)#按键盘‘Q’中断采集if key & 0xFF == ord('q'):breakif __name__ == '__main__':gain_photo(photo_path)
video = cv2.VideoCapture(0)中的0代表的是我的USB相机在我电脑上的驱动位置
if __name__ == '__main__':就是判断这些代码是不是要当作单独的代码执行,如果是就执行if中的内容。
标定
我先上代码吧
import cv2
import sys
import numpy as np
import glob#标定图像保存路径
photo_path = "C:\\Users\\wlx\\Documents\\py_study\\camera calibration\\image"
#标定图像
def calibration_photo(photo_path):#设置要标定的角点个数x_nums = 9 #x方向上的角点个数y_nums = 7# 设置(生成)标定图在世界坐标中的坐标world_point = np.zeros((x_nums * y_nums,3),np.float32) #生成x_nums*y_nums个坐标,每个坐标包含x,y,z三个元素world_point[:,:2] = np.mgrid[:x_nums,:y_nums].T.reshape(-1, 2) #mgrid[]生成包含两个二维矩阵的矩阵,每个矩阵都有x_nums列,y_nums行#.T矩阵的转置#reshape()重新规划矩阵,但不改变矩阵元素#保存角点坐标world_position = []image_position = []#设置角点查找限制criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER,30,0.001)#获取所有标定图images = glob.glob(photo_path+'\\*.jpg')#print(images)for image_path in images:image = cv2.imread(image_path)gray = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)#查找角点ok,corners = cv2.findChessboardCorners(gray,(x_nums,y_nums),None)if ok:#把每一幅图像的世界坐标放到world_position中world_position.append(world_point)#获取更精确的角点位置exact_corners = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)#把获取的角点坐标放到image_position中image_position.append(exact_corners)#可视化角点# image = cv2.drawChessboardCorners(image,(x_nums,y_nums),exact_corners,ok)# cv2.imshow('image_corner',image)# cv2.waitKey(5000)#计算内参数ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(world_position, image_position, gray.shape[::-1], None,None)#将内参保存起来np.savez('C:\\Users\\wlx\\Documents\\py_study\\camera calibration\\data\\intrinsic_parameters',mtx=mtx,dist=dist)print(mtx, dist)#计算偏差mean_error = 0for i in range(len(world_position)):image_position2, _ = cv2.projectPoints(world_position[i], rvecs[i], tvecs[i], mtx, dist)error = cv2.norm(image_position[i], image_position2, cv2.NORM_L2) / len(image_position2)mean_error += errorprint("total error: ", mean_error / len(image_position))if __name__ == '__main__':calibration_photo(photo_path)
使用opencv标定这些图像,步骤大致就是:
1、设置想标定角点的个数
2、创建对应角点个数的世界坐标
3、将采集到的标定图读入缓存
4、灰度处理
5、使用findChessboardCorners(img,patternSize,corners,flags=None)函数,查找图像中的内点
- image:输入的棋盘图像,必须是8位的灰度或者彩色图像
- patternSize:棋盘中每行每列的角点个数
- corners:检测到的角点
- flags:各种操作标准
6、使用cornerSubPix(image,corners,winSize,zeroZone,criteria)函数,精确查找图像上的角点
image:输入图像,,必须是8位的灰度或者彩色图像
corners:输入角点的初始化坐标,也存储精确的输出角点坐标
winSize:搜索窗口的一半尺度,如winSize=(5,5),则使用(2x5+1, 2x5+1)=(11,11)的搜索窗
zeroZone:死区的一半尺寸,死区为不对搜索区做求和运算的区域,当值为(-1,-1)时,表示没有死区
criteria:搜索角点停止的标志
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER,30,0.001)就是一个标志,TERM_CRITERIA_EPS 代表误差也就是精度,TERM_CRITERIA_MAX_ITER代表迭代次数,它两之和就是指两个因素同时作用,这里当迭代次数超过30或误差大于0.001都会停止运算。
7、将图像的世界坐标保存到数组world_position中,将找到的角点坐标保存到数组image_position中
8、使用cv2.calibrateCamera(world_position, image_position, gray.shape[::-1], None,None)计算内外参数
9、计算一下准确度,也就是通过你算出来的内参数,逆运算出角点坐标,然后将这个坐标和识别出来的角点坐标进行误差运算,得到偏差值。
最后,可以将得到的内参数保存到一个文件中,以后在用相机采集图像时,就可以用内参数去矫正图像了,这里我没做图像的矫正,过几天做了再写上来。
另外,用来标定的图像不要太少,我做了实验随着标定图像的增加,最后计算出的偏差会减小;而且拍摄标定图像时,角度要合理,相机固定位置不要发生变化。
下面是我实验,就是为了验证代码是否能运行,所以没有打印标定图纸,而是直接用摄像头拍摄电脑显示器上的图像,摄像头也没固定牢靠,所以结果不是很好。拍摄了20张图像
相机内参:
相机的基础矩阵
M1=[[889.02484885 0. 305.16086021][ 0. 874.05128244 296.97147721][ 0. 0. 1. ]]
相机的畸变矩阵
D1=[[-1.19429590e-01 1.62553701e+00 -1.23085637e-02 -9.56110534e-03-9.61452725e+00]]
偏差值:
error: 0.022833192243376606
生成黑白棋盘标定图和单目相机标定(一)(python+opencv实现)相关推荐
- python绘制黑白棋盘_生成黑白棋盘标定图和单目相机标定(python+opencv实现)
学习记录. 事实上很早就接触过视觉定位这东西,但是到现在才返回头学习一下相机的标定,真是可耻啊!我把想法和过程记录一下. 相机成像 相机的成像原理--小孔成像 然而,在实际由于设计工艺问题.相机安装环 ...
- Halcon例程(基于多个标定图的单目相机标定)详解—— Camera_calibration_multi_image.hdev
一.前言 在我的工业相机专栏里已经将相机标定涉及到的理论部分讲解完毕,为什么要标定以及标定要求出什么参数呢,用一个Halcon 例程来帮助理解. 这个例程是比较经典的标定程序,基本将标定过程讲的比较清 ...
- 【计算机视觉】OpenCV实现单目相机标定
文章目录 单目相机标定(基于Python OpenCV) 1.上期填坑 2.单目相机标定 2.1 数据采集 2.2 角点提取 2.3 参数求解 2.4 参数评估(重投影误差) 2.5 相机位姿(棋盘位 ...
- 一文图解单目相机标定算法
点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 有一天,蟹老板找底下的员工川建国同学: 等蟹老板走后,然后转头问旁边的学霸李雷同学: 李雷同学整理了下 ...
- matlab单目相机标定
文章目录 1.标定图片 2.标定 3.获取标定结果 使用matlab进行相机参数标定非常方便,相机畸变模型请参考另一篇文章相机内参与畸变模型,单目相机标定的结果就是得到相机内参与畸变系数. 1.标定图 ...
- SLAM学习 | 单目相机标定(附代码实测可用)
SLAM学习 | 单目相机标定(附代码实测可用) 1 针孔相机模型 2 张正友标定法 3 VS2013下配置openCV 4 标定过程与结果 5 经验总结与注意事项 概要: 这篇文章介绍如何解决做SL ...
- matlab单目相机标定步骤,matlab相机标定
本文是一篇关于相机标定意义和原理的个人总结,包含了 OpenCV 和 Matlab 中常用的相机 标定函数的注解. 相机标定是机器视觉的基础, 标定结果的好坏直接决定了机器...... 图片数量一定程 ...
- 单目相机标定(使用OpenCV)
关于相机标定的理论知识和使用Matlab软件进行相机标定请看这篇博文:单目相机标定(使用Matlab) 这次因为项目原因需要给海康工业相机进行标定了,下载Matlab软件有些麻烦(主要是我电脑内存小. ...
- 单目相机标定实现--张正友标定法
文章目录 一:相机坐标系,像素平面坐标系,世界坐标系,归一化坐标系介绍 1:概述 公式 二:实现 1:整体流程 4:求出每张图像的单应性矩阵并用LMA优化 5:求解理想无畸变情况下的摄像机的内参数和外 ...
最新文章
- 2018牛客暑期ACM多校训练营第二场 - A题
- python 提高文件查询效率_Python 大量小文件存储提高效率的简单示例
- BugKuCTF WEB 你必须让他停下
- IDEA使用技巧整理
- Android 6.0以上运行权限及RxPermissions的使用
- 模板变量,过滤器和静态文件引用
- iOS里面MVC模式详解
- javascirpt如何模仿块级作用域(js高程笔记)
- [CF1073E]Segment Sum
- RUP---统一软件开发过程
- 恐龙机器人钢索恐龙形态_恐龙有的四脚行走有的两脚行走,有的会飞有的会游,差别咋这么大...
- win7 64位如何安装sql2005
- spss分析方法-因子分析(转载)
- 全球及中国城市规划行业十四五建设方向与运营动态分析报告2022版
- 计算机配置的详细信息,如何查看电脑的配置参数,看电脑详细配置的方法
- 未来办公利器-无影云超级桌面体验
- 京东7天无理由退货换货运费应该谁来出是商家支付还是买家支付?
- 基于android平台的条码扫描软件的设计与实现,基于android平台的条码扫描软件的设计与实现...
- 阿里巴巴2014年校园招聘试题解答
- Flash 实验 遮罩层