OpenCV之getOptimalNewCameraMatrix

2023-05-16

去畸变后的图像四周会出现黑色区域,如果畸变较大,如鱼眼镜头,四周黑色区域将会更大。
opencv中给我们提供了一个函数getOptimalNewCameraMatrix(),用于去除畸变矫正后图像四周黑色的区域。

该函数根据给定参数alpha计算最优的新相机内参矩阵。alpha=0,则去除所有黑色区域,alpha=1,则保留所有原始图像像素,其他值则得到介于两者之间的效果。

通过该函数,我们得到新的相机内参矩阵,然后利用initUndistortRectifyMap()即可获得用于remap()的映射矩阵。

主要步骤

获取内切和外切矩形

通过将整个图像划分9*9网格,然后对每个网格点去畸变。
通过比较这些去畸变后的网格点坐标,确定内切和外切矩形。

源码中提到:Get inscribed and circumscribed rectangles in normalized (independent of camera matrix) coordinates.
这是因为,在调用icvGetRectangles()获取内切和外切矩形时,R和newCameraMatrix为空。从cvUndistortPoints()源码可以知道,当R和newCameraMatrix为空,尤其是newCameraMatrix为空时,像素点去畸变后得到的是相机坐标系下的坐标。

static void
icvGetRectangles(const CvMat* cameraMatrix, const CvMat* distCoeffs,
	const CvMat* R, const CvMat* newCameraMatrix, CvSize imgSize,
	cv::Rect_<float>& inner, cv::Rect_<float>& outer)
{
	const int N = 9;
	int x, y, k;
	cv::Ptr<CvMat> _pts(cvCreateMat(1, N * N, CV_32FC2));
	CvPoint2D32f* pts = (CvPoint2D32f*)(_pts->data.ptr);

	for (y = k = 0; y < N; y++)
		for (x = 0; x < N; x++)
			pts[k++] = cvPoint2D32f((float)x * imgSize.width / (N - 1),
				(float)y * imgSize.height / (N - 1));
	// 由于R和newCameraMatrix为0,因此,去畸变后的坐标为相机坐标系下的坐标,(如 mm)
	cvUndistortPoints(_pts, _pts, cameraMatrix, distCoeffs, R, newCameraMatrix);
	
	float iX0 = -FLT_MAX, iX1 = FLT_MAX, iY0 = -FLT_MAX, iY1 = FLT_MAX;
	float oX0 = FLT_MAX, oX1 = -FLT_MAX, oY0 = FLT_MAX, oY1 = -FLT_MAX;
	// find the inscribed rectangle.
	// the code will likely not work with extreme rotation matrices (R) (>45%)
	for (y = k = 0; y < N; y++)
		for (x = 0; x < N; x++)
		{
			CvPoint2D32f p = pts[k++];
			oX0 = MIN(oX0, p.x);
			oX1 = MAX(oX1, p.x);
			oY0 = MIN(oY0, p.y);
			oY1 = MAX(oY1, p.y);

			if (x == 0)
				iX0 = MAX(iX0, p.x);
			if (x == N - 1)
				iX1 = MIN(iX1, p.x);
			if (y == 0)
				iY0 = MAX(iY0, p.y);
			if (y == N - 1)
				iY1 = MIN(iY1, p.y);
		}
	inner = cv::Rect_<float>(iX0, iY0, iX1 - iX0, iY1 - iY0);
	outer = cv::Rect_<float>(oX0, oY0, oX1 - oX0, oY1 - oY0);
}

新的相机内参矩阵

通过icvGetRectangles()我们得到的内切和外切矩形(相机坐标系下)。接下来我们需要一个新的内参矩阵,将指定区域(内切矩形、外切矩形或介于两者之间的区域)重新投影到整个图像区域。

分别计算出内切矩形和外切矩形对应的相机内参矩阵,然后根据alpha,加权得到最终的相机内参矩阵。

double fx0 = (newImgSize.width - 1) / inner.width; // 可以认为inner所在坐标系下单位长度对应的像素个数
double fy0 = (newImgSize.height - 1) / inner.height;
double cx0 = -fx0 * inner.x; // 将矩形的左上角,移动到图像坐标系的原点
double cy0 = -fy0 * inner.y;

// Projection mapping outer rectangle to viewport
double fx1 = (newImgSize.width - 1) / outer.width;
double fy1 = (newImgSize.height - 1) / outer.height;
double cx1 = -fx1 * outer.x;
double cy1 = -fy1 * outer.y;

