OpenCV unproject 2D 指向具有已知深度“Z”的 3D

2023-11-26

问题陈述

我正在尝试将 2D 点重新投影到其原始 3D 坐标,假设我知道每个点的距离。继OpenCV 文档,我设法让它以零失真的方式工作。然而,当存在扭曲时,结果就不正确。

目前的方法

因此,我们的想法是反转以下内容:

Distorted projection

分为以下内容:

enter image description here

By:

  1. 使用消除任何扭曲cv::undistortPoints
  2. 通过反转上面的第二个方程,使用内在函数返回标准化相机坐标
  3. 乘以z来逆转正常化。

问题

  1. Why do I need to subtract f_x and f_y to get back to the normalized camera coordinates (found empirically when testing)? In the code below, in step 2, if I don't subtract -- even the non-distorted result is off This was my mistake -- I messed up the indexes.
  2. 如果我包含失真,结果就是错误的——我做错了什么?

示例代码(C++)

#include <iostream>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <vector>

std::vector<cv::Point2d> Project(const std::vector<cv::Point3d>& points,
                                 const cv::Mat& intrinsic,
                                 const cv::Mat& distortion) {
  std::vector<cv::Point2d> result;
  if (!points.empty()) {
    cv::projectPoints(points, cv::Mat(3, 1, CV_64F, cvScalar(0.)),
                      cv::Mat(3, 1, CV_64F, cvScalar(0.)), intrinsic,
                      distortion, result);
  }
  return result;
}

std::vector<cv::Point3d> Unproject(const std::vector<cv::Point2d>& points,
                                   const std::vector<double>& Z,
                                   const cv::Mat& intrinsic,
                                   const cv::Mat& distortion) {
  double f_x = intrinsic.at<double>(0, 0);
  double f_y = intrinsic.at<double>(1, 1);
  double c_x = intrinsic.at<double>(0, 2);
  double c_y = intrinsic.at<double>(1, 2);
  // This was an error before:
  // double c_x = intrinsic.at<double>(0, 3);
  // double c_y = intrinsic.at<double>(1, 3);

  // Step 1. Undistort
  std::vector<cv::Point2d> points_undistorted;
  assert(Z.size() == 1 || Z.size() == points.size());
  if (!points.empty()) {
    cv::undistortPoints(points, points_undistorted, intrinsic,
                        distortion, cv::noArray(), intrinsic);
  }

  // Step 2. Reproject
  std::vector<cv::Point3d> result;
  result.reserve(points.size());
  for (size_t idx = 0; idx < points_undistorted.size(); ++idx) {
    const double z = Z.size() == 1 ? Z[0] : Z[idx];
    result.push_back(
        cv::Point3d((points_undistorted[idx].x - c_x) / f_x * z,
                    (points_undistorted[idx].y - c_y) / f_y * z, z));
  }
  return result;
}

int main() {
  const double f_x = 1000.0;
  const double f_y = 1000.0;
  const double c_x = 1000.0;
  const double c_y = 1000.0;
  const cv::Mat intrinsic =
      (cv::Mat_<double>(3, 3) << f_x, 0.0, c_x, 0.0, f_y, c_y, 0.0, 0.0, 1.0);
  const cv::Mat distortion =
      // (cv::Mat_<double>(5, 1) << 0.0, 0.0, 0.0, 0.0);  // This works!
      (cv::Mat_<double>(5, 1) << -0.32, 1.24, 0.0013, 0.0013);  // This doesn't!

  // Single point test.
  const cv::Point3d point_single(-10.0, 2.0, 12.0);
  const cv::Point2d point_single_projected = Project({point_single}, intrinsic,
                                                     distortion)[0];
  const cv::Point3d point_single_unprojected = Unproject({point_single_projected},
                                    {point_single.z}, intrinsic, distortion)[0];

  std::cout << "Expected Point: " << point_single.x;
  std::cout << " " << point_single.y;
  std::cout << " " << point_single.z << std::endl;
  std::cout << "Computed Point: " << point_single_unprojected.x;
  std::cout << " " << point_single_unprojected.y;
  std::cout << " " << point_single_unprojected.z << std::endl;
}

相同的代码(Python)

import cv2
import numpy as np

def Project(points, intrinsic, distortion):
  result = []
  rvec = tvec = np.array([0.0, 0.0, 0.0])
  if len(points) > 0:
    result, _ = cv2.projectPoints(points, rvec, tvec,
                                  intrinsic, distortion)
  return np.squeeze(result, axis=1)

