Compare Columns by Columns

In this method, I would like to find the longest distance between two (adjacent) points for each column in cv::Mat

. At the end, the corresponding points (which have the greatest distance to each other) must be returned.

To achieve this I have researched a lot already and now I have stuck with this piece of code:

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);
                }
        }
    }
}

      

So, I am already iterating through cv::Mat

and also calculating the distance. Now I would like to implement a search for the greatest distance for each column. Here I ask you for help, how could I realize this. Although I thought it if (pointVec[j].x == pointVec[j + 1].x)

should be okay to find the same columns, it seems to be the wrong implementation. Also - how can I return those points that have the greatest distance to each other?

Perhaps for some I am clarifying, here is the image of how it should look (the circular points should be the ones that should be returned): enter image description here

I am glad for any answer!

+3


source to share


1 answer


If I understand the problem correctly, you need to split the algorithm into two phases, as I think you cannot effectively do these two things inside the same loop:

  • Filling pointVec

  • Iterating through pointVec

    and calculating distances

Filling 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));
        }

    }
}

      

I used emplace_back()

instead push_back()

and excluded a temporary variable to store the point, although in this case the performance difference might be negligible due to optimization. I also presented sought_value

because it seems to me to be easier to read, but it is up to you which version you choose.

Iterating through pointVec

and calculating distances



Step 2 will be much easier if we sort pointVec

by columns and then row beforehand. Thus, we know that consecutive points are usually adjacent to each other and belong to the same column. Also, I'm going to use std::tuple<int, float, std::pair<cv::Point, cv::Point>>

to store the maximum distance of each column along with the column number and the points the distance refers to, as we can easily find the points and find the maximum distance for each column as you wanted.

// 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

contains column number, dist and point respectively. I could skip the column number, but I think searching for items based on eg. on the first coordinate of a point belonging to a pair belonging to a tuple (ugh!) in a vector would be really ugly and inconsistent. The tuple above is not the prettiest thing in terms of syntax, but I think it is the same as how you want to use the results.

I haven't had a chance to test this solution so it might contain some minor bugs, but the general idea should work.

If you are going to do a lot of searches based on the column number, consider using std::map

with the column number as key and a tuple of distance and pair cv::Point

as values. The basic algorithm will look the same, but then you have to replace the calls with operator[]

map iterators. Also adding points will be slower, so this solution has its pros and cons as shown above.

0


source







All Articles