// Interpolate between the two optimal projections
M[0][0] = fx0 * (1 - alpha) + fx1 * alpha;
M[1][1] = fy0 * (1 - alpha) + fy1 * alpha;
M[0][2] = cx0 * (1 - alpha) + cx1 * alpha;
M[1][2] = cy0 * (1 - alpha) + cy1 * alpha;

有效的像素区域

上述我们获得了新的相机内参矩阵,利用这个矩阵,我们可以得到像素坐标系下的内切和外切矩形。

if (validPixROI)
{
	// 利用上述得到的投影矩阵(“新相机内参矩阵”),得到像素坐标系下的内切和外切矩形
	icvGetRectangles(cameraMatrix, distCoeffs, 0, &matM, imgSize, inner, outer);
	// 这里我们只关注内切矩形
	cv::Rect r = inner;
	r &= cv::Rect(0, 0, newImgSize.width, newImgSize.height);
	*validPixROI = cvRect(r);
}

完整代码

void cvGetOptimalNewCameraMatrix(const CvMat* cameraMatrix, const CvMat* distCoeffs,
	CvSize imgSize, double alpha,
	CvMat* newCameraMatrix, CvSize newImgSize,
	CvRect* validPixROI, int centerPrincipalPoint)
{
	cv::Rect_<float> inner, outer;
	newImgSize = newImgSize.width * newImgSize.height != 0 ? newImgSize : imgSize;

	double M[3][3];
	CvMat matM = cvMat(3, 3, CV_64F, M);
	cvConvert(cameraMatrix, &matM);

	if (centerPrincipalPoint)
	{
		double cx0 = M[0][2];
		double cy0 = M[1][2];
		double cx = (newImgSize.width - 1) * 0.5;
		double cy = (newImgSize.height - 1) * 0.5;

		icvGetRectangles(cameraMatrix, distCoeffs, 0, cameraMatrix, imgSize, inner, outer);
		double s0 = std::max(std::max(std::max((double)cx / (cx0 - inner.x), (double)cy / (cy0 - inner.y)),	(double)cx / (inner.x + inner.width - cx0)), (double)cy / (inner.y + inner.height - cy0));
		double s1 = std::min(std::min(std::min((double)cx / (cx0 - outer.x), (double)cy / (cy0 - outer.y)), (double)cx / (outer.x + outer.width - cx0)), (double)cy / (outer.y + outer.height - cy0));
		double s = s0 * (1 - alpha) + s1 * alpha;

		M[0][0] *= s;
		M[1][1] *= s;
		M[0][2] = cx;
		M[1][2] = cy;

		if (validPixROI)
		{
			inner = cv::Rect_<float>((float)((inner.x - cx0) * s + cx),
				(float)((inner.y - cy0) * s + cy),
				(float)(inner.width * s),
				(float)(inner.height * s));
			cv::Rect r(cvCeil(inner.x), cvCeil(inner.y), cvFloor(inner.width), cvFloor(inner.height));
			r &= cv::Rect(0, 0, newImgSize.width, newImgSize.height);
			*validPixROI = cvRect(r);
		}
	}
	else
	{
		// Get inscribed and circumscribed rectangles in normalized
		// (independent of camera matrix) coordinates
		icvGetRectangles(cameraMatrix, distCoeffs, 0, 0, imgSize, inner, outer);

		// Projection mapping inner rectangle to viewport
		double fx0 = (newImgSize.width - 1) / inner.width; // 可以认为inner所在坐标系下单位长度对应的像素个数
		double fy0 = (newImgSize.height - 1) / inner.height;
		double cx0 = -fx0 * inner.x; // 将矩形的左上角,移动到图像坐标系的原点
		double cy0 = -fy0 * inner.y;

		// Projection mapping outer rectangle to viewport
		double fx1 = (newImgSize.width - 1) / outer.width;
		double fy1 = (newImgSize.height - 1) / outer.height;
		double cx1 = -fx1 * outer.x;
		double cy1 = -fy1 * outer.y;

		// Interpolate between the two optimal projections
		M[0][0] = fx0 * (1 - alpha) + fx1 * alpha;
		M[1][1] = fy0 * (1 - alpha) + fy1 * alpha;
		M[0][2] = cx0 * (1 - alpha) + cx1 * alpha;
		M[1][2] = cy0 * (1 - alpha) + cy1 * alpha;

		if (validPixROI)
		{
			// 利用上述得到的投影矩阵(新相机内参矩阵),得到像素坐标系下的内切和外切矩形
			icvGetRectangles(cameraMatrix, distCoeffs, 0, &matM, imgSize, inner, outer);
			// 这里我们只关注内切矩形
			cv::Rect r = inner;
			r &= cv::Rect(0, 0, newImgSize.width, newImgSize.height);
			*validPixROI = cvRect(r);
		}
	}

	cvConvert(&matM, newCameraMatrix);
}

