[낙상 감지 프로젝트] 2024년 12월 17일(화)
https://so-fast.tistory.com/entry/%EB%82%99%EC%83%81-%EA%B0%90%EC%A7%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-2024%EB%85%84-12%EC%9B%94-16%EC%9D%BC%EC%9B%94 [낙상 감지 프로젝트] 2024년 12월 16일(월)전체 요약 12.16(월) - YOLOv8-pose
so-fast.tistory.com
전체 요약
12.20(금)
- 영상파일 -> json추출 후 관심랜드마크 설정 후 이상치, 속도값 확인하기
- 시각화 이상치 처리 정의
- 낙상, 비낙상, 위험 3가지의 임계값을 정한 후 클래스 정의
다음에 진행할것 :
위 3가지 클래스 정의를 코드로 구현 후 영상에서 확인할 수 있도록 시각화해보기
▶ 시각화 코드를 기반으로 json파일 추출
import cv2
import json
import numpy as np
from ultralytics import YOLO
# YOLO 모델
model = YOLO('yolov8n-pose.pt')
# 동영상 경로
input_video = '/content/00015_H_A_SY_C5.mp4'
output_video = '/content/output_yolo_track.mp4'
json_output = '/content/output_yolo.json'
cap = cv2.VideoCapture(input_video)
out = cv2.VideoWriter(
output_video,
cv2.VideoWriter_fourcc(*'mp4v'),
int(cap.get(cv2.CAP_PROP_FPS)),
(int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
)
# 랜드마크 정의 (코, 왼쪽 어깨, 오른쪽 어깨)
INTERESTED_LANDMARKS = [0, 5, 6]
landmarks_data = []
frame_idx = 0
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
results = model.track(frame, persist=True)
for result in results:
frame = result.plot()
# JSON저장 파일 양식
frame_data = {"frame_idx": frame_idx}
if result.keypoints is not None and result.keypoints.xyn.numpy().size > 0:
for idx in INTERESTED_LANDMARKS:
frame_data[f'landmark_{idx}_x'] = float(result.keypoints.xyn[0][idx][0])
frame_data[f'landmark_{idx}_y'] = float(result.keypoints.xyn[0][idx][1])
landmarks_data.append(frame_data)
out.write(frame)
frame_idx += 1
cap.release()
out.release()
with open(json_output, 'w') as f:
json.dump(landmarks_data, f, indent=4)
print(f'** 동영상 처리 완료 **')
코, 왼쪽어깨, 오른쪽 어깨를 기준으로 잡아서 낙상의 포인트를 감지해보려고 했습니다.
여기서 json.dump(landmarks_data, f, indent=4) 는 json파일에 공백을 추가해서
사용자가 보기 편한(사람이보기편하게) 해주는 파라미터입니다.

확실히 파라미터를 주지 않았을때보다 잘 보이기때문에 추후 데이터 확인하기도 편했다.
↑는 추출한 json파일입니다.
▶ json파일로 속도값 변수를 구하여 박스플롯으로 시각화 하기
속도값 정의 x, y에 코 , 양쪽 어깨를 더한후 3을 나누어 평균을 잡은 후 중심점을 계산합니다. 그러면 전체적인 중심이동을 계산할 수 있고 현재프레임, 이전프레임간의 x,y좌표 차이를 계한 수 피타고라스 정의를 적용한 뒤 제곱근을 취합니다. 그래서 speed가 크다면 객체가 빠르게 움직이고 있음을 의미합니다. |
# 낙상 임계값 추출 코드
import json
import pandas as pd
# JSON 파일 로드
data = json.load(open('/content/output_yolo.json'))
df = pd.json_normalize(data)
df['center_x'] = (df['landmark_0_x'] + df['landmark_5_x'] + df['landmark_6_x']) / 3
df['center_y'] = (df['landmark_0_y'] + df['landmark_5_y'] + df['landmark_6_y']) / 3
df['speed'] = (df['center_x'].diff()**2 + df['center_y'].diff()**2)**0.5
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# 박스플롯 그리기
plt.figure(figsize=(5, 7))
sns.boxplot(y=df['speed'])
plt.title('Boxplot of Speed')
plt.ylabel('Speed')
plt.show()

여기서 코좌표가 잡히지 않아 속도의 변화가 너무 커서 이상치, 극한치가 너무 크게 생기는것이 문제였습니다. 분명 영상이라 스무스하게 가야될텐데 왜 튀는값이있는지 확인을 해봤습니다.
중심좌표 산출 기준 재설정 (전/후)

speed 변수를 오름차순으로 확인
df.sort_values('speed', ascending=False)

확인해보니 frame_idx = 173이 7.21초고 frame_idx = 172가 7.17인것을 확인했는데
앞 프레임으로 잡아도 될지를 한번 시각화해보기로 했습니다.
▶ 몇초에 구간인지, 어떻게 이상치를 처리할지 고민해보기
어떤부분인지 영상으로 확인하기 위해서 프레임번호를 넣으면 영상에서의 몇초인지를 확인해주고 그 부분을 이미지로 보여주는 코드를 작성했습니다.
import cv2
from google.colab.patches import cv2_imshow # Colab에서 이미지를 표시하기 위한 패치
# 동영상 파일 경로
video_path = '/content/00015_H_A_SY_C5.mp4'
# 동영상 읽기
cap = cv2.VideoCapture(video_path)
# FPS 가져오기
fps = cap.get(cv2.CAP_PROP_FPS)
if not fps or fps == 0:
print("FPS 값을 가져올 수 없습니다.")
cap.release()
exit()
# 특정 프레임 번호를 기반으로 시간 계산
frame_idx = 164 # 예: 이상치 프레임 번호
time_in_seconds = frame_idx / fps
print(f"프레임 {frame_idx}은 동영상에서 {time_in_seconds:.2f}초에 해당합니다.")
# 특정 시간에 해당하는 프레임 가져오기
time_in_seconds = 6.83 # 예: 보고 싶은 특정 시간 (초)
frame_idx = int(time_in_seconds * fps)
print(f"시간 {time_in_seconds}초에 해당하는 프레임 번호: {frame_idx}")
# 해당 프레임으로 이동
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_idx)
# 프레임 읽기
ret, frame = cap.read()
if ret:
# Colab에서 이미지를 표시
cv2_imshow(frame)
# 프레임 저장
output_path = '/content/frame_at_6.83_seconds.jpg'
cv2.imwrite(output_path, frame)
print(f"프레임이 저장되었습니다: {output_path}")
else:
print("해당 프레임을 읽을 수 없습니다.")
cap.release()
![]() |
![]() |
frame_idx = 173 / 7.21초 | frame_idx = 172 / 7.17초 |
이렇게 하면 사실 전 프레임도 못잡는건 같기때문에 이전프레임으로 가져오는것은 의미가 크게 없을것이라고 판단했습니다.
그래서 코를 중심으로 잡는것보다는 양 어깨를 잡아서 이상치를 조금 완화시켜보자 로 변경했습니다.
▶ 양 어깨에서 관절값이 안 잡히는 부분 확인

