1. 无序特征点的挑战与解决方案
在计算机视觉项目中,我们经常会遇到这样的场景:使用SimpleBlobDetector检测图像中的圆形标记点,但得到的特征点顺序是随机的。这时候如果直接把这些无序的点输入到cv::solvePnP函数中,结果往往会错得离谱。我曾在工业检测项目中就踩过这个坑,当时花了两天才发现问题出在特征点顺序上。
想象一下这样的场景:你在处理一个带有四个圆形标记的标定板图像。通过SimpleBlobDetector可以准确地找到这四个圆的中心坐标,但这些点在内存中的存储顺序可能完全随机。而你的世界坐标系中这四个点的顺序是固定的(比如左上、右上、左下、右下)。如果不对应起来,solvePnP就会把错误的3D-2D对应关系输入算法,导致位姿估计完全错误。
解决这个问题的核心思路是:建立图像特征点与世界坐标系点之间的一一对应关系。我常用的方法是基于几何位置的排序算法。比如对于四个点的情况,可以按照以下逻辑处理:
- 先找到x和y坐标之和最小的点作为左上角
- 找到x和y坐标之差最大的点作为右上角
- 找到x和y坐标之和最大的点作为右下角
- 剩下的自然就是左下角
这种方法虽然简单,但在实际项目中非常有效。下面是一个改进版的排序实现,比原始文章中的冒泡排序更高效:
cpp复制void sortPoints(std::vector<cv::Point2f>& points) {
// 计算各点的x+y和x-y
std::vector<float> sumXY, diffXY;
for (const auto& pt : points) {
sumXY.push_back(pt.x + pt.y);
diffXY.push_back(pt.x - pt.y);
}
// 获取排序后的索引
auto topLeftIdx = std::min_element(sumXY.begin(), sumXY.end()) - sumXY.begin();
auto bottomRightIdx = std::max_element(sumXY.begin(), sumXY.end()) - sumXY.begin();
auto topRightIdx = std::max_element(diffXY.begin(), diffXY.end()) - diffXY.begin();
auto bottomLeftIdx = std::min_element(diffXY.begin(), diffXY.end()) - diffXY.begin();
// 重新排序
std::vector<cv::Point2f> sortedPoints = {
points[topLeftIdx],
points[topRightIdx],
points[bottomRightIdx],
points[bottomLeftIdx]
};
points = sortedPoints;
}
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
