题目不知道起啥好,就先按这个题目吧。主要就是记录在日常工作中,经常遇到的一些数据处理的小点,比如不规则多边形计算质心、矩形框归类、元素距离归类等等。后续有更多的,也会不断的更新到这里的。需要的可以价格收藏
1.不规则多边形计算重心点
1.1、法1
def get_barycenter(lis):
area = 0.0
x, y = 0.0, 0.0
a = len(lis)
for i in range(a):
lat = lis[i][0] # weidu
lng = lis[i][1] # jingdu
if i == 0:
lat1 = lis[-1][0]
lng1 = lis[-1][1]
else:
lat1 = lis[i - 1][0]
lng1 = lis[i - 1][1]
fg = (lat * lng1 - lng * lat1) / 2.0
area += fg
x += fg * (lat + lat1) / 3.0
y += fg * (lng + lng1) / 3.0
x = x / area
y = y / area
return x, y
def method1():
img = np.zeros((512, 512))
# points = [[168,150],[150,154],[128,172],[116,189],[109,205],[106,236],[120,240],[135,229],[147,218],[161,209],[161,198],[168,171]]
points = [[396, 205], [388, 251], [380, 265], [363, 279], [336, 280], [318, 271], [300, 257], [288, 276], [293, 290],[298, 301], [305, 313], [335, 332], [373, 326], [390, 310], [404, 292], [409, 279], [412, 238], [407, 219],[400, 207]]
x, y = get_barycenter(np.array(points))
print(x, y)
contour = np.array([points])
contour = np.trunc(contour).astype(int)
cv2.drawContours(img, [contour], -1, color=255, thickness=-1)
cv2.circle(img, (int(x), int(y)), 5, (0, 0, 255), 1)
cv2.imshow('img', img)
cv2.waitKey()
1.2、法2
使用findContours
找到 图像轮扣后,我们需要得到轮廓的质心,那么应该怎么做呢?
如果想根据多边形的轮廓信息得到多边形的多阶矩,可以使用类moments
,这个类可以得到多边形和光栅形状的3阶以内的所有矩,类内有变量m00,m10,m01,m20,m11,m02,m30,m21,m12,m03
,比如多边形的质心为 x = m10 / m00,y = m01 / m00
。
def method2():
img = np.zeros((512, 512))
# points = [[168, 150], [150, 154], [128, 172], [116, 189], [109, 205], [106, 236], [120, 240], [135, 229],
# [147, 218], [161, 209], [161, 198], [168, 171]]
points = [[396, 205], [388, 251], [380, 265], [363, 279], [336, 280], [318, 271], [300, 257], [288, 276], [293, 290],[298, 301], [305, 313], [335, 332], [373, 326], [390, 310], [404, 292], [409, 279], [412, 238], [407, 219],[400, 207]]
contour = np.array([points])
contour = np.trunc(contour).astype(int)
mu = cv2.moments(contour, False)
x, y = mu['m10'] / mu['m00'], mu['m01'] / mu['m00']
print(x, y)
cv2.drawContours(img, [contour], -1, color=255, thickness=-1)
cv2.circle(img, (int(x), int(y)), 5, (0, 0, 255), 1)
cv2.imshow('img', img)
cv2.waitKey()
1.3、结果展示和量化
上述两个方法,打印的结果,如下所示:
对上述两种方法进行时间量化。重复100
次,计算耗时:
- 方法二用时2991ms,
- 方法一用时9967ms。
2.对多个矩形框根据IOU归类
如题,有时候会有一堆的框框,这些框框可能是同一张图的,也可能是目标追踪中不同图的连续帧。所以, 此时就会牵扯到这个问题,哪些框框之间,是有重叠的,可能就是一类的?本节就是解决这个的问题
# coding=utf-8
import numpy as np
from torch.utils.data import Dataset
"""
box type [ymin,xmin,zmin,ymax,xmax,zmax] # roipool and crop mask make diffence
"""
class tmp(Dataset):
def __init__(self) -> None:
super().__init__()
def get_iou_arr(self,arr_box_a, arr_box_b,u_a=False):
"""
计算iou矩阵;
:param arr_box_a:[...,M,4] or [M,4]
:param arr_box_b: [...,N,4] or [N,4]
:return: arr [...,M,N] or [M,N]
"""
assert arr_box_a.shape[-1] == 4 and arr_box_b.shape[-1] == 4, "a box should be described by 4 nums"
if arr_box_a.ndim < 2:
arr_box_a = arr_box_a.reshape([1, -1])
if arr_box_b.ndim < 2:
arr_box_b = arr_box_b.reshape([1, -1])
max_v = np.max((np.max(arr_box_a), np.max(arr_box_b.max())))
arr_box_a = np.expand_dims(arr_box_a, 1) / max_v
arr_box_b = np.expand_dims(arr_box_b, 0) / max_v
left_top = np.maximum(arr_box_a[..., :2], arr_box_b[..., :2])
right_down = np.minimum(arr_box_a[..., 2:], arr_box_b[..., 2:])
cover_wh = np.clip(right_down - left_top, 0, 1)
s_cover = cover_wh[..., 0] * cover_wh[..., 1]
wh_a = arr_box_a[..., 2:] - arr_box_a[..., :2]
s_a = wh_a[..., 0] * wh_a[..., 1]
wh_b = arr_box_b[..., 2:] - arr_box_b[..., :2]
s_b = wh_b[..., 0] * wh_b[..., 1]
iou = s_cover / (s_a + s_b - s_cover)
if u_a:
iou = s_cover/s_a
return iou
def merge_id(self,id1,id2):
merges = []
for i in range(id1.shape[0]):
if {id1[i],id2[i]} not in merges:
merges.append({id1[i],id2[i]})
while 1:
stopflag = 1
for s1,mi in enumerate(merges):
for mii in merges[s1+1:]:
if (mi&mii):
mi.update(mii)
merges.remove(mii)
stopflag = 0
if stopflag:
break
return merges
def box_ids(self,boxes):
ious = self.get_iou_arr(boxes,boxes,u_a=True)
for iou_idx in range(ious.shape[0]):
ious[iou_idx,iou_idx] = 0
del_id,add_id = np.where(ious>0.3)
concat_ids = self.merge_id(del_id,add_id)
for i in range(len(boxes)):
for ii in concat_ids:
if i not in ii:
concat_ids.append({i})
if not concat_ids:
concat_ids.append({i})
return concat_ids
if __name__=="__main__":
box = np.array([[122,267,182,305], [122,267,182,305], [234,248,285,297]])
t = tmp()
print(t.box_ids(box))
打印结果;
[{0, 1}, {2}]
3.根据元素间的距离关系,分成指定的几组
元素之间的距离差异,就能够将一个列表中的元素,自动的归类到不同的组合里面。本文就是解决这个问题的
import numpy as np
def cluster_oneDimensional(a_arr, center_num):
center_dist_min = 30
center = np.asarray(list(set(a_arr)))[:center_num]
while True:
centero = center # 初始化簇作为聚类中心
# cls = [[a[0]], [a[1]], [a[2]]]
cls = [[a_arr[i]] for i in range(center_num)] # 初始化簇,不断的更新该簇
print('1:', cls)
for ai in a_arr: # 遍历数组所有元素
dist = np.abs(center - ai)
print('2:', dist)
for c in cls: # 遍历初始簇
print('c:', c)
if ai in c: # 如果数组已经在初始化簇里面,就移除
c.remove(ai)
cls[dist.argmin()].append(ai)
center = np.asarray([sum(e) / len(e) for e in cls])
center_dist = center[1:] - center[:-1]
for idx, cd in enumerate(center_dist):
if cd < center_dist_min:
center[idx:] += center_dist_min
# print('3:', center, centero)
if np.all(center == centero): # 直到簇中心不再发生改变为止
# print('cls:', cls)
# print('center:', center)
# print('centero:', centero)
# break
return cls
if __name__=='__main__':
x_center_list = [220.6, 399, 300.3, 310, 400, 401, 219, 219.9, 396, 396.2]
a = np.asarray(x_center_list)
cluster_list = cluster_oneDimensional(a, center_num=3)
print(cluster_list)
打印最终结果如下:
[[300.3, 310.0], [399.0, 400.0, 401.0, 396.0, 396.2], [220.6, 219.0, 219.9]]
原提问地址:https://ask.youkuaiyun.com/questions/7616996?answer=53650243&spm=1001.2014.3001.5504
4.线段交点坐标和夹角
import numpy as np
import cv2 as cv
class Point:
x = 0
y = 0
def __init__(self, x=0, y=0):
self.x = x
self.y = y
class Line:
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
def GetLinePara(line):
a = line.p1.y - line.p2.y
b = line.p2.x - line.p1.x
c = line.p1.x * line.p2.y - line.p2.x * line.p1.y
return a, b, c
def GetCrossPoint(l1, l2):
a1,b1,c1 = GetLinePara(l1)
a2,b2,c2 = GetLinePara(l2)
d = a1 * b2 - a2 * b1
p = Point()
if d == 0: #d为0即line1和line2平行
return ()
else:
p.x = int((b1 * c2 - b2 * c1)*1.0 / d)
p.y = int((c1 * a2 - c2 * a1)*1.0 / d)
return p
def GetCrossAngle_method1(l1, l2):
v1 = np.array([(l1.p2.x - l1.p1.x), (l1.p2.y - l1.p1.y)])
v2 = np.array([(l2.p2.x - l2.p1.x), (l2.p2.y - l2.p1.y)])
arccos = np.arccos(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))
angle = np.degrees(arccos)
return np.round(angle, 1)
def GetCrossAngle_method2(l1, l2):
arr_0 = np.array([(l1.p2.x - l1.p1.x), (l1.p2.y - l1.p1.y)])
arr_1 = np.array([(l2.p2.x - l2.p1.x), (l2.p2.y - l2.p1.y)])
cos_value = (float(arr_0.dot(arr_1)) / (np.sqrt(arr_0.dot(arr_0)) * np.sqrt(arr_1.dot(arr_1))))
return np.round(np.arccos(cos_value) * (180 / np.pi), 1)
if __name__ == '__main__':
img = np.zeros((512, 512, 3))
x1, y1, x2, y2 = 343, 374, 417, 372
x3, y3, x4, y4 = 167, 59, 180, 177
cv.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv.circle(img, (int(x1), int(y1)), 5, (0, 0, 255), 1)
cv.circle(img, (int(x2), int(y2)), 5, (0, 255, 0), 1)
cv.line(img, (x3, y3), (x4, y4), (0, 0, 255), 2)
cv.circle(img, (int(x3), int(y3)), 5, (0, 0, 255), 1)
cv.circle(img, (int(x4), int(y4)), 5, (0, 255, 0), 1)
line1 = Line(Point(x1, y1), Point(x2, y2))
line2 = Line(Point(x3, y3), Point(x4, y4))
Pc = GetCrossPoint(line1, line2) # 计算得到的交点坐标
print('CrossPoint:', Pc.x, Pc.y)
cv.circle(img, (Pc.x, Pc.y), 5, (0, 0, 255), 1)
cv.line(img, (Pc.x, Pc.y), (x2, y2), (255, 0, 255), 2)
cv.line(img, (Pc.x, Pc.y), (x4, y4), (255, 0, 255), 2)
angle = GetCrossAngle_method1(line1, line2) # 计算得到的角度
cv.putText(img, (str(angle)), (Pc.x, Pc.y), cv.FONT_HERSHEY_PLAIN, 1.5, (255, 0, 0), 2)
print('angle:', angle)
cv.imshow('img', img)
cv.waitKey()
结果展示:
参考文档:https://blog.51cto.com/u_15549234/5138060