Вычисление прямоугольника вокруг координат пикселя

У меня есть Qvector, содержащий пиксельные координаты изображения. Основная цель состоит в том, чтобы сгруппировать эти пиксели относительно их расстояния друг от друга и получить прямоугольник из этих групп пикселей. Каждый пиксель в векторе не находится близко друг к другу, и именно поэтому я хотел бы их сгруппировать.

Я использую openCv и Qt. Я хотел бы избежать blobDetector из OpenCV, который довольно медленный, и сделать это самостоятельно, если это возможно.

У кого-нибудь есть идея, как с этим справиться?

ИЗМЕНИТЬ:

Допустим, белые точки находятся близко друг к другу в пикселях. Основная цель состоит в том, чтобы обнаружить, что эти пиксели находятся близко друг к другу, и иметь возможность получить прямоугольник из этих пикселей. Это возможно ?

введите здесь описание изображения

EDIT2:

После получения кластеров я попытался получить ограничивающие прямоугольники вокруг этих кластеров с помощью следующего кода. Возможно, я неправильно использую эту функцию.

cv::partition(cvCoordsTable, testVector, Dist(eqludianThreshold));
std::vector<cv::Rect> rectTable;

for(int in = 0; in < testVector.size(); in++)
{
    rectTable.push_back(cv::boundingRect(cvCoordsTable.at(in)));
}

Спасибо за помощь


person Robert Jones    schedule 09.02.2016    source источник
comment
каково ожидаемое количество групп? мне кажется проблема с кластеризацией, не могли бы вы показать некоторые входные и выходные данные, которые вы хотите?   -  person Kornel    schedule 09.02.2016
comment
Я действительно не знаю ожидаемого количества групп. Предположим, что эти координаты пикселей — это пиксели, которые изменились между двумя кадрами видео. В зависимости от этих кадров они могут быть очень разными, поэтому я не могу дать вам ожидаемое число. Я добавил информацию в свой пост   -  person Robert Jones    schedule 09.02.2016


Ответы (2)


Это проблема кластеризации в первую очередь. Поскольку вы не знаете количество кластеров (групп), вы должны использовать некоторый алгоритм, который не требует количества кластеров в качестве входных данных. Вы можете сделать простой cv::partition, который имеет следующая подпись на С++:

int cv::partition(const vector<_Tp>& vec, vector<int>& labels, _EqPredicate predicate=_EqPredicate())

Пример использования:

    std::vector<cv::Point> pixelCoordinatesTable,
    std::vector<int> labelsTable;
    double threshold= 5;//Max eqludian distance between one cluster points
    cv::partition(pixelCoordinatesTable, labelsTable, [&threshold](auto const& l, auto const& r){
        return cv::norm(l - r))<threshold;
    });

Другим и более зрелым вариантом будет использование реального алгоритма кластеризации, такого как DBSCAN. Это алгоритм кластеризации на основе плотности. и вы можете найти реализацию C++ здесь.

После того, как вы получили кластеры (любым методом), просто примените cv::boundingRect к каждому cluster, чтобы получить желаемый rectangle.

ИЗМЕНИТЬ:

Чтобы решить задачу о прямоугольнике:

auto cluster_count = cv::partition(cvCoordsTable, testVector, Dist(eqludianThreshold)); // gettting the number of clusters
std::vector<cv::Rect> rectTable;
rectTable.reserve(cluster_count);//Optimiaztion
for(int in = 0; in < cluster_count; in++){
    std::vector<cv::Point> temp;
    temp.reserve(testVector.size());
    for(size_t i=0;i<testVector.size();++i){
        if(testVector[i]==in){
             temp.emplace_back(rectTable[i]);
        }
    }
    rectTable.emplace_back(cv::boundingRect(temp));
}

Я уверен, что есть лучший и более быстрый способ, я просто объяснял идею, и вы можете оптимизировать ее, как можете.

person Humam Helfawi    schedule 09.02.2016
comment
Это кажется милым. Допустим, у меня есть std::vector‹cv::Point› pixelCoordinatesTable, правильно ли будет использовать cv::partition(pixelCoordinatesTable, labelsTable); или есть другой способ его использования? - person Robert Jones; 09.02.2016
comment
Теперь, когда таблица labelsTable заполнена, как мне вызвать функцию boudingRect? Должен ли я зацикливаться между каждым элементом labelsTable и создавать прямоугольник в соответствии с таблицей pixelCoordinates? Большое спасибо за ваш точный ответ! - person Robert Jones; 09.02.2016
comment
Я только что попытался выполнить цикл с boudingRects, но у меня неверные результаты. Не могли бы вы дать мне совет о том, как я могу его использовать? Спасибо - person Robert Jones; 09.02.2016
comment
см. мой отредактированный пост, пожалуйста. Кроме того, нужно ли сортировать значения внутри de labelsTable? так как это не последовательные числа все время. Например. 0,0,1,1,1,1,2,2,0,3,3,3,3 и т.д... - person Robert Jones; 09.02.2016
comment
@RobertJones Я написал вам небольшой /непроверенный/ пример - person Humam Helfawi; 09.02.2016

@Humam Helfawi победил меня здесь, но в любом случае, если у вас есть список центральных точек, используйте cv::partition для (неконтролируемой) кластеризации:

struct Dist
{
    double D;
    Dist(double d) : D(d) {}
    bool operator()(const Point &a, const Point &b)
    {
        return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) < D;
    }
};
vector<Point> centers; // e.g. from findContours -> boundingRect
vector<int> labels;
cv::partition(centers,labels,Dist(800));
cerr << Mat(labels).t() << endl;

[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]

person berak    schedule 09.02.2016
comment
Это также прекрасно, как и @Human Helfawi. Однако один вопрос: какое среднее эклюдианское расстояние я должен использовать? Вы используете порог 800, тогда как Humam использует 5. - person Robert Jones; 09.02.2016
comment
Я этого не делал :D Я просто поставил случайное число. - person Humam Helfawi; 09.02.2016
comment
800 больше квадрата расстояния (чтобы сохранить вызов sqrt) - person berak; 09.02.2016
comment
@HumamHelfawi О, хорошо, я просто сомневался в этом! - person Robert Jones; 09.02.2016
comment
@berak хорошая идея, так как нам не нужно знать реальное расстояние .. sqrt действительно дорого. - person Humam Helfawi; 09.02.2016