【问题标题】:Multiply std::vector<cv::Point3f> by a cv::Mat?将 std::vector<cv::Point3f> 乘以 cv::Mat?
【发布时间】:2017-12-01 22:56:30
【问题描述】:

如上所述,我有一个 cv::Point3f 的 std::vector。我有一个转换矩阵。我需要将向量乘以 Mat 的倒数。

我的垫子:(T 是结果转换)

cv::Mat R(3,3,rvec.type());
cv::Rodrigues(rvec, R); // R is 3x3
cv::Mat T(4, 4, R.type()); // T is 4x4
T(cv::Range(0, 3), cv::Range(0, 3)) = R * 1; // copies R into T
T(cv::Range(0, 3), cv::Range(3, 4)) = tvec * 1; // copies tvec into T
float *p = T.ptr<float>(3);
p[0] = p[1] = p[2] = 0; p[3] = 1;

我的矢量:

std::vector&lt;cv::Point3f&gt; objectPoints;

我试过了:

cv::Mat V = T.inv() * cv::Mat(objectPoints, false) 
V.copyTo(cv::Mat(objectPoints, false));

(断言失败,类型错误)

for (int i = 0; i < objectPoints.size(); i++)
{
    cv::Mat dst = cv::Mat(objectPoints[i], false);  
    dst = -T*dst; //USE MATRIX ALGEBRA 
//  cv::Point3f tmp3 = cv::Point3f(dst(0, 0), dst(1, 0), dst(2, 0));

}

(断言失败)

    std::vector<cv::Point3f> p3d;
perspectiveTransform(objectPoints, p3d, -T);

(运行,但值非常不正确)

cv::transform(objectPoints, p3d, -T);

(断言错误)

这样做的正确方法是什么(如果有办法!)?

谢谢。

【问题讨论】:

  • 所以你想将std::vector&lt;cv::Point3f&gt;cv::Mat 的倒数相乘,你希望结果是一个Mat 还是没关系?
  • 理想情况下,结果将是一个新的 std::vector。感谢您的回复。
  • 理想情况下,cv::Mat V = T.inv() * cv::Mat(objectPoints, false) V.copyTo(cv::Mat(objectPoints, false)); 应该可以工作。可以添加Mat的类型吗?
  • 它使用与rvec相同的类型,我初始化为:cv::Mat rvec(3, 1, cv::DataType&lt;float&gt;::type);
  • 我认为问题在于你试图将一个 4*4 矩阵与一个 n*3 相乘(cv::Point3f 转换为 Mat)这不起作用

标签: c++ opencv vector


【解决方案1】:

正如 Rick M. 指出的那样,您正在尝试将 4x4 矩阵与长度为 3 的点相乘。要仅使用一个矩阵乘法(即使用 4x4 组合 R-T 矩阵)执行转换,您首先必须在齐次坐标中表示该点,这实际上只是将 1 作为您的点的第 4 个元素;转换后,您将新点除以第 4 个元素以将其值保持为 1。Here's 是 3D-3D 转换的一个很好的来源,幻灯片 14 中讨论了齐次坐标。

由于 OpenCV 没有 Point4f 类,因此您必须在创建点的 Mat 形式时添加这个 1。这未经测试,但可能有效:

std::vector<cv::Point3f> dstPoint;
for (int i = 0; i < objectPoints.size(); i++) {
    // Convert Point3f to 4x1 Mat (in homogeneous coordinates, with 1 as 4th element)
    cv::Point3f pt = objectPoints[i];
    cv::Mat ptMat = (cv::Mat_<float>(4,1) << pt.x, pt.y, pt.z, 1);

    // Perform matrix multiplication and store as Mat_ for easy element access
    cv::Mat_<float> dstMat(T.inv() * ptMat); 

    // Divide first three resulting elements by the 4th (homogenizing 
    // the point) and store as Point3f
    float scale = dstMat(0,3);
    cv::Point3f dst(dstMat(0,0)/scale, dstMat(0,1)/scale, dstMat(0,2)/scale);
    dstPoints.push_back(dst)
}

会测试,但我在工作,这台计算机上没有 OpenCV。

更新:

复制到 T 时,试试这个:

cv::Mat T(4, 4, cv::DataType<float>::type);
cv::Mat rot   = T(cv::Range(0, 3), cv::Range(0, 3));
cv::Mat trans = T(cv::Range(0, 3), cv::Range(3, 4));
R.copyTo(rot);
tvec.copyTo(trans);

【讨论】:

  • 谢谢!这让我在cv::Mat_&lt;float&gt; dstMat(T.inv() * ptMat); 崩溃,其中:OpenCV Error: Assertion failed (type == _src2.type() &amp;&amp; (type == CV_32F || type == CV_64F)) in cv::solve, file D:\thirdParty\opencv_320\opencv-master\modules\co re\src\lapack.cpp, line 1094
  • 该死。有种铸造行不通的感觉。当你只做 cv::Mat dstMat = T.inv() * ptMat; 时会发生什么?这条线失败了吗?
  • 更仔细地查看错误,这似乎是矩阵乘法中的类型问题。所以我会检查 T 和 ptMat 的类型,以确保它们相同并且是 32 位或 64 位浮点数。
  • 好的!将T 初始化更改为:cv::Mat T(4, 4, cv::DataType&lt;float&gt;::type); 已编译,但结果值为:-nan(ind) -nan(ind) -nan(ind)
  • 哎呀...你能打印出矩阵T.inv()吗?我感觉T 是一个病态矩阵...如果定义了T.inv(),我会打印出您为scale 获得的值,以确保它们不为零。
【解决方案2】:

根据 DCSmith 的回答,我可以正常工作。我不得不做出这个小小的改变:

cv::Mat T(4, 4, cv::DataType<float>::type);
R.copyTo(T(cv::Rect(0, 0, 3, 3)));
tvec.copyTo(T(cv::Rect(3, 0, 1, 3)));

让整个函数看起来像:

std::vector<cv::Point3f> p3d;

cv::Mat R(3,3, cv::DataType<float>::type);
cv::Rodrigues(rvec, R); // R is 3x3
cv::Mat T(4, 4, cv::DataType<float>::type);

R.copyTo(T(cv::Rect(0, 0, 3, 3)));
tvec.copyTo(T(cv::Rect(3, 0, 1, 3)));

float *p = T.ptr<float>(3);
p[0] = p[1] = p[2] = 0; p[3] = 1;


std::vector<cv::Point3f> dstPoint;
for (int i = 0; i < objectPoints.size(); i++) {
    cv::Point3f pt = objectPoints[i];
    cv::Mat ptMat = (cv::Mat_<float>(4, 1) << pt.x, pt.y, pt.z, 1);

    // Perform matrix multiplication and store as Mat_ for easy element access
    cv::Mat_<float> dstMat = T.inv() * ptMat;

    // Divide first three resulting elements by the 4th (homogenizing 
    // the point) and store as Point3f
    float scale = dstMat(0, 3);
    cv::Point3f dst(dstMat(0, 0) / scale, dstMat(0, 1) / scale, dstMat(0, 2) / scale);
    p3d.push_back(dst);
}

感谢您的帮助!

【讨论】:

    猜你喜欢
    • 2017-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多