Tìm hình chữ nhật diện tích tối thiểu cho các điểm đã cho?


71

Như bạn thấy trong hình, câu hỏi là:

Làm thế nào để tìm hình chữ nhật diện tích tối thiểu (MAR) được trang bị trên các điểm đã cho?

và một câu hỏi hỗ trợ là:

Có bất kỳ giải pháp phân tích cho vấn đề?

(Sự phát triển của câu hỏi sẽ là khớp một hộp (3D) với một cụm điểm trong đám mây điểm 3D.)

Ở giai đoạn đầu tiên, tôi đề xuất tìm vỏ lồi cho các điểm cải cách vấn đề (bằng cách loại bỏ các điểm đó không liên quan đến giải pháp) để: khớp MAR với đa giác. Phương thức cần thiết sẽ cung cấp X ( tâm hình chữ nhật ), D ( hai chiều ) và A ( góc ).


Đề xuất của tôi cho giải pháp:

  • Tìm tâm của đa giác (xem Tìm tâm của hình học của đối tượng? )
  • [S] Lắp một hình chữ nhật được trang bị đơn giản, song song với trục X và Y
    • bạn có thể sử dụng minmaxhàm cho X và Y của các điểm đã cho (ví dụ: các đỉnh của đa giác)
  • Lưu trữ diện tích của hình chữ nhật được trang bị
  • Xoay đa giác về tâm bằng ví dụ, 1 độ
  • Lặp lại từ [S] cho đến khi hoàn thành một vòng quay
  • Báo cáo góc của diện tích tối thiểu là kết quả

Theo tôi có vẻ hứa hẹn, tuy nhiên vẫn tồn tại những vấn đề sau:

  • chọn một độ phân giải tốt để thay đổi góc có thể là một thách thức,
  • chi phí tính toán cao
  • giải pháp không phải là phân tích mà là thử nghiệm.

nhập mô tả hình ảnh ở đây

Câu trả lời:


45

Vâng, có một giải pháp phân tích cho vấn đề này. Thuật toán bạn đang tìm kiếm được biết đến trong khái quát đa giác là "hình chữ nhật nhỏ nhất xung quanh".

Thuật toán bạn mô tả là ổn nhưng để giải quyết các vấn đề bạn đã liệt kê, bạn có thể sử dụng thực tế là hướng của MAR giống như một trong các cạnh của thân mây lồi điểm . Vì vậy, bạn chỉ cần kiểm tra sự định hướng của các cạnh vỏ lồi. Bạn nên:

  • Tính toán vỏ lồi của đám mây.
  • Đối với mỗi cạnh của thân lồi:
    • tính toán hướng cạnh (với arctan),
    • xoay thân tàu lồi bằng cách sử dụng hướng này để tính toán dễ dàng diện tích hình chữ nhật giới hạn với min / max của x / y của thân lồi xoay,
    • Lưu trữ hướng tương ứng với diện tích tối thiểu được tìm thấy,
  • Trả về hình chữ nhật tương ứng với diện tích tối thiểu được tìm thấy.

Một ví dụ về việc thực hiện trong java có sẵn ở đó .

Trong 3D, áp dụng tương tự, ngoại trừ:

  • Thân tàu lồi sẽ là một thể tích,
  • Các định hướng được thử nghiệm sẽ là các định hướng (trong 3D) của các mặt thân tàu lồi.

Chúc may mắn!


11
+1 câu trả lời rất hay! Tôi muốn chỉ ra rằng xoay vòng thực tế của đám mây là không cần thiết. Đầu tiên - bạn có thể có ý này - chỉ các đỉnh của thân tàu phải được xem xét. Thứ hai, thay vì quay, biểu diễn phía hiện tại dưới dạng một cặp vectơ đơn vị trực giao. Lấy các sản phẩm dấu chấm của chúng với tọa độ đỉnh thân (có thể được thực hiện dưới dạng một phép toán ma trận) sẽ cho tọa độ xoay: không cần lượng giác, nhanh và chính xác hoàn toàn.
whuber

