OpenCV中的相机标定

news2025/4/13 6:28:07

      之前在https://blog.csdn.net/fengbingchun/article/details/130039337 中介绍了相机的内参和外参,这里通过OpenCV中的接口实现对内参和外参的求解。
      估计相机参数的过程称为相机标定(camera calibration)。相机标定是使用已知的真实世界模式(例如棋盘)来估计相机镜头和传感器的外在参数(旋转和平移, Rotation(R) and Translation(t), 相机相对于某些世界坐标系的方向)和内在参数(例如镜头的焦距fx,fy、光学中心cx,cy、畸变系数k1,k2,k3,p1,p2)的过程,以减少相机缺陷引起的畸变误差。
      棋盘标定是执行相机标定和估计未知参数值的常用技术。棋盘非常适合用于相机标定:
      (1).它是平坦的(flat),棋盘上的所有点都在同一平面上;
      (2).有清晰的corners和points,它们都出现在直线上,易于在图像中检测到,棋盘上的正方形角非常适合定位它们,便于将3D真实世界坐标系中的点映射到相机2D像素坐标系上的点。
      OpenCV中相机标定步骤:
      (1).使用已知大小的棋盘格定义3D点的真实世界坐标;
      (2).从多幅图像捕获棋盘格的不同视点(different viewpoints);
      (3).查找不同图像的棋盘格的2D坐标:
            查找棋盘角:findChessboardCorners
            完善棋盘角:cornerSubPix
      (4).标定相机:calibrateCamera

     以下是C++的实现:

int test_opencv_camera_calibration()
{
#ifdef _MSC_VER
	std::string path = "../../../test_images/camera_calibration/*.jpg";
#else
	std::string path = "test_images/camera_calibration/*.jpg";
#endif
	std::vector<std::string> images;
	cv::glob(path, images, false);
	if (images.size() == 0) {
		std::cout << "Error: the requested images were not found: " << path << "\n";
		return -1;
	}

	auto pos = path.find_last_of("/");
	std::string path_result = path.substr(0, pos + 1);

	auto get_image_name = [](const std::string& image) {
#ifdef _MSC_VER
		auto pos = image.find_last_of("\\");
#else
		auto pos = image.find_last_of("/");
#endif
		auto name = image.substr(pos + 1, image.size());
		return name.substr(0, name.size() - 4);
	};

	// the dimensions of checkerboard
	const int CHECKERBOARD[2] = { 11, 13 }; // rows,cols

	// the world coordinates for 3D points
	std::vector<cv::Point3f> pts_3d_world_coord;
	for (auto i = 0; i < CHECKERBOARD[1]; ++i) {
		for (auto j = 0; j < CHECKERBOARD[0]; ++j)
			pts_3d_world_coord.push_back(cv::Point3f(j, i, 0));
	}

	// vector to store the pixel coordinates of detected checker board corners 
	std::vector<cv::Point2f> pts_corners;
	cv::Mat frame, gray;
	bool success = false;

	// store vectors of 3D points for each checkerboard image
	std::vector<std::vector<cv::Point3f> > pts_3d;
	// store vectors of 2D points for each checkerboard image
	std::vector<std::vector<cv::Point2f> > pts_2d;

	for (auto i = 0; i < images.size(); ++i) {
		frame = cv::imread(images[i]);
		if (frame.empty()) {
			std::cout << "Error: fail to read image: " << images[i] << "\n";
			return -1;
		}
		cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);

		// finding checker board corners
		success = cv::findChessboardCorners(gray, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), pts_corners, cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_FAST_CHECK | cv::CALIB_CB_NORMALIZE_IMAGE);
		if (!success) {
			std::cout << "Error: fail to find chess board corners: " << images[i] << "\n";
			return -1;
		}

		cv::TermCriteria criteria(cv::TermCriteria::EPS | cv::TermCriteria::MAX_ITER, 30, 0.001);

		// refining pixel coordinates for given 2d points
		cv::cornerSubPix(gray, pts_corners, cv::Size(11, 11), cv::Size(-1, -1), criteria);

		// displaying the detected corner points on the checker board
		cv::drawChessboardCorners(frame, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), pts_corners, success);

		pts_3d.push_back(pts_3d_world_coord);
		pts_2d.push_back(pts_corners);

		//cv::imshow("Image", frame);
		//cv::waitKey(0);
		cv::imwrite(path_result + "result_" + get_image_name(images[i]) + ".png", frame);
	}

	cv::Mat camera_matrix, dist_coeffs, R, t;
	cv::calibrateCamera(pts_3d, pts_2d, cv::Size(gray.rows, gray.cols), camera_matrix, dist_coeffs, R, t);
	std::cout << "camera_matrix:\n" << camera_matrix << "\n"; // 3*3 matrix
	std::cout << "dist_coeffs:\n" << dist_coeffs << "\n"; // 5*1 vector
	std::cout << "R:\n" << R << "\n"; // each image, 3*1 vector
	std::cout << "t:\n" << t << "\n"; // each image, 3*1 vector

	return 0;
}

      终端输出结果如下:5幅测试图像来自于手机拍摄

      其中测试图像1.jpg角点检测结果如下所示:

      以下是参考C++实现的Python代码:

import cv2
import numpy as np
import glob
from sys import platform

def get_image_name(path):
	if platform == "win32":
		pos = path.rfind("\\")
	elif platform == "linux":
		pos = path.rfind("/")
	else:
		raise Exception(f"Error: Unsupported platform: {platform}")
	
	return path[pos+1:len(path)-4]

def camera_calibration(checkerboard_size, path):
	images = glob.glob(path)
	if len(images) == 0:
		raise Exception(f"Error: the requested images were not found: {path}")

	if platform == "win32":
		pos = images[0].rfind("\\")
	elif platform == "linux":
		pos = images[0].rfind("/")
	else:
		raise Exception(f"Error: Unsupported platform: {platform}")

	path_result = images[0][0:pos+1]

	# the world coordinates for 3D points
	pts_3d_world_coord = np.zeros((1, checkerboard_size[0] * checkerboard_size[1], 3), np.float32)
	pts_3d_world_coord[0,:,:2] = np.mgrid[0:checkerboard_size[0], 0:checkerboard_size[1]].T.reshape(-1, 2)
	#print(f"pts_3d_world_coord: {pts_3d_world_coord}")

	# store vectors of 3D points for each checkerboard image
	pts_3d = []
	# store vectors of 2D points for each checkerboard image
	pts_2d = []

	for name in images:
		frame = cv2.imread(name)
		if frame is None:
			raise Exception(f"Error: fail to read image: {frame}")

		gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
		ret, pts_corners = cv2.findChessboardCorners(gray, checkerboard_size, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE)
		if ret != True:
			raise Exception(f"Error: fail to find chess board corners: {name}")
		
		criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
		# refining pixel coordinates for given 2d points
		pts_corners = cv2.cornerSubPix(gray, pts_corners, (11,11), (-1,-1), criteria)

		# displaying the detected corner points on the checker board
		frame = cv2.drawChessboardCorners(frame, checkerboard_size, pts_corners, ret)

		pts_3d.append(pts_3d_world_coord)
		pts_2d.append(pts_corners)

		#cv2.imshow("Image", frame)
		#cv2.waitKey(0)
		cv2.imwrite(path_result + "result_" + get_image_name(name) + ".png", frame)

	ret, camera_matrix, dist_coeffs, R, t = cv2.calibrateCamera(pts_3d, pts_2d, gray.shape[::-1], None, None)
	print(f"Camera matrix:\n{camera_matrix}")
	print(f"dist_coeffs:\n{dist_coeffs}")
	print(f"R:\n{R}")
	print(f"t:\n{t}")

if __name__ == "__main__":
	# the dimensions of checkerboard
	CHECKERBOARD = (11, 13)
	# images path
	path = "../../test_images/camera_calibration/*.jpg"
	camera_calibration(CHECKERBOARD, path)

	print("test finish")

      终端输出结果如下:与C++一致

      GitHub:https://github.com/fengbingchun/OpenCV_Test

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/414307.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从风靡全球到风口,他们是如何做到的?