def Unproject(points, Z, intrinsic, distortion):
  f_x = intrinsic[0, 0]
  f_y = intrinsic[1, 1]
  c_x = intrinsic[0, 2]
  c_y = intrinsic[1, 2]
  # This was an error before
  # c_x = intrinsic[0, 3]
  # c_y = intrinsic[1, 3]

  # Step 1. Undistort.
  points_undistorted = np.array([])
  if len(points) > 0:
    points_undistorted = cv2.undistortPoints(np.expand_dims(points, axis=1), intrinsic, distortion, P=intrinsic)
  points_undistorted = np.squeeze(points_undistorted, axis=1)

  # Step 2. Reproject.
  result = []
  for idx in range(points_undistorted.shape[0]):
    z = Z[0] if len(Z) == 1 else Z[idx]
    x = (points_undistorted[idx, 0] - c_x) / f_x * z
    y = (points_undistorted[idx, 1] - c_y) / f_y * z
    result.append([x, y, z])
  return result

f_x = 1000.
f_y = 1000.
c_x = 1000.
c_y = 1000.

intrinsic = np.array([
  [f_x, 0.0, c_x],
  [0.0, f_y, c_y],
  [0.0, 0.0, 1.0]
])

distortion = np.array([0.0, 0.0, 0.0, 0.0])  # This works!
distortion = np.array([-0.32, 1.24, 0.0013, 0.0013])  # This doesn't!

point_single = np.array([[-10.0, 2.0, 12.0],])
point_single_projected = Project(point_single, intrinsic, distortion)
Z = np.array([point[2] for point in point_single])
point_single_unprojected = Unproject(point_single_projected,
                                     Z,
                                     intrinsic, distortion)
print "Expected point:", point_single[0]
print "Computed point:", point_single_unprojected[0]

零失真的结果(如上所述)是正确的:

Expected Point: -10 2 12
Computed Point: -10 2 12

但是当包括扭曲时,结果是关闭的:

Expected Point: -10 2 12
Computed Point: -4.26634 0.848872 12

更新1.澄清

这是相机到图像的投影——我假设 3D 点位于相机框架坐标中。

更新2. 解决了第一个问题

好吧,我算出了减法f_x and f_y——我太蠢了,把索引弄乱了。更新了代码以进行更正。另一个问题仍然存在。

更新3.添加Python等效代码

为了增加可见性,添加Python代码,因为它有同样的错误。


回答问题2

我发现了问题所在——3D 点坐标很重要!我假设无论我选择什么 3D 坐标点,重建都会处理它。然而,我注意到一些奇怪的事情:当使用一系列 3D 点时,只有这些点的子集被正确重建。经过进一步调查,我发现只有相机视野内的图像才能正确重建。视场是内部参数的函数(反之亦然)。

为了使上述代码正常工作,请尝试按如下方式设置参数(内部参数来自我的相机):

...
const double f_x = 2746.;
const double f_y = 2748.;
const double c_x = 991.;
const double c_y = 619.;
...
const cv::Point3d point_single(10.0, -2.0, 30.0);
...

另外,不要忘记在相机坐标中为负y坐标是UP :)

回答问题1:

有一个错误,我试图使用访问内在函数

...
double f_x = intrinsic.at<double>(0, 0);
double f_y = intrinsic.at<double>(1, 1);
double c_x = intrinsic.at<double>(0, 3);
double c_y = intrinsic.at<double>(1, 3);
...

But intrinsic was a 3x3 matrix.

故事的道德启示编写单元测试!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenCV unproject 2D 指向具有已知深度“Z”的 3D 的相关文章

