กำลังค้นหาสี่เหลี่ยมจัตุรัสขั้นต่ำสำหรับจุดใด


71

ตามที่เห็นในรูปคำถามคือ:

จะหาพื้นที่ต่ำสุดสี่เหลี่ยมผืนผ้า (MAR) ในจุดที่กำหนดได้อย่างไร?

และคำถามที่สนับสนุนคือ:

มีวิธีการวิเคราะห์ปัญหาหรือไม่?

(การพัฒนาคำถามจะต้องพอดีกับกล่อง (3D) กับกลุ่มของจุดในคลาวด์จุด 3D)

เป็นขั้นตอนแรกที่ฉันเสนอเพื่อค้นหาตัวเรือนูนสำหรับจุดที่แก้ไขปัญหา (โดยการลบจุดเหล่านั้นไม่เกี่ยวข้องในการแก้ไขปัญหา) เพื่อ: กระชับ MAR กับรูปหลายเหลี่ยม วิธีที่ต้องการจะให้ X ( กึ่งกลางของสี่เหลี่ยม ), D ( สองมิติ ) และ A ( มุม )


ข้อเสนอของฉันสำหรับการแก้ปัญหา:

  • ค้นหา centroid ของรูปหลายเหลี่ยม (ดูที่การค้นหาจุดศูนย์กลางของเรขาคณิตของวัตถุ? )
  • [S]ติดตั้งสี่เหลี่ยมผืนผ้าที่เรียบง่ายเช่นขนานกับแกน X และ Y
    • คุณอาจใช้minmaxฟังก์ชั่นสำหรับ X และ Y ของจุดที่กำหนด (เช่นจุดยอดของรูปหลายเหลี่ยม)
  • จัดเก็บพื้นที่ของสี่เหลี่ยมผืนผ้าที่ได้รับการติดตั้ง
  • หมุนรูปหลายเหลี่ยมเกี่ยวกับเซนทรอยด์โดยเช่น 1 องศา
  • ทำซ้ำจาก[S]จนกระทั่งการหมุนเสร็จสิ้น
  • รายงานมุมของพื้นที่ขั้นต่ำเป็นผลลัพธ์

ดูเหมือนว่าฉันจะสัญญา แต่ปัญหาต่อไปนี้อยู่:

  • การเลือกความละเอียดที่ดีสำหรับการเปลี่ยนมุมอาจเป็นเรื่องที่ท้าทาย
  • ต้นทุนการคำนวณสูง
  • การแก้ปัญหาไม่ใช่การวิเคราะห์ แต่เป็นการทดลอง

ป้อนคำอธิบายรูปภาพที่นี่

คำตอบ:


45

ใช่มีวิธีวิเคราะห์สำหรับปัญหานี้ อัลกอริทึมที่คุณกำลังมองหานั้นเป็นที่รู้จักกันในลักษณะทั่วไปของรูปหลายเหลี่ยมว่า "สี่เหลี่ยมล้อมรอบที่เล็กที่สุด"

ขั้นตอนวิธีการที่คุณอธิบายเป็นดี แต่เพื่อที่จะแก้ปัญหาที่คุณมีอยู่คุณสามารถใช้ความจริงที่ว่าทิศทางของ MAR เป็นเช่นเดียวกับหนึ่งของหนึ่งในขอบของเมฆจุดเรือนูน ดังนั้นคุณเพียงแค่ต้องทดสอบการหมุนของขอบลำเรือนูน คุณควร:

  • คำนวณฮัลล์นูนของเมฆ
  • สำหรับแต่ละขอบของเปลือกนูน:
    • คำนวณการวางแนวขอบ (ด้วย arctan)
    • หมุนตัวเรือนูนโดยใช้ทิศทางนี้เพื่อคำนวณพื้นที่สี่เหลี่ยมผืนผ้าที่ล้อมรอบได้อย่างง่ายดายด้วยค่าต่ำสุด / สูงสุดของ x / y ของตัวเรือที่หมุนได้
    • เก็บการวางแนวที่สอดคล้องกับพื้นที่ขั้นต่ำที่พบ
  • ส่งคืนสี่เหลี่ยมที่สอดคล้องกับพื้นที่ขั้นต่ำที่พบ

ตัวอย่างของการดำเนินงานใน java สามารถใช้ได้มี

ในแบบ 3 มิติจะใช้เช่นเดียวกันยกเว้น:

  • ฮัลล์นูนจะมีปริมาณ
  • การหมุนทิศทางที่ทดสอบจะเป็นการหมุน (ในแบบ 3 มิติ) ของลำตัวนูน

โชคดี!


11
+1 คำตอบที่ดีมาก! ฉันอยากจะชี้ให้เห็นว่าการหมุนของคลาวด์จริงนั้นไม่จำเป็น ครั้งแรก - คุณอาจหมายถึงสิ่งนี้ - ต้องพิจารณาเฉพาะจุดยอดของตัวถัง สองแทนการหมุนแทนด้านปัจจุบันเป็นเวกเตอร์หน่วยมุมฉาก การนำผลิตภัณฑ์ดอทของพวกเขามาพร้อมกับจุดยอดฮัลล์ (ซึ่งสามารถทำได้เป็นการดำเนินการเมทริกซ์เดียว) ให้พิกัดแบบหมุน: ไม่จำเป็นต้องตรีโกณมิติรวดเร็วและแม่นยำอย่างสมบูรณ์
whuber

