当前位置: 首页 > article >正文

最小有向包围盒——2D平面

目录

介绍

主要步骤

代码

__init__.py

min_bounding_rect.py

min_rect.py

qhull_2d.py

结果


介绍

最小有向包围盒算法广泛应用于多个领域,包括:

  • 计算几何:用于分析点集的边界特征。
  • 图形学:用于碰撞检测和物体包围。
  • 数据科学:在聚类分析、异常值检测等场景中用于计算数据的紧致边界。

本文提出了一种基于凸包和最小矩形算法的二维最小有向包围盒计算方法,并展示了如何通过代码实现这一算法。

主要步骤

  1. 凸包计算:使用 qhull2D 方法计算输入点集的凸包。这一步可以确保我们找到点集的外部边界,以此作为最小矩形的候选边界。
  2. 最小矩形计算:利用 minBoundingRect 算法计算最小有向包围盒。算法返回矩形的宽度、高度、角点坐标和面积。

程序结构如下:

调用入口:

输入为numpy格式的n*2数组。

代码

__init__.py

from .min_rect import *
from .qhull_2d import *
from .min_bounding_rect import *

min_bounding_rect.py

import numpy as np
import sys

def minBoundingRect(hull_points_2d):
    # Compute edges (x2-x1, y2-y1)
    edges = np.zeros((len(hull_points_2d)-1, 2))  # empty 2 column array
    for i in range(len(edges)):
        edge_x = hull_points_2d[i+1, 0] - hull_points_2d[i, 0]
        edge_y = hull_points_2d[i+1, 1] - hull_points_2d[i, 1]
        edges[i] = [edge_x, edge_y]

    # Calculate edge angles   atan2(y/x)
    edge_angles = np.zeros(len(edges))  # empty 1 column array
    for i in range(len(edge_angles)):
        edge_angles[i] = np.arctan2(edges[i, 1], edges[i, 0])

    # Check for angles in 1st quadrant
    edge_angles = np.abs(edge_angles % (np.pi/2))  # want strictly positive answers

    # Remove duplicate angles
    edge_angles = np.unique(edge_angles)

    # Test each angle to find bounding box with smallest area
    min_bbox = (0, sys.maxsize, 0, 0, 0, 0, 0, 0)  # rot_angle, area, width, height, min_x, max_x, min_y, max_y
    # print("Testing", len(edge_angles), "possible rotations for bounding box... \n")
    for i in range(len(edge_angles)):
        # Create rotation matrix to shift points to baseline
        R = np.array([[np.cos(edge_angles[i]), np.cos(edge_angles[i]-(np.pi/2))],
                      [np.cos(edge_angles[i]+(np.pi/2)), np.cos(edge_angles[i])]])

        # Apply this rotation to convex hull points
        rot_points = np.dot(R, np.transpose(hull_points_2d))  # 2x2 * 2xn

        # Find min/max x,y points
        min_x = np.nanmin(rot_points[0], axis=0)
        max_x = np.nanmax(rot_points[0], axis=0)
        min_y = np.nanmin(rot_points[1], axis=0)
        max_y = np.nanmax(rot_points[1], axis=0)

        # Calculate height/width/area of this bounding rectangle
        width = max_x - min_x
        height = max_y - min_y
        area = width * height

        # Store the smallest rect found first
        if area < min_bbox[1]:
            min_bbox = (edge_angles[i], area, width, height, min_x, max_x, min_y, max_y)

    # Re-create rotation matrix for smallest rect
    angle = min_bbox[0]
    R = np.array([[np.cos(angle), np.cos(angle-(np.pi/2))],
                  [np.cos(angle+(np.pi/2)), np.cos(angle)]])

    # Project convex hull points onto rotated frame
    proj_points = np.dot(R, np.transpose(hull_points_2d))  # 2x2 * 2xn

    # min/max x,y points are against baseline
    min_x = min_bbox[4]
    max_x = min_bbox[5]
    min_y = min_bbox[6]
    max_y = min_bbox[7]

    # Calculate center point and project onto rotated frame
    # center_x = (min_x + max_x) / 2
    # center_y = (min_y + max_y) / 2
    # center_point = np.dot([center_x, center_y], R)

    # Calculate corner points and project onto rotated frame
    corner_points = np.zeros((4, 2))  # empty 2 column array
    corner_points[0] = np.dot([max_x, min_y], R)
    corner_points[1] = np.dot([min_x, min_y], R)
    corner_points[2] = np.dot([min_x, max_y], R)
    corner_points[3] = np.dot([max_x, max_y], R)

    return  min_bbox[2], min_bbox[3], corner_points,area

