Як виміряти схожість об’єктів SpatialLines


9

Я створив два SpatialLinesоб'єкти в R : фігура.

Ці об'єкти були створені таким чином:

library(sp)
xy <- cbind(x,y)
xy.sp = sp::SpatialPoints(xy)
spl1 <- sp::SpatialLines(list(Lines(Line(xy.sp), ID="a")))

Тепер я хочу якось зробити висновок, що це одна і та ж лінія, повернута і перевернута, і що різниця між ними дорівнює 0 (тобто форма дорівнює).

Для цього можна використовувати maptoolsпакет і обертати рядок №1, наприклад:

spl180 <- maptools::elide(spl1, rotate=180)

Кожен повернутий рядок повинен бути перевірений проти рядка №2 за допомогою rgeosпакету, наприклад:

hdist <- rgeos::gDistance(spl180, spl2, byid=FALSE, hausdorff=TRUE)

Однак це настільки обчислювально дорогий спосіб зіставити SpatialLinesоб'єкти, особливо якщо кількість об'єктів становить близько 1000.

Чи є якийсь розумний спосіб виконати цю роботу?

PS Крім того, вищеописаний підхід не гарантує всіх можливих обертань та переворотів.

P.S2. Якщо рядок №1 зменшено у відношенні лінії №2, різниця між рядком №1 та №2 все одно повинна дорівнювати 0.

ОНОВЛЕННЯ:

введіть тут опис зображення

Відповіді:


9

Будь-який справді ефективний метод загального призначення стандартизує уявлення фігур, щоб вони не змінювалися при обертанні, перекладі, відображенні або тривіальних змінах внутрішнього представлення.

Один із способів зробити це - перерахувати кожну з'єднану форму у вигляді змінної послідовності довжин ребер та (підписаних) кутів, починаючи з одного кінця. (Форма повинна бути "чистою" в тому сенсі, що не має ребер нульової довжини або прямих кутів.) Щоб зробити цю інваріантну під відображенням, заперечте всі кути, якщо перший ненульовий - негативний.