随机推荐

  • 更高种类类型的隐式参数解析

    考虑以下代码 object foo trait Bar Q implicit object OptionBar extends Bar Option def test T C c C T implicit bar Bar C def mai
  • 从 WMI ExecQuery 获取第一条记录

    我有一个简单的 vbscript 用于检索 Windows 版本 Set objWMI GetObject winmgmts impersonationLevel impersonate root cimv2 Set colVersions
  • 如何创建具有透明背景的图像

    如何用GDlib创建透明背景的图像 header content type image png image imagecreatetruecolor 900 350 imagealphablending image true imagesa
  • RecyclerView 上的波纹效果在轻按时不起作用

    我尝试在 RecyclerView 上实现连锁反应 这是我的布局
  • Service Worker 未发送请求标头

    我正在尝试从 Service Worker 获取 Web 服务 该服务是一个使用基本 Apache 身份验证保护的 JSP 因此我必须提供凭据以在请求标头中进行身份验证 以下请求在主窗口中运行良好 self addEventListener
  • throw 0 是什么意思?是“坏”吗?

    Context 我遇到了一些代码 如下所示 if Some Condition throw 0 我用谷歌搜索了一下 发现了一些其他代码片段 使用了那个看起来很奇怪的代码throw 0 form 我想人们会这样理解 catch const i
  • 如何从sklearn的CCA模块获得第一个规范相关性?

    在 Python 的 scikit learn 中 有一个名为 cross decomposition 的模块 其中包含规范相关分析 CCA 类 我一直在试图弄清楚如何给出形状 n m 的 2 类多维向量并获得第一个规范相关系数 查看文档
  • Angular 2 Animate - 更改路线/组件时“* => void”过渡没有可见效果

    使用 Angular 2 Animate RC2 在官方文档的帮助下 以及 Matias 在 YT 频道上一个月前的 ng conf 动画视频中使用的代码 除了最关键的部分之外 我一切正常 更改路由器链接 组件时 我似乎无法让离开过渡 动画
  • tidyverse 未加载,它显示“命名空间‘vctrs’0.2.0 已加载,但需要 >= 0.2.1”

    强文本我在安装时不断遇到问题tidyverse包 这使我无法实现许多文本处理任务 这个问题与 2017 年以来许多以前的线程中提到的问题相同 因为当我输入library tidyverse 或者尝试打开其他相关包 他们总是说需要0 2 1版
  • 为什么二元 + 运算符不能与两个 &mut int 一起使用?

    fn increment number mut int this fails with binary operation cannot be applied to type mut int let foo number number let
  • Eclipse - java.lang.ClassNotFoundException

    当尝试从 Eclipse 中启动 JUnit Test 时 我收到 ClassNotFoundException 从控制台运行 mvn test 时 一切正常 另外 Eclipse 中也没有报告任何问题 我的项目结构如下 parent pr
  • 非静态回调如何在本机代码中工作?

    问这个问题有点奇怪 因为我的代码看起来不应该工作 但它确实工作 虽然我没有抱怨 但我想确认为什么 哈哈 简而言之 我有一个 C 本机 DLL 根本没有 CLR 托管支持 它接受来自 C 代码的回调 Native端存储了一个stdcall回调
  • 将 mySQL 查询作为 cron 作业运行?

    我想清除 SQL 数据库中超过 1 周的所有数据 并且我想每晚执行此操作 所以 我要设置一个 cron 作业 如何查询mySQL而无需每次都手动输入密码 PHP中的查询如下 mysql query DELETE FROM tbl messa
  • 将字符“00:00:00”转换为日期时间“00:00:00”

    我的问题来自这个问题 问题有以下字符串 x lt 2007 02 01 00 00 00 y lt 02 01 2007 00 06 10 如果您尝试将此字符串转换为日期类对象 则会发生一些有趣的事情 这是 nrusell 答案的示例 as
  • 如何将 na.rm 作为参数传递给 tapply?

    我想从数据框中计算平均值和标准差 其中一列用于参数 一列用于组标识符 使用时如何计算它们tapply 我可以用sd v1 group na rm TRUE 但无法适应na rm TRUE使用时进入语句tapply omit na是没有选择
  • Android 多重通知在点击时发送相同的数据

    Android 中的通知在点击时具有相同的意图 我在安装主题后发送通知 考虑我安装了 4 个主题 通知窗口中出现了 4 个通知 但是当我单击每个通知时 它将启动特定的活动 但意图是每个意图具有相同的数据 我的代码是这样的 SuppressW
  • 无法从“const wchar_t *”转换为“_TCHAR *”

    TCHAR strGroupName NULL const TCHAR strTempName NULL Assign some value to strTempName strGroupName tcschr strTempName 92
  • 管理 TPL 队列

    我有一项运行各种服务器扫描的服务 所涉及的网络可能非常庞大 数十万个网络节点 该软件的当前版本使用的是我们设计的队列 线程架构 该架构可以工作 但效率不高 尤其是因为作业可能会产生处理不好的子项 V2 即将推出 我正在考虑使用 TPL 看起
  • Java 标签不规则(可能是错误?)

    如果我们看一下Java标准 14 7 我们看到语句可能有标签前缀 例如 标签声明 标识符 声明 理论上 标签应该能够标记任何后续语句 因此 例如 以下内容将相应编译 public class Test public static void
  • OpenCV unproject 2D 指向具有已知深度“Z”的 3D

    问题陈述 我正在尝试将 2D 点重新投影到其原始 3D 坐标 假设我知道每个点的距离 继OpenCV 文档 我设法让它以零失真的方式工作 然而 当存在扭曲时 结果就不正确 目前的方法 因此 我们的想法是反转以下内容 分为以下内容 By 使用