c++ - 将检测到的 2D 地图调整为参考 2D 地图
问题描述
我有一张地图,其中一些参考位置对应于一些对象的中心(小十字),如下所示:
我拍照是为了找到我的对象,但在图片中我有一些噪音,所以我不能总是找到所有的对象,它可能是这样的:
从少数找到的位置,我需要知道图片中其他未找到的对象应该在哪里。在过去的几天里,我一直在阅读这方面的内容并进行试验,但我找不到合适的方法来做到这一点。在某些示例中,它们首先计算质心并将它们一起平移,然后旋转,其他一些示例使用最小二乘法最小化并从旋转开始。我不能使用 OpenCV 或任何其他 API,只能使用普通 C++。如果有帮助,我可以使用 Eigen 库。谁能给我一些指示?
编辑:
我已经解决了点之间的对应关系,图片与参考没有太大的不同,因此对于每个找到的位置,我都可以搜索其对应的参考。简而言之,我有一个带有参考点的二维矩阵和另一个带有找到点的二维矩阵。在找到的点矩阵中,未找到的点保存为 NaN 只是为了保持相同的矩阵大小,计算中不使用 NaN 点。
解决方案
由于您已经将这些点相互匹配,因此找到变换很简单:
Eigen::Affine2d findAffine(Eigen::Matrix2Xd const& refCloud, Eigen::Matrix2Xd const& targetCloud)
{
// get translation
auto refCom = centerOfMass(refCloud);
auto refAtOrigin = refCloud.colwise() - refCom;
auto targetCom = centerOfMass(targetCloud);
auto targetAtOrigin = targetCloud.colwise() - targetCom;
// get scale
auto scale = targetAtOrigin.rowwise().norm().sum() / refAtOrigin.rowwise().norm().sum();
// get rotation
auto covMat = refAtOrigin * targetAtOrigin.transpose();
auto svd = covMat.jacobiSvd(Eigen::ComputeFullU | Eigen::ComputeFullV);
auto rot = svd.matrixV() * svd.matrixU().transpose();
// combine the transformations
Eigen::Affine2d trans = Eigen::Affine2d::Identity();
trans.translate(targetCom).scale(scale).rotate(rot).translate(-refCom);
return trans;
}
refCloud
是您的参考点集,targetCloud
是您在图像中找到的点集。云与索引匹配很重要,因此refCloud[n]
必须是 的对应点targetCloud[n]
。这意味着您必须从矩阵中删除所有 NaN 并挑选参考点集中的对应项。
这是一个完整的例子。我正在使用 OpenCV 来绘制这些东西:
#include <Eigen/Dense>
#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>
using Point = Eigen::Vector2d;
template <typename TMatrix>
Point centerOfMass(TMatrix const& points)
{
return points.rowwise().sum() / points.cols();
}
Eigen::Affine2d findAffine(Eigen::Matrix2Xd const& refCloud, Eigen::Matrix2Xd const& targetCloud)
{
// get translation
auto refCom = centerOfMass(refCloud);
auto refAtOrigin = refCloud.colwise() - refCom;
auto targetCom = centerOfMass(targetCloud);
auto targetAtOrigin = targetCloud.colwise() - targetCom;
// get scale
auto scale = targetAtOrigin.rowwise().norm().sum() / refAtOrigin.rowwise().norm().sum();
// get rotation
auto covMat = refAtOrigin * targetAtOrigin.transpose();
auto svd = covMat.jacobiSvd(Eigen::ComputeFullU | Eigen::ComputeFullV);
auto rot = svd.matrixV() * svd.matrixU().transpose();
// combine the transformations
Eigen::Affine2d trans = Eigen::Affine2d::Identity();
trans.translate(targetCom).scale(scale).rotate(rot).translate(-refCom);
return trans;
}
void drawCloud(cv::Mat& img, Eigen::Matrix2Xd const& cloud, Point const& origin, Point const& scale, cv::Scalar const& color, int thickness = cv::FILLED)
{
for (int c = 0; c < cloud.cols(); c++)
{
auto p = origin + cloud.col(c).cwiseProduct(scale);
cv::circle(img, {int(p.x()), int(p.y())}, 5, color, thickness, cv::LINE_AA);
}
}
int main()
{
// generate sample reference
std::vector<Point> points = {{4, 9}, {4, 4}, {6, 9}, {6, 4}, {8, 9}, {8, 4}, {10, 9}, {10, 4}, {12, 9}, {12, 4}};
Eigen::Matrix2Xd fullRefCloud(2, points.size());
for (int i = 0; i < points.size(); i++)
fullRefCloud.col(i) = points[i];
// generate sample target
Eigen::Matrix2Xd refCloud = fullRefCloud.leftCols(fullRefCloud.cols() * 0.6);
Eigen::Affine2d refTransformation = Eigen::Affine2d::Identity();
refTransformation.translate(Point(8, -4)).rotate(4.3).translate(-centerOfMass(refCloud)).scale(1.5);
Eigen::Matrix2Xd targetCloud = refTransformation * refCloud;
// find the transformation
auto transform = findAffine(refCloud, targetCloud);
std::cout << "Original: \n" << refTransformation.matrix() << "\n\nComputed: \n" << transform.matrix() << "\n";
// apply the computed transformation
Eigen::Matrix2Xd queryCloud = fullRefCloud.rightCols(fullRefCloud.cols() - refCloud.cols());
queryCloud = transform * queryCloud;
// draw it
Point scale = {15, 15}, origin = {100, 300};
cv::Mat img(600, 600, CV_8UC3);
cv::line(img, {0, int(origin.y())}, {800, int(origin.y())}, {});
cv::line(img, {int(origin.x()), 0}, {int(origin.x()), 800}, {});
drawCloud(img, refCloud, origin, scale, {0, 255, 0});
drawCloud(img, fullRefCloud, origin, scale, {255, 0, 0}, 1);
drawCloud(img, targetCloud, origin, scale, {0, 0, 255});
drawCloud(img, queryCloud, origin, scale, {255, 0, 255}, 1);
cv::flip(img, img, 0);
cv::imshow("img", img);
cv::waitKey();
return 0;
}
推荐阅读
- c# - 在实体框架中使用lambra表达式优化查询
- html - 具有百里香属性的条件内联格式
- cocoa - 替换项目时 NSOutlineView reloadItem/reloadData 不起作用
- vue-component - nuxt 组件:计算与数据
- java - web.xml - 动态而不是硬编码 - 有可能吗?
- c++ - cin 语句在循环的第三次迭代中被跳过
- python - Tkinter:使用多个实例撤回 Tkinter 根窗口的正确方法
- kubernetes - 向现有部署 kubernetes 添加新端口
- javascript - 使用 Jest 假计时器进行所有测试
- node.js - Socket.io:socket.adapter.rooms 对象中的“真”是什么意思?