我想使用两个同时且垂直的图像帧生成的数据来生成微生物的 3D 轨迹。这是去除中值(即背景)后此类视频的帧的样子:
到目前为止,我已经应用了 trackpy 的建议方法,并成功提取了与每个相机对应的数据帧,其中有作为键:
[ 0; y; x; mass; size; ecc; signal; raw_mass; ep; frame; particle]
我现在正在寻找关联两个数据帧,以便它在两个相机共有的垂直
y
位置上匹配。
我一直在操作我获得的数据帧,并使用以下代码在这些数据帧中生成了一个名为
difference
的新列:
df_camA = df_camA.sort_values(by=['particle', 'frame'])
df_camA['difference'] = df_camA.groupby('particle')['y'].diff()
对应于给定粒子在两个连续帧之间的垂直位置的差异。然后我尝试使用以下方法使每个表之间的这些值相对应:
df_camA['difference'].fillna(0, inplace=True)
df_camB['difference'].fillna(0, inplace=True)
difference_vectors_A = df_camA.groupby('particle')['difference'].apply(list)
difference_vectors_B= df_camB.groupby('particle')['difference'].apply(list)
liste_vect_A=[]
for vector in difference_vectors_A:
liste_vect_A.append(np.array(vector))
liste_vect_B=[]
for vector in difference_vectors_B:
liste_vect_B.append(np.array(vector))
i=0
for vect_A in liste_vect_A:
dist=10000
normA=np.linalg.norm(vect_A)
if normA!=0:
index_min=0
j=0
for vect_B in liste_vect_B:
if np.linalg.norm(vect_B)!=0:
diff=np.linalg.norm(vect_B-vect_A)
if diff<dist:
dist=diff
index_min=j
else:
j+=1
continue
else:
j+=1
continue
index_list.append(index_min)
else:
index_list.append(i)
i+=1
我这样做是因为两个相机的垂直位置不是绝对的,因为标高大致相同且不完全相同。
但是,由于两个摄像机之间的检测可能不同,因此阵列具有不同的长度,因为在两个数据帧中可能无法检测到相同的粒子,因此该方法似乎不起作用。
有人知道我可以做些什么来解决这个问题吗?
欢迎任何意见
因此,如果我理解正确的话,您的目标是根据两个摄像机中检测到的粒子的垂直位置进行匹配,并且由于检测不一致而出现挑战,例如粒子可能只出现在一个摄像机中,并且阵列长度的差异使得直接相关不可行。
我的建议是根据 y 坐标的接近程度逐帧匹配两个相机之间的粒子,使用定义的阈值来解决轻微的未对准问题。为此,您需要一个匹配函数来迭代帧,计算所有可能的粒子对的
y
位置之间的绝对距离,并在距离低于阈值时选择最接近的匹配。
有点,你可能有无与伦比的粒子。因此,您可以做的是还拥有一个扩展函数,包括通过附加带有缺失数据的空值的行来仅在一台摄像机中检测到的粒子。
这是我的建议(这适用于我提供的示例数据,因此请检查它是否适合您。或上传示例数据)
import pandas as pd
import numpy as np
from scipy.spatial import distance
df_camA = pd.DataFrame({
'frame': [1, 1, 2, 2],
'particle': [0, 1, 0, 1],
'y': [10, 20, 11, 21],
'x': [5, 15, 6, 16]
})
df_camB = pd.DataFrame({
'frame': [1, 1, 2],
'particle': [0, 2, 0],
'y': [10.5, 19.8, 10.8],
'x': [4.5, 14.5, 5.8]
})
def match_particles(df_camA, df_camB, threshold=1.0):
matched_data = []
for frame in df_camA['frame'].unique():
frameA = df_camA[df_camA['frame'] == frame]
frameB = df_camB[df_camB['frame'] == frame]
for _, rowA in frameA.iterrows():
closest_particle = None
min_distance = float('inf')
for _, rowB in frameB.iterrows():
dist = abs(rowA['y'] - rowB['y'])
if dist < min_distance and dist <= threshold:
min_distance = dist
closest_particle = rowB
if closest_particle is not None:
matched_data.append({
'frame': frame,
'particle_camA': rowA['particle'],
'particle_camB': closest_particle['particle'],
'y_camA': rowA['y'],
'y_camB': closest_particle['y'],
'x_camA': rowA['x'],
'x_camB': closest_particle['x']
})
return pd.DataFrame(matched_data)
def include_unmatched(df_camA, df_camB, threshold=1.0):
matched_df = match_particles(df_camA, df_camB, threshold)
unmatched_camA = df_camA[~df_camA['particle'].isin(matched_df['particle_camA'])]
unmatched_camB = df_camB[~df_camB['particle'].isin(matched_df['particle_camB'])]
unmatched_camA = unmatched_camA.assign(
particle_camB=np.nan, y_camB=np.nan, x_camB=np.nan)
unmatched_camB = unmatched_camB.assign(
particle_camA=np.nan, y_camA=np.nan, x_camA=np.nan)
return pd.concat([matched_df, unmatched_camA, unmatched_camB], ignore_index=True)
matched_df = include_unmatched(df_camA, df_camB, threshold=1.0)
print(matched_df)
这给了你
frame particle_camA particle_camB y_camA y_camB x_camA x_camB \
0 1 0.0 0.0 10.0 10.5 5.0 4.5
1 1 1.0 2.0 20.0 19.8 15.0 14.5
2 2 0.0 0.0 11.0 10.8 6.0 5.8
particle y x
0 NaN NaN NaN
1 NaN NaN NaN
2 NaN NaN NaN