Кривые Безье


Исходные данные: набор кривых Безье (шаблонов), произвольный набор координат точек образующих фигуру ("проверяемую").

Задача: определить степень похожести проверяемой фигуры заданной набором точек и шаблона заданного кривой Безье.

Для начала рассмотрим очень упрощенно, и лишь для одного куска кривой Безье. Например для кривой Безье, заданной тремя контрольными точками A, B, C:

X(a) = Xa*a^2 + Xb*2*a*b + Xc*b^2
Y(a) = Ya*a^2 + Yb*2*a*b + Yc*b^2
Z(a) = Za*a^2 + Zb*2*a*b + Zc*b^2 а = 0..1 b = 1-a
XA, YA, ZA, XB, YB, ZB, XC, YC,ZC - координаты x, y, z исходных узлов A, B, C соответственно.

Тогда, если подставлять вместо X(a), Y(a), Z(a) координаты точки из проверяемой фигуры, можно находить для каждого уравнения значение коэффициента а:

ax= (±sqrt((Xc-2*Xb+Xa)*X-Xa*Xc+Xb^2)+Xc-Xb)/(Xc-2*Xb+Xa)
ay= (±sqrt((Yc-2*Yb+Ya)*Y-Ya*Yc+Yb^2)+Yc-Yb)/(Yc-2*Yb+Ya)
az= (±sqrt((Zc-2*Zb+Za)*Z-Za*Zc+Zb^2)+Zc-Zb)/(Zc-2*Zb+Za)

Берём только те а, которые положительны, тогда "точность" попадания точки в шаблон, можно будет определять по тому, как точно совпадают эти три значения. Например можно оценивать это в относительных единицах, используя среднюю квадратическую сумму "коэффициентов похожести":
Delta=sqrt((k1^2+k2^2+k3^2)/3)
где "коэффициент похожести" определяется как:
k1=ax/ay
k2=ax/az
k3=ay/az (FIXME: yx should be ay)

Чем ближе полученное значение Δ к 1, тем точнее попадание.

Или, для ускорения обсчетов, можно рассчитывать среднее арифметическое:
delta=(k1+k2+k3)

В итоге становится понятно, как определить степень попадания одной точки в область линии. А дальше идёт рутинная работа, вроде определения степени попадания каждой из точек, определения весового коэффициента для них (в простейшем случае — 1/N для каждой, однако при неравномерных расстояниях между точками такой подход уже не годится), и определение "общей" степени попадания набора точек в заданную кривую. При использовании шаблона сложенного из нескольких кривых Безье ситуация немного усложняется тем, что неизвестно с какого момента нужно сравнивать точки со следующей кривой.

В качестве альтернативы, из набора точек можно генерировать новую кривую Безье и пытаться сравнивать её уравнения с уравнением исходной кривой. Самый первый "недостаток" этого метода в том, что точки должны быть упорядочены. Кроме того, либо узлов в этой кривой будет столько же сколько было точек, либо "отрезков" кривых Безье будет примерно столько же (каждый из отрезков лежит между двумя соседними точками), а это может быть в разы больше чем количество кривых шаблона. И равномерность расстояний между точками проверяемой фигуры тоже начинает влиять на результат. Существуют алгоритмы упрощения кривых Безье, благодаря которым можно уменьшить количество полученных кривых, а также уменьшить влияние равномерности расстояний между точками. Я оценил работу одного из таких алгоритмов на примере буквы греческого алфавита гамма:
  • Вручную мне удалось изобразить её с помощью кривых Безье используя 6 узлов (2 кривые). 
  • Используя "аппроксимацию на глаз" прямыми отрезками было получено 40-70 узлов, которые, после "сглаживания", алгоритм упрощения из Inkscape уменьшал до 10-17 (медиана около 12). 
Т.е. до того, чего можно добиться рисованием "вручную", этому алгоритму достаточно далеко. Плюс ничто не гарантирует, того что разбиение шаблона и сгенерированной кривой будет одинаковым, т.к. визуально одну и ту же кривую можно представить огромным количеством способов:

demo

Поэтому, несмотря на первоначальную привлекательность сравнивать с помощью генерирования новых кривых Безье или невозможно, или требует каких-то дополнительных преобразований, до которых я не дошел. Итого: я нашел только один способ которым, при доработке, можно сравнивать шаблон из кривых Безье с дискретным набором координат. Потенциальные особенности такого подхода:
  • Может быть реализован так, что не будет зависеть от порядка точек в проверяемой фигуре и от равномерности/неравномерности расстояний между ними. Однако в этом случае "весомость" каждой точки придется считать одинаковой, либо хотя бы порядок должен быть известен.
  • Чувствителен к преобразованиям массива точек (сдвиг, поворот, масштабирование)
  • Может игнорировать целостность проверяемой фигуры. Например: все имеющиеся точки могут точно попадать в кривую, однако при этом покрывают лишь малую её часть, или не покрывают важные характерные изгибы. Может быть решено с помощью анализа того как рассчитанные значения коэффициента a покрывают диапазон 0 .. 1
  • Может быть слишком чувствительным к некоторым аномалиям проверяемой фигуры (неравномерные расстояния между точками, "шум" и т.п.) 
  • Данный метод может быть неудобен, если надо сравнивать поочередно со многими шаблонами.
Возможно самый "простой" подход будет более эффективным: кривая Безье рендерится в растровое изображение, а затем сравнивается с набором точек используя один из "стандартных" алгоритмов сравнения изображений.


Статьи в интернете для лучшего понимания, что такое кривые Безье: статья c Gamedev, статья Википедии, интересная статья о упрощении кривых Безье с www.rsdn.ru (archived)