大家好&#xff0c;我是湖北鑫优尚电子商务。我们是从2019年下半年就注意到了TikTok的风口&#xff0c;并在2020年初&#xff0c;团队就开始投入TikTok的运营及商业变现。从2019年起&#xff0c;围绕TikTok一系列的产品功能迭代和商业模式开放的声音一直不绝于耳&#xff0c;直…

零基础可以学习数据分析吗,有没有好的培训机构推荐?

数据分析从沿海火到了中西部的软件园&#xff0c;从传统互联网企业火到了新经济领域&#xff0c;火到了第一二产业。数字化成为这个时代的标签&#xff0c;而数据也成为了最有价值的资源&#xff0c;更多企业重视数据&#xff1b;因为有了真实数据的支撑&#xff0c;所有的决策…

快速排序(挖坑法 前后指针法 非递归版本)

上一次我们说了快速排序的hoare的版本&#xff0c;但是该版本有很多问题&#xff0c;首先是需要控制很多边界&#xff0c;比较复杂一点 其次就是上一次的快速排序还是有很多的其他问题 我们试着想一下&#xff0c;如果我们用快速排序拍有序数组&#xff0c;那会怎么样&#x…

数字化和自动化的潮流:外贸企业如何应对?

当今世界正面临着数字化和自动化的浪潮&#xff0c;这些技术的应用正在深刻地改变着各个领域的生产、交易和管理方式。尤其在外贸行业&#xff0c;数字化和自动化的应用已成为一种趋势和必然。在本文中&#xff0c;我们将探讨数字化和自动化对外贸行业的影响和挑战&#xff0c;…

Windows环境下实现设计模式——解释器模式(JAVA版)

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下Windows环境下如何编程实现解释器模式&#xff08;设计模式&#xff09;。 不知道大家有没有这样的感觉&#xff0c;看了一大堆编程和设计模式的书&#xff0c;却还是很难理解设计模式&#xff…

巧用千寻位置GNSS软件|CAD功能全解析

千寻位置GNSS软件中的CAD功能&#xff0c;用于已有 CAD的图形的导入和编辑&#xff0c;并且可以对 CAD图形已有线条进行线放样&#xff0c;在日常测绘工作中十分常见。下面向各位介绍CAD功能的使用技巧。点击【测量】->【CAD】&#xff0c;进入 CAD功能如图 5.3-1所示。以下…

三、线程状态【3/12】【多线程】

