졸업 프로젝트 - 임의의 영상에서 특정 사람 검출

date
Jun 19, 2022
thumbnail
slug
graduation-project
author
status
Published
tags
Project
summary
사용자가 입력한 영상에서 상의와 하의의 색을 바탕으로 원하는 사람을 검출하는 프로젝트 (Feat. YOLO, Sementic Segmantation)
type
Post
updatedAt
Jan 23, 2023 12:35 PM
wkddntjr1123/human-detection-using-clothes

1️⃣ 배경

집이 강원도에서 펜션을 하는데, 가끔 외부의 기물이 파손되거나 하는 경우가 있었다. 집에 CCTV는 있지만, 이런 영상 장비들은 대개 24시간 작동되기 때문에 언제 기물이 파손되었는 지 찾으려면 아주 긴 영상을 넘기면서 사람이 등장하는 부분을 일일이 수동으로 찾아봐야 했다.
이런 불편함을 해소하기 위해 녹화된 영상에서 특정 사람을 찾는 시스템을 개발하기로 했다. 단순히 ‘녹화된 영상에서 사람 찾기’에서 더 나아가서, 사람의 상의와 하의를 통해서 사용자 입력 영상에서 상하의 색에 따른 원하는 사람 찾기로 주제를 정하였다.

2️⃣ 시스템 개요

notion image

Client

  1. 클라이언트에서 분석할 영상과 사람의 상의 색, 하의 색을 선택
  1. WebSocket을 통해서 서버와 세션 연결 후, 사용자 입력 값들을 전송
    1. notion image
  1. 5초 단위로 위와 같은 영상의 분석 결과 JSON 데이터를 받음
      • Key는 영상에서 객체가 발견된 시간, Value는 각 객체의 좌상단좌표(x1,y1)와 우하단좌표(x2,y2)가 리스트로 주어짐
  1. JSON 데이터를 파싱하여, 영상에서 객체가 검색된 시간대에 객체 위치에 사각형을 그림

Server

  1. 사용자가 전송한 영상을 OpenCV로 프레임 단위로 읽음
  1. YOLO를 통해 프레임 내 사람 검출
  1. 사람이 검출된 프레임에 한하여 Semantic Segmentation을 통해 사람의 신체를 분할
  1. 사람의 상의와 하의의 색을 판별하여, 사용자 입력과 일치하면 검출 시간과 영상 내 좌표를 응답 결과에 기록
  1. 5초마다 기록된 JSON 데이터를 클라이언트로 전송

3️⃣ 정확도 개선

deepLabV3+를 이용한 사람 segmentation
deepLabV3+를 이용한 사람 segmentation
초기 구상은 deepLabV3+를 이용하여 사람을 segmentation하여 단순히 인식된 사람의 높이를 절반으로 잘라서 상하의를 판단하려 했다. 하지만 인식된 사람을 반으로 나눈다는 것이 상하의를 올바르게 나누는 경우가 매우 드물다는 문제를 해결할 수 없었다.
YOLO를 통한 사람 검출
YOLO를 통한 사람 검출
Semantic Segmentation을 통한 사람의 신체 분할
Semantic Segmentation을 통한 사람의 신체 분할
따라서 semantic segmentation을 통해서 사람의 신체를 분할하였다.
이를 통해 사람의 신체별로 클래스화가 가능하지만, 어떤 사람의 신체인지는 판별이 불가능하다.
따라서 YOLO는 각 사람의 판별을, Semantic Segmentation은 전체 사람 신체의 분할을 담당하여 프레임 내에서 각 사람의 상하의 판별이 가능해졌다.

4️⃣ 처리속도 개선

YOLO로 사람을 찾는 것과 더불어 semantic segmentation자체가 무거운 연산이다보니 서비스가 불가능한 수준의 분석 속도가 나왔다.
  • 1분 길이의 약 1200개의 프레임을 갖는 영상을 분석하는데 3분 정도 소요
따라서 이를 아래와 같은 방법들로 해결하였다.

1. Frame 해상도 낮추기

