target_parts_indexies = [5,6,7,8,9,10,11,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46]
results_dict = {n:[] for n in target_parts_indexies}
dlib の顔のパーツ番号は以下の通りとなっています。
上記で指定している番号は index となっているため、画像の番号 - 1 の値となっています。以下のコードで座標を取得します。
one_cluster_image_paths = sorted(glob.glob("/path/to/images/*.jpg")) #.jpgは仮置き。画像ファイル名は{フレーム番号}_{顔検出時の番号}.jpgとしています。
for i, path in tqdm(enumerate(one_cluster_image_paths)):
frame_no = int(path.split('/')[-1].split('_')[0])
try:
frame = cv2.imread(path)
frame = imutils.resize(frame, width=500) #frameの画像の表示サイズを整える
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #gray scaleに変換する
dets = detector(gray, 0) #grayから顔を検出
if len(dets) > 0:
parts = predictor(frame, dets[0]).parts()
for n in target_parts_indexies:
results_dict[n].append((parts[n].x, parts[n].y, frame_no))
except:
print('Error occurred : ', path)
座標の時系列処理
まずは、必要なライブラリをインポートします。
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import colors as mcolors
import random
import math
以下のコードを実行し、顔のパーツが大きく縦移動したフレームを抽出します。
colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)
fig = plt.figure()
ax = Axes3D(fig)
violate_z = {}
for i in results_dict.keys():
x = [p[0] for p in results_dict[i]]
y = [p[1] for p in results_dict[i]]
z = [p[2] for p in results_dict[i]]
ax.plot(x, y, z, color='blue')
moving_average_x, moving_average_y = x[0], y[0]
ave_x = sum(x)/len(x)
ave_y = sum(y)/len(y)
std_x = math.sqrt((sum([xi**2 for xi in x]) / len(x)) - ave_x**2)
std_y = math.sqrt((sum([yi**2 for yi in y]) / len(y)) - ave_y**2)
for x, y, z in zip(x, y, z):
if (abs(y - moving_average_y) > std_x * 2) and (abs(x - moving_average_x) < std_y * 2):
# 縦(y座標)だけが大きく動いた点を抽出します。
# 「大きく動いた」の判定は移動平均と座標の差が、標準偏差2つ分以上である場合としています。
ax.scatter([x,], [y,], [z,], color='yellow')
if z in violate_z.keys():# 各フレームごとに大きく動いたパーツの数を記録します。
violate_z[z] += 1
else:
violate_z[z] = 1
# 移動平均は10フレーム分として、既存の移動平均 : 新座標 = 9 : 1として移動平均を算出
moving_average_x = (moving_average_x * 9 / 10) + (x / 10)
moving_average_y = (moving_average_y * 9 / 10) + (y / 10)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
# 表示
plt.show()
agg_dict = {}
for k, v in zip(violate_z_df[violate_z_df[0] > 5].index, violate_z_df[0]):
k = int(k/60)
if k in agg_dict.keys():
agg_dict[k] += v
else:
agg_dict[k] = v
以下のコードより時系列で描画してみます。
plt.bar(
[k for k in range(min(agg_dict.keys()), max(agg_dict.keys()), 1)],
[agg_dict[k] if k in agg_dict.keys() else 0 for k in range(min(agg_dict.keys()), max(agg_dict.keys()), 1)],
1.0
)