2
ขอบคุณสำหรับลิงค์ แน่นอนการหมุนเพียง # ของขอบทำให้วิธีที่เสนอนั้นมีประสิทธิภาพมาก ฉันสามารถหากระดาษพิสูจน์ได้ว่า แม้ว่าฉันจะทำเครื่องหมายว่านี่เป็นคำตอบสำหรับความภักดีต่อคำตอบแรกที่ดี (ไม่สามารถเลือกคำตอบที่ดีสองข้อขึ้นไป :() ฉันอยากจะแนะนำอย่างยิ่งเมื่อพิจารณาคำตอบทั้งหมดของ whuberด้านล่างประสิทธิภาพของวิธีการที่กำหนดไว้ เหลือเชื่อและขั้นตอนทั้งหมดเป็นโค้ดเพียงไม่กี่บรรทัดสำหรับฉันแล้วมันสามารถแปลเป็น Python ได้อย่างง่ายดาย :)
นักพัฒนา

คุณช่วยอัพเดทลิงค์การติดตั้งจาวาได้ไหม?
Myra

ใช่มันเสร็จแล้ว!
Julien

1
โปรดทราบว่าการขยายสู่ 3D นั้นซับซ้อนกว่านั้นเล็กน้อย แต่ละใบหน้าของลำตัวนูน 3D กำหนดทิศทางที่เป็นไปได้ของใบหน้าหนึ่งของกล่องที่มีขอบเขต แต่ไม่ใช่การวางแนวของใบหน้าที่ตั้งฉากกับมัน ปัญหาของการหมุนกล่องในระนาบนั้นจะกลายเป็นปัญหาของขอบเขตสี่เหลี่ยมผืนผ้าขั้นต่ำแบบ 2D ในระนาบของใบหน้านั้น สำหรับแต่ละส่วนของลำตัวนูนของคลาวด์ที่ฉายลงบนระนาบที่กำหนดคุณสามารถวาดกล่องขอบเขตซึ่งจะให้เสียงที่แตกต่างในแบบ 3 มิติ
จะ

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

เวลาถูก จำกัด ด้วยความเร็วของอัลกอริธึมฮัลล์นูนเนื่องจากจำนวนของจุดยอดในตัวเรือนั้นน้อยกว่าผลรวมทั้งหมด อัลกอริธึมฮัลล์นูนส่วนใหญ่เป็นแบบไม่เชิงเส้น O (n * log (n)) สำหรับจุดn : คุณสามารถคำนวณได้เกือบเร็วเท่าที่คุณสามารถอ่านพิกัด


+1 ช่างเป็นทางออกที่ยอดเยี่ยม! ความคิดดังกล่าวเกิดขึ้นหลังจากมีประสบการณ์ที่ยาวนานเท่านั้น จากนี้ไปฉันจะอยากรู้อยากเห็นเพื่อเพิ่มประสิทธิภาพรหัสที่ฉันมีอยู่ให้เป็นแรงบันดาลใจด้วยคำตอบที่ยอดเยี่ยม
นักพัฒนา

ฉันหวังว่าฉันจะลงคะแนนได้สองครั้ง ฉันกำลังเรียนรู้ R และคำตอบของคุณเป็นแหล่งของแรงบันดาลใจอย่างต่อเนื่อง
John Powell

1
@retrovius ขอบเขตสี่เหลี่ยมผืนผ้าของชุด (หมุน) จุดถูกกำหนดโดยตัวเลขสี่ตัว: พิกัด x ที่เล็กที่สุด, พิกัด x ที่ใหญ่ที่สุด, พิกัด y ที่เล็กที่สุดและพิกัด y ที่ใหญ่ที่สุด นั่นคือสิ่งที่ "สุดขั้วไปตามขอบ" หมายถึง
whuber

1
@retrovius ที่มาไม่มีบทบาทในการคำนวณเหล่านี้เพราะทุกอย่างขึ้นอยู่กับความแตกต่างของพิกัดยกเว้นในตอนท้ายที่สี่เหลี่ยมผืนผ้าที่ดีที่สุดที่คำนวณได้ในพิกัดที่หมุนจะหมุนกลับ แม้ว่าจะเป็นความคิดที่ฉลาดที่จะใช้ระบบพิกัดที่จุดกำเนิดอยู่ใกล้กับจุด (เพื่อลดการสูญเสียความแม่นยำของจุดลอยตัว) แต่ต้นกำเนิดนั้นไม่เกี่ยวข้อง
whuber

