1632
правки
Изменения
м
Пусть у нас есть Дана некоторая аппроксимированная криваяполилиния, заданная последовательностью точек <tex> a_1, a_2, ..., a_n</tex>, и некоторое <tex>\varepsilon</tex>. Требуется найти цепь <tex> a_1, a_i, a_j, ..., a_n </tex>, являющейся подпоследовательностью исходной. Для этой цепи верно, что для всех <tex>k</tex>, таких что <tex> i < k < j:</tex> <tex>\rho(a_k, Segment(a_i, a_j)) \le \varepsilon</tex> для любых соседних <tex>i</tex> и <tex>j</tex>. Существует также альтернативная задача, все точки в которой нам вместо <tex>\varepsilon</tex> задано число <tex>k</tex> вершин в итоговой цепи. В этом случае требуется составить цепь <tex> a_1, a_i, a_j, ..., a_n </tex> заданной длины таким образом, что максимально необходимое <tex>\varepsilon</tex> для условия <tex> \rho(a_k, Segment(a_i, a_j)) \le \varepsilon</tex> было минимально. Также упрощение можно выполнять с помощью введения новой полилинии, не нужнысохраняющей точки исходной, а большинство из них лишь занимает местоно такая вариация задачи рассмотрена не будет. Такое ==Мотивация==Такая задача встречается при обработки векторной графики и построении карт. Расмотрим пример для построения карт. Пусть у нас Например, есть путь из Москвы цепь, несколько точек которой попадают в Санкт-Петербург, заданный точками через каждый километр путиодин и тот же пиксель. В маштабах всей России такая точность явно ни к чемуОчевидно, стоит оставить лишь что тогда можно упростить все эти точки, отражающие ключевые участки путив одну. В этом случае нам и пригодится упрощение, одним из вариантов реализации которого является алгоритм Дугласа-Пекера(Douglas-Peucker). В случае, когда у нас есть несколько устройств с разным dpi, например, монитор и принтер, то, взяв за <tex>\varepsilon</tex> половину расстояния, которое помещается на одной из границ пикселя, мы можем растеризовать одну и ту же цепь для разных устройств.
Суть алгоритма Дугласа-Пекера(Douglas-Peucker) состоит в том, чтобы по данной ломаной, аппроксимирующей кривую, построить ломаную с меньшим числом точек. Алгоритм определяет расхождениеАлгоритму задается исходная полилиния и максимальное расстояние, которое вычисляется по максимальному расстоянию может быть между исходной и упрощённой кривымиполилиниями (то есть, максимальное расстояние от точек исходной к ближайшему участку полученной полилинии). Упрощенная кривая полилиния состоит из подмножества точек, которые определяются из исходной кривой.
ЛинияПолилиния, полученная в результате работы алгоритма, отражается черной пунктирной линией.
==Поиск расстояния от точки до отрезка==
===Идея===
При определении расстояния от точки до отрезка нужно сначала проанализировать взаимное расположение точки и отрезка прямой, то есть, проверить, куда опустится перпендикуляр из точки: непосредственно на отрезок или на прямую, являющуюся продолжением рассматриваемого отрезка. Если на отрезок, то ответ это расстояние от исходной точки до точки пересечения отрезка с перпендикуляром, если нет, то расстояние от исходной точки до одного из концов отрезка. Первое, что приходит в голову, это найти точку пересечения перпендикуляра и прямой, и в зависимости от ее положения вычислить ответ. На самом деле этот анализ может быть произведен путем построения треугольника, вершинами которого являются концы отрезка и точка, и сопоставления соотношения длин его сторон.
===Реализация===
[[Файл:DistancePointToSegment.gif|300px|right]]
Пусть даны точка <tex>(x_0; y_0)</tex> и отрезок, заданный точками <tex>(x_1; y_1)</tex> и <tex>(x_2; y_2)</tex>.
Тогда обозначим за <tex>R_1</tex> расстояние от <tex>(x_0; y_0)</tex> до <tex>(x_1; y_1)</tex>,
за <tex>R_2</tex> расстояние от <tex>(x_0; y_0)</tex> до <tex>(x_2; y_2)</tex> и
за <tex>R_{12}</tex> расстояние от <tex>(x_1; y_1)</tex> до <tex>(x_2; y_2)</tex>.
Если <tex>R_1 \ge \sqrt{R_2^2+R_{12}^2}</tex>, то ответ это <tex>R_2</tex>.
Если <tex>R_2 \ge \sqrt{R_1^2+R_{12}^2}</tex>, то ответ это <tex>R_1</tex>.
Если оба условия ложны, то ответ это расстояние от точки до продолжения отрезка до прямой, который выражается как модуль <tex>|R_{12} * R_1|/|R_{12}|</tex>, где <tex>R_{12}</tex> и <tex>R_1</tex> нужно рассматривать как вектора, а умножение как векторное произведение.
rollbackEdits.php mass rollback
Упрощение полигональной цепи {{- --}} процесс, позволяющий уменьшить число точек кривой, аппроксимированной серией точекполилинии.
==Задача==
==Алгоритм Дугласа-Пекера==
===Описание===
Начальная кривая полилиния представляет собой упорядоченный набор точек.
Алгоритм рекурсивно делит линиюполилинию. Входом алгоритма служат координаты всех точек между первой и последнейвключая их, а так же <tex>\varepsilon</tex>. Первая и последняя точка сохраняются неизменными. После чего алгоритм находит точку, наиболее удалённую от отрезка, состоящего из первой и последней(оптимальный способ поиска расстояния от точки до отрезка рассмотрен ниже). Если точка находится на расстоянии, меньше , чем епсилон<tex>\varepsilon</tex>, то все точки, которые ещё не были отмечены к сохранению, могут быть выброшены из набора , и получившаяся прямая сглаживает кривую с точностью не ниже епсилон<tex>\varepsilon</tex>.
Если же расстояние больше епсилон<tex>\varepsilon</tex>, то алгоритм рекурсивно вызывает себя с на наборе от начальной до данной и от данной до конечной точках точек (что означает, что данная точка будет отмечена к сохранению).
По окончанию всех рекурсивных вызовов выходная ломаная итоговая полилиния строится только из тех точек, что были отмечены к сохранению.
===Псевдокод===
<texcode>Douglas–PeuckerDouglasPeucker(int first, int last, double eps)</texcode>:<code>Для всех точек междуmax = -infinity</code> :<texcode>firstindex = -1</tex> <code>и:</code> for (int i = first + 1; i <tex>last; i++)</texcode>::<code>Если точка удалена дальше чем предыдущие запомнить ее вdistance = dist(points[i], segment(points[first],points[last]))</code> ::<texcode>if (distance >max&& distance > eps)</texcode>:::<code>Если расстояние от</code> <tex>max= distance, index = i</tex> <code>до отрезка:</code> <tex>firstif(index == -last1)</tex> <code>меньше</code> <tex>eps</tex>::<code>Вернутьсяreturn</code>:<code>Иначеelse</code>::<code>Добавить точку</code> <tex>max</tex> <code>в ответanswer[index] = true</code>::<texcode>DouglasPeucker(first, maxindex, eps)</texcode>::<texcode>DouglasPeuckedDouglasPeucker(maxindex, last, eps)</texcode>
===Пример===
[[Файл:Example_DP.png|300px|right]]
Рассмотрим пример для точек, заданных на рисунке, где сплошная линия отражает исходную линию, и епсилон равному <tex>\varepsilon = \sqrt 2</tex>.
{| class="wikitable"
! Шаг || Действие
| <tex>9</tex> || Алгоритм завершен
|}
===Время работы===
Ожидаемая сложность алгоритма может быть оценена выражением <tex>T(n) = 2T\left(\frac{n}{2}\right) + O(n)</tex>, которая упрощается в <tex>T(n) \in \Theta(n\log n)</tex>в лучшем случае, когда номер наиболее удаленной точки всегда оказывается лексикографически центральным. Однако , в худшем случае сложность алгоритма составляет <tex>O\left(n^2\right)</tex>.Это достигается, например, в случае, если номер наиболее удаленной точки всегда соседний к номеру граничной точки.
===Замечания к алгоритму===
====Топология====
К сожалению, алгоритм Дугласа-Пекера в ходе своей работы топологию не сохраняеттопологию. Это означает, что означает в ответе мы можем получить линию с самопересечениями. В статье де Берга (de Berg) ''[http://www.bowdoin.edu/~ltoma/teaching/cs350/spring06/Lecture-Handouts/deberg95new.pdf "A New Approach to Subdivision Simplification"]''(1995) приведен алгоритм, позволяющий решать чуть более общую задачу, чем текущая: упрощение полигональной цепи с учетом обязательных особых точек, не входящих в нее и с учетом топологии. Можно использовать алгоритм и для текущего случая, задав множество особых точек пустым. Время работы алгоритма при этом составит <tex>O(n^2\log n)</tex>. Краткое описание алгоритма дано ниже.
====Оптимальность====
[[Файл:DP(1).png|100px|thumb|right|Оптимальный по количеству точек ответ]]
[[Файл:DP(2).png|100px|thumb|left|Ответ алгоритма Дугласа-Пекера]]
Алгоритм может находить не минимальный по количеству точек ответ. Рассмотрим пример, где в котором исходная линия с некоторым приближением будет представлять полуокружность. Мы можем подобрать такое епсилон<tex>\varepsilon</tex>, что алгоритм добавит три точки помимо стартовой и конечной(точки через каждую четверть исходной линии), в то же время мы можем взять две точки через
каждую треть исходной линии, для которых упрощение также верно.
<br clear="all">
===Решение альтернативной задачи===
Решение альтернативной задачи очень схоже с исходном алгоритмом. На каждой итерации алгоритм находит подучасток исходной цепи, на котором расстояние до наиболее удаленной точки максимально, делит ее так же как и в исходном алгоритме и запоминает полученные в результате деления подучастки для следующих итераций. Алгоритм стартует, когда для выбора доступна только исходная цепь.
====Реализация====
Практически очевидно, что данный вариант алгоритма легко реализовать на приоритетной очереди. Будем хранить расстояние, на котором находится наиболее удаленная вершина, как ключ, а номера вершин-границ как значение. На каждой итерации мы выбираем обьект с наибольшим ключем, делим и получившиеся части кладем обратно в очередь с новыми ключами.
==Поиск расстояния от точки до отрезка==
===Идея===
При определении расстояния от точки до отрезка нужно сначала проанализировать взаимное расположение точки и отрезка прямой, то есть, проверить, куда опустится перпендикуляр из точки: непосредственно на отрезок или на прямую, являющуюся продолжением рассматриваемого отрезка.
Если перпендикуляр падает на отрезок, то ответом будет расстояние от исходной точки до точки пересечения отрезка с перпендикуляром, иначе {{---}} расстояние от исходной точки до одного из концов отрезка.
Самое очевидное {{---}} найти точку пересечения перпендикуляра и прямой и, в зависимости от ее положения, вычислить ответ. Или же, этот анализ может быть произведен путем построения треугольника, вершинами которого являются концы отрезка и точка, и сопоставления соотношения длин его сторон.
===Реализация===
[[Файл:DistancePtoS1.png|400px|right]]
Даны точка <tex>O</tex> и отрезок, заданный точками <tex>A</tex> и <tex>B</tex>.
Введём обозначения:
*<tex>R_1</tex> {{---}} отрезок <tex>[O; A]</tex>
*<tex>R_2</tex> {{---}} отрезок <tex>[O; B]</tex>
*<tex>R_{12}</tex> {{---}} отрезок <tex>[A; B]</tex>
Если:
*<tex>|R_1| \ge \sqrt{|R_2|^2+|R_{12}|^2}</tex>, то ответ это <tex>|R_2|</tex>, так как угол между <tex>R_2</tex> и <tex>R_{12}</tex> при данном условии <tex> \ge 90^{\circ}</tex>
*<tex>|R_2| \ge \sqrt{|R_1|^2+|R_{12}|^2}</tex>, то ответ это <tex>|R_1|</tex>, так как угол между <tex>R_1</tex> и <tex>R_{12}</tex> при данном условии <tex> \ge 90^{\circ}</tex>
*Оба предыдущих условия ложны, то <tex>\operatorname{abs} (\frac{| \overrightarrow{R_{12}} \times \overrightarrow{R_1}|}{|\overrightarrow{R_{12}}|})</tex>, где <tex> \overrightarrow{R_{12}} = B - A</tex> и <tex> \overrightarrow{R_1} = A - O</tex>. Это следует из формул для площади параллелограмма через векторное произведение и через произведения основания на высоту
<br clear="all"/>
==Обзор ускорения работы алгоритма Дугласа-Пекера==
Как описано выше, в худшем случае алгоритм работает за <tex>O(n^2)</tex>. Можно внести дополнения, которые позволят получить <tex>O(n\log n)</tex> в худшем случае. Ускорение основывается на уменьшении времени поиска наиболее удаленной вершины. Это можно осуществить благодаря идее о том, что наиболее удаленная вершина лежит на выпуклой оболочке полигональной цепи. Описание ускорения взято из статьи Хершберга(Hershberger) ''[http://www.bowdoin.edu/~ltoma/teaching/cs350/spring06/Lecture-Handouts/hershberger92speeding.pdf "Speeding Up the Douglas-Peucker Line-Simplification Algorithm"]'' (1992).
Для построения выпуклой оболочки используется [http://www.ams.sunysb.edu/~jsbm/courses/545/melkman.pdf алгоритм Мелкмана], который работает для двумерной полигональной цепи без самопересечений за <tex>O(n)</tex>, строя все промежуточные выпуклые оболочки, которые пригодятся в дальнейшем.
После построения выпуклой оболочки используется бинарный поиск для нахождения наиболее удаленной вершины за <tex>O(\log n)</tex>. Затем, если потребуется, действия повторятся рекурсивно, как и в оригинальном алгоритме, но заново строить оболочки не имеет смысла, так как промежуточные уже были построены. Использовав их, можно разбить текущую оболочку на две за <tex>O(\log n)</tex>.
В итоге получается, что в худшем случае <tex>O(n)</tex> разбиений за <tex>O(\log n)</tex> и поиск за <tex>O(\log n)</tex>, что дает <tex>O(n\log n)</tex>.
===Замечания===
К сожалению, у данного метода есть несколько недостатков по сравнению с оригинальным алгоритмом, некоторые из которых уже были упомянуты:
*исходная цепь должна быть без самопересечений для использования алгоритма Мелкмана
*ускорение подходит лишь для двумерного случая из-за способа построения выпуклой оболочки
*в некоторых случаях оригинальный алгоритм Дугласа-Пекера работает быстрее (например в случае, когда цепь приближено является окружностью)
==Алгоритм Реуманна-Виткама==
[[Файл:Pw2.png|200px|right]]
[http://psimpl.sourceforge.net/reumann-witkam.html Алгоритм Реуманна-Виткама] (Reumann-Witkam) определяет прямую через первые две точки цепи, последняя из последовательных точек начиная со второй, удаленных небольше чем на <tex>\varepsilon</tex>, соединяются прямой, а все промежуточные точки исключаются.
Алгоритм продолжится последовательно для оставшихся точек до тех пор, пока не будет достигнута последняя.
На рисунке прямая, проходящая через точки отображена красной линией, границы, в которые попадают вершины, допустимые для упрощения, отображены красными пунктирными линиями, точки попавшие в итоговую цепь отображены черным.
<br clear="all"/>
==Алгоритм Опхейма==
[[Файл:Op.png|200px|left]]
[http://psimpl.sourceforge.net/opheim.html Алгоритм Опхейма] (Opheim) несколько схож с алгоритмом Реуманна-Виткама. В этом алгоритме мы рассматриваем все вершины в <tex>\varepsilon</tex> радиусе от первой, и строим луч из текущей и последней, попавшей в радиус. Если таких точек нет, то берется следующая за исходной.
Последующие вершины упрощаются до тех пор, пока их расстояние до луча превосходит <tex>\varepsilon</tex> и радиальное расстояние до первой точки превосходит <tex>\varepsilon</tex>. Затем алгоритм продолжается для оставшихся точек до тех пор, пока не будет достигнута последняя.
На рисунке радиус, луч и максимальное радиальное расстояние отображены красными линиями и дугами. Точки попавшие в итоговую цепь отображены черным.
<br clear="all"/>
==Алгоритм Ланга==
[[Файл:La.png|200px|right]]
[http://psimpl.sourceforge.net/lang.html Алгоритм Ланга] (Lang) определяет фиксированный размер поисковой области для упрощения. Две точки, что образуют поисковую область, составляют отрезок. Этот отрезок используется для расчета перпендикулярного расстояния до каждой промежуточной точки. Если рассчитанное расстояние больше заданного <tex>\varepsilon</tex>, область поиска будет уменьшен путем исключения ее последней точки.
Этот процесс будет продолжаться, пока все рассчитанные расстояния не будут ниже заданного <tex>\varepsilon</tex>, или когда нет больше промежуточных точек. Все промежуточные точки удаляются, а новая область поиска определяется, начиная с последней точки в старой области.
На рисунке граница поисковой области помечена красной линией, допустимая область для упрощения - красной пунктирной линией, точки попавшие в итоговую цепь отображены черным.
<br clear="all"/>
==Алгоритм, сохраняющий топологию==
Алгоритм сохраняющий топологию, как было упомянуто ранее, содержится в статье де Берга A New Approach to Subdivision Simplification, где расскрываются также детали реализации, здесь же описывается идея алгоритма. Также стоит обратить внимание, что исходная полигональная цепь должна быть без самопересечений.
Алгоритм делится на четыре основных этапа:
*Создание графа <tex>G_1</tex>, в котором помимо исходных ребер, добавлены все возможные сокращенные ребра. Иначе говоря ребра, для который верно, что <tex>i < j</tex> и для любого <tex>t</tex>, такого что <tex>i < t < j</tex>, верно <tex>\rho(V_t, \overline{V_iV_j}) \le \varepsilon</tex>.
*Создание графа <tex>G_2</tex>, в котором добавлены все возможные ребра, не нарушающие топологию. На этом этапе построение выполняется без учета <tex>\varepsilon</tex>.
*Создание графа <tex> G = G_1 \cap G_2</tex>, в котором будет содержаться кратчайшая полигональная цепь без самопересечений по построению <tex>G_1</tex> и <tex>G_2</tex>.
*Поиск кратчайшего пути в <tex>G</tex>, как в невзвешенном графе, этот путь и будет являться ответом на задачу.
==Ссылки==
*[http://ru.wikipedia.org/wiki/Алгоритм_Рамера_—_Дугласа_—_Пекера Алгоритм Дугласа-Пекера] *[http://pers.narod.ru/algorithms/pas_dist_from_point_to_line.html Поиск расстояния от точки до отрезка]*[http://algolist.manual.ru/maths/geom/distance/pointline.php Поиск расстояния от точки до прямой]*[http://www.bowdoin.edu/~ltoma/teaching/cs350/spring06/Lecture-Handouts/hershberger92speeding.pdf "Speeding Up the Douglas-Peucker Line-Simplification Algorithm"]*[http://www.ams.sunysb.edu/~jsbm/courses/545/melkman.pdf Алгоритм Мелкмана]*[http://softsurfer.com/Archive/algorithm_0203/algorithm_0203.htm Алгоритм Мелкмана]*[http://softsurfer.com/Archive/algorithm_0112/algorithm_0112.htm Двоичный поиск на выпуклой оболочке]*[http://www.bowdoin.edu/~ltoma/teaching/cs350/spring06/Lecture-Handouts/deberg95new.pdf "A New Approach to Subdivision Simplification"]*[http://www.codeproject.com/Articles/114797/Polyline-Simplification#headingRD Алгоритмы и визуализатор упрощения полигональной цепи]
[http[Категория://algolist.manual.ru/maths/geom/distance/pointline.php Поиск расстояния от точки до прямойВычислительная геометрия]]