AI 탐구노트

Fall Detection : Mediapipe의 Pose 추정을 이용한 쓰러짐 감지 본문

DIY 테스트

Fall Detection : Mediapipe의 Pose 추정을 이용한 쓰러짐 감지

42morrow 2024. 11. 4. 16:39

1.서론

1.1.쓰러짐 감지(Fall Detection)

쓰러짐 감지(fall detection)는 주로 노인이나 신체적 약자가 갑자기 쓰러졌을 때 이를 자동으로 인식하고 알림을 보내기 위한 기술입니다. 위험 상황에서 즉각적인 대응이 가능해져, 사람들의 안전을 확보하는 데 도움이 되죠. 실제로 낙상이나 건강 상의 이상으로 쓰러졌을 때 생명까지 위태로울 수 있고 도움의 손길을 줄 수 있는 골든타임도 그리 길지 않기 때문에 최대한 빨리 감지해서 알림을 주는 것이 중요합니다. 최근에는 헬스케어, 작업장 안전, 보행자 안전 등의 용도로 큰 관심을 받고 있는 주제이기도 합니다. 이번 글에서는 쓰러짐 감지 기술에 대한 간단한 소개와 카메라 영상 분석을 통해 이를 활용하는 사례를 하나 만들어 보겠습니다. 

 

2.쓰러짐 감지 기술

2.1.쓰러짐 감지에 사용되는 기술

쓰러짐 감지 기술은 다양한 센서를 이용하고, 전용 감지 알고리즘을 이용하는 경우가 대부분인데, 대략 아래와 같은 방식이 사용됩니다. 

  • 가속도계 및 자이로스코프 등의 IoT 센서 사용 : 이 센서들은 스마트폰이나 스마트워치와 같은 기기에 흔히 탑재되어 있으며, 쓰러질 때 발생하는 급격한 가속도 변화와 자세 변화를 감지합니다. 쓰러짐 감지 알고리즘은 가속도와 자세의 급격한 변화 패턴을 분석하여 이를 인식합니다. 또는 웨어러블 기기나 스마트 홈 시스템과 연결하여 사용자 주변의 센서들이 동시에 데이터를 수집하고 분석합니다. 이를 통해 더 정확한 쓰러짐 탐지가 가능합니다.
  • AI(ML/DL) 기반 영상 처리 기반 감지 : CCTV나 IoT 카메라를 이용해 사람의 움직임과 자세 변화를 분석합니다. 머신러닝이나 딥러닝 알고리즘을 적용하여 사람의 쓰러짐을 실시간으로 탐지할 수 있으며, 다양한 쓰러짐 패턴을 학습하여 일반적인 움직임과 쓰러짐을 구분할 수도 있습니다. 

 

가속도계 등으로 센싱해서 쓰러짐을 감지할 수 있는 스마트폰이나 스마트와치, 스마트링 등이 최근에 많이 나오고 있습니다. 비용도 저렴해서 몇 만원 수준이면 구할 수도 있구요. AI 기반으로 영상분석을 하는 사례는 주로 원거리에 있는 센서 미착용  상태의 사람들을 대상으로 합니다. 

 

 

2.2.영상 기반 쓰러짐 감지의 기술

영상 기반으로 쓰러짐 감지를 하는 방법은 다양한데 대표적인 것을 들어보면 다음과 같습니다. 

  • 포즈 추정 (인체 관절 추정) : 사람의 관절 키포인트를 실시간 추적해 해당 부분의 위치들이 기울어진 정도나 땅에 가까워지는 등의 패턴을 감지하는 방식입니다. 
  • 바운딩 박스의 비율 변화 감지 : 사람으로 감지된 바운딩 박스의 세로, 가로 비율을 분석하는 방식입니다. 
  • 움직임과 속도 분석 : Optical Flow와 같은 움직임 분석 기법을 사용하며, 사람이 바닥 쪽으로 빠르게 움직이는 경우를 감지합니다. 
  • 딥러닝 기반 행동 인식 모델 : 연속된 영상 프레임에서 사람의 행동 패턴을 분석해서 쓰러짐과 다른 행동을 구분하는 방법을 학습하는 방법입니다.
  • 배경과의 위치 변화 감지 : Background subtraction 기법을 통해 진행되며 사람이 특정 구역에서 바닥에 가까운 위치로 이동하거나 오래 움직이지 않는 경우를 쓰러짐으로 판단하는 방식입니다. 

 

2.3.쓰러짐 감지 기술 적용처

