逐列比较

时间:2017-08-07 21:41:52

标签: c++ opencv

在这种方法中,我想找到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)应该可以找到相同的列,但它似乎是错误的实现。另外 - 我怎么能归还那些彼此距离最远的点呢?

也许对于我的一些澄清,这里是一个图像,它应该是什么样子(带圆圈的点应该是那些,必须返回): enter image description here

我对任何答案感到高兴!

1 个答案:

答案 0 :(得分:0)

如果我理解正确的任务,你需要将你的算法分为两个阶段,因为我认为你可以在同一个循环中有效地完成这两件事:

  1. 填充pointVec

  2. 迭代pointVec并计算距离

  3. 填充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[]的调用。点插入也会更慢,所以这个解决方案的优点和缺点就像上面显示的那样。