주어진 점에 대한 최소 면적 직사각형을 찾으십니까?


71

그림에서 볼 수 있듯이 질문은 다음과 같습니다.

주어진 점에 맞는 최소 면적 직사각형 (MAR)을 찾는 방법은 무엇입니까?

그리고지지하는 질문은 :

문제에 대한 분석 솔루션이 있습니까?

(질문의 개발은 박스 (3D)를 3D 포인트 클라우드의 포인트 클러스터에 맞추는 것입니다.)

첫 번째 단계 로 MAR에 다각형을 맞추기 위해 (문제에 포함되지 않은 점을 제거하여) 문제를 개혁하는 점에 대한 볼록 껍질을 찾을 것을 제안 합니다. 필요한 방법은 X ( 사각형 중심 ), D ( 2 차원 ) 및 A ( 각도 )를 제공합니다.


솔루션에 대한 나의 제안 :

  • 다각형의 중심을 구합니다 ( 물체의 형상 중심 찾기? 참조 )
  • [S] 축 X 및 Y에 평행 한 간단한 장착 사각형을 맞 춥니 다.
    • minmax주어진 점의 X와 Y에 함수를 사용할 수 있습니다 (예 : 다각형의 정점)
  • 장착 된 사각형의 면적을 저장
  • 중심을 중심으로 다각형을 회전합니다. 예 : 1도
  • 완전히 회전 할 때까지 [S] 부터 반복
  • 최소 면적의 각도를 결과로보고

유망한 것으로 보이지만 다음과 같은 문제가 있습니다.

  • 각도 변경에 적합한 해상도를 선택하는 것이 어려울 수 있습니다.
  • 계산 비용이 높고
  • 솔루션은 분석적인 것이 아니라 실험적인 것입니다.

여기에 이미지 설명을 입력하십시오

답변:


45

예,이 문제에 대한 분석 솔루션이 있습니다. 찾고있는 알고리즘은 다각형 일반화 에서 "가장 작은 주변 사각형"으로 알려져 있습니다.

설명하는 알고리즘은 훌륭하지만 나열된 문제를 해결하기 위해 MAR의 방향이 점 구름 볼록 껍질의 가장자리 중 하나와 동일 하다는 사실을 사용할 수 있습니다 . 따라서 볼록 껍질 선단의 방향을 테스트하면됩니다. 당신은해야합니다 :

  • 구름의 볼록 껍질을 계산합니다.
  • 볼록 껍질의 각 모서리에 대해 :
    • 모서리 방향 (아크 탄 포함) 계산
    • 회전 된 볼록 껍질의 최소 / 최대 x / y로 경계 사각형 영역을 쉽게 계산하기 위해이 방향을 사용하여 볼록 껍질을 회전시킵니다.
    • 발견 된 최소 영역에 해당하는 방향을 보관하십시오.
  • 찾은 최소 영역에 해당하는 사각형을 반환합니다.

Java 구현 예제가 여기에 있습니다.

3D에서는 다음을 제외하고 동일하게 적용됩니다.

  • 볼록 껍질은 볼륨이되고
  • 테스트 된 방향은 볼록 껍질면의 방향 (3D)입니다.

행운을 빕니다!


11
+1 아주 좋은 답변! 클라우드의 실제 회전이 필요하지 않다고 지적하고 싶습니다. 첫째, 아마도 이것을 의미했을 것입니다. 선체의 꼭짓점 만 고려하면됩니다. 둘째, 회전하는 대신에, 현재 측을 한 쌍의 직교 단위 벡터로 나타낸다. 선체 정점 좌표 (단일 행렬 연산으로 수행 할 수 있음)를 사용하여 내적을 계산하면 회전 좌표가 제공됩니다. 삼각법이 필요하지 않고 빠르고 완벽하며 정확합니다.
whuber

