【鱼眼+普通相机】相机标定

2024-05-12 05:28
文章标签 相机 标定 普通 鱼眼

本文主要是介绍【鱼眼+普通相机】相机标定,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、简介

本文提供了鱼眼及普通相机的标定python版源码,调用opencv的接口,原理为张正友标定法。这里不过多赘述,话不多说无套路直接上源码,亲测有效,搬走即用。

注:

  1. 需准备一个标定版,在此网站下载打印出来即可;
  2. 例如12x8的棋盘格内角点为11x7.

二 、源码

2.1 鱼眼相机标定

"""
~~~~~~~~~~~~~~~~~~~~~~~~~~
鱼眼相机标定
~~~~~~~~~~~~~~~~~~~~~~~~~~用法:python calibrate_camera.py \-i 0 \-grid 9x6 \-out fisheye.yaml \-framestep 20 \--resolution 640x480--fisheye
"""
import argparse
import os
import numpy as np
import cv2# 将相机参数文件保存到此目录
TARGET_DIR = os.path.join(os.getcwd(), "yaml")# 默认参数文件
DEFAULT_PARAM_FILE = os.path.join(TARGET_DIR, "calib.yaml")def main():global grayparser = argparse.ArgumentParser()# 输入视频流parser.add_argument("-i", "--input", type=int, default=0,help="输入相机设备")# 棋盘格大小parser.add_argument("-grid", "--grid", default="10x7",help="标定棋盘格的大小")parser.add_argument("-r", "--resolution", default="640x480",help="相机图像的分辨率")parser.add_argument("-framestep", type=int, default=20,help="在视频中使用每第n帧")parser.add_argument("-o", "--output", default=DEFAULT_PARAM_FILE,help="输出yaml文件的路径")args = parser.parse_args()if not os.path.exists(TARGET_DIR):os.mkdir(TARGET_DIR)text1 = "按下 c 进行标定"text2 = "按下 q 退出"text3 = "设备: {}".format(args.input)font = cv2.FONT_HERSHEY_SIMPLEXfontscale = 0.6resolution_str = args.resolution.split("x")W = int(resolution_str[0])H = int(resolution_str[1])grid_size = tuple(int(x) for x in args.grid.split("x"))grid_points = np.zeros((1, np.prod(grid_size), 3), np.float32)grid_points[0, :, :2] = np.indices(grid_size).T.reshape(-1, 2)objpoints = []  # 真实世界空间中的3D点imgpoints = []  # 图像平面中的2D点device = args.inputcap = cv2.VideoCapture(device)  # 打开设备# 设置分辨率cap.set(cv2.CAP_PROP_FRAME_WIDTH, W)cap.set(cv2.CAP_PROP_FRAME_HEIGHT, H)if cap is None or not cap.isOpened():print("无法打开设备")returnquit = Falsedo_calib = Falsei = -1while True:i += 1_r, img = cap.read()img= cv2.resize(img, (W, H))if i % args.framestep != 0:continueprint("在第 " + str(i) + " 帧中寻找棋盘格角点...")gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)found, corners = cv2.findChessboardCorners(gray,grid_size,cv2.CALIB_CB_ADAPTIVE_THRESH +cv2.CALIB_CB_NORMALIZE_IMAGE +cv2.CALIB_CB_FILTER_QUADS)if found:term = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.01)cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), term)print("OK")imgpoints.append(corners)objpoints.append(grid_points)cv2.drawChessboardCorners(img, grid_size, corners, found)cv2.putText(img, text1, (20, 70), font, fontscale, (255, 200, 0), 2)cv2.putText(img, text2, (20, 110), font, fontscale, (255, 200, 0), 2)cv2.putText(img, text3, (20, 30), font, fontscale, (255, 200, 0), 2)cv2.imshow("corners", img)key = cv2.waitKey(1) & 0xFFif key == ord("c"):print("\n进行标定...\n")N_OK = len(objpoints)if N_OK < 12:print("检测到的角点少于12个 (%d),标定失败" %(N_OK))continueelse:do_calib = Truebreakelif key == ord("q"):quit = Truebreakif quit:print("标定已退出。")cap.release()cv2.destroyAllWindows()if do_calib:N_OK = len(objpoints)K = np.zeros((3, 3))D = np.zeros((4, 1))rvecs = [np.zeros((1, 1, 3), dtype=np.float64) for _ in range(N_OK)]tvecs = [np.zeros((1, 1, 3), dtype=np.float64) for _ in range(N_OK)]calibration_flags = (cv2.fisheye.CALIB_RECOMPUTE_EXTRINSIC +cv2.fisheye.CALIB_CHECK_COND +cv2.fisheye.CALIB_FIX_SKEW)ret, mtx, dist, rvecs, tvecs = cv2.fisheye.calibrate(objpoints,imgpoints,(W, H),K,D,rvecs,tvecs,calibration_flags,(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-6))if ret:fs = cv2.FileStorage(args.output, cv2.FILE_STORAGE_WRITE)fs.write("resolution", np.int32([W, H]))fs.write("camera_matrix", mtx)fs.write("dist_coeffs", dist)fs.release()print("相机数据保存成功")cv2.putText(img, "SUCCESS!", (220, 240), font, 2, (0, 0, 255), 2)else:cv2.putText(img, "FAILED!", (220, 240), font, 2, (0, 0, 255), 2)cv2.imshow("corners", img)cv2.waitKey(0)if __name__ == "__main__":main()

