https://github.com/google/mediapipe/blob/master/docs/solutions/holistic.md
포즈, 양 손 좌표까지 뽑는 것이 목적이었으나, 해당 공식 문서 내에 이미지와 웹캠에 대해 좌표를 뽑는 것만 나와있어서 동영상에서 좌표 뽑는 것으로 코드 수정
데이터는 ai hub 내 수어영상 데이터를 사용하였다.
1. mediapipe 설치
#mdeiapipe 다운로드
#로컬 환경에서 돌릴 경우: pip install mediapipe opencv-python
!pip install mediapipe
2. 동영상 위에서 mediapipe 돌리기 (pose만) : 예제에는 pose 좌표를 활용하는 법만 나와있었다.
import cv2
import mediapipe as mp
import numpy as np
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_pose = mp.solutions.pose
#파일 위치 지정
input_video_path = "/content/drive/MyDrive/NIA_SL_WORD1501_REAL17_F"
save_video_path = '/content/drive/MyDrive/result_NIA_SL_WORD1501_REAL17_F_onlypose.mp4'
cap = cv2.VideoCapture(input_video_path)
#재생할 파일의 넓이와 높이
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
#video controller
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
out = cv2.VideoWriter(save_video_path, fourcc, 30.0, (int(width), int(height)))
with mp_pose.Pose(
min_detection_confidence=0.7,
min_tracking_confidence=0.7) as pose:
while cap.isOpened():
success, image = cap.read()
if not success:
print("카메라를 찾을 수 없습니다.")
# 웹캠을 불러올 경우는 'continue', 동영상을 불러올 경우 'break'를 사용합니다.
break
# 필요에 따라 성능 향상을 위해 이미지 작성을 불가능함으로 기본 설정합니다.
image.flags.writeable = False
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = pose.process(image)
# 포즈 주석을 이미지 위에 그립니다.
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
mp_drawing.draw_landmarks(
image,
results.pose_landmarks,
mp_pose.POSE_CONNECTIONS,
landmark_drawing_spec=mp_drawing_styles.get_default_pose_landmarks_style())
# 보기 편하게 이미지를 좌우 반전합니다.
out.write(image)
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
out.release()
cv2.destroyAllWindows()
3. 동영상 위에서 mediapipe 돌리기 - pose, lefthand, righthand
(face까지 하려했으나, 지금의 좌표만으로도 충분하다고 생각해서 face는 X)
import cv2
import mediapipe as mp
import numpy as np
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_holistic = mp.solutions.holistic
#파일 위치 미리 지정
input_video_path = "/content/drive/MyDrive/data preprocessing/testdata/NIA_SL_WORD1501_REAL17_F.mp4"
save_video_path = '/content/drive/MyDrive/result_test_NIA_SL_WORD1501_REAL17_F_withHands.mp4'
cap = cv2.VideoCapture(input_video_path)
#재생할 파일의 넓이와 높이
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
#video controller
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
out = cv2.VideoWriter(save_video_path, fourcc, 30.0, (int(width), int(height)))
with mp_holistic.Holistic(
min_detection_confidence=0.7,
min_tracking_confidence=0.7) as pose:
while cap.isOpened():
success, image = cap.read()
if not success:
print("카메라를 찾을 수 없습니다.")
# 웹캠을 불러올 경우는 'continue', 동영상을 불러올 경우 'break'를 사용합니다.
break
# 필요에 따라 성능 향상을 위해 이미지 작성을 불가능함으로 기본 설정합니다.
image.flags.writeable = False
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = pose.process(image)
# 포즈 주석을 이미지 위에 그립니다.
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
#포즈
#코드 합쳐도 나오는지 보기, 좌표 출력해서 텍스트 or csv or json 파일 만들어보기, 로컬에서 돌리면 10gb짜리 동영상 돌렸을 때 얼마나 나오는지
mp_drawing.draw_landmarks(
image,
results.pose_landmarks,
mp_pose.POSE_CONNECTIONS,
landmark_drawing_spec=mp_drawing_styles.get_default_pose_landmarks_style())
mp_drawing.draw_landmarks(
image,
results.left_hand_landmarks,
mp_holistic.HAND_CONNECTIONS,
landmark_drawing_spec=mp_drawing_styles.get_default_hand_landmarks_style())
mp_drawing.draw_landmarks(
image,
results.right_hand_landmarks,
mp_holistic.HAND_CONNECTIONS,
landmark_drawing_spec=mp_drawing_styles.get_default_hand_landmarks_style())
# 보기 편하게 이미지를 좌우 반전합니다.
# cv2.imshow('MediaPipe Pose', image) #코랩에서 돌릴거면 imshow()문은 주석처리할 것.
out.write(image)
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
out.release()
cv2.destroyAllWindows()
Hand관련 메서드들은 https://github.com/google/mediapipe/blob/master/docs/solutions/hands.md 여기서 확인 가능
차례대로 원본, pose만, hand까지 좌표를 뽑은 동영상들이다.
4. 좌표 출력
import cv2
import mediapipe as mp
import numpy as np
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_holistic = mp.solutions.holistic
#파일 위치 미리 지정
input_video_path = "/content/drive/MyDrive/data preprocessing/testdata/NIA_SL_WORD1501_REAL17_F.mp4"
save_video_path = '/content/drive/MyDrive/result_NIA_SL_WORD1501_REAL17_F.mp4'
cap = cv2.VideoCapture(input_video_path)
#재생할 파일의 넓이와 높이
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
#video controller
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
with mp_holistic.Holistic(
min_detection_confidence=0.7,
min_tracking_confidence=0.7) as pose:
while cap.isOpened():
success, image = cap.read()
if not success:
print("카메라를 찾을 수 없습니다.") #웹캠 사용 안 할 경우 출력
# 웹캠을 불러올 경우는 'continue', 동영상을 불러올 경우 'break'를 사용합니다.
break
# 필요에 따라 성능 향상을 위해 이미지 작성을 불가능함으로 기본 설정합니다.
image.flags.writeable = False
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = pose.process(image)
if results.pose_landmarks:
print(
f'Nose coordinates: ('
f'{results.pose_landmarks.landmark[mp_holistic.PoseLandmark.NOSE].x * width}, '
f'{results.pose_landmarks.landmark[mp_holistic.PoseLandmark.NOSE].y * height})'
)
#포즈 좌표 출력
if results.pose_landmarks:
print("Pose Landmarks:")
for landmark in results.pose_landmarks.landmark:
landmark_x = landmark.x * width
landmark_y = landmark.y * height
print(f"({landmark_x}, {landmark_y})")
# #얼굴 좌표출력 -> pose에서 추출되는 얼굴 좌표만으로도 될 것 같은데
# if results.face_landmarks:
# print("Face Landmarks:")
# for landmark in results.face_landmarks.landmark:
# landmark_x = landmark.x * width
# landmark_y = landmark.y * height
# print(f"({landmark_x}, {landmark_y})")
#왼손 좌표 출력
if results.left_hand_landmarks:
print("Left Hand Landmarks:")
for landmark in results.left_hand_landmarks.landmark:
landmark_x = landmark.x * width
landmark_y = landmark.y * height
print(f"({landmark_x}, {landmark_y})")
#오른손 좌표 출력
if results.right_hand_landmarks:
print("Right Hand Landmarks:")
for landmark in results.right_hand_landmarks.landmark:
landmark_x = landmark.x * width
landmark_y = landmark.y * height
print(f"({landmark_x}, {landmark_y})")
cap.release()
out.release()
cv2.destroyAllWindows()
이런 형태로 좌표가 쭉 출력된다.
5. morpheme 파일(영상 의미가 mapping되어있는 json파일)에서 의미 뽑고, 영상에서 키포인트를 뽑아서 json파일로 저장
import json
import cv2
import mediapipe as mp
import numpy as np
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_holistic = mp.solutions.holistic
# JSON 파일에 저장할 데이터를 담을 딕셔너리 초기화
data_dict = {
"pose_keypoint": [],
"left_hand_keypoint": [],
"right_hand_keypoint": [],
"meaning": ""
}
# 동일 파일이름_morpheme.json에서 의미를 가져와서 data_dict에 추가하는 함수
def get_meaning(file_name):
morpheme_file_path = "/content/drive/MyDrive/" + file_name.split('.')[0] + "_morpheme.json"
try:
with open(morpheme_file_path, 'r') as f:
morpheme_data = json.load(f)
# 여기서는 첫 번째 attributes에 있는 첫 번째 name 값을 가져옵니다.
meaning = morpheme_data["data"][0]["attributes"][0]["name"]
return meaning
except FileNotFoundError:
print(f"Warning: {morpheme_file_path} 파일이 존재하지 않습니다.")
return None
# MediaPipe 결과에서 좌표값을 JSON 형태로 변환하여 data_dict에 추가하는 함수
def add_landmarks_to_data(landmarks, keypoint_name):
keypoints = []
for landmark in landmarks:
keypoints.append({"x": landmark.x, "y": landmark.y})
data_dict[keypoint_name] = keypoints
# 파일 경로 설정
input_video_path = "/content/drive/MyDrive/NIA_SL_WORD1501_REAL17_F.mp4"
output_json_path = '/content/drive/MyDrive/NIA_SL_WORD1501_REAL17_F_keypoints.json'
cap = cv2.VideoCapture(input_video_path)
# MediaPipe 초기화 및 동영상 불러오기
with mp_holistic.Holistic(min_detection_confidence=0.7, min_tracking_confidence=0.7) as holistic:
while cap.isOpened():
success, image = cap.read()
if not success:
print("카메라를 찾을 수 없습니다.")
break
image.flags.writeable = False
results = holistic.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
# 좌표값 추출 및 JSON에 추가
if results.pose_landmarks:
add_landmarks_to_data(results.pose_landmarks.landmark, "pose_keypoint")
if results.left_hand_landmarks:
add_landmarks_to_data(results.left_hand_landmarks.landmark, "left_hand_keypoint")
if results.right_hand_landmarks:
add_landmarks_to_data(results.right_hand_landmarks.landmark, "right_hand_keypoint")
# 동일 파일이름_morpheme.json에서 의미 가져와서 추가
file_name = input_video_path.split('/')[-1]
meaning = get_meaning(file_name)
if meaning:
data_dict["meaning"] = meaning
# JSON 파일로 저장
with open(output_json_path, 'w', encoding='utf-8') as json_file:
json.dump(data_dict, json_file, indent=4, ensure_ascii=False)
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
*한글로 의미가 되어있기에, utf-8로 인코딩을 꼭 진행해주어야한다.
이런 형태로 json파일이 잘 만들어진 것을 확인가능!