쓰러짐 감지 기술은 다음과 같은 곳에 많이 사용되고 있습니다. 

  • 웨어러블 기기, 스마트폰에 탑재된 센서의 움직임을 기반으로 쓰러짐 감지. 예) 애플 와치의 긴급 구조 요청 기능
  • 가정용 스마트 IoT 시스템 : 스마트 카메라 등. 예) 독거노인 쓰러짐 감지 스마트 카메라
  • 요양시설 모니터링 시스템 : 예) 노인 침상 낙상 감지 시스템 
  • 도로 교통 모니터링 : 예) 도로 변 취객 쓰러짐 감지 및 알림 서비스 

 

3.쓰러짐 감지 테스트

가속도계나 IoT 센서의 경우는 현재 가진 것이 없으므로 카메라 영상을 기반으로, 포즈 추정 알고리즘을 이용하는 모델을 선정하고 이를 이용해 쓰러짐을 감지하고 이를 통지하는 것을 진행해 보겠습니다. 

 

3.1. 환경 구성

필요한 사전 패키지 설치

간단하게 Mediapipe를 이용해서 진행하며, 영상 처리를 위해 OpenCV가 필요합니다. 참고로 Mediapipe는 가벼워서 CPU에서도 빠르게 처리되는 장점이 있지만, 아무래도 감지성능이 떨어지고 영상 속의 1명에 대해서만 처리가 가능합니다. 이는 나중에 MoveNet 등과 같은 다른 모델을 이용해서 개선할 수 있을 것 같습니다. 

$ pip install mediapipe opencv-python

 

테스트 데이터 준비

테스트에 사용할 영상은 인터넷 상에서 찾아봅니다. 다양한 경우를 해 보는 것이 좋겠지만 앞서 언급한 mediapipe의 제약으로 인해 1명의 움직임만 있는 영상을 찾아 봤습니다. 

 

1) 무술가의 움직임 영상

아무래도 쓰러지는 액션이 많아서 다양한 포즈를 테스트해 볼 수 있을 것 같아 선택했습니다. 

 

human-fall-detection/queda.mp4 at master · Bravonoid/human-fall-detection

Real-time human fall detection system based on MoveNet model - Bravonoid/human-fall-detection

github.com

 

2) GMDCSA24: A Dataset for Human Fall Detection in Videos

쓰러짐 감지 영상 데이터셋입니다. 1명의 사람이 방 안에서 쓰러짐과 정상적인 상황을 구분할 수 있도록 학습하기 위해 만들어진 것인데 이 가운데 쓰러짐 영상을 이용해서 테스트를 진행해 봤습니다. 

 

GitHub - ekramalam/GMDCSA24-A-Dataset-for-Human-Fall-Detection-in-Videos

Contribute to ekramalam/GMDCSA24-A-Dataset-for-Human-Fall-Detection-in-Videos development by creating an account on GitHub.

github.com

 

3.2.코드

 

기본적인 요구사항은 다음과 같습니다. 

1.영상 속의 1명의 움직임에 대해 관절 키포인트를 예측하고 이를 기준으로 쓰러짐을 감지합니다. 
2.감지는 어깨, 히프, 무릎의 중간점을 잇는 라인의 각도를 이용해서 threshold 내에 있으면 쓰러짐으로 판별합니다.
3.쓰러짐이 감지되면 일정 시간동안 화면에 경고 (번쩍임, 쓰러짐 감지 텍스트)를 표시합니다. 

 

위 요구사항이 반영되어 만들어진 코드는 다음과 같습니다. 

import cv2
import mediapipe as mp
import numpy as np
import math
import time

# Mediapipe 초기화
mp_drawing = mp.solutions.drawing_utils
mp_pose = mp.solutions.pose

# 각도 계산 함수
def calculate_angle(a, b, c):
    """
    세 점 a, b, c의 각도를 계산합니다.
    a, b, c는 (x, y) 형식의 튜플입니다.
    """
    a = np.array(a)
    b = np.array(b)
    c = np.array(c)
    
    # 벡터 계산
    ba = a - b
    bc = c - b
    
    # 코사인 유사도 계산
    cosine_angle = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc) + 1e-6)
    angle = np.arccos(cosine_angle)
    
    return np.degrees(angle)

