Как стать автором
Обновить

Комментарии 16

Тут важно как-то детектировать хотел ли пользователь изобразить примитивную фигуру или нет. А даже если фигура и сложная, типа звезды, например, то её всё равно было бы не плохо "распознавать".

Я думаю тут стоит идти в направлении сглаживания: детектировать точки повторота, а точки между ними апроксимировать кривыми безье. Тогда, например, можно будет нарисовать "пулю" и она будет представлена как равнобедренный треугольник с округлыми бёдрами.

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

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

Конечно интересно. Я думаю к своему вайтборду что-то такое прикрутить, но всё руки не доходят. Пока что есть лишь сглаживание кривых.

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

Ну и поскольку алгоритм ориентирован на выпуклые многоугольники — вполне логично, что он не может распознать эллипсы. Для эллипсов нужен отдельный алгоритм, и отдельный этап определения, какой из них лучше аппроксимирует данные пользователя (например, через среднеквадратичное отклонение).

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

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

Данное ядро было выбрано
А вы пробовали другие варианты? Я бы ещё попробовал более острые и с уходом в отрицательную область,
типа таких

На момент написания статьи, ядро с квадратом — третий вариант, первые два работали хуже. Сейчас я изменил его такой вариант: y=\sqrt{1.0 +tan(|x|)^2},x=0..\pi/4, рассчитанный на 3-5 угольники.

выглядит так (немного более острый пик)

К сожалению, ядро с ярко выраженным пиком плохо различает тупые углы, из-за чего трапеции часто распознаются как треугольники.

Пример неправильного распознавания трапеции
К сожалению, ядро с ярко выраженным пиком плохо различает тупые углы, из-за чего трапеции часто распознаются как треугольники
Из чего к сожалению следует, что такой алгоритм является неустойчивым и требует доработки. Потому что небольшие различия в ядре фильтра не должны приводить к настолько фатальным последствиям.

Мне кажется, что такой эффект будет наблюдаться в любом алгоритме, использующем пороговые значения. Например, если пользователь достаточно криво нарисовал ребро, то, в зависимости от фильтра, ребро может разделяться дополнительной вершиной или оставаться прямым (как в примере с трапецией). Тем не менее насчет доработки я с вами согласен — здесь еще есть над чем работать.

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

1) считает среднее арифметическое,
2) вычитает его из исходных данных,
3) для каждой точки считает модуль и аргумент (те самые расстояние и угол),
4) определяет дискретную (уже действительную) функцию как модуль от аргумента,
5) фильтрует её фильтром высоких частот,
6) по локальным максимумам выбирает узловые точки.

Возможно, такое описание и может показаться переусложнённым — но зато оно более математически прозрачно. В частности, можно фильтровать и исходные данные тоже (например, чтобы получить веса перед вычислением центра или в качестве множителя аргумента), оптимальный фильтр высоких частот выбирать исходя из его спектральных характеристик, а шаги 1, 3 и 4 могут и вообще не понадобится.

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

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

Здесь в качестве ядра свёртки используется квадрат треугольного окна, и для детектирования углов можно использовать не только расстояние, но и угол по отношению к касательной фильтрованного набора точек.

Спасибо за пояснение. Я думал, что вы имеете в виду какой-то классический алгоритм.

На основании вашего предыдущего комментария я написал что-то более-менее рабочее. Постараюсь описать результат своего творчества в следующей части статьи.

Напишите распознаватель карточек из настольной игры set.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории