Does OpenCV offer a squared norm function for cv::Point?

前端 未结 2 834
时光取名叫无心
时光取名叫无心 2021-01-17 20:30

I have to check several distances between points against a distance threshold. What I can do is taking the square of my threshold and compare it with the squared norm of

相关标签:
2条回答
  • 2021-01-17 21:17

    Note from OP:
    I accepted this answer as it's the best method one can achieve using OpenCV,
    but I think the best solution in this case is going for a custom function.


    Yes, it's NORM_L2SQR:

    #include <opencv2\opencv.hpp>
    #include <vector>
    using namespace cv;
    using namespace std;
    
    int main()
    {
        vector<Point> pts{ Point(0, 2) };
    
        double n = norm(pts, NORM_L2SQR);
        // n is 4
    
        return 0;
    }
    

    You can see in the function cv::norm in stat.cpp that if you use NORM_L2SQR you don't compute the sqrt on the norm:

    ...
    if( normType == NORM_L2 )
    {
        double result = 0;
        GET_OPTIMIZED(normL2_32f)(data, 0, &result, (int)len, 1);
        return std::sqrt(result);
    }
    if( normType == NORM_L2SQR )
    {
        double result = 0;
        GET_OPTIMIZED(normL2_32f)(data, 0, &result, (int)len, 1);
        return result;
    }
    ...
    

    Regarding the specific issue:

    My actual problem is: I have a vector of points, merge points closer to each other than a given distance. "Merging" means remove one and move the other half way towards the just removed point.

    You can probably

    • take advantage of the partition function with a predicate that returns true if two points are within a given threshold.
    • retrieve all points in the same cluster
    • compute the centroid for each cluster

    Here the code:

    #include <opencv2\opencv.hpp>
    #include <vector>
    using namespace cv;
    using namespace std;
    
    int main()
    {
        vector<Point> pts{ Point(0, 2), Point{ 1, 0 }, Point{ 10, 11 }, Point{11,12}, Point(2,2) };
    
        // Partition according to a threshold
        int th2 = 9;
        vector<int> labels;     
        int n = partition(pts, labels, [th2](const Point& lhs, const Point& rhs) { 
            return ((lhs.x - rhs.x)*(lhs.x - rhs.x) + (lhs.y - rhs.y)*(lhs.y - rhs.y)) < th2;
        });
    
        // Get all the points in each partition
        vector<vector<Point>> clusters(n);
        for (int i = 0; i < pts.size(); ++i)
        {
            clusters[labels[i]].push_back(pts[i]);
        }
    
        // Compute the centroid for each cluster
        vector<Point2f> centers;
        for (const vector<Point>& cluster : clusters)
        {
            // Compute centroid
            Point2f c(0.f,0.f);
            for (const Point& p : cluster)
            {
                c.x += p.x;
                c.y += p.y;
            }
            c.x /= cluster.size();
            c.y /= cluster.size();
    
            centers.push_back(c);
        }
    
        return 0;
    }
    

    will produce the two centers:

    centers[0] : Point2f(1.0, 1.3333);
    centers[1] : Point2f(10.5, 11.5)
    
    0 讨论(0)
  • 2021-01-17 21:17

    It seems there's nothings specific to address this problem.

    I thought that one solution could be using the ddot method (dot product), and compute something like

    cv::Point distVec = a-b;
    double squaredNorm = distVec.ddot(distVec);
    

    If one is willing to take the risk, there's cv::normL2Sqr which requires the input to be in array format: cv::Point might be directly convertible to int[].

    I will personally move on by writing my own squared norm.

    0 讨论(0)
提交回复
热议问题