2.2 普通工业相机标定

"""
~~~~~~~~~~~~~~~~~~~~~~~~~~
普通工业相机标定
~~~~~~~~~~~~~~~~~~~~~~~~~~
用法:python calibrate_camera.py \-i 0 \-grid 7x10 \-out calib.yaml \-framestep 20 \--resolution 640x480
"""
import argparse
import os
import numpy as np
import cv2# 将相机参数文件保存到此目录
TARGET_DIR = os.path.join(os.getcwd(), "calib_yaml")# 默认参数文件
DEFAULT_PARAM_FILE = os.path.join(TARGET_DIR, "calib.yaml")def main():global grayparser = argparse.ArgumentParser()  # 创建解析器# 输入视频流parser.add_argument("-i", "--input", default="0",help="输入相机设备")    # type=int,# 棋盘格大小parser.add_argument("-grid", "--grid", default="11x8",help="标定棋盘格的大小")parser.add_argument("-r", "--resolution", default="640x480",help="相机图像的分辨率")parser.add_argument("-framestep", type=int, default=20,help="在视频中使用每第n帧")parser.add_argument("-o", "--output", default=DEFAULT_PARAM_FILE,help="输出yaml文件的路径")args = parser.parse_args()if not os.path.exists(TARGET_DIR):os.mkdir(TARGET_DIR)text1 = "Press c for calibration"text2 = "Press q to exit"text3 = "device: {}".format(args.input)font = cv2.FONT_HERSHEY_SIMPLEXfontscale = 0.6resolution_str = args.resolution.split("x")  # 分辨率W = int(resolution_str[0])H = int(resolution_str[1])grid_size = tuple(int(x) for x in args.grid.split("x"))  # 棋盘格大小grid_points = np.zeros((1, np.prod(grid_size), 3), np.float32)  # 3D点grid_points[0, :, :2] = np.indices(grid_size).T.reshape(-1, 2)  # 2D点objpoints = []  # 真实世界空间中的3D点imgpoints = []  # 图像平面中的2D点device = args.input  # 设备cap = cv2.VideoCapture(device)  # 打开设备# 设置分辨率cap.set(cv2.CAP_PROP_FRAME_WIDTH, W)cap.set(cv2.CAP_PROP_FRAME_HEIGHT, H)if cap is None or not cap.isOpened():print("无法打开设备")returnquit = Falsedo_calib = Falsei = -1while True:i += 1_r, img = cap.read()img= cv2.resize(img, (W, H))if i % args.framestep != 0:continueprint("在第 " + str(i) + " 帧中寻找棋盘格角点...")gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像found, corners = cv2.findChessboardCorners(  # 检测棋盘格角点gray,grid_size,cv2.CALIB_CB_ADAPTIVE_THRESH +cv2.CALIB_CB_NORMALIZE_IMAGE +cv2.CALIB_CB_FILTER_QUADS)if found:  # 如果找到棋盘格角点term = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.01)  # 终止条件cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), term)  # 亚像素级角点检测print("OK")imgpoints.append(corners)  # 2D点objpoints.append(grid_points)  # 3D点cv2.drawChessboardCorners(img, grid_size, corners, found)  # 绘制棋盘格角点cv2.putText(img, text1, (20, 70), font, fontscale, (255, 200, 0), 2)cv2.putText(img, text2, (20, 110), font, fontscale, (255, 200, 0), 2)cv2.putText(img, text3, (20, 30), font, fontscale, (255, 200, 0), 2)cv2.imshow("corners", img)key = cv2.waitKey(1) & 0xFFif key == ord("c"):print("\n进行标定...\n")N_OK = len(objpoints)  # 有效的标定图像数量if N_OK < 12:print("检测到的角点少于12个 (%d),标定失败" % (N_OK))continueelse:do_calib = Truebreakelif key == ord("q"):quit = Truebreakif quit:print("标定已退出。")cap.release()cv2.destroyAllWindows()if do_calib:N_OK = len(objpoints)K = np.zeros((3, 3))D = np.zeros((4, 1))rvecs = [np.zeros((1, 1, 3), dtype=np.float64) for _ in range(N_OK)]tvecs = [np.zeros((1, 1, 3), dtype=np.float64) for _ in range(N_OK)]ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints,imgpoints,gray.shape[::-1],None,None)if ret:fs = cv2.FileStorage(args.output, cv2.FILE_STORAGE_WRITE)fs.write("resolution", np.int32([W, H]))fs.write("camera_matrix", mtx)  # 内参矩阵fs.write("dist_coeffs", dist)  # 畸变系数# print("rvecs:", rvecs)  # 旋转向量# print("tvecs:", rvecs)  # 平移向量fs.release()print("相机数据保存成功")cv2.putText(img, "SUCCESS!", (220, 240), font, 2, (0, 0, 255), 2)else:cv2.putText(img, "FAILED!", (220, 240), font, 2, (0, 0, 255), 2)cv2.imshow("corners", img)cv2.waitKey(0)if __name__ == "__main__":main()

