Python求凸包及多边形面积教程

一般有两种算法来计算平面上给定n个点的凸包:Graham扫描法(Graham's scan),时间复杂度为O(nlgn);Jarvis步进法(Jarvis march),时间复杂度为O(nh),其中h为凸包顶点的个数。这两种算法都按逆时针方向输出凸包顶点。

Graham扫描法

用一个栈来解决凸包问题,点集Q中每个点都会进栈一次,不符合条件的点会被弹出,算法终止时,栈中的点就是凸包的顶点(逆时针顺序在边界上)。

算法步骤如下图:

import sys
import math
import time
import random

#获取基准点的下标,基准点是p[k]
def get_leftbottompoint(p):
 k = 0
 for i in range(1, len(p)):
  if p[i][1] < p[k][1] or (p[i][1] == p[k][1] and p[i][0] < p[k][0]):
   k = i
 return k

#叉乘计算方法
def multiply(p1, p2, p0):
 return (p1[0] - p0[0]) * (p2[1] - p0[1]) - (p2[0] - p0[0]) * (p1[1] - p0[1])

#获取极角,通过求反正切得出,考虑pi/2的情况
def get_arc(p1, p0):
 # 兼容sort_points_tan的考虑
 if (p1[0] - p0[0]) == 0:
  if ((p1[1] - p0[1])) == 0:
   return -1;
  else:
   return math.pi / 2
 tan = float((p1[1] - p0[1])) / float((p1[0] - p0[0]))
 arc = math.atan(tan)
 if arc >= 0:
  return arc
 else:
  return math.pi + arc

#对极角进行排序,排序结果list不包含基准点
def sort_points_tan(p, pk):
 p2 = []
 for i in range(0, len(p)):
  p2.append({"index": i, "arc": get_arc(p[i], pk)})
 #print('排序前:',p2)
 p2.sort(key=lambda k: (k.get('arc')))
 #print('排序后:',p2)
 p_out = []
 for i in range(0, len(p2)):
  p_out.append(p[p2[i]["index"]])
 return p_out

def convex_hull(p):
 p=list(set(p))
 #print('全部点:',p)
 k = get_leftbottompoint(p)
 pk = p[k]
 p.remove(p[k])
 #print('排序前去除基准点的所有点:',p,'基准点:',pk)

 p_sort = sort_points_tan(p, pk) #按与基准点连线和x轴正向的夹角排序后的点坐标
 #print('其余点与基准点夹角排序:',p_sort)
 p_result = [pk,p_sort[0]]

 top = 2
 for i in range(1, len(p_sort)):
  #####################################
  #叉乘为正,向前递归删点;叉乘为负,序列追加新点
  while(multiply(p_result[-2], p_sort[i],p_result[-1]) > 0):
   p_result.pop()
  p_result.append(p_sort[i])
 return p_result#测试
if __name__ == '__main__':
 pass
 test_data = [(220, -100), (0,0), (-40, -170), (240, 50), (-160, 150), (-210, -150)]
 print(test_data)

 result = convex_hull(test_data)
 print(result)
 t=0

import matplotlib.pyplot as plt
x1=[]
y1=[]
for i in range(len(test_data)):
 ri=test_data[i]
 #print(ri)
 x1.append(ri[0])
 y1.append(ri[1])

plt.plot(x1,y1,linestyle=' ',marker='.')

xx=[]
yy=[]
for i in range(len(result)):
 ri=result[i]
 #print(ri)
 xx.append(ri[0])
 yy.append(ri[1])

plt.plot(xx,yy,linestyle=' ',marker='*')

计算多边形面积

(1)顺时针给定构成凸包的n个点坐标,叉乘法求多边形面积:

def GetAreaOfPolyGonbyVector(points):
 # 基于向量叉乘计算多边形面积
 area = 0
 if(len(points)<3):
  raise Exception("error")

 for i in range(0,len(points)-1):
  p1 = points[i]
  p2 = points[i + 1]

  triArea = (p1[0]*p2[1] - p2[0]*p1[1])/2
  #print(triArea)
  area += triArea

 fn=(points[-1][0]*points[0][1]-points[0][0]*points[-1][1])/2
 #print(fn)
 return abs(area+fn)

points = []
x = [1,3,2]
y = [1,2,2]
#[(1,1),(3,1),(5,3),(3,5),(1,3)]
# x=[1,3,5,3,1]
# y=[1,1,3,5,3]
for index in range(len(x)):
 points.append((x[index],y[index]))
area = GetAreaOfPolyGonbyVector(points)
print(area)
#print(math.ceil(area))

(2)顺时针给定构成凸包的n个点经纬度坐标,先将经纬度坐标转化成凸多边形的边的经纬度距离,利用海伦公式求多边形面积:

from geopy.distance import vincenty
import math
def HeronGetAreaOfPolyGonbyVector(points):
 # 基于海伦公式计算多边形面积
 area = 0
 if(len(points)<3):
  raise Exception("error")

 pb=((points[-1][0]+points[0][0])/2,(points[-1][1]+points[0][1])/2) #基准点选为第一个点和最后一个点连线边上的中点

 for i in range(0,len(points)-1):
  p1 = points[i]
  p2 = points[i + 1]

  db1 = vincenty(pb,p1).meters #根据维度转化成经纬度距离
  d12 = vincenty(p1,p2).meters
  d2b = vincenty(p2,pb).meters
  #print(db1,d12,d2b)

  hc = (db1+d12+d2b)/2 #db1是基准点和p1的距离,d12是p1和p2的距离,d2b是p2和基准点距离
  #print(hc, hc-db1, hc-d12, hc-d2b)
  triArea = math.sqrt(hc*(hc-db1)*(hc-d12)*(hc-d2b))
  #print(triArea)
  area += triArea

 return area

points = []
x = [1,3,2]
y = [1,2,2]
#[(1,1),(3,1),(5,3),(3,5),(1,3)]
# x=[1,3,5,3,1]
# y=[1,1,3,5,3]
for index in range(len(x)):
 points.append((x[index],y[index]))

area = HeronGetAreaOfPolyGonbyVector(points)
print(area)
#print(math.ceil(area))

Graham程序原理

(1)基准点的确认原则:

有唯一的某个点纵坐标最小,该点为基准点;

不止一个点的纵坐标最小,选这些点里最靠左的为基准点

(2)计算叉乘【后续利用叉乘正负判断夹角是否大于180o】:

(3)获取极角,通过求反正切得出:

若横纵坐标都相等(两点相同),返回-1;

若横坐标相等/纵坐标不相等(两点连线垂直y轴),返回

(4)对极角进行排序,排序结果list不包含基准点:

p2=[{"index":0, "arc":get_arc(p[0],p[k])},
 {"index":1, "arc":get_arc(p[1],p[k])},
 ···
 {"index":k-1, "arc":get_arc(p[k-1],p[k])},
 {"index":k+1, "arc":get_arc(p[k+1],p[k])},
 ···
 {"index":n, "arc":get_arc(p[n],p[k])}]
#get_arc(p[0],p[k])即获得p[0]点与基准点p[k]连线的极角(与x轴正向夹角)
#根据p2的“arc”键的值从小到大排序,最后输出按该角度值排序对应顺序的各个点

(5)逆时针确定凸多边形:

主要是找角度是否大于180o——差乘正负——点进出栈顺序三者关系

...一直遍历到最后一个点...一直遍历到最后一个点

规律:叉乘>0,夹角小于180o,递归向前删点;叉乘<0,夹角大于180o,不删点,加入新点,向后遍历叉乘>0,夹角小于180o,递归向前删点;叉乘<0,夹角大于180o,不删点,加入新点,向后遍历

注意:(a)上述给非基准点按极角从到大小排号时,有两个及以上点“和基准点连线构成的极角”相等时,这些点的排号挨着但是没有固定顺序,这点并不影响算法给出凸包的准确性。(b)对排号最后的一个点,扫描算法里没有任何删除该点的机制,但是这点也不影响算法给出凸包的准确性。(c)上述程序需要额外加入,判断结束栈内点数小于3和筛选凸包前点数小于3,不能计算多边形面积的情况,可以直接给这种情况赋值0返回。

以上这篇Python求凸包及多边形面积教程就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • python实现根据给定坐标点生成多边形mask的例子

    处理数据集的过程中用到了mask 但是源数据集中只给了mask顶点的坐标值,那么在python中怎么实现生成只有0.1表示的mask区域呢? 主要借鉴cv2中的方法: (我使用的数据情况是将顶点坐标分别存储在roi.mat中的x和y元素) matfn = 'roi.mat' data = sio.loadmat(matfn) x_cor = data['x'] y_cor = data['y'] im = np.zeros(图像对应尺寸, dtype="uint8") cor_xy =

  • python opencv圆、椭圆与任意多边形的绘制实例详解

    圆形的绘制 : OpenCV中使用circle(img,center,radius,color,thickness=None,lineType=None,shift=None)函数来绘制圆形 import cv2 import numpy as np image=np.zeros((400,400,3),np.uint8) cv2.circle(image,(200,200),50,(0,0,255),2) #画圆 ''' 参数2 center:必选参数.圆心坐标 参数3 radius:必选参数

  • python3射线法判断点是否在多边形内

    本文实例为大家分享了python3射线法判断点是否在多边形内的具体代码,供大家参考,具体内容如下 #!/usr/bin/python3.4 # -*- coding:utf-8 -*- def isPointinPolygon(point, rangelist): #[[0,0],[1,1],[0,1],[0,0]] [1,0.8] # 判断是否在外包矩形内,如果不在,直接返回false lnglist = [] latlist = [] for i in range(len(rangelist

  • python绘制封闭多边形教程

    数据格式:(polygon.txt) 里面含有2个多边形,一行是一个点 0.085, 0.834, 0.024, 0.744, 0, 0.63, 0.024, 0.516, 0.085, 0.427, 0.5, 0.02, 0.675, 0.191, 0.795, 0.071, 0.815, 0.052, 0.835, 0.032, 0.84, 0.026, 0.844, 0.022, 0.856, 0.012, 0.871, 0.005, 0.886, 0.001, 0.903, 0, 0.8

  • Python随机生成均匀分布在三角形内或者任意多边形内的点

    Python有一随机函数可以产生[0,1)区间内的随机数,基于此函数生成随机分布在任意三角形内的点 由数学知识得知: 几何体的向量表达形式 直线: 线段: 推广到高维 三维平面: 三角形: 注释,v这个向量表示的是在图形上的点的坐标,根据数学知识得知,直线和三维平面内的v构成的点集是放射集,而线段则是凸集, 其余向量是不在同一个点或者同一个平面的点的坐标构成的列向量 那么针对三角形可以写成如下: 我们可以先生成随机的贝塔,然后随机生成阿尔法,然后处理阿尔法,使得点是随机落在三角形内的,这里用的是

  • Python求凸包及多边形面积教程

    一般有两种算法来计算平面上给定n个点的凸包:Graham扫描法(Graham's scan),时间复杂度为O(nlgn):Jarvis步进法(Jarvis march),时间复杂度为O(nh),其中h为凸包顶点的个数.这两种算法都按逆时针方向输出凸包顶点. Graham扫描法 用一个栈来解决凸包问题,点集Q中每个点都会进栈一次,不符合条件的点会被弹出,算法终止时,栈中的点就是凸包的顶点(逆时针顺序在边界上). 算法步骤如下图: import sys import math import time

  • python Graham求凸包问题并画图操作

    python Graham求凸包并画图 python写Graham没有c++那么好写,但是python画图简单.只需要用matplotlib里的pyplot,c++画图太难了. Graham算法写起来比较简单,只需要想办法对最小点和其他的点所连成的直线,与x轴正半轴的夹角进行排序,然后其他的就直接套用Graham算法模板就好了,因为c++可以重载排序函数sort,不用计算角度(用其他的数学方法),但是python不行(也许是我不知道而已,菜). python必须要在结构体里面加上角度这个变量,然

  • Python求正态分布曲线下面积实例

    正态分布应用最广泛的连续概率分布,其特征是"钟"形曲线.这种分布的概率密度函数为: 其中,μ为均值,σ为标准差. 求正态分布曲线下面积有3σ原则: 正态曲线下,横轴区间(μ-σ,μ+σ)内的面积为68.268949%,横轴区间(μ-1.96σ,μ+1.96σ)内的面积为95.449974%,横轴区间(μ-2.58σ,μ+2.58σ)内的面积为99.730020%. 求任意区间内曲线下的面积,通常可以引用scipy包中的相关函数 norm函数生成一个给定均值和标准差的正态分布,cdf(x

  • Python求一批字符串的最长公共前缀算法示例

    本文实例讲述了Python求一批字符串的最长公共前缀算法.分享给大家供大家参考,具体如下: 思路一:这个题一拿到手,第一反应就是以第一个字符串strs[0]为标准,如果其他字符串的第一个字符和str[0]的第一个字符串相同,则再比较第二个字符串,以此类推直到出现不同为止. def longestCommonPrefix(self, strs): """ :type strs: List[str] :rtype: str """ if not st

  • Python GUI之tkinter窗口视窗教程大集合(推荐)

    一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 二.Tkinter是什么 Tkinter 是使用 python 进行窗口视窗设计的模块.Tkinter模块("Tk 接口")是Python的标准Tk GUI工具包的接口.作为 python 特定的GUI界面,是一个图像的窗口,tkinter是python 自带的,可以编辑的GUI界面,我们可以用GUI 实现很多直观的功能,比如想开发一个计算器,如果只是一个程序输入,输

  • python math模块的基本使用教程

    what's the math 模块 Python math 模块提供了许多对浮点数的数学运算函数.需要注意的是,这些函数一般是对平台 C 库中同名函数的简单封装, 所以一般情况下, 不同平台下计算的结果可能稍微地有所不同, 有时候甚至有很大出入. 主要功能有: 幂数:幂次方.平方根 对数:2.10.e相关的对数操作 圆相关:π.弧度与角度的转换 三角函数:正三角函数.反三角函数 其他常用:小数的整数部分.向上取整.向下取整.两个数的最大公约数.取余数... 幂数 幂与平方根 # pow(x,

  • Python中使用ipython的详细教程

    ipython简介 ipython他是一个非常流行的python解释器,相比于原生的python解释器,有太多优点和长处,因此几乎是python开发人员的必知必会. 1.ipython相比于原生的python有什么优势 (1) python shell不能在退出保存历史:ipython历史记录自动保存:保存在history.sqlite文件下:可用"_"."__"."___"调用最近三次记录: (2) python shell不支持tab自动补全

  • Python+OpenCV进行不规则多边形ROI区域提取

    多边形ROI,主要利用鼠标交互进行绘制: 1. 单击左键,选择多边形的点: 2. 单击右键,删除最近一次选择的点: 3. 单击中键,确定ROI区域并可视化. 4. 按”S“键,将多边形ROI区域的点保存到本地”config.pkl"文件中. 话不多说,以下是核心代码 import cv2 import numpy as np import joblib pts = [] # 用于存放点 # 统一的:mouse callback function def draw_roi(event, x, y,

  • python虚拟环境的安装配置图文教程

    使用pip安装     pip install virtualenv 因为已经安装过了,所以显示这样 在这里我想在这里推荐大家以后再安装类库时可以用豆瓣源来安装,速度很快,因为在国内访问 官方pypi源非常慢,pip安装包经常会出错 豆瓣源 我来演示下django的安装 可以看到速度很快 好了回到虚拟环境的安装 virtualenv scrapytest(环境名) 安装好后输入activate.bat进入虚拟环境后输入python查看当前python版本为2.7 退出 退出虚拟环境:输入deac

  • Python求两个list的差集、交集与并集的方法

    本文实例讲述了Python求两个list的差集.交集与并集的方法.分享给大家供大家参考.具体如下: list就是指两个数组之间的差集,交集,并集了,这个小学数学时就学过的东西,下面就以实例形式对此加以分析. 一.两个list差集 如有下面两个数组: a = [1,2,3] b = [2,3] 想要的结果是[1] 下面记录一下三种实现方式: 1. 正常的方式 复制代码 代码如下: ret = [] for i in a:     if i not in b:         ret.append(

随机推荐