2
Cảm ơn các liên kết. Thật vậy, chỉ xoay cho # các cạnh làm cho phương pháp được đề xuất rất hiệu quả. Tôi có thể tìm thấy bài báo chứng minh điều đó. Mặc dù tôi đã đánh dấu đây là câu trả lời cho sự trung thành với câu trả lời tốt đầu tiên (không thể chọn hai / nhiều câu trả lời hay hơn :() Tôi muốn đề xuất mạnh mẽ xem xét câu trả lời hoàn chỉnh của người đánh giá dưới đây. Hiệu quả của phương pháp đã cho ở đó (tránh xoay vòng!) Là không thể tin được, và toàn bộ quy trình chỉ là một vài dòng mã. Đối với tôi, nó có thể dịch dễ dàng sang Python :)
Nhà phát triển

Bạn có thể vui lòng cập nhật liên kết thực hiện java?
Myra

vâng, xong rồi
julien

1
Lưu ý rằng phần mở rộng thành 3D phức tạp hơn thế một chút. Mỗi mặt của thân lồi 3D xác định hướng có thể có của một mặt của hộp giới hạn, nhưng không phải là hướng của các mặt vuông góc với nó. Vấn đề làm thế nào để xoay hộp trong mặt phẳng đó trở thành vấn đề hình chữ nhật giới hạn tối thiểu 2D trong mặt phẳng của mặt đó. Đối với mỗi cạnh của thân lồi của đám mây được chiếu lên một mặt phẳng nhất định, bạn có thể vẽ một hộp giới hạn sẽ cung cấp cho bạn một khối lượng khác nhau trong 3D.
Sẽ

40

Để bổ sung cho giải pháp tuyệt vời của @ julien, đây là một triển khai hoạt động R, có thể đóng vai trò là mã giả để hướng dẫn bất kỳ triển khai cụ thể nào của GIS (hoặc Rdĩ nhiên được áp dụng trực tiếp ). Đầu vào là một mảng tọa độ điểm. Đầu ra (giá trị của mbr) là một mảng các đỉnh của hình chữ nhật giới hạn tối thiểu (với cái đầu tiên được lặp lại để đóng nó). Lưu ý sự vắng mặt hoàn toàn của bất kỳ tính toán lượng giác.

MBR <- function(p) {
  # Analyze the convex hull edges     
  a <- chull(p)                                   # Indexes of extremal points
  a <- c(a, a[1])                                 # Close the loop
  e <- p[a[-1],] - p[a[-length(a)], ]             # Edge directions
  norms <- sqrt(rowSums(e^2))                     # Edge lengths
  v <- e / norms                                  # Unit edge directions
  w <- cbind(-v[,2], v[,1])                       # Normal directions to the edges

  # Find the MBR
  vertices <- p[a, ]                              # Convex hull vertices
  x <- apply(vertices %*% t(v), 2, range)         # Extremes along edges
  y <- apply(vertices %*% t(w), 2, range)         # Extremes normal to edges
  areas <- (y[1,]-y[2,])*(x[1,]-x[2,])            # Areas
  k <- which.min(areas)                           # Index of the best edge (smallest area)

  # Form a rectangle from the extremes of the best edge
  cbind(x[c(1,2,2,1,1),k], y[c(1,1,2,2,1),k]) %*% rbind(v[k,], w[k,])
}

Đây là một ví dụ về việc sử dụng nó:

# Create sample data
set.seed(23)
p <- matrix(rnorm(20*2), ncol=2)                 # Random (normally distributed) points
mbr <- MBR(points)

# Plot the hull, the MBR, and the points
limits <- apply(mbr, 2, range) # Plotting limits
plot(p[(function(x) c(x, x[1]))(chull(p)), ], 
     type="l", asp=1, bty="n", xaxt="n", yaxt="n",
     col="Gray", pch=20, 
     xlab="", ylab="",
     xlim=limits[,1], ylim=limits[,2])                # The hull
lines(mbr, col="Blue", lwd=3)                         # The MBR
points(points, pch=19)                                # The points

MBR

Thời gian bị giới hạn bởi tốc độ của thuật toán vỏ lồi, bởi vì số lượng đỉnh trong thân tàu hầu như luôn luôn ít hơn nhiều so với tổng số. Hầu hết các thuật toán vỏ lồi là không có triệu chứng O (n * log (n)) cho n điểm: bạn có thể tính toán gần như nhanh như bạn có thể đọc tọa độ.