관절값이 0인부분(누락)은 없어야하니까 describe()로 확인해보니 다행이게도 누락값은 발견되지 않았습니다.
▶ 양 어깨의 중앙값으로만 박스플롯 시각화
import json
import pandas as pd
# JSON 파일 로드
data = json.load(open('/content/output_yolo.json'))
df = pd.json_normalize(data)
df['center_x'] = (df['landmark_5_x'] + df['landmark_6_x']) / 2
df['center_y'] = (df['landmark_5_y'] + df['landmark_6_y']) / 2
df['speed'] = (df['center_x'].diff()**2 + df['center_y'].diff()**2)**0.5
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# 박스플롯 그리기
plt.figure(figsize=(5, 7))
sns.boxplot(y=df['speed'])
plt.title('Boxplot of Speed')
plt.ylabel('Speed')
plt.show()

이렇게본다면 갑자기 너무 튀는값도 줄어들었고 이정도의 결과에서
낙상,비낙상,위험의 임계값을 정할 수 있을것같아서 랜드마크는 양쪽어깨인 5번 6번으로 작성하기로 했습니다.
다음시간에 이어서 할 부분은
3개의 클래스를의 임계값을 설정하고 영상에 적용(시각화)를 해보려고합니다.
'Project > 낙상' 카테고리의 다른 글
[낙상 감지 프로젝트] 2025년 2월 22일 (토) _프로젝트 완성 (0) | 2025.02.22 |
---|---|
[낙상 감지 프로젝트] 2025년 2월 15일 (토) (0) | 2025.02.15 |
[낙상 감지 프로젝트] 2024년 12월 31일(화) (1) | 2024.12.31 |
[낙상 감지 프로젝트] 2024년 12월 17일(화) (1) | 2024.12.17 |
[낙상 감지 프로젝트] 2024년 12월 16일(월) (0) | 2024.12.16 |