olrlobt

[Pose Estimation] Mediapipe 2배속 분석과 분석 결과 중간 부족한 프레임 채우기 본문

Spring/Project

[Pose Estimation] Mediapipe 2배속 분석과 분석 결과 중간 부족한 프레임 채우기

olrlobt 2023. 4. 21. 02:40

Mediapipe 분석 시간

현재 내가 진행 중인 프로젝트에서는 Mediapipe의 분석 결과를 가지고 여러 가지 작업을 진행할 예정이다.

하지만 Mediapipe의 경우, 실시간 영상 데이터만을 활용하여 분석 결과를 보여주게 된다.

 

즉, 30초 길이의 영상의 분석 결과를 갖기 위해서는 30초간 영상을 재생하면서 데이터를 실시간으로 보내주어야만 결과 값을 얻을 수 있었다.

 

나는 이 점이 내가 만들 서비스에서 치명적인 문제가 될 수 있다고 생각했다. 사용자가 서비스를 이용할 때, 30초 영상을 사용하려면 30초를 기다려야 한다는 얘기니, 큰 불만사항이 될 수 있다.

 


 

Mediapipe로 2배속 영상 분석

 

앞서 말한, 분석 시간을 줄일 수는 없을까 고민을 해 보았다.

 

가장 먼저 떠오른 방법은 단순히 배속된 영상을 분석하면 되지 않을까?라는 생각이었다. 간단히 2배속으로 분석을 진행한다 하더라도 30초 길이의 영상을 분석하는 시간은 15초로 크게 단축될 것이라 생각한다.

 

또한, Mediapipe의 자세추적 기능은 컴퓨터 성능에 따라 차이가 나지만 최대 60 FPS까지 지원하고, 내 PC에서 테스트하여도 초당 10 ~ 15 개의 Frame 추출이 가능하기 때문에 중요한 자세는 다 잡을 수 있을 것이라 생각했다.

 

 

대강적인 아이디어가 떠오르자 바로 구현을 시작하였다.

 

영상을 2배속으로 만들기 위하여 FFmpeg 라이브러리를 사용하여 사용자가 업로드한 영상을 2배속 해 주었다.

 

FFmpeg를 사용하는 방법은 아래 포스팅을 참고하자.

https://olrlobt.tistory.com/53

 

[Spring boot] FFmpeg로 영상 배속 설정하기

FFmpeg FFmpeg는 비디오, 오디오를 처리하는 오픈소스 라이브러리이다. 대표적으로 지원하는 동작으로는 파일변환, 편집, 스트리밍, 인코딩과 디코딩 등이 있고, 비디오에서 썸네일을 자동으로 뽑

olrlobt.tistory.com

 

 

그리고는 먼저 원본 파일(1배속)로 Mediapipe 분석을 진행하였다.

 

영상은 아래와 같이 3.64초 정도 길이의 짧은 영상을 사용했고, 분석은 같은 timeStamp를 갖는 frame이 나오는 부분을 기준으로 측정하였다.

 

3.64초 영상에서의 frame 은 42개가 나왔다. 초당 frame 11개 정도로, 2배속 영상에서도 같은 속도로 측정이 된다고 했을 때,

 

2배속 분석 결과를 1배속 영상에서 사용하려고 하는 것이기 때문에 초당 5~6 frame 정도의 결과를 얻을 수 있다고 볼 수 있다. 즉, 0.2초마다 프레임이 전환된다는 이야기인데, 매우 부자연스럽게 끊겨 보일 것으로 예상된다.

 

 

이어서, FFmpeg를 통하여 2배속 한 영상을 Mediapipe로 포즈 분석을 진행하였다.

영상은 절반크기인 1.81의 길이이고, 예상 프레임은 21 정도이다.

 

 

2배속 영상에서의 frame은 예상치와 근접한 24가 나왔다. 예상 frame 수 보다 조금 더 나오긴 했지만, frame이 부족하여 끊겨 보일 수밖에 없다고 생각한다.

 

 

이 끊김 문제를 고민하면서 여러 번 테스트를 진행해 보았는데, 측정 시마다 frame 수가 다르게 측정된다는 것을 알게 되었다. 예로 다시 원본 영상의 프레임을 측정하였는데 38의 결과를 보였다.

 

이유는 분석하는 과정에서 사용하는 requestAnimationFrame()에 있었다.

function requestAnalyze(videoElement, poseModel) {
    poseModel.send({ image: videoElement });
    requestAnimationFrame(() =>
        requestAnalyze(videoElement, poseModel)
    );
}

 

requestAnimationFrame()은 성능의 영향을 많이 받는다. Frame의 업데이트 주기를 브라우저에서 어떤 요소든지 업데이트가 생기게 되면 등록된 콜백함수를 호출하게 되어있는데, 여기서 frame의 차이가 발생했다. 

 

