凹包求解之滚球法-程序员宅基地

技术标签: 凹包  计算几何算法与实现  

滚球法的思想最初源自于从二维点集重建平面形状的问题,后来笔者将这个思想用于做平面曲线的简化。

例如,下面的平面点集,如何构建其分布区域表示的大致轮廓形状呢?

算法思路的来源-Graham扫描

       笔者最初想到的一个从求凸包的Graham Scan算法衍生出来的一个方法。求凸包的Graham Scan算法先找到一个Y最低的点作为起始点,然后使用叉积角度判断的方法去判断点的走向,最后在栈内留下了凸包的点序列。具体的算法讲解与代码,网上一搜各种有,这里就不详细表述。本文要介绍的方法也是和Graham Scan法从同一个出发点出发,通过扫描角度来确定下一个点。具体算法流程从下面的图文说明可以大致看出来:

首先要实现这个算法,需要对随机的一个点查询距离其几何距离在R内的所有点,即求所谓的R邻域。这个R邻域算法是KNN(K-nearest neighbors)算法的一个变形,可以在小于O(n2)的复杂度下求取R领域,本文为了简单起见没有实现这个基于K-d Tree的算法,感兴趣的读者可以查阅相关资料了解这个算法。本文涉及的应用场景因为点数目不大,所以使用的方法没有过多考虑效率,实现R邻域的方式是先建立一个n*n的二维数组存储所有点两两之间的距离,然后遍历一次二维数组,为所有的点建立一个R邻域列表,该列表中存储了对应点R邻域的索引值,这个列表很有用,下面要介绍的滚球法也利用了这个列表。

  实际上不难理解,假设AB为凹包的一个边,那么其下一个点C必然是在B的R邻域点中选择。我们实现这个算法的关键,就是为AB边找一个合适的C点。这里笔者设想的寻找C的方法使用如下原则:

  1. 假如B的R领域除了A就只有一个点,那么那个点就是C。
  2. 以B为圆心从向量BA出发转圈扫描,遇到的第一个点为C。

  这里涉及到一个小算法,即所谓的极坐标方向排序问题,这个问题的描述是:给定一个原点P和一个初始方向n,如何为平面上的点集S排序,使得点集中的点P1,P2...PN与P的连接是按从n开始的逆时针排列的。这个问题搜一搜stackoverflow即得到一个很好的解答,这个链接里一个人给出一个用于比较的函数,一旦有了比较函数,排序也不成问题,这个比较函数在后面的方法中会出现。其具体的比较原理如果对向量的点积叉积有所了解也不难理解。这里不妨提一下点集叉积的结果符号的几何含义:

  1. 向量a与b的点集结果为一个实数,计算方式是ax*bx+ay*by,满足交换率,为正值代表ab夹角小于90度,为锐角,负值代表夹角大于90度(谈夹角的话是指0-180度范围),为钝角。
  2. 向量a与b的叉积结果为一个向量,其数值的计算方法是ax*bx-ay*by,不满足交换率,为正值代表向量b处于向量a的逆时针方向(即a逆时针转一个小于180的角能转到b),负值代表向量b处于向量a的顺时针方向(即a顺时针转一个小于180的角能转到b,非要逆时针转则必然超过180度)。

  那么找C点的第二条实现的方式就类似于对一个数组找最小值那样,通过比较找到最小的角度,这个有最小角偏向的就是C点。不过遗憾的是这个思路其实是问题的,测试表明对一些点集采用这个方法会有BUG出现。例如当C点出现在BA向量小于90度的方向时,形成的BC边和AB边具有大致相反的方向,会导致下一步的寻点出现逆向,故这个思路不算是一个成功的思路,不过失败是成功之母,它却引出另一个滚球法的思路,相比这个思路具有更好的鲁棒性。

滚球法

        对于任何点集,我们把这些点想象为钉在平面上的钉子。假如拿一个半径大于一定值的球去从边界接近这个钉群,我们可以用这个球在这些钉子群的边界滚一圈,每次滚动,球都能接触到两个钉子而被卡住。

  这个思路要求一个合法的R,R太小就没法形成一个闭合的图形。由于我们讨论问题的初衷是要形成一个合适的多边形而不是0个或多个,这样对R的选择就应该有一个限制,太小的R必然出不了结果,这里姑且假设给的R值是合适的。此过程若形成一个多边形,则多变形的最长的边一定小于球的直径。所以算法输入参数为R,意味着拿一个半径为R/2的圆去滚。如下图显示了这个滚球的过程:

 

       我们不难发现一个性质,对于任何一次翻滚,假设从弦DE翻转到新弦EF,圆不可能扫过任何的其他点,因为假如扫到其他点,必然会被这个点所卡住,那么新弦就不可能是EF了。这样我们只需对极坐标排序后的E点的R邻域依次寻找符合不覆盖其他点的圆即可。

这个算法的执行过程和滚边法相似,算法结构都是先找起始点,然后循环寻找下一个点,核心问题也是从边DE线段出发找新线段EF,只是不再使用边去扫,而是用圆去扫。这里先给出这个算法的大致步骤:

  1. 先像求凸包那样求出一个Y值最小(Y值相同则取X最大)的点,作为初始点A,此点一定在凹包上。
  2. 从此点出发,(0,-1)为基准向量,先找一个小于R的边作为初始边,这时这个点即为B,此时一个半径为R/2的圆就卡在了AB上,此时第一个弦AB就找到了。
  3. 循环寻找接下来的弦,假如上一条弦为DE,则下一条弦必然从E开始,连接到E的一个R领域内的点F。寻找F可以使用如下的原则:先对E的R领域的点,以E为中心EO向量为基准进行极坐标方向排序,之后依次为R领域点F0~FN建立以EFi为弦的圆,然后检查其中是否包含其他F0~FN的点,若不存在,则EFi即为新弦,则跳出循环。
  4. 依次找到所有的弦,直到找不到新弦或遇到以前已经作为弦的点为止。

 一旦R值给的比较好,这个过程一定能给出一个闭合的图形。下图为滚球法执行过程的图片示例:

滚球法应用于平面多段线

前处理

对于平面多段线,由于其连续两点的连线代表了边,边已经表示了图形的轮廓,因此需要对边进行足够密的采样。采样间距一般小于球半径即可。

效率优化

在滚球法的每一步,我们需要计算下一步可能滚到的点,也就是R领域中的点,如果搜索所有点,那就复杂度太高了,时间复杂度将达到O(N2)。

但是如果注意到这样一条性质:

对于平面简单曲线,在滚球法的每一步,下一个滚到的点,不可能越过下一个凸包上的点。

根据这个性质,我们就只需要搜索,本次滚到的点到下一个凸包点之间的点即可。这样大大减少了搜索区间,实测时间复杂度降低到接近线性。

效果实例

如下两幅图中的示例,绿色线表示原图,黄色点表示最后滚到的点,粉色线表示最后的简化结果。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u013279723/article/details/108085334

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签