线程的状态3. 线程的状态3.1 观察线程的所有状态3.2 线程状态和状态转移的意义3.3 观察线程的状态和转移3. 线程的状态 3.1 观察线程的所有状态 线程的状态是一个枚举类型 Thread.State public class ThreadState {public static void main(String[] args) {for (Thread.State…

项目7:(aliyun)实现短信的发送和验证微服务和上传文件删除文件微服务

项目7&#xff1a;实现短信的发送和验证 1.对gulimall-common补充 2.短信验证的流程&#xff08;aliyun的sms&#xff09; 3.具体接口的编写&#xff08;新建微服务service-sms&#xff09; 4.上传和删除文件流程&#xff08;aliyun的oss&#xff09; 5.具体接口的编写&am…

区块链智能合约开发学习

最近正在肝区块链知识学习&#xff0c;入手学习智能合约的开发&#xff0c;由于网上资料实在是太少了&#xff0c;好不容易东拼西凑完成了智能合约的开发、编译、部署、web3js调用&#xff08;网页页面&#xff09;和web3j调用&#xff08;java调用&#xff09;&#xff0c;赶紧…

Linux 内存回收,思维导图记录

最近天天跟内存斗智斗勇&#xff0c;整理下学习的记录 一些图片 参考 Tuning Linux Kernel Parameters For PostgreSQL Optimization PostgreSQL recommendations - Documentation for BMC Client Management 12.6 - BMC Documentation PostgreSQL load tuning on Red Hat E…

【vSphere | Python】vSphere Automation SDK for Python Ⅵ—— VM Guest Processes APIs

目录12. VM APIs12.1 VM Guest Processes APIsProcesses 进程Operations 操作&#xff08;1&#xff09;List Guest Processes&#xff08;2&#xff09;Get Guest Processes&#xff08;3&#xff09;Create Guest Processes&#xff08;4&#xff09;Delete Guest Processes参…

PaddleHub 更改模型默认下载位置

文章目录1.PaddleHub介绍2.PaddleHub安装3.PaddleHub使用中出现的问题4.更改PaddleHub模型的默认下载位置5. PaddleHub的简单使用1.PaddleHub介绍 PaddleHub 是基于 PaddlePaddle 开发的预训练模型管理工具&#xff0c;可以借助预训练模型更便捷地开展迁移学习工作&#xff0c…

docker内部执行nvidia-smi无任何显示的解决方法

docker内部执行nvidia-smi无任何显示的解决方法 贺志国 2023.4.11 今天在Docker内部编译程序&#xff0c;发现与CUDA相关的代码居然没有编译&#xff0c;于是在Docker内部执行Nvidia显卡驱动检测命令nvidia-smi&#xff0c;执行完毕后&#xff0c;无任何输出&#xff0c;也没…

计算机视觉面试题-网络结构相关问题总结-未完待续

VGG卷积核为什么取33 &#xff1f; VGG使用33卷积核的优势是什么? Resnet 主要解决什么问题 为什么会有ResNet&#xff1f; 深度网络退化的原因 Resnet的针对网络退化提出的残差网络 Resnet网络结构 Resnet网络结构中如何实现的下采样 Resnet50网络结构Resnet特点 vgg16与 res…

Java并发篇二

ForkJoin 在JDK1.7&#xff0c;并行执行任务&#xff0c;提高效率&#xff0c;大数据量才会使用 特点&#xff1a;大任务拆分成小任务&#xff0c;工作窃取&#xff0c;里面维护的是双端队列 package com.kuang.forkjoin;import java.util.concurrent.RecursiveTask;/*** 如…

(PCB系列三)AD六层板布线经验累积

目录 1、布局&#xff1a; 2、创建电源类PWR 3、高速部分可以加屏蔽罩&#xff0c; 4、EMMC和NANDFLASH采取兼容放置&#xff08;创建联合&#xff09; 5、HDMI设计 6、就近原则摆放 7、AV端口 8、模拟信号&#xff08;1字型或L型走线&#xff09; 9、WIFI模块 10、局…

研究生,但是一直在摆烂学不进去

好的&#xff0c;我来为您创作一首歌曲&#xff0c;希望能够帮助您每天保持自律&#xff0c;专注学习。 《自律之歌》 第1节&#xff1a; 每天都要努力 学习不停歇 独自一人也要坚持 不放弃自己的梦想 读文献 写论文 我们不停探索 穷孩子的荣耀 就在不远处等候 合唱&#xf…

面试手撕算法题--下一个排列

前言 面试官描述这个题的时候&#xff0c;我就感觉似曾相识似乎做过&#xff0c;面完以后到leetcode找到原题恨不得给自个儿来一下子&#xff0c;的确&#xff0c;当时调api爽了&#xff0c;然后呢面试被拷打了啊&#xff0c;我想不起来这个api具体怎么解决这个题目的底层原理…

【非递归】手搓快速排序

欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e;&#x1f49e; 前言&#xff1a; 快速排序已经带大家实现过了&#xff0c;我们用到的方法是递归法&#xff0c;你知道吗&#xff0c;用循环也可以实现快速排序&#xff0c;来看看吧。 注&#xff1a; 这篇博客属于数据结构…

FE_CSS 基础选择器 字体属性 文本属性 综合案例

1 CSS 基础选择器 选择器分为基础选择器和复合选择器两个大类&#xff0c;基础选择器是由单个选择器组成的&#xff0c;基础选择器又包括&#xff1a;标签选择器、类选择器、id 选择器和通配符选择器。 1.1 标签选择器 标签名{属性1: 属性值1; 属性2: 属性值2; 属性3: 属性…