Алгоритм упрощения пути - PullRequest
       67

Алгоритм упрощения пути

4 голосов
/ 11 августа 2011

Учитывая путь, я хочу оптимизировать его, чтобы можно было удалить все вершины, прямые на линии.

Например: Path:

*******
*      *
*        *
***********

Может быть оптимизирован для:

*-----*
|      \
|        \
*---------*

Однако я хочу контролировать отклонение от наклона, чтобы оно не должно быть точно на склоне.

Какой алгоритм может это сделать?

Спасибо

Ответы [ 7 ]

7 голосов
/ 11 августа 2011

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

Это может быть реализовано за O (n) время с простым проходом по данным, если у вас есть точки, хранящиеся в структуре данных, такой как двусвязный список.

Надеюсь, это поможет!

3 голосов
/ 11 августа 2011

Это будет немного абстрагированное представление, так как я не очень-то разбираюсь в C ++, но вот так ...

Давайте посмотрим на один момент прямо сейчас:

*******
*      *
*        *<- this one, lets call it X
***********

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

*******
*      *<- A
*        *
***********<- B

Если угол от A до X одинаков (или в пределах ошибки вы считаете достаточно точной) в качестве угла от X до B, тогда X не требуется.

Это НЕ приведет к тому же результату, что и алгоритм выпуклой оболочки.Это просто уменьшит разрешение пути.Вы можете получить побочные эффекты, если допустимая ошибка слишком велика, например:

     *        *
    *         |
   *          |
    *    ->   |
     *        |
    *         |
   *          *

Или если ваша ошибка слишком мала, вы можете вообще не изменять путь.

Такжеобратите внимание, что выпуклая оболочка может сильно изменить путь, Пример:

  *   *            *---*
 * * * *          /     \
*   *   *   ->   *       *
*       *        |       |
*********        *-------*
3 голосов
/ 11 августа 2011

Вы должны использовать алгоритм выпуклой оболочки (это зависит от того, как ваш полигон хранится в памяти), а затем очистить точки с минимальным углом на обеих соседних точках.Тогда у вас будет многоугольник с только точкой в ​​конце.

Вот оно: http://en.wikipedia.org/wiki/Convex_hull

Это много возможных реализаций. Это зависит от того, на каком языке вы программируетеи данные, с которыми вы играете ..

Редактировать: я не знал, что у вас уже есть точки в данных. Просто итерируйте по точкам и рассчитайте угол между той, на которой вы находитесь.Пред и след.если угол ~ = 180, стереть текущую точку.

0 голосов
/ 02 марта 2014

Я реализовал решение @ templatetypedef в C++ для замкнутой многоугольной цепочки, описываемой двумя x,y векторами.Я прохожу многоугольник, и если точка коллинеарна предыдущей и следующей точке, я удаляю ее:

template<class T> void del_collinear_vanilla(std::vector<T> &x,
        std::vector<T> &y) {
    assert(x.size() == y.size());
    size_t i = x.size();
    size_t im1, ip1 = 0;
    do {
        i--;
        im1 = i ? i - 1 : x.size() - 1;
        if (are_collinear(x[ip1], y[ip1], x[i], y[i], x[im1], y[im1])) {
            x.erase(x.begin() + i);
            y.erase(y.begin() + i);
        }
        ip1 = i;
    } while (i != 0);
}

, где реализация зависит от макроса / шаблона are_collinear(x0,y0,x1,y1,x2,y2).

Однако в некоторых случаях у меня все еще оставались коллинеарные точки на выходе.Это пример входных данных, с которыми алгоритм терпит неудачу:

enter image description here

В этом примере P5 совпадает с P0, а P4 имеет одинаковую ординату P0 и P1;Я немного изменил их координаты, чтобы показать все сегменты.Алгоритм должен возвращать только прямоугольник с вершинами P1, P2, P3, P4.

Выше, P6 коллинеарен с P5 и P0.Затем, как только P6 исключен, P5 и P0 совпадают, и они оба коллинеарны с P4 и P1.

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

(В примере, скажем, вы начинаете с P0, и вы обнаружите, что он не коллинеарен точке до P6 и точке после P1. Тогда выпереходите к P1, P2, ... пока не достигнете P6. P6 коллинеарен, вы удаляете его, и цикл завершается. Но теперь P0 коллинеарен с P4 и P1, и он должен был быть удален!)

Тот же недостаток существует для открытого пути.Алгоритм работает нормально, пока входной путь не свернулся сам по себе , в некотором смысле.

Решение состоит в том, чтобы при каждом удалении точки делать шаг назад, чтобы проверить,предыдущая точка теперь стала коллинеарной:

template<class T> void del_collinear(std::vector<T> &x, std::vector<T> &y) {
    assert(x.size() == y.size());
    size_t target = x.size() - 1;
    size_t i = x.size() - 1;
    do {
        size_t im1 = i ? i - 1 : x.size() - 1;
        size_t ip1 = (i == x.size() - 1) ? 0 : i + 1;
        if (are_collinear(x[ip1], y[ip1], x[i], y[i], x[im1], y[im1])) {
            x.erase(x.begin() + i);
            y.erase(y.begin() + i);
            // I do not decrease i in this case, as the the previous (alread
            // processed) point may now be a collinear point that must be
            // deleted. I mod it because i may now exceed x.size()
            i = i % x.size();
            //Increment the target as well.
            target = (i + 1 + x.size()) % x.size();
        } else
            //go for the next point.
            i = i ? i - 1 : x.size() - 1;
    } while (i != target);
}
0 голосов
/ 12 августа 2011

Мне кажется, эта страница должна вам помочь: Простые полигоны (и я также рекомендую книгу ).

0 голосов
/ 12 августа 2011

Более сложное решение будет включать методы обработки изображений.Вы можете попробовать преобразование Хафа , которое допускает отклонения.Отклонения могут быть включены путем «размывания» пространства параметров.Однако алгоритм не прост.Также я не знаю, насколько хорошо он обрабатывает большое количество линий, когда количество точек на каждой линии сильно отличается.Поскольку ваши точки упорядочены, вы можете попытаться взглянуть на пространство параметров и удалить все точки, которые дали совпадение.Если вы сначала выберете лучшие совпадения, у вас, вероятно, останется хорошее решение.

0 голосов
/ 12 августа 2011
set `D` to a maximum deviance of 10 degrees or so.  
set `P` to the first point.
set `Q` to the point after `P`. 
set `A` to the angle from `P` to `Q`.
while `Q` is not that last point in the list
    if the angle from `P` to `Q` is within of `A` plus or minus `D`
        remove `Q`
    else
        set `P` to `Q`
        set `A` to the angle from `P` to `Q`.
    set `Q` to the point after `P`

Это немного сложнее, чем ответ templatetypedef, но имеет то преимущество, что оно лучше подходит для больших кривых.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...