나는 같은 영상의 데이터에서 Frame 값의 차이 없는 것을 원하기 때문에, videoElement의 currentTime을 직접 조절하여 분석을 보내기로 하였다. 이 방식을 사용하면, PC의 성능 차이와는 관계없이 frame 수가 일정하게 보장되면서, 자연스레 frame 수도 증가 될 것으로 예상했다.

 

function requestAnalyze(videoElement, canvasCtx, poseModel, loading, loading_background) {
        let videoPre = videoElement.currentTime;
        videoElement.currentTime += 50 / 1000;

        if (videoPre === videoElement.currentTime) {
            // 분석 종료
            return;
        }

        function onTimeUpdate() {
            poseModel.send({ image: videoElement });
            requestAnimationFrame(() =>
                requestAnalyze(videoElement, canvasCtx, poseModel, loading, loading_background)
            );
        }

        videoElement.addEventListener('timeupdate', onTimeUpdate, { once: true });
    }

왼쪽 - 2배속 영상 / 오른쪽 - 원본 1배속 영상

 

그 결과, 구동 환경과는 관계없이 영상의 시간에 따라 분석 결과를 저장할 수 있게 되었고, 평균적인 frame 수가 증가하였다. 또한, 몇 번의 분석을 하든지 같은 frame 수를 보장하게 되었다.

 

여기서 비디오의 분석 주기를 50/1000초보다 더 줄이면 더 큰 frame 수를 얻을 수 있겠지만 requestAnimationFrame()을 호출하는 것은 똑같기 때문에 시간을 줄이면 줄일수록 분석에 걸리는 시간이 늘어날 것이다.

 

현재의 frame수도 부족하긴 하지만, 3.6초 영상에 36개의 프레임 즉 0.1초에 단위면 어떨까 눈으로 확인하기 위하여, 결과를 출력해 보았다.

 

 

결과

결과를 눈으로 직접 확인하고 나니, frame의 부족함이 확 와닿았다. 0.1초 정도면 솔직히 괜찮은 수치이지 않을까 잠깐 생각하기도 했었는데, 예전에 게임을 할 때 30 FPS로도 끊김을 느꼈던 때를 떠 올려보니 더 많은 frame이 필요하다는 것을 깨달았다.

 

 


프레임과 프레임 사이 중간 프레임 채우기

 

더 많은 프레임을 구할 방법을 생각하다 보니, 프레임과 프레임 사이에 보정값을 넣어주면 어떨까 라는 생각이 들었다.

 

 

예를 들어 위와 같은 두 프레임이 있다면, 두 프레임에서 추출된 좌표값의 중간값을 이용해서 중간 프레임을 구할  수 있다는 생각이었다.

 

 

 

당연하게도 위의 사진에서는 중간 사진을 얻을 수 없을 것이라 생각한다. 이유는 두 사진 사이의 시간 차에 있는데, 시간 차이가 클수록 사이 움직임이 많기 때문에 중간 값을 정확하게 예상할 수 없다.

 

하지만, 내가 이 전에 구해놓은 데이터의 경우, 2배속 영상을 분석한 결과를 1배속 영상 속도에 맞게 매치했을 때, 0.1초에 1 Frame 정도로 예상이 가능하다. 즉, 사람의 움직임이 0.1초 안에 기상천외하게 움직이지 않는다면 분석 결과가 어느 정도 일치할 것이다.

 

또한, 메이웨더는 1초에 주먹을 4~5번 휘두를 수 있다고 한다. 주먹 한 번에 0.2초라고 해도, 그 시간보다 짧은 시간 단위로 보정값을 채워주는 작업이기 때문에, 큰 오차는 발생하지 않을 것이라 생각된다.

 

 

 

보정하는 작업의 경우, 프레임과 프레임의 좌표값의 중앙값을 채워 넣어주면 된다. 처음에는 프레임과 프레임 사이에 보정프레임을 하나만 넣어주었는데, 이 또한 약간 부족한 느낌이 들어서 보정 프레임을 두 개씩 채워주는 작업을 진행했다.

 

간단하게 frame과 frame의 좌표를 이은 선분에서 1/3 지점과 2/3 지점을 추가해 주었다고 생각하면 된다.

 

로그로 출력해 본 결과, 앞서 얻은 37개의 분석 결과는 동일하며, 중간 보정작업을 통해 총 109개의 frame을 저장하였다.

 

 

결과

 

Frame 수가 증가함에 따라 부드러운 애니메이션을 보여준다.

중간에 살짝 튀는 프레임을 제외하고는 만족스러운 결과를 보여주고 있다. 유독 이 영상에서만 튀는 프레임이 발생하여 확인 중에 있고, 다른 영상의 경우 아주 정상적으로 작동하고 있다.

 

이로써, 분석 시간은 1/2로 단축하면서 frame 수는 원본 영상의 약 1.5배가량으로 증가시키는 작업을 성공적으로 마무리하게 되었다.

Comments