(Оскільки будь-яка з'єднана полілінія з n вершин матиме n -1 ребра, розділених n -2 кутами, мені в Rкоді нижче було зручно використовувати структуру даних, що складається з двох масивів, одного для довжини ребер, $lengthsа іншого для кути $angles,. Відрізок лінії взагалі не матиме кутів, тому в такій структурі даних важливо обробляти масиви нульової довжини.)

Такі уявлення можна упорядкувати лексикографічно. Необхідно враховувати помилки з плаваючою комою, накопичені в процесі стандартизації. Елегантна процедура оцінить ці помилки як функцію від початкових координат. У нижченаведеному рішенні застосовується більш простий метод, при якому дві довжини вважаються рівними, коли вони відрізняються дуже невеликою кількістю відносно. Кути можуть відрізнятися лише дуже невеликою кількістю за абсолютною ознакою.

Щоб зробити їх інваріантними при оберненні основної орієнтації, виберіть лексикографічно найдавніше зображення між полілінією та її переворотом.

Для обробки багаторядних поліліній розташуйте їх компоненти в лексикографічному порядку.

Щоб знайти класи еквівалентності під евклідовими перетвореннями ,

  • Створіть стандартизовані зображення фігур.

  • Виконайте лексикографічний вид стандартизованих уявлень.

  • Зробіть перехід через відсортований порядок, щоб визначити послідовності рівних уявлень.

Час обчислення пропорційний O (n * log (n) * N), де n - кількість ознак, а N - найбільша кількість вершин у будь-якій функції. Це ефективно.

Напевно, варто згадати, що попереднє групування на основі легко обчислених інваріантних геометричних властивостей, таких як довжина полілінії, центр та моменти щодо цього центру, часто може бути застосоване для упорядкування всього процесу. Потрібно лише знайти підгрупи конгруентних ознак у межах кожної такої попередньої групи. Повний метод, наведений тут, був би потрібний для форм, які в іншому випадку були б настільки надзвичайно схожі, що такі прості інваріанти все-таки не розрізнили б їх. Наприклад, такі прості функції, побудовані з растрових даних, можуть мати такі характеристики. Однак, оскільки рішення, яке дається тут, настільки ефективне, що, якщо хтось збирається докладати зусиль щодо його реалізації, він може спрацювати все добре сам по собі.


Приклад

На малюнку ліворуч зображено п’ять поліліній плюс ще 15, отриманих від них шляхом випадкового перекладу, обертання, відображення та зміни внутрішньої орієнтації (що не видно). Фігура правої руки забарвлює їх відповідно до класу еквівалентності Евкліда: всі фігури загального кольору є конгруентними; різні кольори не є конгруентними.

Малюнок

Rкод наступним чином. Коли введення було оновлено до 500 фігур, 500 додаткових (конгруентних) форм із середнім значенням 100 вершин на фігуру, час виконання на цій машині становив 3 секунди.

Цей код є неповним: оскільки Rвін не має нативного лексикографічного сортування, і мені не здавалося, що кодують його з нуля, я просто виконую сортування за першою координатою кожної стандартизованої форми. Це буде добре для випадкових форм, створених тут, але для виробничих робіт слід реалізувати повний лексикографічний сорт. Ця функція order.shapeбула б єдиною, на яку вплинула ця зміна. Його вхід - це список стандартизованої форми, sа його вихід - це послідовність покажчиків, sякі б сортували його.

#
# Create random shapes.
#
n.shapes <- 5      # Unique shapes, up to congruence
n.shapes.new <- 15 # Additional congruent shapes to generate
p.mean <- 5        # Expected number of vertices per shape
set.seed(17)       # Create a reproducible starting point
shape.random <- function(n) matrix(rnorm(2*n), nrow=2, ncol=n)
shapes <- lapply(2+rpois(n.shapes, p.mean-2), shape.random)
#
# Randomly move them around.
#
move.random <- function(xy) {
  a <- runif(1, 0, 2*pi)
  reflection <- sign(runif(1, -1, 1))
  translation <- runif(2, -8, 8)
  m <- matrix(c(cos(a), sin(a), -sin(a), cos(a)), 2, 2) %*%
    matrix(c(reflection, 0, 0, 1), 2, 2)
  m <- m %*% xy + translation
  if (runif(1, -1, 0) < 0) m <- m[ ,dim(m)[2]:1]
  return (m)
}
i <- sample(length(shapes), n.shapes.new, replace=TRUE)
shapes <- c(shapes, lapply(i, function(j) move.random(shapes[[j]])))
#
# Plot the shapes.
#
range.shapes <- c(min(sapply(shapes, min)), max(sapply(shapes, max)))
palette(gray.colors(length(shapes)))
par(mfrow=c(1,2))
plot(range.shapes, range.shapes, type="n",asp=1, bty="n", xlab="", ylab="")
invisible(lapply(1:length(shapes), function(i) lines(t(shapes[[i]]), col=i, lwd=2)))
#
# Standardize the shape description.
#
standardize <- function(xy) {
  n <- dim(xy)[2]
  vectors <- xy[ ,-1, drop=FALSE] - xy[ ,-n, drop=FALSE]
  lengths <- sqrt(colSums(vectors^2))
  if (which.min(lengths - rev(lengths))*2 < n) {
    lengths <- rev(lengths)
    vectors <- vectors[, (n-1):1]
  }
  if (n > 2) {
    vectors <- vectors / rbind(lengths, lengths)
    perps <- rbind(-vectors[2, ], vectors[1, ])
    angles <- sapply(1:(n-2), function(i) {
      cosine <- sum(vectors[, i+1] * vectors[, i])
      sine <- sum(perps[, i+1] * vectors[, i])
      atan2(sine, cosine)
    })
    i <- min(which(angles != 0))
    angles <- sign(angles[i]) * angles
  } else angles <- numeric(0)
  list(lengths=lengths, angles=angles)
}
shapes.std <- lapply(shapes, standardize)
#
# Sort lexicographically.  (Not implemented: see the text.)
#
order.shape <- function(s) {
  order(sapply(s, function(s) s$lengths[1]))
}
i <- order.shape(shapes.std)
#
# Group.
#
equal.shape <- function(s.0, s.1) {
  same.length <- function(a,b) abs(a-b) <= (a+b) * 1e-8
  same.angle <- function(a,b) min(abs(a-b), abs(a-b)-2*pi) < 1e-11
  r <- function(u) {
    a <- u$angles
    if (length(a) > 0) {
      a <- rev(u$angles)
      i <- min(which(a != 0))
      a <- sign(a[i]) * a
    }
    list(lengths=rev(u$lengths), angles=a)
  }
  e <- function(u, v) {
    if (length(u$lengths) != length(v$lengths)) return (FALSE)
    all(mapply(same.length, u$lengths, v$lengths)) &&
      all(mapply(same.angle, u$angles, v$angles))
    }
  e(s.0, s.1) || e(r(s.0), s.1)
}
g <- rep(1, length(shapes.std))
for (j in 2:length(i)) {
  i.0 <- i[j-1]
  i.1 <- i[j]
  if (equal.shape(shapes.std[[i.0]], shapes.std[[i.1]])) 
    g[j] <- g[j-1] else g[j] <- g[j-1]+1
}
palette(rainbow(max(g)))
plot(range.shapes, range.shapes, type="n",asp=1, bty="n", xlab="", ylab="")
invisible(lapply(1:length(i), function(j) lines(t(shapes[[i[j]]]), col=g[j], lwd=2)))

Коли в групу перетворень включаються довільні дилатації (або "ізототії"), класи еквівалентності - це класи конфігурації афінної геометрії . З цим ускладненням легко впоратися: стандартизуйте всі полілінії, наприклад, загальну довжину одиниці.
whuber

Дуже дякую. Лише одне запитання: чи фігури повинні бути представлені у вигляді просторових ліній чи просторових полігонів?
Klausos Klausos

Полігони створюють ще одне ускладнення: їх межі не мають визначених кінцевих точок. Існує багато способів вирішити це, наприклад стандартизувати подання, яке починається з (скажімо) вершини, яка спочатку сортує в лексикографічному порядку xy і проходить у напрямку проти годинникової стрілки навколо полігону. (Топологічно "чистий" зв'язаний багатокутник матиме лише одну таку вершину.) Чи вважається форма багатокутником або полілінією, залежить від того, яку особливість вона представляє: немає жодного внутрішнього способу сказати про будь-який закритий список точок, чи це призначений бути полілінією або багатокутником.
whuber

Вибачте за просте запитання, але я мушу попросити його зрозуміти ваш приклад. Ваш об'єкт shape.std має як $ length, так і $ кути. Якщо я запускаю цей код на своїх даних xy (наприклад, [1,] 3093,5 -2987,8 [2,] 3072,7 -2991,0 тощо), він не оцінює кути, а також не малює фігури. Якщо я запускаю сюжет (форми [[1]]), то я можу чітко бачити свою полілінію. Отже, як я можу зберегти полілінії в R, щоб мати можливість перевірити ваш код на моїх даних?
Klausos Klausos

Я почав із тієї самої структури даних, що і ви: масив координат (x, y). Мій масив поміщає ці координати в стовпці (як би ви використовували rbind(x,y)замість них cbind(x,y)). Це все, що вам потрібно: spбібліотека не використовується. Якщо ви хочете слідувати тому , що робиться в деталях, я пропоную вам почати з, скажімо, n.shapes <- 2, n.shapes.new <- 3, і p.mean <- 1. Потім shapesі shapes.stdт. Д. Всі досить малі, щоб їх легко оглянути. Елегантний - і "правильний" спосіб вирішити все це було б створити клас стандартизованих представлень функцій.
whuber

1

Ви багато чого просите з довільним обертанням і розширенням! Не знаєте, наскільки корисна буде відстань Хаусдорфа, але перевірте це. Мій підхід полягає у зменшенні кількості випадків перевірки за допомогою дешевих даних. Наприклад, ви можете пропустити дорогі порівняння, якщо довжина двох рядків рядків не є цілим коефіцієнтом ( якщо вважати масштабування цілого / градуйованого ). Ви також можете перевірити, чи добре розміщена площа обмежувальної коробки або їх опуклі корпуси. Я впевнений, що є багато дешевих перевірок, які ви могли б зробити проти центру, як, наприклад, відстані або кути від початку / кінця.

Тільки тоді, якщо ви виявите масштабування, скасуйте його та зробіть дійсно дорогі перевірки.

Пояснення: я не знаю пакетів, які ви використовуєте. Під цілим співвідношенням я мав на увазі, що ви повинні розділити обидві відстані, перевірте, чи є результат цілим числом, якщо ні, інвертуйте це значення (може бути, ви вибрали неправильний порядок) і перевірте повторно. Якщо ви отримаєте ціле число або досить близько, ви можете зробити висновок про те, що можливе масштабування. Або це могли бути просто дві різні форми.

Що стосується обмежувального поля, то, ймовірно, ви отримали протилежні точки прямокутника, який його репрезентує, тому отримання площі з них проста арифметика. Принцип порівняння співвідношення той самий, тільки що результат був би квадратним. Не турбуйтеся з опуклими корпусами, якщо ви не зможете їх добре витягти з цього пакету R, це була лише ідея (швидше за все, недостатньо дешева).


Дуже дякую. Чи можете ви пояснити, як визначити, якщо довжина двох рядків не є цілим числом? Також я дуже ціную, якщо ви можете навести приклад перевірки "якщо зона обмежувальної коробки або опуклі ділянки корпусу в хорошому співвідношенні"
Клаусос Клаусос

Наприклад, якщо я витягую просторове обмежувальне поле з просторових даних, то я отримую лише два бали: spl <- sp :: SpatialLines (список (Рядки (Рядок (xy.sp), ID = i))) спл)
Klausos Klausos

Розширений основний пост.
lynxlynxlynx

"Якщо ви отримаєте ціле число або достатньо близько, ви можете зробити висновок про те, що можливе масштабування." Чи не міг користувач застосувати шкалу 1,4 чи більше?
Герман Каррільо

Звичайно, але моє припущення було зрозуміло, особливо з пізнішими редакціями. Я уявляв масштабування стилю веб-карти, де один з них досить обмежений.
lynxlynxlynx

1

Хорошим методом порівняння цих поліліній було б покластися на подання у вигляді послідовності (відстані, кути повороту) на кожній вершині: Для лінії, що складається з точок P1, P2, ..., PN, така послідовність буде такою:

(відстань (P1P2), кут (P1, P2, P3), відстань (P2P3), ..., кут (P (N-2), P (N-1), PN), відстань (P (N-1) ) ПН)).

Відповідно до ваших вимог, два рядки дорівнюють тоді і тільки тоді, коли їх відповідні послідовності однакові (за модулем порядок і напрямок кута). Порівняння послідовностей чисел тривіально.

Обчислюючи кожну полілінійну послідовність лише один раз і, як це запропонував lynxlynxlynx, перевіряючи подібність послідовностей лише для поліліну, що має однакові тривіальні характеристики (довжина, число вершин ...), обчислення повинні бути дійсно швидкими!


Це правильна ідея. Для того, щоб воно справді працювало, потрібно вирішити багато деталей, таких як впоратися з відображеннями, внутрішня орієнтація, можливість декількох підключених компонентів та помилка округлення плаваючої точки. Вони обговорюються в запропонованому нами рішення.
whuber

Так, я описав лише основну думку. Ваша відповідь напрочуд повніша (як часто :-)
липень
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.