# 쓰러짐 판정 함수
def is_fall_condition_met(keypoints):
    """
    mediapipe가 감지한 keypoints를 이용해 쓰러짐 여부를 판별합니다.
    """
    try:
        # Get keypoints for shoulders, hips, and knees
        left_shoulder = keypoints[mp_pose.PoseLandmark.LEFT_SHOULDER.value]
        right_shoulder = keypoints[mp_pose.PoseLandmark.RIGHT_SHOULDER.value]
        left_hip = keypoints[mp_pose.PoseLandmark.LEFT_HIP.value]
        right_hip = keypoints[mp_pose.PoseLandmark.RIGHT_HIP.value]
        left_knee = keypoints[mp_pose.PoseLandmark.LEFT_KNEE.value]
        right_knee = keypoints[mp_pose.PoseLandmark.RIGHT_KNEE.value]

        # Ensure keypoints are within the frame (visibility > 0.5)
        if (left_shoulder.visibility < 0.5 or right_shoulder.visibility < 0.5 or
                left_hip.visibility < 0.5 or right_hip.visibility < 0.5 or
                left_knee.visibility < 0.5 or right_knee.visibility < 0.5):
            return False

        # Calculate the midpoint of shoulders and hips
        shoulder = [(left_shoulder.x + right_shoulder.x) / 2, (left_shoulder.y + right_shoulder.y) / 2]
        hip = [(left_hip.x + right_hip.x) / 2, (left_hip.y + right_hip.y) / 2]
        knee = [(left_knee.x + right_knee.x) / 2, (left_knee.y + right_knee.y) / 2]

        # Calculate the angle between shoulder-hip and hip-knee lines
        angle_shoulder_hip_knee = calculate_angle(shoulder, hip, knee)

        # If both angles are below or above certain thresholds, consider it as falling
        if (angle_shoulder_hip_knee < 30 or angle_shoulder_hip_knee > 150) :
            return False
        else:
            return True
    except:
        return False

cap = cv2.VideoCapture('queda.mp4')

# 포즈 객체 초기화
with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose:
    # 쓰러짐 상태 추적 변수
    fall_detected = False
    fall_start_time = None
    required_duration = 0.2  # 초 단위

    # 플래싱 효과를 위한 변수
    flashing = False
    last_flash_time = 0
    flash_interval = 0.5  # 초 단위
    mask_visible = False

    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            print("비디오를 불러올 수 없습니다.")
            break

        # 이미지 좌우 반전
        image = cv2.flip(frame, 1)
        image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        results = pose.process(image_rgb)

        # 초기화
        angles = {}

        if results.pose_landmarks:
            # Mediapipe 랜드마크 추출
            keypoints = results.pose_landmarks.landmark

            # 각도 판정
            fall_condition = is_fall_condition_met(keypoints)

            current_time = time.time()

            if fall_condition:
                if not fall_detected:
                    fall_start_time = current_time
                    fall_detected = True
                    flashing = False
                else:
                    elapsed_time = current_time - fall_start_time
                    if elapsed_time >= required_duration:
                        # 쓰러짐 감지 알람 텍스트 표시
                        cv2.putText(image, 'Fall Detected', (50, 50),
                                    cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
                        # 플래싱 효과 시작
                        flashing = True
            else:
                fall_detected = False
                fall_start_time = None
                # 플래싱 효과 중지
                flashing = False
                mask_visible = False

            # 플래싱 효과 구현
            if flashing:
                if current_time - last_flash_time >= flash_interval:
                    mask_visible = not mask_visible
                    last_flash_time = current_time
                if mask_visible:
                    # 붉은색 투명 마스크 생성
                    overlay = image.copy()
                    red_color = (0, 0, 255)  # BGR 형식
                    alpha = 0.4  # 투명도 (0.0 ~ 1.0)
                    cv2.rectangle(overlay, (0, 0), (image.shape[1], image.shape[0]), red_color, -1)
                    # 원본 이미지와 오버레이 이미지 합성
                    image = cv2.addWeighted(overlay, alpha, image, 1 - alpha, 0)

            # 랜드마크 그리기
            mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)

        # 화면에 이미지 표시
        cv2.imshow('Fall Detection', image)

        # 'q' 키를 누르면 종료
        if cv2.waitKey(10) & 0xFF == ord('q'):
            break

# 자원 해제
cap.release()
cv2.destroyAllWindows()

 

3.3.테스트 결과 확인

앞서 소개한 무술가의 영상을 가지고 테스트를 진행했습니다. 일부만 캡처해서 보여 드리면 다음과 같습니다. 

 

비록 '쓰러짐'은 아니지만... 그리고 판정 결과가 늘 정확한 것은 아니지만 대충은 잡아내는 것 같습니다. 현재는 몸의 중심 축이 이루는 각도만 고려한 것이라 한계가 상당히 많고 딱 적당한 영상에서만 효과를 볼 수 있을 겁니다. 실제로 쓰러짐 감지의 경우는 쓰러진 상태가 일정 시간 이상 유지된다거나 하는 조건이 더 들어가야 하고 CCTV의 위치, 보는 방향 등 다양한 조건을 감안해야 할 겁니다. 하지만, 테스트이니 이 정도 수준으로 넘어갑니다. ^^

 

3.4.개선 필요 사항

