我从显示球轨迹的电影中提取了一组点。 每个点的坐标 (x,y) 对应于从电影中提取的图像中球的位置,以及对应于时间帧的 z 坐标。 下面的最终表示是在同一个 3D 图形中渲染的逐帧提取的所有点的总和。
这是一张没有“时间”数据的图片: 这是相机的角度。
问题是我还捕获了背景中玩家的一些运动(但由于它是平面图像,因此坐标没有提供玩家位于背景中的信息),甚至捕获了背景中另一个场地上的一些其他球后退 ! 但是当你从所有这些点的 3 维角度观察时,你仍然可以非常清楚地识别出主球的轨迹是什么,以及噪音是什么(球员的移动、后面比赛场地中的另一个球等)。 ..)
我希望能够对这些(x,y,时间戳)点进行聚类以消除噪音。 但到目前为止我使用的工具没有给出任何结果:meanshift、DBSCAN 不起作用。自组织映射或 Kohonen 网络都不是。 无论我改变什么参数,结果都不满意。
你知道为什么我的做法没有达到预期的结果吗?有没有更合适的方法来聚类 3D 数据?
这是我从电影中提取的一组数据:
http://arbalette.hopto.org/images/forums/shoot_prise_8_objets_detectes.json
图片已发
是第 2 次拍摄,但您有 10 次拍摄。 每个检测到的物体(球和噪声)都在“objects”对象中,X,Y 是坐标,frameNb 是时间(我图片中的 z 轴)
我尝试过的(例如使用 DBSCAN)如下:
with open("objets_detectes.json") as json_file:
objets = json.load(json_file)
for shoot in objets:
positions_spatiales = []
for objet in objets[shoot]["objets"]:
point = [objets[shoot]["objets"][objet]["x"], objets[shoot]["objets"][objet]["y"], objets[shoot]["objets"][objet]["frameNb"]]
positions_spatiales.append(point)
positions_spatiales = np.array(positions_spatiales)
#DBSCAN
dbscan = DBSCAN(eps=30, min_samples=10)
clusters = dbscan.fit_predict(positions_spatiales)
unique_clusters = np.unique(clusters)
然后,我用 matplotlib 绘制它以查看每次拍摄的结果以及失败......
fig = plt.figure()
plt.title('Tir ' + str(shoot) + ', relation position/temps - analyse du bruit et clustering des points')
ax = fig.add_subplot(111, projection='3d')
color_indice = 0
for cluster in unique_clusters:
x = []
y = []
z = []
cluster_points = positions_spatiales[clusters == cluster]
for point in cluster_points:
x.append(point[0])
y.append(point[1])
z.append(point[2])
ax.scatter(x, y, z, c=[replace_by_unique_color_by_cluster], label='Cluster ' + str(cluster), marker='o')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('temps')
plt.show(block=True)
您可以在这里看到,绘制了具有以下参数的 3 个不同结果:我们可以清楚地看到主球的轨迹未被识别(1 个颜色 = 1 个已识别的簇)。
dbscan = DBSCAN(eps=30, min_samples=10)
eps = 30:
dbscan = DBSCAN(eps=70, min_samples=10)
eps = 70:
dbscan = DBSCAN(eps=120, min_samples=10)
eps = 120:
到目前为止,我有以下答案,不是很好,但它暗示需要调整
eps
和点数才能获得最佳值:
读取数据和包:
import json
import pandas as pd
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import time
with open("shoot_prise_8_objets_detectes.json") as json_file:
objets = json.load(json_file)
shoots = []
positions_spatiales = []
x = []
y = []
area = []
frameNumber = []
for shoot in objets:
for objet in objets[shoot]["objets"]:
x.append(objets[shoot]["objets"][objet]["x"])
y.append(objets[shoot]["objets"][objet]["y"])
area.append(objets[shoot]["objets"][objet]["area"])
frameNumber.append(objets[shoot]["objets"][objet]["frameNb"])
shoots.append(shoot)
df = pd.DataFrame(columns = ["shoots", "x", "y", "frame number"])
df["shoots"] = shoots
df["x"] = x
df["y"] = y
df["area"] = area
df["frame number"] = frameNumber
机器学习部分:
for shoot in df["shoots"].unique():
#print("processing shoot "+str(shoot)+"...")
subDf = df[df["shoots"] == shoot]
#print(subDf) # printing
dataForClustering = subDf[["x","y","frame number"]] # get data for clustering
scaler = StandardScaler() # scale
scaledDataForClustering = scaler.fit_transform(dataForClustering) # get scaled data
#print("_____________________________________________") # niceness
#print(scaledDataForClustering) # print to check
# adjust as necessary...
eps = 1
min_samples = 3
dbscan = DBSCAN(eps=eps, min_samples=min_samples)
#print("_____________________________________________")
clusters = dbscan.fit_predict(scaledDataForClustering) # predict clusters
clusters_shifted = clusters + 1 # dshit for np.bincount
cluster_counts = np.bincount(clusters_shifted) # get count of clusters
most_common_cluster = np.argmax(cluster_counts) # find the cluster with the highest count
clusters[clusters != most_common_cluster - 1] = -1 # assign all but non most frequent cluster as -1
clusters[clusters == most_common_cluster - 1] = 1 # assign most frequent cluster as 1
# till the end is for plotting...
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for cluster in np.unique(clusters):
if cluster == -1:
label = "noise"
else:
label = "ball"
clusterPoints = subDf[clusters == cluster]
ax.scatter(clusterPoints['x'], clusterPoints['y'], clusterPoints['frame number'], label=f'Cluster {label}')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Frame Number')
ax.set_title('Shoot number: '+str(shoot))
ax.legend()
fig.canvas.draw()
plt.savefig("soccer/shoot"+str(shoot)+".png", dpi = 330)
time.sleep(0.1)
简单来说,我将最常见的簇指定为信号(非噪声),将其他所有簇指定为噪声。午餐后我会改进..这是迄今为止的结果(对于第一次拍摄,所有拍摄看起来都很相似):