You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
4.2 KiB
112 lines
4.2 KiB
import csv
|
|
from datetime import datetime
|
|
|
|
import cv2
|
|
import numpy as np
|
|
import mediapipe as mp
|
|
from mediapipe.tasks import python
|
|
from mediapipe.tasks.python import vision
|
|
|
|
# データ保存用バッファ [(対象領域, Yaw値, Pitch値, Roll値)]
|
|
data_log = []
|
|
check_areas = ['left upper', 'center', 'right upper', 'left lower', 'right lower']
|
|
yaw, pitch, roll = 0, 0, 0
|
|
current_area_index = 0
|
|
|
|
# 1. クラスのセットアップ (FaceLandmarkerに変更)
|
|
BaseOptions = python.BaseOptions
|
|
FaceLandmarker = vision.FaceLandmarker
|
|
FaceLandmarkerOptions = vision.FaceLandmarkerOptions
|
|
VisionRunningMode = vision.RunningMode
|
|
|
|
# 2. 顔ランドマーカーの設定
|
|
# 変換行列(顔の向きデータ)を出力するようにフラグを立てます
|
|
options = FaceLandmarkerOptions(
|
|
base_options=BaseOptions(model_asset_path='face_landmarker.task'),
|
|
running_mode=VisionRunningMode.VIDEO,
|
|
output_facial_transformation_matrixes=True, # 顔の向き取得に必須
|
|
num_faces=1 # 検出する顔の最大数
|
|
)
|
|
|
|
cap = cv2.VideoCapture(0)
|
|
|
|
# FaceLandmarkerのインスタンスを生成
|
|
with FaceLandmarker.create_from_options(options) as landmarker:
|
|
while cap.isOpened():
|
|
success, frame = cap.read()
|
|
if not success:
|
|
break
|
|
|
|
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|
frame_timestamp_ms = int(cap.get(cv2.CAP_PROP_POS_MSEC))
|
|
|
|
# 稀にタイムスタンプが0や重複になるのを防ぐ安全策
|
|
if frame_timestamp_ms == 0:
|
|
frame_timestamp_ms = 1
|
|
|
|
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb_frame)
|
|
|
|
# 3. 検出の実行
|
|
result = landmarker.detect_for_video(mp_image, frame_timestamp_ms)
|
|
|
|
now_area = check_areas[current_area_index]
|
|
|
|
text_now_area = f"Checking Area: {now_area}"
|
|
cv2.putText(frame, text_now_area, (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
|
|
|
|
# 4. 変換行列から顔の向き(オイラー角)を計算
|
|
if result.facial_transformation_matrixes:
|
|
for matrix in result.facial_transformation_matrixes:
|
|
# 4x4の変換行列から、左上の3x3の回転行列のみを抽出
|
|
rot_mat = matrix[:3, :3]
|
|
|
|
# OpenCVを使って回転行列をオイラー角(度数法)に分解
|
|
angles, _, _, _, _, _ = cv2.RQDecomp3x3(rot_mat)
|
|
|
|
# ピッチ(上下), ヨー(左右), ロール(傾き)の取得
|
|
# ※カメラや座標系によって軸の順番は変わる場合があります
|
|
pitch = angles[0]
|
|
yaw = angles[1]
|
|
roll = angles[2]
|
|
|
|
# 画面上に角度を描画
|
|
text_pitch = f"Pitch (Up/Down): {pitch:.1f}"
|
|
text_yaw = f"Yaw (Left/Right): {yaw:.1f}"
|
|
text_roll = f"Roll (Tilt): {roll:.1f}"
|
|
|
|
cv2.putText(frame, text_pitch, (20, 70), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
|
|
cv2.putText(frame, text_yaw, (20, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
|
|
cv2.putText(frame, text_roll, (20, 130), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
|
|
|
|
cv2.imshow('Area of Interest Record', frame)
|
|
|
|
current_yaw = yaw if yaw is not None else 0.0
|
|
current_pitch = pitch if pitch is not None else 0.0
|
|
current_roll = roll if roll is not None else 0.0
|
|
|
|
# 'r'が押されたら記録し,次の領域へ
|
|
if cv2.waitKey(1) & 0xFF == ord('r'):
|
|
print(f"Checked Area: {now_area}")
|
|
data_log.append([
|
|
now_area,
|
|
round(current_yaw, 2),
|
|
round(current_pitch, 2),
|
|
round(current_roll, 2)
|
|
])
|
|
current_area_index += 1
|
|
|
|
if current_area_index >= len(check_areas):
|
|
print('All Area Checked.')
|
|
break
|
|
|
|
if cv2.waitKey(1) & 0xFF == ord('q'):
|
|
break
|
|
|
|
cap.release()
|
|
cv2.destroyAllWindows()
|
|
|
|
filename = f"AOI_record_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
|
|
with open(filename, 'w', newline='', encoding='utf-8') as f:
|
|
writer = csv.writer(f)
|
|
writer.writerow(["Area", "Yaw", "Pitch", "Roll"])
|
|
writer.writerows(data_log) |