这篇关于【鱼眼+普通相机】相机标定的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/981721

相关文章

什么是 Ubuntu LTS?Ubuntu LTS和普通版本区别对比

《什么是UbuntuLTS?UbuntuLTS和普通版本区别对比》UbuntuLTS是Ubuntu操作系统的一个特殊版本,旨在提供更长时间的支持和稳定性,与常规的Ubuntu版本相比,LTS版... 如果你正打算安装 Ubuntu 系统,可能会被「LTS 版本」和「普通版本」给搞得一头雾水吧?尤其是对于刚入

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(五):Blender锥桶建模

前言 本系列教程旨在使用UE5配置一个具备激光雷达+深度摄像机的仿真小车,并使用通过跨平台的方式进行ROS2和UE5仿真的通讯,达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础,Nav2相关的学习教程可以参考本人的其他博客Nav2代价地图实现和原理–Nav2源码解读之CostMap2D(上)-CSDN博客往期教程: 第一期:基于UE5和ROS2的激光雷达+深度RG

海鸥相机存储卡格式化如何恢复数据

在摄影的世界里,‌每一张照片都承载着独特的记忆与故事。‌然而,‌当我们不慎将海鸥相机的存储卡格式化后,‌那些珍贵的瞬间似乎瞬间消逝,‌让人心急如焚。‌但请不要绝望,‌数据恢复并非遥不可及。‌本文将详细介绍在海鸥相机存储卡格式化后,‌如何高效地恢复丢失的数据,‌帮助您重新找回那些宝贵的记忆。‌ 图片来源于网络,如有侵权请告知 一、‌回忆备份情况 ‌海鸥相机存储卡格式化如何恢复数据?在意

