Трехмерная маркировка соединенных точек на основе евклидовых расстояний

В настоящее время я работаю над проектом, который пытается сгруппировать 3D-точки из набора данных, указав связность как минимальное евклидово расстояние. Мой алгоритм прямо сейчас — это просто 3D-адаптация наивной заливки.

size_t PointSegmenter::growRegion(size_t & seed, size_t segNumber) {
    size_t numPointsLabeled = 0;

    //alias for points to avoid retyping
    vector<Point3d> & points = _img.points;
    deque<size_t> ptQueue;
    ptQueue.push_back(seed);
    points[seed].setLabel(segNumber);
    while (!ptQueue.empty()) {
        size_t currentIdx = ptQueue.front();
        ptQueue.pop_front();
        points[currentIdx].setLabel(segNumber);
        numPointsLabeled++;
        vector<int> newPoints = _img.queryRadius(currentIdx, SEGMENT_MAX_DISTANCE, MATCH_ACCURACY);
        for (int i = 0; i < (int)newPoints.size(); i++) {
            int newIdx = newPoints[i];
            Point3d &newPoint = points[newIdx];
            if(!newPoint.labeled()) {
                newPoint.setLabel(segNumber);
                ptQueue.push_back(newIdx);
            }
        }
    }

    //NOTE to whoever wrote the other code, the compiler optimizes i++ 
    //to ++i in cases like these, so please don't change them just for speed :)
    for (size_t i = seed; i < points.size(); i++) {
        if(!points[i].labeled()) {
            //search for an unlabeled point to serve as the next seed.
            seed = i;
            return numPointsLabeled;
        }
    }
    return numPointsLabeled;
}

Где этот фрагмент кода запускается снова для нового начального числа, а _img.queryRadius() представляет собой поиск с фиксированным радиусом с библиотекой ANN:

vector<int> Image::queryRadius(size_t index, double range, double epsilon) {
    int k = kdTree->annkFRSearch(dataPts[index], range*range, 0);
    ANNidxArray nnIdx = new ANNidx[k];
    kdTree->annkFRSearch(dataPts[index], range*range, k, nnIdx);
    vector<int> outPoints;
    outPoints.reserve(k);
    for(int i = 0; i < k; i++) {
        outPoints.push_back(nnIdx[i]);
    }
    delete[] nnIdx;
    return outPoints;
}

Моя проблема с этим кодом заключается в том, что он работает слишком медленно для больших наборов данных. Если я не ошибаюсь, этот код будет выполнять поиск для каждой отдельной точки, и поиск будет O (NlogN), что дает временную сложность (N ^ 2 * log (N)).

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

Итак, мой вопрос: есть ли лучший способ сделать это? Особенно таким образом, который будет расти линейно вместе с набором данных?

Спасибо за любую помощь, которую вы можете оказать

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

Может у людей есть другие идеи? Я честно сейчас в тупике.


person Xzhsh    schedule 10.09.2010    source источник
comment
Взгляните на en.wikipedia.org/wiki/K-means_clustering (k- значит кластеризация)   -  person Dr. belisarius    schedule 11.09.2010
comment
Проблема с кластеризацией k-средних заключается в том, что я понятия не имею, сколько кластеров у меня получится, и во многих случаях у меня есть что-то вроде двух больших параллельных плоскостей, расположенных близко друг к другу, и они должны быть в разных сегментах, потому что расстояние между плоскостями больше, чем мой максимальный радиус для сегментации.   -  person Xzhsh    schedule 24.09.2010
comment
Сколько баллов у вас есть? К-средние, как правило, довольно плохи для таких особых случаев, чтобы найти что-то хорошее, работающее даже со сложным набором данных, вам нужно посмотреть текущие исследования по кластеризации. Непростая задача... но вам могут не понадобиться общие решения. Какое у вас приложение? Есть ли у вас определенные ограничения/особые случаи?   -  person Loïc Février    schedule 26.09.2010
comment
Спасибо за ответ Феврье. Я знаю, что это очень специализированный вопрос, так как я имею дело с аэрофотосъемкой данных LIDAR для обнаружения зданий, деревьев и т. д. Я провожу исследования, пока работаю над этим проектом, но я подумал, что опубликую здесь, если кто-то работал над чем-то подобным.   -  person Xzhsh    schedule 26.09.2010


