画出两个多边形并且计算它们的并交比iou
import numpy as np
import shapely
from shapely.geometry import Polygon, MultiPoint # 多边形
from PIL import Image, ImageDraw, ImageFont
import math
from graham_scan import graham_scan
def calulate_iou(line1, line2):
# 代码来源:https://blog.csdn.net/u012433049/article/details/82909484
# 只能计算凸边形的iou
a = np.array(line1).reshape(len(line1)//2, 2) # 四边形二维坐标表示
poly1 = Polygon(a).convex_hull # python四边形对象,会自动计算四个点,最后四个点顺序为:左上 左下 右下 右上 左上
print(Polygon(a).convex_hull) # 可以打印看看是不是这样子
b = np.array(line2).reshape(len(line2)//2, 2)
poly2 = Polygon(b).convex_hull
print(Polygon(b).convex_hull)
union_poly = np.concatenate((a, b)) # 合并两个box坐标,变为8*2
# print(union_poly)
# print(MultiPoint(union_poly).convex_hull) # 包含两四边形最小的多边形点
if not poly1.intersects(poly2): # 如果两四边形不相交
iou = 0
else:
try:
inter_area = poly1.intersection(poly2).area # 相交面积
print("inter_area:", inter_area)
# union_area = MultiPoint(union_poly).convex_hull.area # 包含两个四边形最小多边形的面积,第一种算法
union_area = poly1.area + poly2.area - inter_area # 两四边形并集,第二种常见算法
print("union_area:", union_area)
if union_area == 0:
iou = 0
iou = float(inter_area) / union_area
"""
源码中给出了两种IOU计算方式:
第一种:交集部分/包含两个四边形最小多边形的面积
第二种:交集 / 并集(常见矩形框IOU计算方式)
"""
except shapely.geos.TopologicalError:
print('shapely.geos.TopologicalError occured, iou set to 0')
iou = 0
# print(a)
print("iou:", iou)
return [iou, inter_area, union_area]
def draw_iou_in_image(figure1, figure2, iou, image_path):
# 来源:https://blog.csdn.net/pursuit_zhangyu/article/details/80410939?utm_source=distribute.pc_relevant.none-task
image = Image.open(image_path)
# 创建一个可以在给定图像上绘图的对象
draw = ImageDraw.Draw(image)
figure1 = graham_scan(figure1)
figure2 = graham_scan(figure2)
draw.polygon([i for i in figure1], outline=(255, 0, 0))
draw.polygon([i for i in figure2], outline=(0, 255, 255))
# 添加文字参考:https://blog.csdn.net/jacke121/article/details/88060686
font = ImageFont.truetype(font="consola.ttf", size=15, encoding="unic") # 设置字体,设置大小
draw.text((image.size[0]*0.7, image.size[0] * 0.05), u"iou={:.4f}".format(iou[0]), (255, 0, 0), font)
draw.text((image.size[0] * 0.7 - 20, image.size[0] * 0.05+20), u"inter_area={:.1f}".format(iou[1]), (255, 255, 0), font)
draw.text((image.size[0] * 0.7 - 20, image.size[0] * 0.05+40), u"union_area={:.1f}".format(iou[2]), (0, 255, 0), font)
# ImageDraw.Draw.text参数:第一个元组(x,y)是标注位置,第二个是内容,第三个是字体颜色,第四个是字体。image.size[0]图片宽,[1]为高,
image.show()
image.save("./man_detected.png") # 很奇怪,不能保存为jpg
image_path = 'man.jpg'
# figure1 = [180, 190, 306, 196, 304, 284, 190, 275] # 四边形四个点坐标的一维数组表示,[x1,y1,x2,y2....]
# figure2 = [142, 100, 331, 117, 332, 295, 126, 312]
figure1 = np.random.randint(100, 400, size=16, dtype=np.int)
figure2 = np.random.randint(100, 400, size=16, dtype=np.int)
iou = calulate_iou(figure1, figure2)
draw_iou_in_image(figure1, figure2, iou, image_path)
"""
author:UryWu
time :2020年2月18日12点29分
bug1:
ink = self.draw.draw_ink(ink, self.mode)
TypeError: function takes exactly 1 argument (3 given)
solution:我的pillow为6.2.1版,降为6.1后正常。pip install pillow==6.1
bug2:
raise IOError("cannot write mode %s as JPEG" % im.mode)
OSError: cannot write mode RGBA as JPEG
solution:不能保存为jpg,只好改为png.
"""
graham_scan.py
import math
def get_bottom_point(points):
"""
返回points中纵坐标最小的点的索引,如果有多个纵坐标最小的点则返回其中横坐标最小的那个
:param points:
:return:
"""
min_index = 0
n = len(points)
for i in range(0, n):
if points[i][1] < points[min_index][1] or (
points[i][1] == points[min_index][1] and points[i][0] < points[min_index][0]):
min_index = i
return min_index
def sort_polar_angle_cos(points, center_point):
"""
按照与中心点的极角进行排序,使用的是余弦的方法
:param points: 需要排序的点
:param center_point: 中心点
:return:
"""
n = len(points)
cos_value = []
rank = []
norm_list = []
for i in range(0, n):
point_ = points[i]
point = [point_[0] - center_point[0], point_[1] - center_point[1]]
rank.append(i)
norm_value = math.sqrt(point[0] * point[0] + point[1] * point[1])
norm_list.append(norm_value)
if norm_value == 0:
cos_value.append(1)
else:
cos_value.append(point[0] / norm_value)
for i in range(0, n - 1):
index = i + 1
while index > 0:
if cos_value[index] > cos_value[index - 1] or (
cos_value[index] == cos_value[index - 1] and norm_list[index] > norm_list[index - 1]):
temp = cos_value[index]
temp_rank = rank[index]
temp_norm = norm_list[index]
cos_value[index] = cos_value[index - 1]
rank[index] = rank[index - 1]
norm_list[index] = norm_list[index - 1]
cos_value[index - 1] = temp
rank[index - 1] = temp_rank
norm_list[index - 1] = temp_norm
index = index - 1
else:
break
sorted_points = []
for i in rank:
sorted_points.append(points[i])
return sorted_points
def vector_angle(vector):
"""
返回一个向量与向量 [1, 0]之间的夹角, 这个夹角是指从[1, 0]沿逆时针方向旋转多少度能到达这个向量
:param vector:
:return:
"""
norm_ = math.sqrt(vector[0] * vector[0] + vector[1] * vector[1])
if norm_ == 0:
return 0
angle = math.acos(vector[0] / norm_)
if vector[1] >= 0:
return angle
else:
return 2 * math.pi - angle
def coss_multi(v1, v2):
"""
计算两个向量的叉乘
:param v1:
:param v2:
:return:
"""
return v1[0] * v2[1] - v1[1] * v2[0]
def graham_scan(points):
points = [[points[i*2], points[i*2+1]] for i in range(len(points)//2)]
# print("Graham扫描法计算凸包")
bottom_index = get_bottom_point(points)
bottom_point = points.pop(bottom_index)
sorted_points = sort_polar_angle_cos(points, bottom_point)
m = len(sorted_points)
if m < 2:
print("点的数量过少,无法构成凸包")
return
stack = []
stack.append(bottom_point)
stack.append(sorted_points[0])
stack.append(sorted_points[1])
for i in range(2, m):
length = len(stack)
top = stack[length - 1]
next_top = stack[length - 2]
v1 = [sorted_points[i][0] - next_top[0], sorted_points[i][1] - next_top[1]]
v2 = [top[0] - next_top[0], top[1] - next_top[1]]
while coss_multi(v1, v2) >= 0:
stack.pop()
length = len(stack)
top = stack[length - 1]
next_top = stack[length - 2]
v1 = [sorted_points[i][0] - next_top[0], sorted_points[i][1] - next_top[1]]
v2 = [top[0] - next_top[0], top[1] - next_top[1]]
stack.append(sorted_points[i])
list1 = []
for i in stack:
for j in i:
list1.append(j)
return list1