最后

  1. 该函数提供了一种思路,通过划分网格来确定图像有效区域的矩形边界。
  2. 在去畸变函数cvUndistortPoints()中,通过内参矩阵将像素点转化到相机坐标系下,然后根据畸变系数进行畸变矫正。如果给定旋转矩阵matR,可以对去畸变后的点进行旋转变换(可用于旋转补偿,如视频防抖)。如果给定matP,则可以对去畸变后的点重新投影到像素坐标系(如投影到指定图像区域)。
void cvUndistortPoints(const CvMat* _src, CvMat* _dst, const CvMat* _cameraMatrix,
	const CvMat* _distCoeffs, const CvMat* matR, const CvMat* matP)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenCV之getOptimalNewCameraMatrix 的相关文章

  • 如何将 mat 转换为 array2d

    我为dlib http dlib net face landmark detection ex cpp html那里的面部地标代码使用 array2d 来获取图像 但我喜欢使用 Mat 读取图像并转换为 array2d 因为 dlib 仅支
  • 来自连接到远程机器的相机的 Opencv 流

    我正在用 python 开发一个 wx 应用程序 用于流式传输和显示来自两个不同网络摄像头的视频 这工作正常 但现在我需要在不同的场景中执行此操作 其中两个摄像头连接在通过网络连接的 Windows 上运行的单独计算机中 我的应用程序将在机
  • opencv 2.3.* 读取不工作

    我无法让 imread 工作 与这个人有同样的问题 OpenCV imwrite 2 2 在 Windows 7 上导致异常 并显示消息 OpenCV 错误 未指定错误 无法找到指定扩展名的编写器 https stackoverflow c
  • BASH 脚本编译多个 C++ 文件 - OpenCV

    请参见在C 和OpenCV中调用其他文件中的函数 https stackoverflow com questions 24442836 call functions in other files in c and opencv 对于最初的问
  • 如何在 OpenCV 中从 YUV 文件读取帧?

    如何在 OpenCV 中从 YUV 文件读取帧 我编写了一个非常简单的 python 代码来从二进制文件读取 YUV NV21 流 import cv2 import numpy as np class VideoCaptureYUV de
  • OpenCV Visual Studio ntdll.dll

    我尝试在 Visual Studio 2013 上使用 OpenCV 2 4 10 创建一个项目 但由于以下异常 到目前为止我运气不佳 请建议帮助 TIA letstryitonemoretime exe Win32 Loaded C Us
  • OpenCV C++ 如何知道每行的轮廓数进行排序?

    我有一个二值图像 https i stack imgur com NRLVv jpg在这张图片中 我可以使用重载的函数轻松地对从上到下 从左到右找到的轮廓进行排序std sort 我首先通过以下方式从上到下排序 sort contours
  • OpenCV 2.3 与 VS 2008 - 鼠标事件

    强制性 我是新手 有一份涉及编程的工作 并且我一边工作一边自学 不用说 作为一名老师 我经常犯彻底的错误 我现在所处的位置 我创建了 Graph 类 它 令人惊讶的是 制作了图表 但现在我想通过单击鼠标来修改图形 但我似乎无法让鼠标处理程序
  • 使用 OpenCV 改进特征点匹配

    我想匹配立体图像中的特征点 我已经用不同的算法找到并提取了特征点 现在我需要一个良好的匹配 在本例中 我使用 FAST 算法进行检测和提取 BruteForceMatcher用于匹配特征点 匹配代码 vector lt vector
  • OpenCV 跟踪器:模型未在函数 init 中初始化

    在视频的第一帧 我运行一个对象检测器 它返回对象的边界框 如下所示
  • 如何绘制更大的边界框和仅裁剪边界框文本 Python Opencv

    我正在使用 easyocr 来检测图像中的文本 该方法给出输出边界框 输入图像如下所示 Image 1 Image 2 使用下面的代码获得输出图像 But I want to draw a Single Bigger bounding bo
  • cv2.drawContours() - 取消填充字符内的圆圈(Python,OpenCV)

    根据 Silencer的建议 我使用了他发布的代码here https stackoverflow com questions 48244328 copy shape to blank canvas opencv python 482465
  • 将 4 通道图像转换为 3 通道图像

    我正在使用 OpenCV 2 4 6 我正在尝试将 4 通道 RGB IplImage 转换为 4 通道 HSV 图像 下面是我的代码 给出错误 OpenCV 错误 未知函数断言失败 我认为 cvCvtColor 支持 3 通道图像 有没有
  • YOLOv8获取预测边界框

    我想将 OpenCV 与 YOLOv8 集成ultralytics 所以我想从模型预测中获取边界框坐标 我该怎么做呢 from ultralytics import YOLO import cv2 model YOLO yolov8n pt
  • OpenCV VideoWriter 未写入 Output.avi

    我正在尝试编写一段简单的代码来获取视频 裁剪视频并写入输出文件 系统设置 OS Windows 10 Conda Environment Python Version 3 7 OpenCV Version 3 4 2 ffmpeg Vers
  • 如何在 cv2.VideoWriter 中使用 FPS 参数?

    好的 所以我正在制作视频 我想确切地知道如何使用 FPS 参数 它是一个浮点数 所以我假设这是我想要的每帧之间的间隔 你能给个例子吗 我只想知道视频会如何随着 FPS 参数值的变化而变化 因为我制作的视频现在太快了 谢谢 确实只是这样 fr
  • VideoCapture.read() 返回过去的图像

    我在跑python3 6 with openCV on the Raspberry pi OS is Raspbian 代码的大致结构如下 The image以时间间隔 3 5 分钟 捕获 被捕获image在函数中处理并返回度量 精度的种类
  • 如何加速 svm.predict?

    我正在编写一个滑动窗口来提取特征并将其输入到 CvSVM 的预测函数中 然而 我偶然发现 svm predict 函数相对较慢 基本上 窗口以固定的步幅长度在图像比例上滑动穿过图像 遍历图像加上提取每个图像特征的速度 窗口大约需要 1000
  • 让网络摄像头在 OpenCV 中工作

    我正在尝试让我的网络摄像头在 Windows 7 64 位中的 OpenCV 版本 2 2 中捕获视频 但是 我遇到了一些困难 OpenCV 附带的示例二进制文件都无法检测到我的网络摄像头 最近我发现这篇文章表明答案在于重新编译一个文件 o
  • HoughLinesP后如何合并线?

    My task is to find coordinates of lines startX startY endX endY and rectangles 4 lines Here is input file 我使用下一个代码 img c

