Как вычислить "разницу" между двумя последовательностями точек?

У меня есть две последовательности длины n и m. Каждая из них представляет собой последовательность точек вида (x, y) и представляет собой кривые на изображении. Мне нужно найти, насколько разные (или похожие) эти последовательности даны тем фактом, что

  1. одна последовательность, вероятно, длиннее другой (т. е. одна может быть наполовину или на четверть длиннее другой, но если они прослеживают примерно одну и ту же кривую, они одинаковы)
  2. эти последовательности могут быть в противоположных направлениях (т. е., последовательность 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.