데모에서조차 몇 가지 개선사항이 보입니다.

  • 어깨, 히프, 무릎 라인 각각의 중간점을 기준으로 하고 있는데 이들 가운데 일부라도 감지가 되지 않으면 pass하고 있습니다. 완전히 쓰러지거나 일부가 폐색(가려짐)이 발생하면 감지를 제대로 못하는 문제가 있는데, 이에 대한 처리 로직이 들어가는게 필요해 보입니다. 
  • mediapipe의 제약으로 1명만 감지합니다. 여럿이 있는 영상에서는 가장 큰 개체로 대상이 자동으로 바뀌는 것 같습니다. 다수 인원을 커버할 수 있는 모델로 변경하는 것이 필요할 것 같습니다. -> (업데이트) Mediapipe로 복수의 인원에 대해 감지하는 것도 가능하다고 하니 나중에 다시 수정해 볼 생각입니다. 
  • keypoints 감지 성능이 떨어집니다. 사람이 사라진 이후에도 화면 상에 잔상이 남는 것인지 keypoint들이 감지된 것처럼 남는 경우가 있습니다. 이는 threshold 값을 조정하거나 더 성능 좋은 모델 혹은 파인튜닝을 해야할 것입니다. 

 

4.후기

이번 글에서는 영상 + 자세예측 기반으로 쓰러짐 감지를 간단하게 테스트 해 봤습니다. 늘 말씀드리지만, 제가 하는 테스트는 데모 수준으로 아하... 이렇게 동작하는거구나 하는 것을 보여드리는 것입니다. AI업체들이 제공하는 실제 서비스들은 전처리, 후처리, 파인튜닝 등등 다양한 작업들이 추가되어 높은 성능으로 제공됩니다. 

 

의도치 않은 쓰러짐 감지는 생명과 직결되는 경우가 많기 때문에 사회의 많은 분야에 도움이 될 것입니다. 최근에는 VLM 등을 통해 이미지 내의 상황을 이해하는 모델들도 나오고 있어 정확도는 이전 대비 훨씬 높아질 것 같습니다. 처리 속도와 서비스 비용에 대한 문제만 해결된다면 참 좋은 대안이 될 것 같은데... 곧 그렇게 되겠죠? ^^

 


참고정보

 

1.Yolov8 Fall Detection

Yolov8s 모델을 이용해 사람을 감지하고 감지된 사람의 바운딩박스 영역의 비율이 '세로>가로' 에서 '세로<가로'가 되면 쓰러졌다고 가정하는 간단한 방식으로 진행됩니다. 

 

 

GitHub - Tech-Watt/Fall-Detection: This repo contains code for the fall detection youtube tutorial

This repo contains code for the fall detection youtube tutorial - Tech-Watt/Fall-Detection

github.com

 

 

2.DeepFall

개인정보 보호 문제를 처리하기 위해 열화상, 깊이 카메라에서 낙상 감지하는 방식을 취한 사례입니다.

 

 

GitHub - JJN123/Fall-Detection: Non-invasive Fall Detection with Keras and Tensorflow

Non-invasive Fall Detection with Keras and Tensorflow - JJN123/Fall-Detection

github.com

 

 

 

3.Fall Detection using Pose Estimation

openpifpaf 를 기반으로 자세 예측을 이용해 쓰러짐을 감지하는 모델입니다. 

 

 

GitHub - cwlroda/falldetection_openpifpaf: Fall Detection using OpenPifPaf's Human Pose Estimation model

Fall Detection using OpenPifPaf's Human Pose Estimation model - cwlroda/falldetection_openpifpaf

github.com

 

 

 


4.Fall-Detection-with-CNNs-and-Optical-Flow

CNN과 Optical Flow를 이용한 쓰러짐 감지 방식입니다.

 

 

 

GitHub - AdrianNunez/Fall-Detection-with-CNNs-and-Optical-Flow: Repository containing the material required to reproduce the res

Repository containing the material required to reproduce the results of the paper "Vision-Based Fall Detection with Convolutional Neural Networks" - AdrianNunez/Fall-Detection-with-CNNs-a...

github.com

 

 

 

5.Human Falling Detection and Tracking

Tiny-Yolo로 사람 감지, AlphaPose로 관절 위치, 포즈 예측, ST-GCN으로 동작 예측을 수행해서 서기, 걷기, 앉기, 눕기, 일어서기, 앉기, 넘어지기 등 7가지 동작을 감지합니다. 

 

 

GitHub - GajuuzZ/Human-Falling-Detect-Tracks: AlphaPose + ST-GCN + SORT.

AlphaPose + ST-GCN + SORT. Contribute to GajuuzZ/Human-Falling-Detect-Tracks development by creating an account on GitHub.

github.com