이미지 처리 연산 자체가 절대적인 픽셀의 수의 영향을 크게 받는다.
이미지를 확대하면 각 픽셀에 대해 보간을, 이미지를 축소하면 각 픽셀에 대해 병합을 수행하게 된다. 따라서 이미지를 축소하여 절대적인 픽셀의 수를 감소시켜도, 전체적인 색의 비율은 유의미하게 유지된다.

2. 프레임 I/O를 위한 스레드를 분리

프레임의 크기를 아무리 작게 줄여도 처리 속도가 줄어들지 않는 현상이 발생했고, OpenCv에서 동영상의 한 프레임을 읽는 작업이 Blocking I/O임을 확인하였다. 따라서 메인 스레드로만 작업을 하게되면 아래와 같은 흐름을 따르게 된다.
스레드에서 프레임 읽는 I/O작업을 OS에 요청 → 스레드 Wait 상태 → I/O작업 완료되어 인터럽트 발생 → OS가 스레드를 대기큐로 이동 → 스레드가 CPU를 할당받아 프레임을 처리 → 다음 프레임 읽는 I/O작업…
따라서 프레임을 읽어오는 스레드와 프레임을 처리하는 스레드를 분리하고 공통된 Queue를 통해 동기화를 수행하였다.
파이썬(CPython)은 GIL때문에 유저 레벨 스레드를 만들어도 실제로는 1개의 커널 스레드가 동작한다고 알고있었다. 하지만 실제로 스레드 모듈을 이용해서 I/O작업을 분리하니 매우 큰 속도 향상이 일어났다. 관련 링크를 찾아보니 파이썬 runtime에 관련한 영역은 Lock을 통해 1개의 유저 레벨 스레드만 접근이 가능하지만, I/O같은 Blocking작업은 커널 레벨 스레드로 처리가 가능하여 I/O작업 시 성능 향상이 가능하다.

3. 분석결과를 클라이언트에서 처리

각 프레임을 분석하는 작업 자체도 무거운데 서버가 분석된 프레임들에 결과 박스를 표시하고, 모아서 영상으로 재구성하는 작업의 부하가 너무 컸다. 또한 서버에서 영상을 재구성해서 클라이언트로 전송하게되면 네크워크 트래픽도 훨씬 커진다. (클라우드 환경에서는 이런 트래픽 자체가 돈이다…) 따라서 서버는 분석 결과를 JSON 데이터로 전송한다. 또한 사용자에게 빠르게 결과를 보여주기 위해 Web Socket을 이용하여 5초마다 서버에서 응답을 보낸다. 클라이언트는 받은 JSON데이터를 바탕으로 영상에서 각 검출된 사람에 대해 HTML div태그와 CSS를 이용하여 박스를 그리게 된다.

5️⃣ 최종 결과

최종적으로 1분 길이의 1200개의 프레임을 갖는 영상을 분석하는데 약 5초 정도로 시간을 줄일 수 있었다.
갈색 상의와 파란색 하의를 입력한 결과
갈색 상의와 파란색 하의를 입력한 결과
흰색 상의와 검은색 하의를 입력한 결과
흰색 상의와 검은색 하의를 입력한 결과

6️⃣ 후기

사실 코딩테스트 준비, CS지식 공부 등 하반기 취업을 위한 준비들에 집중하다보니, 졸업 프로젝트에 할애한 시간은 2주도 안된다.
영상처리, 딥러닝 분야에 문외한이라 처음 프로젝트를 기획할 때 상하의 색으로 사람을 판단하는 게 단순할 줄 알았는데, 막상 시작하니 DeepLabV3+로 삽질도 몇번 하고 여러 라이브러리를 테스트하면서 적합한 것을 찾는 과정이 쉽지는 않았다. 또 적합하다고 판단되는 기술을 적용하니, 성능적으로 개선할 부분이 많았다.
그래도 여러 가지 아이디어를 떠올리고 적용하면서 개선해나가서 실제 서비스가 가능한 수준의 정확도와 처리 시간으로 줄여가는 과정이 즐거웠다.