+1 Thật là một giải pháp tuyệt vời! Một ý tưởng như vậy chỉ đến sau khi có kinh nghiệm lâu dài. Từ giờ tôi sẽ tò mò để tối ưu hóa các mã hiện có của mình được truyền cảm hứng với câu trả lời tuyệt vời này.
Nhà phát triển

Tôi ước tôi có thể nâng cao điều này hai lần. Tôi đang học R và câu trả lời của bạn là nguồn cảm hứng liên tục.
John Powell

1
@retrovius Hình chữ nhật giới hạn của một tập hợp các điểm (xoay) được xác định bởi bốn số: tọa độ x nhỏ nhất, tọa độ x lớn nhất, tọa độ y nhỏ nhất và tọa độ y lớn nhất. Đó là những gì "cực đoan dọc theo các cạnh" đề cập đến.
whuber

1
@retrovius Nguồn gốc không có vai trò trong các tính toán này, bởi vì mọi thứ đều dựa trên sự khác biệt của tọa độ ngoại trừ ở cuối, trong đó hình chữ nhật tốt nhất như được tính trong tọa độ xoay chỉ đơn giản được quay trở lại. Mặc dù đó là một ý tưởng thông minh để sử dụng một hệ tọa độ trong đó gốc tọa độ gần với các điểm (để giảm thiểu mất độ chính xác của dấu phẩy động), nhưng mặt khác thì không liên quan.
whuber

1
@Retrovius Bạn có thể diễn giải điều này theo tính chất của các phép quay: cụ thể là ma trận của một phép quay là trực giao. Do đó, một loại tài nguyên sẽ là một nghiên cứu về đại số tuyến tính (nói chung) hoặc hình học Euclide phân tích (cụ thể). Tuy nhiên, tôi đã thấy rằng cách dễ nhất để xử lý các phép quay (và dịch và thay đổi kích thước) trong mặt phẳng là xem các điểm dưới dạng số phức: phép quay được thực hiện đơn giản bằng cách nhân giá trị với số đơn vị độ dài.
whuber

8

Tôi chỉ tự thực hiện điều này và đăng câu trả lời của mình lên StackOverflow , nhưng tôi đoán rằng tôi sẽ thả phiên bản của mình ở đây để người khác xem:

import numpy as np
from scipy.spatial import ConvexHull

def minimum_bounding_rectangle(points):
    """
    Find the smallest bounding rectangle for a set of points.
    Returns a set of points representing the corners of the bounding box.

    :param points: an nx2 matrix of coordinates
    :rval: an nx2 matrix of coordinates
    """
    from scipy.ndimage.interpolation import rotate
    pi2 = np.pi/2.

    # get the convex hull for the points
    hull_points = points[ConvexHull(points).vertices]

    # calculate edge angles
    edges = np.zeros((len(hull_points)-1, 2))
    edges = hull_points[1:] - hull_points[:-1]

    angles = np.zeros((len(edges)))
    angles = np.arctan2(edges[:, 1], edges[:, 0])

    angles = np.abs(np.mod(angles, pi2))
    angles = np.unique(angles)

    # find rotation matrices
    # XXX both work
    rotations = np.vstack([
        np.cos(angles),
        np.cos(angles-pi2),
        np.cos(angles+pi2),
        np.cos(angles)]).T
#     rotations = np.vstack([
#         np.cos(angles),
#         -np.sin(angles),
#         np.sin(angles),
#         np.cos(angles)]).T
    rotations = rotations.reshape((-1, 2, 2))

    # apply rotations to the hull
    rot_points = np.dot(rotations, hull_points.T)

    # find the bounding points
    min_x = np.nanmin(rot_points[:, 0], axis=1)
    max_x = np.nanmax(rot_points[:, 0], axis=1)
    min_y = np.nanmin(rot_points[:, 1], axis=1)
    max_y = np.nanmax(rot_points[:, 1], axis=1)

    # find the box with the best area
    areas = (max_x - min_x) * (max_y - min_y)
    best_idx = np.argmin(areas)

    # return the best box
    x1 = max_x[best_idx]
    x2 = min_x[best_idx]
    y1 = max_y[best_idx]
    y2 = min_y[best_idx]
    r = rotations[best_idx]

    rval = np.zeros((4, 2))
    rval[0] = np.dot([x1, y2], r)
    rval[1] = np.dot([x2, y2], r)
    rval[2] = np.dot([x2, y1], r)
    rval[3] = np.dot([x1, y1], r)

    return rval

