本文主要是介绍【鱼眼+普通相机】相机标定,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、简介
本文提供了鱼眼及普通相机的标定python版源码,调用opencv的接口,原理为张正友标定法。这里不过多赘述,话不多说无套路直接上源码,亲测有效,搬走即用。
注:
- 需准备一个标定版,在此网站下载打印出来即可;
- 例如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()
这篇关于【鱼眼+普通相机】相机标定的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!