Bất kỳ phương pháp hiệu quả thực sự có mục đích chung nào cũng sẽ tiêu chuẩn hóa các biểu diễn của các hình dạng để chúng không thay đổi khi xoay, dịch, phản xạ hoặc thay đổi nhỏ trong biểu diễn bên trong.
Một cách để làm điều này là liệt kê mỗi hình dạng được kết nối thành một chuỗi xen kẽ các độ dài cạnh và các góc (đã ký), bắt đầu từ một đầu. .
(Bởi vì bất kỳ polyline kết nối của n đỉnh sẽ có n -1 cạnh nhau bởi n -2 góc, tôi đã tìm thấy nó thuận tiện trong R
code dưới đây để sử dụng một cấu trúc dữ liệu bao gồm hai mảng, một cho độ dài cạnh $lengths
và một cho các các góc , $angles
. Một đoạn đường sẽ không có góc nào cả, vì vậy điều quan trọng là phải xử lý các mảng có độ dài bằng không trong cấu trúc dữ liệu như vậy.)
Các đại diện như vậy có thể được đặt hàng từ vựng. Một số phụ cấp nên được thực hiện cho các lỗi dấu phẩy động được tích lũy trong quá trình tiêu chuẩn hóa. Một thủ tục tao nhã sẽ ước tính các lỗi đó là một hàm của tọa độ ban đầu. Trong giải pháp dưới đây, một phương pháp đơn giản hơn được sử dụng trong đó hai độ dài được coi là bằng nhau khi chúng khác nhau một lượng rất nhỏ trên cơ sở tương đối. Các góc có thể chỉ khác nhau một lượng rất nhỏ trên cơ sở tuyệt đối.
Để làm cho chúng bất biến dưới sự đảo ngược của định hướng cơ bản, chọn biểu diễn từ vựng sớm nhất giữa đa giác và đảo ngược của nó.
Để xử lý các polylines đa phần, sắp xếp các thành phần của chúng theo thứ tự từ điển.
Để tìm các lớp tương đương dưới các phép biến đổi Euclide, sau đó,
Tạo các đại diện tiêu chuẩn của các hình dạng.
Thực hiện một loại từ điển của các đại diện được tiêu chuẩn hóa.
Thực hiện chuyển qua thứ tự được sắp xếp để xác định chuỗi các biểu diễn bằng nhau.
Thời gian tính toán tỷ lệ thuận với O (n * log (n) * N) trong đó n là số lượng tính năng và N là số lượng đỉnh lớn nhất trong bất kỳ tính năng nào. Đây là hiệu quả.
Có lẽ đáng nói đến khi thông qua rằng một nhóm sơ bộ dựa trên các thuộc tính hình học bất biến được tính toán dễ dàng, chẳng hạn như chiều dài đa tuyến, tâm và các khoảnh khắc về trung tâm đó, thường có thể được áp dụng để hợp lý hóa toàn bộ quá trình. Người ta chỉ cần tìm các nhóm nhỏ các tính năng phù hợp trong mỗi nhóm sơ bộ như vậy. Phương pháp đầy đủ được đưa ra ở đây sẽ là cần thiết cho các hình dạng mà nếu không thì sẽ rất giống nhau đến mức những bất biến đơn giản như vậy vẫn không thể phân biệt được chúng. Các tính năng đơn giản được xây dựng từ dữ liệu raster có thể có các đặc điểm như vậy, ví dụ. Tuy nhiên, vì giải pháp được đưa ra ở đây dù sao cũng rất hiệu quả, nên nếu ai đó sẽ nỗ lực thực hiện nó, thì nó có thể tự hoạt động tốt.
Thí dụ
Hình bên trái cho thấy năm polylines cộng thêm 15 cái nữa thu được từ những thứ đó thông qua dịch thuật ngẫu nhiên, xoay, phản xạ và đảo ngược hướng bên trong (không nhìn thấy được). Hình bên tay phải tô màu chúng theo lớp tương đương Euclide của chúng: tất cả các hình có màu chung là đồng dạng; màu sắc khác nhau không đồng nhất.
R
mã theo sau. Khi các đầu vào được cập nhật thành 500 hình, 500 hình phụ (đồng dạng), với giá trị trung bình là 100 đỉnh trên mỗi hình, thời gian thực hiện trên máy này là 3 giây.
Mã này không đầy đủ: vì R
không có sắp xếp từ vựng gốc và tôi không cảm thấy như mã hóa từ đầu, tôi chỉ đơn giản thực hiện sắp xếp trên tọa độ đầu tiên của mỗi hình dạng chuẩn. Điều đó sẽ tốt cho các hình dạng ngẫu nhiên được tạo ra ở đây, nhưng đối với công việc sản xuất, nên thực hiện một loại từ điển đầy đủ. Chức năng order.shape
sẽ là người duy nhất bị ảnh hưởng bởi sự thay đổi này. Đầu vào của nó là một danh sách các hình dạng được tiêu chuẩn hóa s
và đầu ra của nó là chuỗi các chỉ mục vào s
đó sẽ sắp xếp nó.
#
# 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)))