Dưới đây là bốn ví dụ khác nhau của nó trong hành động. Với mỗi ví dụ, tôi tạo ra 4 điểm ngẫu nhiên và tìm thấy hộp giới hạn.

nhập mô tả hình ảnh ở đây

Nó cũng tương đối nhanh đối với các mẫu trên 4 điểm:

>>> %timeit minimum_bounding_rectangle(a)
1000 loops, best of 3: 245 µs per loop

Xin chào JesseBuesking, bạn có thể tạo hình chữ nhật với các góc 90 độ không? Mã của bạn đang hoạt động rất tốt để có được hình bình hành nhưng góc 90 độ là bắt buộc trong trường hợp sử dụng cụ thể của tôi. Bạn có thể đề nghị làm thế nào mã của bạn có thể được sửa đổi để đạt được điều đó? Cảm ơn!
Nader Alexan

@NaderAlexan Nếu bạn hỏi liệu nó có thể xử lý các hình vuông hay không, thì chắc chắn là nó có thể! Tôi vừa thử nó trên một hình vuông đơn vị points = np.array([[0, 0], [0, 1], [1, 0], [1, 1]]), và đầu ra là array([[1.00000000e+00, 6.12323400e-17], [0.00000000e+00, 0.00000000e+00], [6.12323400e-17, 1.00000000e+00], [1.00000000e+00, 1.00000000e+00]])chính hình vuông đơn vị (bao gồm một số lỗi làm tròn điểm nổi). Lưu ý: hình vuông chỉ là một hình chữ nhật có các cạnh bằng nhau, vì vậy tôi giả sử nếu nó có thể xử lý một hình vuông thì nó sẽ khái quát cho tất cả các hình chữ nhật.
JesseBuesking

Cảm ơn bạn vì câu trả lời. Vâng, nó hoạt động rất tốt nhưng tôi đang cố gắng buộc nó luôn tạo ra một hình chữ nhật (4 cạnh với góc 90 độ cho mỗi bên) trên bất kỳ đa giác 4 ​​mặt nào khác, mặc dù trong một số trường hợp, nó không tạo ra một hình chữ nhật là một ràng buộc không đổi, bạn có biết cách sửa đổi mã để thêm ràng buộc này không? Cảm ơn!
Nader Alexan

Có lẽ gis.stackexchange.com/a/22934/48041 có thể hướng dẫn bạn hướng tới một giải pháp, vì câu trả lời của họ dường như có ràng buộc này? Khi bạn tìm thấy giải pháp, bạn nên đóng góp vì tôi chắc chắn những người khác sẽ thấy nó hữu ích. Chúc may mắn!
JesseBuesking

7