1
@Retrovius คุณสามารถตีความสิ่งนี้ในแง่ของคุณสมบัติของการหมุน: กล่าวคือเมทริกซ์ของการหมุนคือมุมฉาก ดังนั้นทรัพยากรประเภทหนึ่งคือการศึกษาพีชคณิตเชิงเส้น (โดยทั่วไป) หรือการวิเคราะห์เรขาคณิตแบบยูคลิด (โดยเฉพาะ) อย่างไรก็ตามฉันได้พบว่าวิธีที่ง่ายที่สุดในการจัดการกับการหมุน (และการแปลและ rescalings) ในระนาบคือการดูคะแนนเป็นจำนวนเชิงซ้อน: การหมุนจะดำเนินการโดยการคูณค่าด้วยจำนวนความยาวหน่วย
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 หากคุณถามว่ามันสามารถรองรับสี่เหลี่ยมได้หรือไม่ ฉันเพิ่งลองมันบนจตุรัสหน่วย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

ขอบคุณสำหรับคำตอบ. ใช่มันใช้งานได้ดี แต่ฉันพยายามบังคับให้มันสร้างสี่เหลี่ยม (ด้านข้างที่มีมุม 90 องศาสำหรับแต่ละด้าน) เสมอ 4 ด้านเหนือรูปหลายเหลี่ยม 4 ด้านอื่น ๆ แม้ว่าในบางกรณีมันจะสร้างรูปสี่เหลี่ยมดูเหมือนไม่ เป็นข้อ จำกัด คงที่คุณรู้วิธีการแก้ไขรหัสเพื่อเพิ่มข้อ จำกัด นี้หรือไม่? ขอบคุณ!
Nader Alexan

บางทีgis.stackexchange.com/a/22934/48041อาจแนะนำคุณถึงวิธีแก้ปัญหาเนื่องจากคำตอบของพวกเขาดูเหมือนจะมีข้อ จำกัด นี้หรือไม่? เมื่อคุณหาวิธีแก้ไขแล้วคุณควรมีส่วนร่วมเพราะฉันมั่นใจว่าคนอื่นจะเห็นว่ามีประโยชน์ โชคดี!
JesseBuesking

7

มีเครื่องมือใน Whitebox GAT ( http://www.uoguelph.ca/~hydrogeo/Whitebox/ ) ที่เรียกว่า Minimum Bounding Box เพื่อแก้ไขปัญหานี้ นอกจากนี้ยังมีเครื่องมือฮัลล์นูนต่ำสุดด้วย เครื่องมือหลายอย่างในกล่องเครื่องมือรูปร่างของแพทช์เช่นการวางแนวของแพทช์และการยืดตัวจะขึ้นอยู่กับการค้นหากล่องขอบต่ำสุด

ป้อนคำอธิบายรูปภาพที่นี่


4

ฉันเจอชุดข้อความนี้ในขณะที่มองหาโซลูชัน Python สำหรับขอบเขตสี่เหลี่ยมผืนผ้าขั้นต่ำ

นี่คือการดำเนินการของฉันซึ่งผลลัพธ์ได้รับการยืนยันด้วย Matlab

รหัสการทดสอบนั้นรวมอยู่ในรูปหลายเหลี่ยมอย่างง่ายและฉันใช้มันเพื่อหากล่องขอบเขตและแกนขั้นต่ำ 2D สำหรับ 3D PointCloud


คำตอบของคุณถูกลบไปแล้วหรือยัง?
Paul Richter

@ พอลริชเตอร์เห็นได้ชัดว่า แหล่งที่มาอยู่ที่นี่github.com/dbworth/minimum-area-bounding-rectangleแม้ว่า
sehe

3

ขอบคุณ @ คำตอบของ whuber มันเป็นทางออกที่ดี แต่ช้าสำหรับคลาวด์จุดใหญ่ ฉันพบว่าconvhullnฟังก์ชั่นในแพ็คเกจ R geometryนั้นเร็วกว่ามาก (138 วินาทีเทียบกับ 0.03 วินาทีสำหรับ 20,000 คะแนน) ฉันวางรหัสของฉันที่นี่สำหรับทุกคนที่น่าสนใจสำหรับการแก้ปัญหาได้เร็วขึ้น

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)   

สองวิธีได้คำตอบเดียวกัน (ตัวอย่างเช่น 2,000 คะแนน):

ป้อนคำอธิบายรูปภาพที่นี่


เป็นไปได้หรือไม่ที่จะขยายการใช้งานนี้ไปยังพื้นที่ 3 มิติ (เช่นค้นหากล่องระดับเสียงขั้นต่ำซึ่งรวมคะแนนทั้งหมดที่กำหนดไว้ในพื้นที่ 3 มิติ)
Sasha

0

ฉันเพียงแค่แนะนำฟังก์ชั่นบิวด์อินของ OpenCV minAreaRectซึ่งพบว่าสี่เหลี่ยมผืนผ้าที่หมุนได้ของพื้นที่ต่ำสุดที่ล้อมรอบชุดจุดอินพุท 2D หากต้องการดูวิธีใช้งานฟังก์ชั่นนี้อาจอ้างถึงบทช่วยสอนนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.