2
링크 주셔서 감사합니다. 실제로, 가장자리 수만큼만 회전하면 제안 된 방법이 매우 효율적입니다. 나는 그 종이가 그것을 증명한다는 것을 알 수 있었다. 나는 이것을 첫 번째 좋은 답변에 대한 충성도에 대한 답변으로 표시했지만 (두 가지 이상의 훌륭한 답변을 선택할 수는 없습니다 :() 아래 whuber의 완전한 답변을 강력하게 고려하는 것이 좋습니다 . 놀라운, 전체 절차는 코드의 단지 몇 줄의 나를 위해 그것을 :) 파이썬에 쉽게 번역입니다.
개발자

자바 구현 링크를 업데이트 하시겠습니까?
Myra

예, 끝났습니다!
julien

1
3D 로의 확장은 그보다 약간 더 복잡합니다. 3 차원 볼록 선체의 각 얼굴의 방향을 정의 할 수 일면 그것에 수직 인면의 방향을 바운딩 박스가 아니라. 해당 평면에서 상자를 회전시키는 방법의 문제는 해당 평면의 평면에서 2D 최소 경계 사각형 문제가됩니다. 주어진 평면에 투영 된 구름의 볼록 껍질의 각 모서리에 대해 3D로 다른 볼륨을 줄 경계 상자를 그릴 수 있습니다.

40

@julien의 훌륭한 솔루션을 보완하기 위해 여기에 실제 구현이 있습니다.이 구현은 R모든 GIS 특정 구현을 안내하는 의사 코드 역할을 할 수 있습니다 (또는 R물론 직접 적용 ). 입력은 점 좌표의 배열입니다. 출력값 ( mbr)은 최소 경계 사각형의 정점 배열입니다 (첫 번째 사각형은 반복하여 닫습니다). 삼각법 계산이 전혀 없음에 유의하십시오.

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,])
}

사용 예는 다음과 같습니다.

# 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

선체의 정점 수가 거의 항상 전체보다 훨씬 작기 때문에 타이밍은 볼록 껍질 알고리즘의 속도에 의해 제한됩니다. 대부분의 볼록 껍질 알고리즘은 n 개의 점에 대해 무증상 O (n * log (n))입니다 . 좌표를 읽을 수있는 한 빨리 계산할 수 있습니다.


+1 정말 놀라운 해결책입니다! 그러한 아이디어는 오랜 경험을 가진 후에 만 ​​온다. 이제부터는이 훌륭한 답변에 영감을 받아 기존 코드를 최적화 할 수있을 것입니다.
개발자

나는 이것을 두 번 공표 할 수 있으면 좋겠다. 나는 R을 배우고 있으며 당신의 대답은 지속적인 영감의 원천입니다.
존 파월

1
@retrovius (회전 된) 점 세트의 경계 사각형은 가장 작은 x 좌표, 가장 큰 x 좌표, 가장 작은 y 좌표 및 가장 큰 y 좌표의 네 가지 숫자로 결정됩니다. 그것이 바로 "가장자리를 따르는 극단"이 말하는 것입니다.
whuber

1
@retrovius 원점은이 계산에서 아무런 역할을하지 않습니다. 모든 것은 끝을 제외하고 좌표의 차이를 기반으로하기 때문입니다. 회전 좌표로 계산 된 최상의 사각형이 단순히 뒤로 회전하기 때문입니다. 부동 소수점 정밀도의 손실을 최소화하기 위해 원점이 점에 가까운 좌표 시스템을 사용하는 것이 현명한 아이디어이지만, 그렇지 않으면 원점이 관련이 없습니다.
whuber

1
@Retrovius 회전 속성 측면에서 해석 할 수 있습니다. 즉, 회전 행렬은 직교입니다. 따라서 한 종류의 자원은 선형 대수학 (일반적으로) 또는 분석 유클리드 기하학 (구체적으로)에 대한 연구입니다. 그러나 평면에서 회전 (및 변환 및 크기 조정)을 처리하는 가장 쉬운 방법은 점을 복잡한 숫자로 보는 것입니다. 회전은 단순히 값에 단위 길이의 숫자를 곱하여 수행됩니다.
whuber

8

