Как вычислить "разницу" между двумя последовательностями точек?
У меня есть две последовательности длины n и m. Каждая из них представляет собой последовательность точек вида (x, y) и представляет собой кривые на изображении. Мне нужно найти, насколько разные (или похожие) эти последовательности даны тем фактом, что
- одна последовательность, вероятно, длиннее другой (т. е. одна может быть наполовину или на четверть длиннее другой, но если они прослеживают примерно одну и ту же кривую, они одинаковы)
-
эти последовательности могут быть в противоположных направлениях (т. е., последовательность 1 идет слева направо, в то время как последовательность 2 идет справа налево)
Я изучил некоторые оценки различий, такие как Левенштейн, а также расстояния редактирования в структурном сходстве, соответствующем складыванию белка, но ни один из них, похоже, не делает трюк. Я мог бы написать свой собственный метод грубой силы, но я хочу знать, есть ли лучший способ.
спасибо.
5 ответов
метод по этим линиям может работать:
для обеих последовательностей:
установите кривую через последовательность. Убедитесь, что у вас есть непрерывная функция "один к одному" от [0,1] до точек на этой кривой. То есть для каждого (вещественного) числа от 0 до 1 эта функция возвращает точку на кривой, принадлежащей ей. Проследив функцию для всех чисел от 0 до 1, Вы получаете весь кривой.
один из способов подгонки кривой - нарисовать прямую линию между каждой парой последовательных точек (это не хорошая кривая, потому что она имеет резкие изгибы, но это может быть хорошо для вашей цели). В этом случае функция может быть получена путем вычисления общей длины всех отрезков (теорема Пифагора). Точка на кривой, соответствующая числу Y (между 0 и 1), соответствует точке на кривой, которая имеет расстояние Y * (общая длина всех отрезков линии) от первой точки последовательности, измеренное путем перемещения по отрезкам линии (!!).
теперь, после того, как мы получили такую функцию F(double) для первой последовательности и G(double) для второй последовательности, мы можем вычислить сходство следующим образом:
double epsilon = 0.01;
double curveDistanceSquared = 0.0;
for(double d=0.0;d<1.0;d=d+epsilon)
{
Point pointOnCurve1 = F(d);
Point pointOnCurve2 = G(d);
//alternatively, use G(1.0-d) to check whether the second sequence is reversed
double distanceOfPoints = pointOnCurve1.EuclideanDistance(pointOnCurve2);
curveDistanceSquared = curveDistanceSquared + distanceOfPoints * distanceOfPoints;
}
similarity = 1.0/ curveDistanceSquared;
возможные улучшения:
- найдите улучшенный способ подгонки кривых. Обратите внимание, что вам все еще нужна функция, которая отслеживает кривую для приведенного выше метода.
-при расчете расстояния, рассмотреть reparametrizing функция G таким образом, что расстояние сведено к минимуму. (Это означает, что у вас есть возрастающая функция R, такая, что R (0) = 0 и R (1)=1, но в остальном это общее. При расчете расстояния вы используете
Point pointOnCurve1 = F(d);
Point pointOnCurve2 = G(R(d));
впоследствии вы пытаетесь выбрать R таким образом, чтобы расстояние было сведено к минимуму. (чтобы увидеть, что происходит, обратите внимание, что G(R(d)) также отслеживает кривую)).
вы имеете в виду,что пытаетесь сопоставить кривые, которые были переведены в координатах x, y? Один из методов обработки изображений - использование цепных кодов [Я ищу достойную ссылку, но все, что я могу найти прямо сейчас этой] для кодирования каждой последовательности, а затем сравнить эти цепочки кодов. Вы можете взять сумму разностей (по модулю 8), и если результат равен 0, кривые идентичны. Поскольку последовательности имеют разную длину и не обязательно начните с того же относительного местоположения, вам придется сдвигать одну последовательность и делать это снова и снова, но вам нужно только создать коды цепочки один раз. Единственный способ определить, обращена ли одна из последовательностей-попробовать как вперед, так и назад одной из последовательностей. Если кривые не совсем похожи, сумма будет больше нуля, но не просто сказать,насколько отличаются кривые от суммы.
этот метод не будет вращательно-инвариантной. Если вам нужен метод, который является ротационно инвариантным,вы должны посмотреть на Полярное кодирование с центром на границе. Я не могу найти бесплатную ссылку для этого, но если вам нужно, чтобы я описал это, дайте мне знать.
Почему бы не сделать какую-то процедуру подгонки кривой (наименьшие квадраты, будь то обычные или нелинейные) и посмотреть, совпадают ли коэффициенты по параметрам формы. Если вы запускаете его как модель типа panel-data, существуют явные статистические тесты, существенно ли отличаются наборы параметров друг от друга. Это решило бы проблему одной и той же кривой, но с разными разрешениями.
Шаг 1: Канонизируйте ориентацию. Например, предположим, что все изогнутые начинаются с конечной точки с наименьшим лексикографическим порядком.
def inCanonicalOrientation(path):
return path if path[0]<path[-1] else reversed(path)
Шаг 2: Вы можете быть либо примерно точными, либо очень точными. Если вы хотите быть очень точным, вычислите сплайн или установите обе кривые в полином соответствующей степени и сравните коэффициенты. Если вы хотите только приблизительную оценку, сделайте следующее:
def resample(path, numPoints)
pathLength = pathLength(path) #write this function
segments = generateSegments(path)
currentSegment = next(segments)
segmentsSoFar = [currentSegment]
for i in range(numPoints):
samplePosition = i/(numPoints-1)*pathLength
while samplePosition > pathLength(segmentsSoFar)+currentSegment.length:
currentSegment = next(segments)
segmentsSoFar.insert(currentSegment)
difference = samplePosition - pathLength(segmentsSoFar)
howFar = difference/currentSegment.length
yield Point((1-howFar)*currentSegment.start + (howFar)*currentSegment.end)
Это может быть изменено с линейной повторной выборки на нечто лучшее.
def error(pathA, pathB):
pathA = inCanonicalOrientation(pathA)
pathB = inCanonicalOrientation(pathB)
higherResolution = max([len(pathA), len(pathB)])
resampledA = resample(pathA, higherResolution)
resampledB = resample(pathA, higherResolution)
error = sum(
abs(pointInA-pointInB)
for pointInA,pointInB in zip(pathA,pathB)
)
averageError = error / len(pathAorB)
normalizedError = error / Z(AorB)
return normalizedError
где Z-это что-то вроде "диаметра" вашего пути, возможно, максимальное Евклидово расстояние между любыми двумя точками пути.
Я бы использовал процедуру подгонки кривой, но также бросил бы постоянный член, т. е. 0 =B0 + B1*X + B2 * Y + B3*X * Y + B4 * x^2 etc. Это поймает поступательную дисперсию, а затем вы можете сделать статистическое сравнение оцененных коэффициентов кривых, образованных двумя наборами точек, как способ их классификации. Я предполагаю, что вам придется делать двухвариантную интерполяцию, если данные образуют произвольные кривые в плоскости x-y.