min_rect.py

import numpy as np
import matplotlib.pyplot as plt
from .qhull_2d import qhull2D
from .min_bounding_rect import minBoundingRect

def min_area_rect(xy_points):
    # Find convex hull
    hull_points = qhull2D(xy_points)

    # Reverse order of points, to match output from other qhull implementations
    hull_points = hull_points[::-1]

    # print('Convex hull points: \n', hull_points, "\n")

    # Find minimum area bounding rectangle
    width, height, corner_points,area = minBoundingRect(hull_points)


    # # Visualization
    plt.figure()
    plt.plot(xy_points[:, 0], xy_points[:, 1], 'o', label='Points')
    plt.plot(hull_points[:, 0], hull_points[:, 1], 'r--', lw=2, label='Convex Hull')

    # Plot the bounding box
    box = np.vstack([corner_points, corner_points[0]])  # Close the box by repeating the first point
    plt.plot(box[:, 0], box[:, 1], 'g-', lw=2, label='Min Bounding Box')


    plt.legend()
    plt.xlabel('X')
    plt.ylabel('Y')
    plt.title('Convex Hull and Minimum Area Bounding Box')
    plt.grid(True)
    plt.axis('equal')
    plt.show()

    return  width, height,corner_points,area

qhull_2d.py

from __future__ import division
from numpy import *


def link(a, b):
    return concatenate((a, b[1:]))


def edge(a, b):
    return concatenate(([a], [b]))


def qhull2D(sample):
    def dome(sample, base, depth=0, max_depth=1000):
        h, t = base
        dists = dot(sample - h, dot(((0, -1), (1, 0)), (t - h)))

        if len(dists) == 0:
            return base

        # Handle cases where all distances are very small
        if all(abs(dists) < 1e-10):
            return base

        outer = sample[dists > 0]
        # print("dists:",dists,"outer:",outer)
        # Handle empty outer case
        if len(outer) == 0:
            return base

        pivot_index = argmax(dists)
        pivot = sample[pivot_index]

        # Ensure depth does not exceed maximum allowed
        if depth > max_depth:
            return base

        # Recursive case
        left_dome = dome(outer, edge(h, pivot), depth + 1, max_depth)
        right_dome = dome(outer, edge(pivot, t), depth + 1, max_depth)

        return link(left_dome, right_dome)

    if len(sample) > 2:
        axis = sample[:, 0]
        base = take(sample, [argmin(axis), argmax(axis)], axis=0)
        left_dome = dome(sample, base)
        right_dome = dome(sample, base[::-1])

        return link(left_dome, right_dome)
    else:
        return sample

结果


http://www.kler.cn/a/421587.html

相关文章:

  • arkTS:持久化储存UI状态的基本用法(PersistentStorage)
  • ElasticSearch easy-es 聚合函数 group by 混合写法求Top N 词云 分词
  • 详细了解索引规约
  • 用postgresql实现数组中的模糊字符串查询
  • vscode + conda + qt联合开发
  • Brain.js(二):项目集成方式详解——npm、cdn、下载、源码构建
  • 【机器学习】CatBoost 模型实践:回归与分类的全流程解析
  • commitlint——Git提交规范
  • HTMLCSS 创意工坊:卡片网格的鼠标魔法秀
  • dns实验3:主从同步-完全区域传输
  • 蓝桥杯准备训练(lesson1,c++方向)
  • WebGL vendor [显卡]指纹
  • getchar()
  • L16.【LeetCode笔记】前序遍历
  • tp6 合成两个pdf文件(附加pdf或者替换pdf)
  • 力扣hot100道【贪心算法后续解题方法心得】(三)
  • idea的version control
  • SpringBoot 监听Redis键过期事件 过期监听
  • 在macOS上从源码部署RAGFlow-0.14.1
  • centos新建磁盘
  • 网络安全 社会工程学 敏感信息搜集 密码心理学攻击 密码字典生成
  • 40分钟学 Go 语言高并发:内存管理与内存泄漏分析
  • 前端 vue3 + element-plus + ts 组件通讯,defineEmits,子传父示例
  • Neo4j APOC-01-图数据库 apoc 插件介绍
  • 使用OpenCV和卡尔曼滤波器进行实时活体检测
  • LearnOpenGL学习(光照 -- 颜色,基础光照,材质,光照贴图)