Chapter 13 普通组件的注册使用

欢迎大家订阅【Vue2+Vue3】入门到实践 专栏,开启你的 Vue 学习之旅! 文章目录 前言一、组件创建二、局部注册三、全局注册 前言 在 Vue.js 中,组件是构建应用程序的基本单元。本章详细讲解了注册和使用 Vue 的普通组件的两种方式:局部注册和全局注册。 本篇文章参考黑马程序员 一、组件创建 ①定义 Vue 组件是一种具有特定功能的 Vue 实

解析apollo纵向控制标定表程序

百度apollo采用标定表描述车辆速度、加速度与油门/刹车之间的关系。该表可使无人车根据当前车速与期望加速度得到合适的油门/刹车开合度。除了文献《Baidu Apollo Auto-Calibration System - An Industry-Level Data-Driven and Learning based Vehicle Longitude Dynamic Calibrating

机器视觉硬件选型根据某项目相机镜头

一 项目总需求 1、大视野检测需求: (1)大视野: ①产品尺寸15.6寸屏幕,产品大小:350mm x 225mm; ②产品料盘尺寸大小:565mm x 425mm; ③工作距离:880mm;检测精度:500μm; 1、大视野检测需求: (1)大视野: ①产品尺寸15.6寸屏幕,产品大小:350mm x 225mm; ②产品料盘尺寸大小:565mm x 425mm; 工作距离:

005:VTK世界坐标系中的相机和物体

VTK医学图像处理---世界坐标系中的相机和物体 左侧是成像结果                                                    右侧是世界坐标系中的相机与被观察物体 目录 VTK医学图像处理---世界坐标系中的相机和物体 简介 1 在三维空间中添加坐标系 2 世界坐标系中的相机 3 世界坐标系中vtkImageData的参数 总结:

箭头函数跟普通函数的区别

箭头函数(Arrow Function)和普通函数(Function Declaration/Expression)在 JavaScript 中有一些重要的区别,主要体现在以下几个方面: 1. 语法 箭头函数: 语法更简洁,没有 function 关键字。 对于单一表达式函数,结果会隐式返回,不需要 return 关键字。 不需要使用大括号 {},但如果使用,必须显式返回值。 示例:

热泵专用压缩机与普通压缩机的区别

普通压缩机和低温压缩机在空气源热泵机组上应用对比: 1.普通压缩机 1.低温工况运行时,压缩机高压缩比、产生过热、润滑油变质、涡旋盘磨损等;高温工况运行时,电机过载、轴承磨损、电机绕组超过极限。 2.高水温运行时,高冷凝温度,电机过载、过热;润滑油变质,涡旋盘磨损、轴承磨损,电机绕组超过极限。 3.在低温工况时,能力不够和能效比低,经济性差,在-10度以下能力衰减可达40%以上,COP<1.5W/

独立双端App《瓦格相机》的开发过程分享

前言 Hello大家好,我是灯灯,独立开发者灯灯,也是天天学藏语的灯灯,哈哈哈... 好了屁话少说,今天和大家分享一下最近自己独立制作一款应用的经验历程,希望能对刚刚起步的新手们、还有独立开发者们有所帮助。 什么样的应用 我想做的是一款能够将照片转换成文字拼成的图片应用,也就是,图片中的每一个像素点都将会被文字取代,同时对应色彩、密集程度等。 之所以想做这样的应用是因为早在我高中的时候,