Có một công cụ trong Whitebox GAT ( http://www.uoguelph.ca/~hydrogeo/Whitebox/ ) được gọi là Hộp giới hạn tối thiểu để giải quyết vấn đề chính xác này. Ngoài ra còn có một công cụ vỏ lồi tối thiểu trong đó. Một số công cụ trong hộp công cụ Patch Shape, ví dụ: định hướng và độ giãn dài của bản vá, dựa trên việc tìm hộp giới hạn tối thiểu.

nhập mô tả hình ảnh ở đây


4

Tôi đã xem qua chủ đề này trong khi tìm kiếm một giải pháp Python cho hình chữ nhật giới hạn diện tích tối thiểu.

Đây là triển khai của tôi , trong đó các kết quả đã được xác minh với Matlab.

Mã kiểm tra được bao gồm cho các đa giác đơn giản và tôi đang sử dụng nó để tìm hộp giới hạn tối thiểu 2D và hướng trục cho một PointCloud 3D.


Câu trả lời của bạn đã bị xóa chưa?
Paul Richter

@PaulRichter rõ ràng. Nguồn ở đây là github.com/dbworth/minimum-area-bounding-r chữ nhật mặc dù
sehe

3

Cảm ơn câu trả lời của @ whuber. Đó là một giải pháp tuyệt vời, nhưng chậm cho đám mây điểm lớn. Tôi thấy convhullnchức năng trong gói R geometrynhanh hơn nhiều (138 giây so với 0,03 giây cho 200000 điểm). Tôi đã dán mã của tôi ở đây cho bất cứ ai là thú vị cho một giải pháp nhanh hơn.

library(alphahull)                                  # Exposes ashape()
MBR <- function(points) {
    # Analyze the convex hull edges                       
    a <- ashape(points, alpha=1000)                 # One way to get a convex hull...
    e <- a$edges[, 5:6] - a$edges[, 3:4]            # Edge directions
    norms <- apply(e, 1, function(x) sqrt(x %*% x)) # Edge lengths
    v <- diag(1/norms) %*% e                        # Unit edge directions
    w <- cbind(-v[,2], v[,1])                       # Normal directions to the edges

    # Find the MBR
    vertices <- (points) [a$alpha.extremes, 1:2]    # Convex hull vertices
    minmax <- function(x) c(min(x), max(x))         # Computes min and max
    x <- apply(vertices %*% t(v), 2, minmax)        # Extremes along edges
    y <- apply(vertices %*% t(w), 2, minmax)        # Extremes normal to edges
    areas <- (y[1,]-y[2,])*(x[1,]-x[2,])            # Areas
    k <- which.min(areas)                           # Index of the best edge (smallest area)

    # Form a rectangle from the extremes of the best edge
    cbind(x[c(1,2,2,1,1),k], y[c(1,1,2,2,1),k]) %*% rbind(v[k,], w[k,])
}

MBR2 <- function(points) {
    tryCatch({
        a2 <- geometry::convhulln(points, options = 'FA')

        e <- points[a2$hull[,2],] - points[a2$hull[,1],]            # Edge directions
        norms <- apply(e, 1, function(x) sqrt(x %*% x)) # Edge lengths

        v <- diag(1/norms) %*% as.matrix(e)                        # Unit edge directions


        w <- cbind(-v[,2], v[,1])                       # Normal directions to the edges

        # Find the MBR
        vertices <- as.matrix((points) [a2$hull, 1:2])    # Convex hull vertices
        minmax <- function(x) c(min(x), max(x))         # Computes min and max
        x <- apply(vertices %*% t(v), 2, minmax)        # Extremes along edges
        y <- apply(vertices %*% t(w), 2, minmax)        # Extremes normal to edges
        areas <- (y[1,]-y[2,])*(x[1,]-x[2,])            # Areas
        k <- which.min(areas)                           # Index of the best edge (smallest area)

        # Form a rectangle from the extremes of the best edge
        as.data.frame(cbind(x[c(1,2,2,1,1),k], y[c(1,1,2,2,1),k]) %*% rbind(v[k,], w[k,]))
    }, error = function(e) {
        assign('points', points, .GlobalEnv)
        stop(e)  
    })
}


# Create sample data
#set.seed(23)
points <- matrix(rnorm(200000*2), ncol=2)                 # Random (normally distributed) points
system.time(mbr <- MBR(points))
system.time(mmbr2 <- MBR2(points))


# Plot the hull, the MBR, and the points
limits <- apply(mbr, 2, function(x) c(min(x),max(x))) # Plotting limits
plot(ashape(points, alpha=1000), col="Gray", pch=20, 
     xlim=limits[,1], ylim=limits[,2])                # The hull
lines(mbr, col="Blue", lwd=10)                         # The MBR
lines(mbr2, col="red", lwd=3)                         # The MBR2
points(points, pch=19)   

Hai phương pháp nhận được cùng một câu trả lời (ví dụ cho 2000 điểm):

nhập mô tả hình ảnh ở đây


Có thể mở rộng triển khai này sang không gian 3d (tức là tìm hộp âm lượng tối thiểu bao gồm tất cả các điểm đã cho trong không gian 3d)?
Sasha

0

Tôi chỉ đơn giản đề xuất chức năng tích hợp của OpenCV minAreaRect, tìm thấy hình chữ nhật xoay của khu vực tối thiểu kèm theo tập hợp điểm 2D đầu vào. Để xem cách sử dụng chức năng này, người ta có thể tham khảo hướng dẫn này .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.