mediapipe의 포즈추정을 이용한 포즈 분류 model을 제작하는데 사용할
skeleton angle calculator(관절 각도 계산기)를 사용해보려한다.
mediapipe에서 사용된 blaze pose를 이용해서 33개의 landmarks 좌표값을 뽑아낼 수 있다.
33개의 관절 좌표 중 관절각도를 계산하려면 3개 관절의 좌표를 이용해서 사잇각을 구해줄 수 있다.
해당 사잇각을 pose마다 main이 되는 신체 관절의 사잇각으로 활용하여
pose classification 성능을 높여보려고 한다.
포즈 분류의 기준
1. skeleton landmarks의 좌표 값이 일치할 때 분류
2. 주요 관절의 각도가 기준점을 충족할 때 분류
1. pose classification 모델 생성
#classification
# 추출한 skeleton landmarks 좌표 x,y,z 값을 input으로 활용
inputs = tf.keras.layers.Input(shape=(99))
layer = tf.keras.layers.Dense(64, activation='relu')(inputs)
layer = tf.keras.layers.Dense(32, activation='relu')(layer)
output = tf.keras.layers.Dense(4, activation='softmax')(layer)
model = tf.keras.models.Model(inputs, output)
model.compile(loss='categorical_crossentropy', optimizer=tf.keras.optimizers.Adam(), metrics=['accuracy'])
model.summary()
일반적인 backbone classification 모델에 skeleton data를 train시켜서 image 혹은 video가 들어올 때
대상 image/video의 사람 skeleton landmarks가 model에 train된 값을 나타낸다면 자세를 분류해줄 수 있다.
skeleton landmarks의 일치성으로 포즈를 분류해줄 수 있지만,
포즈 분류를 보다 세밀하게 확정해주기 위해 model에만 분류를 맡기지 않고
skeleton data의 angle을 계산하여 한번 더 후처리하여 더 컴팩트한 분류를 하기 위함이다.
2. skeleton angle calculator
x, y, z 3개의 landmark 사이의 각도를 계산할 수 있는 함수를 생성한다.
첫 번째 landmark는 첫번째 라인 시작점으로 간주하고,
두 번째 landmark는 첫번째 라인 끝점과 두번째 라인 시작점으로 간주하고,
세 번째 landmark는 첫번째 라인의 끝점으로으로 간주된다.
# 앵글 계산 함수
def calculateAngle(landmark1, landmark2, landmark3):
# Get the required landmarks coordinates.
x1, y1, _ = landmark1
x2, y2, _ = landmark2
x3, y3, _ = landmark3
# Calculate the angle between the three points
angle = math.degrees(math.atan2(y3 - y2, x3 - x2) - math.atan2(y1 - y2, x1 - x2))
# Check if the angle is less than zero.
if angle < 0:
# Add 360 to the found angle.
angle += 360
# Return the calculated angle.
return angle
# 함수 실행
# Calculate the angle between the three landmarks.
angle = calculateAngle((558, 326, 0), (642, 333, 0), (718, 321, 0))
# Display the calculated angle.
print(f'The calculated angle is {angle}')
POSE 마다 분류를 판단할 메인 각도 정하기
포즈 | 포즈 예시 | 메인 각도 |
Lunge | - 왼다리 사잇각 - 오른다리 사잇각 |
|
Pushup | - 왼팔/오른팔 사잇각 | |
Squat | - 왼다리/오른다리 사잇각 | |
Jumping jack |
|
- 왼팔/오른팔 사잇각 |
# 분류 함수
def classifyPose(landmarks, output_image, display=False):
# Initialize the label of the pose. It is not known at this stage.
label = 'Unknown Pose'
# Specify the color (Red) with which the label will be written on the image.
color = (0, 0, 255)
# Calculate the required angles.
#----------------------------------------------------------------------------------------------------------------
# Get the angle between the left shoulder, elbow and wrist points.
# 11번, 13번, 15번 landmark
# 왼쪽 어깨, 왼쪽 팔꿈치, 왼쪽 손목 landmark angle 값 계산
left_elbow_angle = calculateAngle(landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value],
landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value],
landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value])
# 12번, 14번, 16번 landmark
# 오른쪽 어깨, 오른쪽 팔꿈치, 오른쪽 손목 landmark angle 값 계산
right_elbow_angle = calculateAngle(landmarks[mp_pose.PoseLandmark.RIGHT_SHOULDER.value],
landmarks[mp_pose.PoseLandmark.RIGHT_ELBOW.value],
landmarks[mp_pose.PoseLandmark.RIGHT_WRIST.value])
# 13번, 15번, 23번 landmark
# 왼쪽 어깨, 왼쪽 팔꿈치, 왼쪽 엉덩이, landmark angle 값 계산
left_shoulder_angle = calculateAngle(landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value],
landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value],
landmarks[mp_pose.PoseLandmark.LEFT_HIP.value])
# 12번, 14번, 24번 landmark
# 오른쪽 어깨, 오른쪽 팔꿈치, 오른쪽 엉덩이 landmark angle 값 계산
right_shoulder_angle = calculateAngle(landmarks[mp_pose.PoseLandmark.RIGHT_HIP.value],
landmarks[mp_pose.PoseLandmark.RIGHT_SHOULDER.value],
landmarks[mp_pose.PoseLandmark.RIGHT_ELBOW.value])
# 23번, 25번, 27번 landmark
# 왼쪽 엉덩이, 왼쪽 무릎, 왼쪽 발목 landmark angle 값 계산
left_knee_angle = calculateAngle(landmarks[mp_pose.PoseLandmark.LEFT_HIP.value],
landmarks[mp_pose.PoseLandmark.LEFT_KNEE.value],
landmarks[mp_pose.PoseLandmark.LEFT_ANKLE.value])
# 24번, 26번, 28번 landmark
# 오른쪽 엉덩이, 오른쪽 무릎, 오른쪽 발목 landmark angle 값 계산
right_knee_angle = calculateAngle(landmarks[mp_pose.PoseLandmark.RIGHT_HIP.value],
landmarks[mp_pose.PoseLandmark.RIGHT_KNEE.value],
landmarks[mp_pose.PoseLandmark.RIGHT_ANKLE.value])
#----------------------------------------------------------------------------------------------------------------
# 전사포즈인지 확인
#----------------------------------------------------------------------------------------------------------------
# 한쪽다리를 곧게 뻗고 있는지 확인한다.
if left_knee_angle > 165 and left_knee_angle < 195 or right_knee_angle > 165 and right_knee_angle < 195:
# 한쪽다리를 곧게 뻗고 있다면
# 다른쪽 다리를 90도 ~ 120 사이로 굽히고 있는지 확인한다.
if left_knee_angle > 90 and left_knee_angle < 120 or right_knee_angle > 90 and right_knee_angle < 120:
# 양팔을 일자로 펼치고, 한쪽 다리는 곧게, 다른쪽 다리는 굽히고 있다면
# 전사자세로 분류한다.
label = 'Warrior II Pose'
#----------------------------------------------------------------------------------------------------------------
# T포즈인지 확인
#----------------------------------------------------------------------------------------------------------------
# 양 다리 모두 곧게 뻗고 있는지 확인한다.
if left_knee_angle > 160 and left_knee_angle < 195 and right_knee_angle > 160 and right_knee_angle < 195:
# 양팔을 일자로 펼치고, 양쪽다리 모두 일자로 펴고 있다면
# T포즈로 분류한다
label = 'T Pose'
#----------------------------------------------------------------------------------------------------------------
return output_image, label
3D XYZ 좌표 3개의점 사잇각 구하기
def calculate_3d(point1, point2, point3):
# Calculate the vectors between the left hand, elbow, and shoulder landmarks
point_1_2 = [(point2[i] - point1[i]) for i in range(3)]
point_2_3 = [(point2[i] - point3[i]) for i in range(3)]
# Calculate the dot product and the magnitudes of the vectors
dot_product = sum([point_1_2[i] * point_2_3[i] for i in range(3)])
point_1_2_mag = math.sqrt(sum([coord**2 for coord in point_1_2]))
point_2_3_mag = math.sqrt(sum([coord**2 for coord in point_2_3]))
# Calculate the angle between the left hand, elbow, and shoulder landmarks in degrees
angle = math.degrees(math.acos(dot_product / (point_1_2_mag * point_2_3_mag)))
return angle
첫 번째 단계는 입력 포인트를 사용하여 두 벡터를 계산하는 것이다.
벡터를 계산하는데 각 포인트 1, 2, 3의 좌표를 이용하여 계산하게 되는데, POINT2가 점을 이어주는 중간 지점이기때문에 point1과 point2 사이의 벡터와 point2와 point3 사이의 벡터를 계산한다.
두번째는 계산된 두 벡터의 내적을 계산하는 것이다.
내적은 벡터의 좌표 곱의 합을 나타내는 것으로 두 벡터의 x 좌표를 곱하고 더한 다음 두 벡터의 y 좌표를 곱하고 더하고 마지막으로 두 벡터의 z 좌표를 곱하여 계산한다.
세 번째는 두 벡터의 크기를 계산하여 각도 계산식에 활용하는 단계이다.
point1과 point2 사이의 벡터 크기와 point2와 point3 사이의 벡터 크기를 계산하고, 내적값에 2 벡터 크기를 곱한 값을 나눈 뒤 역코사인 함수를 사용하면 3D 좌표계 3점 사이의 angle 각도를 구할 수 있다.
- 벡터 크기 : √(x^2 + y^2 + z^2)
- 각도 = acos(dot_product / (크기1 * 크기2))
def calculate_angle(pose_results):
# 오른팔 정의값
rhand_index = mp.solutions.pose.PoseLandmark.RIGHT_WRIST.value
relbow_index = mp.solutions.pose.PoseLandmark.RIGHT_ELBOW.value
rshoulder_index = mp.solutions.pose.PoseLandmark.RIGHT_SHOULDER.value
# 왼팔 정의값
lhand_index = mp.solutions.pose.PoseLandmark.LEFT_WRIST.value
lelbow_index = mp.solutions.pose.PoseLandmark.LEFT_ELBOW.value
lshoulder_index = mp.solutions.pose.PoseLandmark.LEFT_SHOULDER.value
# Get the world landmarks
landmarks = pose_results.pose_world_landmarks
# Get the 3D coordinates of left
lhand_coords = landmarks.landmark[lhand_index].x, landmarks.landmark[lhand_index].y, landmarks.landmark[lhand_index].z
lelbow_coords = landmarks.landmark[lelbow_index].x, landmarks.landmark[lelbow_index].y, landmarks.landmark[lelbow_index].z
lshoulder_coords = landmarks.landmark[lshoulder_index].x, landmarks.landmark[lshoulder_index].y, landmarks.landmark[lshoulder_index].z
# Get the 3D coordinates of right
rhand_coords = landmarks.landmark[rhand_index].x, landmarks.landmark[lhand_index].y, landmarks.landmark[lhand_index].z
relbow_coords = landmarks.landmark[relbow_index].x, landmarks.landmark[lelbow_index].y, landmarks.landmark[lelbow_index].z
rshoulder_coords = landmarks.landmark[rshoulder_index].x, landmarks.landmark[lshoulder_index].y, landmarks.landmark[lshoulder_index].z
# 왼쪽팔 / 오른쪽팔
left_arm_angle = calculate_3d(lhand_coords, lelbow_coords, lshoulder_coords)
print('left_arm_Angle: {:.2f} degrees'.format(left_arm_angle))
right_arm_angle = calculate_3d(rhand_coords, relbow_coords, rshoulder_coords)
print('right_arm_Angle: {:.2f} degrees'.format(right_arm_angle))
# 왼쪽 어깨 / 오른쪽 어깨
left_shoulder_angle = calculate_3d(lelbow_coords, lshoulder_coords, lhip_coords)
print('left_shoulder_angle: {:.2f} degrees'.format(left_shoulder_angle))
right_shoulder_angle = calculate_3d(relbow_coords, rshoulder_coords, rhip_coords)
print('right_shoulder_angle: {:.2f} degrees'.format(right_shoulder_angle))
return left_arm_angle, right_arm_angle, left_shoulder_angle, right_shoulder_angle
위 코드는 pose 마다 메인이되는 신체 관절 부위의 각도를 구하고 return해주는 함수이다.
'머신러닝딥러닝 > 딥러닝' 카테고리의 다른 글
Batch Normalization VS Layer Normalization (0) | 2023.05.18 |
---|---|
h5파일 TFlite 변환하기, h5파일 keras 파일로 변환하기 (0) | 2023.05.16 |
[딥러닝] 심층신경망 은닉층 패션아이템 분류 - 혼자공부하는 딥러닝 (0) | 2022.12.01 |
[딥러닝] 인공신경망 패션아이템 분류 - 혼자공부하는 딥러닝 (0) | 2022.11.30 |
코사인 유사도(Cosine Similarity)vs 유클라디안 유사도(Euclidean Similarity)vs 자카드 유사도(Jaccard Similarity) (0) | 2022.11.28 |
댓글