随机推荐

  • 姿态解算基础:欧拉角、方向余弦、四元数

    什么是姿态解算 xff1a 飞行器的姿态解算过程涉及到两个坐标系 xff0c 一个是运载体的机体坐标系 xff0c 该坐标系与运载体固连 xff0c 当运载体转动的时候 xff0c 这个坐标系也跟着转动 xff0c 我们假设运载体的坐标系为
  • 姿态解算进阶:互补滤波(陀螺仪、加速度计、地磁计数据融合)

    互补滤波原理 xff1a 在四轴入门理论知识那节我们说 xff0c 加速度计和磁传感器都是极易受外部干扰的传感器 xff0c 都只能得到2维的角度关系 xff0c 但是测量值随时间的变化相对较小 xff0c 结合加速度计和磁传感器可以得到3
  • C++实现线程池

    本文转载自 xff1a https blog csdn net caoshangpa article details 80374651 1 为什么需要线程池技术 目前的大多数网络服务器 xff0c 包括Web服务器 Email服务器以及数据
  • 详解coredump

    1 什么是coredump xff1a 2 开启或关闭core文件的生成 xff1a 3 core文件的存储位置和文件名 xff1a 4 造成程序core的原因 xff08 参考 xff09 xff1a 5 用GDB调试coredump x
  • C++中二进制、字符串、十六进制、十进制之间的转换

    1 十进制和二进制相互转换 2 字符串和二进制相互转换 3 字符串和十进制相互转换 4 十进制和十六进制相互转换 5 二进制和十六进制 1 十进制和二进制相互转换 xff08 1 xff09 十进制转二进制 int a 61 10 bits
  • 解决 docker: Invalid containerPort: 5000 .

    复制粘贴的命令报这个错误 xff0c 结果手敲了一下就好了 可能就是 v 那里字符有点问题 xff0c 或者多个空格之类的 看了下其他人说的解决办法 xff0c 说也有可能是大写字母的问题 学习不要图省事 xff0c 真的 xff01
  • linux输入yum后提示: -bash: /usr/bin/yum: No such file or directory的解决方法

    一 首先了解Linux系统下这两个命令的区别 yum xff1a 属于 xff1a RedHat系列 常见系统有 xff1a Redhat Centos Fedora等 apt get xff1a 属于 xff1a Debian系列 常见系
  • OpenCV矩形检测

    点击我爱计算机视觉标星 xff0c 更快获取CVML新技术 今天在52CV交流群里有朋友问到矩形检测的问题 xff0c 恰好前几天做了一个与此相关的项目 xff0c 调研了一下相关的算法 xff08 期间被某带bug的开源代码坑了很久 xf
  • 修改树莓派系统的虚拟内存大小(SWAP)

    树莓派默认的虚拟内存大小才100M xff0c 有时候我们需要扩大它 xff0c 树莓派的虚拟内存配置文件和debian默认的位置不一样 xff0c 所以这里我们修改的是 etc dphys swapfile sudo nano etc d
  • Python爬虫—request模块与验证码识别

    相关文章链接 xff1a Python爬虫 爬虫基础简介 Python爬虫 数据解析及案例 xff08 4K图片爬取 xff09 一 request模块 1 1 概念 python中原生的一种基于网络请求的模块 xff0c 功能非常强大 x
  • 学习 ROS 机器人没有前途?!

    点击蓝字 关注我们 本文转载自蓝桥云课合作作者 xff1a 机器马 xff0c 文末有小惊喜哦 01 ROS 是什么 机器人操作系统 xff08 ROS xff09 是一种用于编写机器人软件的灵活框架 它是工具 xff0c 库和协议的集合
  • 运维必学的监控系统——Prometheus,大牛免费直播带你入门~

    关注 实验楼 xff0c 每天分享一个项目教程 实验1小时 明晚开启 xff0c 腾讯大牛天火老师带你入门Promentheus xff08 普罗米修斯 xff09 这一当下超火的监控系统 提到监控系统 xff0c 人们往往会想到Zabbi
  • VINS-Mono代码阅读笔记(十一):进入pose_graph节点代码分析

    本篇笔记紧接着上一篇VINS Mono代码阅读笔记 xff08 十 xff09 xff1a vins estimator中的非线性优化 xff0c 来接着学习VINS Mono系统中重定位和全局优化部部分的代码 这部分代码在pose gra
  • ROS中插件plugin的简单使用方法

    插件 xff0c 如同其名字一样 xff0c 第一次接触的时候让我想到了U盘或者USB线这类东西 xff0c 它们和电脑没有关系 xff0c 但是插入 xff08 挂载 xff09 电脑USB口后却可以正常使用 xff0c 仿佛扩展了电脑的
  • ROS中的roslaunch命令和launch文件(ROS入门学习笔记四)

    ROS中的基本对象和概念学习笔记 ROS入门学习笔记一 ROS中创建工作区和包 ROS入门学习笔记二 ROS功能包中CMakeLists txt的说明 ROS入门学习笔记三 1 roslaunch命令 我们知道 xff0c rosrun命令
  • 嵌入式软件面试题整理

    基础试题 1 用预处理指令 define 声明一个常数 xff0c 用以表明1年中有多少秒 xff08 忽略闰年问 define SECONDS PER YEAR 60 60 24 365 UL 说明 xff1a define 语法的基本知
  • 硬件在环仿真(HiL)测试介绍

    一 HiL是什么 xff1f 硬件在环仿真 xff08 Hardware in the Loop xff0c 简称HIL xff09 是真 的控制器连接假 的被控对象 xff0c 以一种高效低成本的方式对控制器进行全面测试 它是一种用于复杂
  • Docker Dockerfile详解

    dockerfike快速创建自定义的Docker镜像 一 目录 1 docker典型结构 2 指令介绍 3 创建docker镜像 二 结构 DockerFile分为四部分组成 xff1a 基础镜像信 维护者信息 镜像操作指令和容器启动时执行
  • 树莓派3B+(Raspberry Pi 3 Model B+)安装Ubuntu MATE 18.04及简单配置

    树莓派3B 43 安装Ubuntu MATE 18 04及简单配置 安装Ubuntu18 04 MATE下载Ubuntu 18 04 MATE准备Raspberry Pi Imager镜像烧录工具开机启动 简单配置设置root密码更新列表配
  • OpenCV之getOptimalNewCameraMatrix

    去畸变后的图像四周会出现黑色区域 xff0c 如果畸变较大 xff0c 如鱼眼镜头 xff0c 四周黑色区域将会更大 opencv中给我们提供了一个函数getOptimalNewCameraMatrix xff0c 用于去除畸变矫正后图像四