在这种方法中,我想找到cv::Mat
中每个列的两个(adjecent)点之间最大距离。最后应返回相应的点(彼此之间的距离最大)。
为了实现这个目标,我已经研究了很多,现在我坚持使用这段代码:
cv::Mat mat;
std::vector<cv::Point> pointVec, finalPointVec;
std::vector<float> allDist;
for (int i = 0; i < mat.rows; i++) {
for (int j = 0; j < mat.cols; j++) {
c = mat.col(j);
if (c.at<Vec3b>(i, j)[0] == 0
&& c.at<Vec3b>(i, j)[1] == 0
&& c.at<Vec3b>(i, j)[2] == 255) {
cv::Point diPoint(j, i);
pointVec.push_back(diPoint);
if (pointVec[j].x == pointVec[j + 1].x) {
//std::cout << pointVec[j].y << "\n";
float diffY = pointVec[j].y - pointVec[j + 1].y;
float diffX = pointVec[j].x - pointVec[j + 1].x;
float dist = sqrt((diffY * diffY) + (diffX * diffX));
for (int d = 0; d < pointVec[j].x; d++) {
allDist.push_back(dist);
}
}
}
}
所以我已遍历cv::Mat
并计算距离。现在我想实现找到每个列的最大距离。在这里,我要求你的帮助,我是如何实现它的。虽然我认为if (pointVec[j].x == pointVec[j + 1].x)
应该可以找到相同的列,但它似乎是错误的实现。另外 - 我怎么能归还那些彼此距离最远的点呢?
也许对于我的一些澄清,这里是一个图像,它应该是什么样子(带圆圈的点应该是那些,必须返回):
我对任何答案感到高兴!
答案 0 :(得分:0)
如果我理解正确的任务,你需要将你的算法分为两个阶段,因为我认为你可以在同一个循环中有效地完成这两件事:
填充pointVec
迭代pointVec
并计算距离
pointVec
cv::Mat mat;
std::vector<cv::Point> pointVec;
const cv::Vec3b sought_value(0, 0, 255);
for (int i = 0; i < mat.rows; i++) {
for (int j = 0; j < mat.cols; j++) {
if (mat.at<Vec3b>(i, j) == sought_value) {
pointVec.emplace_back(cv::Point(i, j));
}
}
}
我使用emplace_back()
代替push_back()
并删除了用于存储点的临时变量,即使在这种情况下由于优化而性能差异也很小。我还介绍了sought_value
,因为我认为这种方式更容易阅读,但是由您决定选择哪个版本。
pointVec
并计算距离如果我们事先根据列对pointVec
进行排序,然后再对行进行排序,那么第2步会更容易。这样我们就知道连续的点通常彼此相邻并且属于同一列。此外,我将使用std::tuple<int, float, std::pair<cv::Point, cv::Point>>
来存储每列的最大距离以及列号和距离所指的点,因为这样我们可以轻松找到点并搜索每列的最大距离如你所愿。
// keeps the results - column number, distance and points respectively:
using ColDistPointsTuple = std::tuple<int, float, std::pair<cv::Point, cv::Point>>;
std::vector <ColDistPointsTuple> column_maxes;
std::sort(column_maxes.begin(), column_maxes.end(),
[](const cv::Point& a, const cv::Point& b) -> bool
{
if (a.x < b.x)
return true;
else if (a.x == b.x)
return a.y < b.y;
else
return false;
}
);
for (int j = 0; j < pointVec.size() - 1; ++j) // note '-1' here - otherwise you'll get out of bounds exception
{
if (pointVec[j].x == pointVec[j + 1].x) {
float diffY = pointVec[j].y - pointVec[j + 1].y;
float diffX = pointVec[j].x - pointVec[j + 1].x;
float dist = sqrt((diffY * diffY) + (diffX * diffX));
if (!column_maxes.empty())
{
if (std::get<0>(column_maxes.back()) == pointVec[j].x) // belongs to the same column
{
if (dist > std::get<1>(column_maxes.back())) // distance greater than the one stored for that column
{
column_maxes.back() =
std::make_tuple( pointVec[j].x, dist, pointVec[j], pointVec[j + 1] );
continue;
}
}
}
column_maxes.emplace_back( pointVec[j].x, dist, pointVec[j], pointVec[j+1] );
}
}
column_maxes
分别包含列号,dist和点。我本可以跳过列号,但我想基于例如搜索元素。在属于属于一个向量中的元组(呃!)的对的一个点的第一个坐标上,这将是非常丑陋和违反直觉的。上面的元组在语法方面不是最漂亮的东西,但我认为匹配你想要使用结果的方式。
我没有机会测试这个解决方案,所以它可能包含一些小错误,但总体思路应该有效。
如果要根据列号进行大量搜索,请考虑使用std::map
将列号作为键,使用由距离和一对cv::Point
组成的元组作为价值观。基本算法看起来一样,但是你必须用映射迭代器替换对operator[]
的调用。点插入也会更慢,所以这个解决方案的优点和缺点就像上面显示的那样。