방금 이것을 직접 구현하고 StackOverflow에 답변을 게시 했지만 다른 사람들이 볼 수 있도록 내 버전을 여기 에 놓을 것이라고 생각했습니다.

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

다음은 실제로 사용되는 네 가지 예입니다. 각 예제에서 4 개의 임의의 점을 생성하고 경계 상자를 찾았습니다.

여기에 이미지 설명을 입력하십시오

이 샘플들에 대해서도 4 점에서 비교적 빠릅니다.

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

JesseBuesking, 90도 모서리가있는 사각형을 생성 할 수 있습니까? 귀하의 코드는 평행 사변형을 얻는 데 효과적이지만 특정 사용 사례에는 90도 모서리가 필요합니다. 코드에 도달하도록 코드를 수정하는 방법을 추천 해 주시겠습니까? 감사!
Nader Alexan

@NaderAlexan 사각형을 다룰 수 있는지 묻는다면 확실히 가능합니다! 방금 단위 square points = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])에서 시도했지만 출력은 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]])단위 사각형 자체입니다 (부동 소수점 반올림 오류 포함). 참고 : 정사각형은 측면이 같은 직사각형 일 뿐이므로 정사각형을 처리 할 수 ​​있다면 모든 사각형으로 일반화됩니다.
JesseBuesking

답변 주셔서 감사합니다. 예, 잘 작동하지만 다른 4면 다각형에 대해 항상 사각형 (각면에 90도 각도의 4면)을 생성하도록 강요하려고하지만 특정 경우에는 사각형이 생성되지 않습니다. 상수 제약 조건이 되려면이 제약 조건을 추가하기 위해 코드를 수정하는 방법을 알고 있습니까? 감사!
Nader Alexan

아마도 gis.stackexchange.com/a/22934/48041 에서 답변 에이 제약 조건이있는 것으로 보이면 솔루션을 안내 할 수 있습니까? 솔루션을 찾으면 다른 사람들이 유용하다고 확신하므로 솔루션을 제공해야합니다. 행운을 빕니다!
JesseBuesking

7

Whitebox GAT ( http://www.uoguelph.ca/~hydrogeo/Whitebox/ )에는이 정확한 문제를 해결하기위한 최소 경계 상자라는 도구가 있습니다. 최소 볼록 껍질 도구도 있습니다. 패치 모양 도구 상자의 여러 도구 (예 : 패치 방향 및 신장)는 최소 경계 상자 찾기를 기반으로합니다.

여기에 이미지 설명을 입력하십시오


4

최소 면적 경계 사각형에 대한 Python 솔루션을 찾는 동안이 스레드를 발견했습니다.

여기 제의 구현 결과를 matlab에 확인 된 위해가.

간단한 다각형에 대한 테스트 코드가 포함되어 있으며 3D PointCloud의 2D 최소 경계 상자 및 축 방향을 찾는 데 사용합니다.


답변이 삭제 되었습니까?
Paul Richter

@PaulRichter는 분명합니다. 소스는 여기에 있었다 github.com/dbworth/minimum-area-bounding-rectangle 하지만
sehe

3

@ whuber의 답변에 감사드립니다. 훌륭한 솔루션이지만 빅 포인트 클라우드에서는 느립니다. 내가 발견 convhullnR 패키지의 기능 geometry((138 개)의 대 200000 포인트 0.03의) 훨씬 빠릅니다. 더 빠른 솔루션을 원하면 누구나 내 코드를 여기에 붙여 넣었습니다.

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)   

두 가지 방법이 같은 답을 얻습니다 (2000 점의 예).

여기에 이미지 설명을 입력하십시오


이 구현을 3D 공간으로 확장 할 수 있습니까 (즉, 3D 공간에서 모든 주어진 지점을 포함하는 최소 볼륨 상자를 찾으십시오)?
사샤

0

minAreaRect입력 2D 포인트 세트를 포함하는 최소 영역의 회전 사각형을 찾는 OpenCV의 내장 기능을 권장합니다 . 이 기능을 사용하는 방법을 보려면 이 튜토리얼을 참조하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.