Ответы (3)


Предлагаю следующий алгоритм:

  1. Вычислите трехмерную триангуляцию Делоне ваших точек данных.

  2. Удалите все ребра, которые длиннее вашего порогового расстояния, O (N) в сочетании с шагом 3.

  3. Найдите компоненты связности в полученном графе размером O(N), это делается за O(N (N)).

Узким местом является шаг 1, который можно выполнить за O(N2) или даже за O(N log N) в соответствии с этой страницей http://www.ncgia.ucsb.edu . Однако это определенно не алгоритм из 100 строк.

person Yakov Galka    schedule 27.09.2010
comment
@Alexandre: я посмотрел на cgal, но не смог найти сложность реализации. У вас есть ссылка? - person Yakov Galka; 27.09.2010
comment
citeseerx.ist.psu.edu/viewdoc/ Наконец-то я нашел его. Люди из INRIA, разрабатывающие эти алгоритмы, реализуют их в CGAL. - person Alexandre C.; 27.09.2010
comment
довольно классное решение, определенно дало мне новый взгляд на эту проблему. Спасибо! - person Xzhsh; 29.09.2010

Когда я делал что-то в этом роде, я выбирал где-то «начало» за пределами набора данных и сортировал все точки по расстоянию до этого начала. Тогда у меня был гораздо меньший набор точек для выбора на каждом шаге, и мне нужно было пройти только область «луковой шелухи» вокруг рассматриваемой точки. Вы будете проверять соседние точки до тех пор, пока расстояние до ближайшей точки не станет меньше ширины проверяемого диапазона.

Хотя это хорошо сработало для меня, аналогичную версию этого можно достичь, отсортировав все ваши точки по одной оси (что будет представлять «происхождение», находящееся бесконечно далеко), а затем просто проверяя точки снова, пока ваша «ширина поиска» не превысит расстояние до ближайшей найденной точки.

person dash-tom-bang    schedule 10.09.2010
comment
Это действительно хороший трюк, спасибо! Как вы организовали свой набор данных? - person Xzhsh; 10.09.2010
comment
Я думаю, что в то время я запихнул все это в карту, но просто отсортированный вектор (std::sort с соответствующим функтором) тоже поможет, если вы не собираетесь часто менять свой набор данных. - person dash-tom-bang; 10.09.2010

Точки должны быть лучше организованы. Для более эффективного поиска вместо vector<Point3d> вам нужна какая-то хеш-карта, где хэш-коллизия подразумевает, что две точки находятся близко друг к другу (поэтому вы используете хеш-коллизии в своих интересах). Например, вы можете разделить пространство на кубы размером, равным SEGMENT_MAX_DISTANCE, и использовать хэш-функцию, которая возвращает триплет целых чисел вместо одного целого числа, где каждая часть триплета вычисляется как point.<corresponding_dimension> / SEGMENT_MAX_DISTANCE.

Теперь для каждой точки в этом новом наборе вы ищете только точки в том же кубе и в соседних кубах пространства. Это значительно сокращает пространство поиска.

person Dialecticus    schedule 26.09.2010
comment
Будет ли это чем-то похоже на решение для сетки? - person Xzhsh; 27.09.2010
comment
Да, вы группируете точки по их положению в 3D-сетке. Цель описываемой хеш-карты в том, что если у вас есть расположение одной ячейки сетки, то у вас сразу есть все точки этой ячейки, а также все соседние ячейки. А если у вас есть точка, то вы сразу знаете ее ячейку сетки. - person Dialecticus; 27.09.2010
comment
Возможно, мне следует указать, что мой ответ - это не другое решение, а улучшение вашего текущего решения. Поскольку _img.points организован лучше, _img.queryRadius следует изменить, чтобы использовать его для запроса только тех точек, которые являются хорошими кандидатами. Если вам не хочется менять _img.points с вектора‹Point3d› на предложенную структуру, вы все равно можете использовать вектор, но с точками, сгруппированными по сетке, и использовать структуру только для хранения граничных данных для каждого сегмента внутри вектора. Я мог бы написать пример кода, если хотите. - person Dialecticus; 27.09.2010
comment
... однако тогда мне понадобится код для kdTree-›annkFRSearch. - person Dialecticus; 27.09.2010