본문 바로가기

IT/개발

pytorch openpose를 사용해보자(초간단, yolov9 연동 결과)

반응형

OpenPose는 사람의 자세 인식을 위한 오픈 소스 딥러닝 모델 입니다. 

이 라이브러리는 단일 이미지나 비디오에서 사람의 뼈대구조에 대한 키포인트를 실시간으로 감지할 수 있습니다. 

주로 다음과 같은 기능을 제공합니다. 

(1) 다수의 사람 객체 인식 
 이미지나 비디오에서 여러 사람을 동시에 감지하고 각 사람의 자세를 추출
(2) 다중 키포인트 인식
 사람의 신체 부위(어깨, 팔꿈치, 손목, 무릎, 발목 등)와 얼굴, 손의 키포인트를 인식
(3) 실시간 처리
 GPU를 활용하여 실시간으로 이미지나 영상에서 사람의 자세를 분석


Body Pose Estimation: 신체의 18개 주요 부위를 감지합니다.
Hand Keypoint Detection: 각 손의 21개 키포인트를 감지합니다.

그외에 잘 사용하지는 않지만 Facial Landmark Detection(얼굴의 70개 키포인트)과 Foot Keypoint Detection(양 발의 6개 키포인트)도 사용할 수 있습니다. 

 

모델에 대한 분석은 필요없고 일단 사용법만 알아보겠습니다. 

 

https://github.com/CMU-Perceptual-Computing-Lab/openpose

여기는 base인데 caffe 모델이라서 아래 pytorch 모델로 받아서 사용합니다. 

사용하기가 무지하게 쉽습니다. 

 

https://github.com/Hzzone/pytorch-openpose

 

GitHub - Hzzone/pytorch-openpose: pytorch implementation of openpose including Hand and Body Pose Estimation.

pytorch implementation of openpose including Hand and Body Pose Estimation. - Hzzone/pytorch-openpose

github.com

 

pretrain 모델 다운로드

https://drive.google.com/drive/folders/1JsvI4M4ZTg98fmnCZLFM-3TeovnCRElG

 

body와 hand 모델을 받아서 시험해 보겠습니다. 

 

 

 

1. 관련 모듈 설치 

numpy
matplotlib
opencv-python
scipy
scikit-image
tqdm

또 빌드시 에러나는 것 등등..

 

2. 간단 예제 소스 

소스에 들어 있는 예제로 먼저 해봅니다. 

 

 

import cv2
import matplotlib.pyplot as plt
import copy
import numpy as np

from src import model
from src import util
from src.body import Body
from src.hand import Hand

body_estimation = Body('model/body_pose_model.pth', device = 'cuda')


test_image = 'images/demo.jpg'
oriImg = cv2.imread(test_image)  # B,G,R order
candidate, subset = body_estimation(oriImg)
canvas = copy.deepcopy(oriImg)
canvas = util.draw_bodypose(canvas, candidate, subset)

#hand 처리. 속도 문제나 핸드 분석이 불필요하면 안해도 됨
# detect hand
hand_estimation = Hand('model/hand_pose_model.pth', device = 'cpu')

hands_list = util.handDetect(candidate, subset, oriImg)


all_hand_peaks = []
for x, y, w, is_left in hands_list:
    # cv2.rectangle(canvas, (x, y), (x+w, y+w), (0, 255, 0), 2, lineType=cv2.LINE_AA)
    # cv2.putText(canvas, 'left' if is_left else 'right', (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)

    # if is_left:
        # plt.imshow(oriImg[y:y+w, x:x+w, :][:, :, [2, 1, 0]])
        # plt.show()
    peaks = hand_estimation(oriImg[y:y+w, x:x+w, :])
    peaks[:, 0] = np.where(peaks[:, 0]==0, peaks[:, 0], peaks[:, 0]+x)
    peaks[:, 1] = np.where(peaks[:, 1]==0, peaks[:, 1], peaks[:, 1]+y)
    # else:
    #     peaks = hand_estimation(cv2.flip(oriImg[y:y+w, x:x+w, :], 1))
    #     peaks[:, 0] = np.where(peaks[:, 0]==0, peaks[:, 0], w-peaks[:, 0]-1+x)
    #     peaks[:, 1] = np.where(peaks[:, 1]==0, peaks[:, 1], peaks[:, 1]+y)
    #     print(peaks)
    all_hand_peaks.append(peaks)

canvas = util.draw_handpose(canvas, all_hand_peaks)

# 결과 이미지를 파일로 저장
result_image_path = 'result/result_pose.jpg'
cv2.imwrite(result_image_path, canvas)

#plt.imshow(canvas[:, :, [2, 1, 0]])
#plt.axis('off')
#plt.show()

 

 

3. yolov9와 함께 연동해서 사용 해보기

 

여기서는 다음 소스를 사용하였습니다.

 

https://github.com/WongKinYiu/yolov9 

 

GitHub - WongKinYiu/yolov9: Implementation of paper - YOLOv9: Learning What You Want to Learn Using Programmable Gradient Inform

Implementation of paper - YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information - WongKinYiu/yolov9

github.com

 

 

대충 yolo에서 person을 detection해서 해당 bbox 영역을 openpose의 입력으로 넣고 

그 결과를 원본 이미지의 bbox영역에 대체해서 저장하는 방식을 사용하였습니다. 

영상은 서울시에서 제공하는 CCTV의 실시간 영상을 일부 사용하였습니다. 

 

 

 

4. 연동 코드 작업 (관련 부분만)

실시간성을 위해서 핸드 부분 모델은 제외하고 적용하였습니다. 

 

openpose 코드 수정 

import cv2
import matplotlib.pyplot as plt
import copy
import numpy as np
import sys
import os

from src import model
from src import util
from src.body import Body
from src.hand import Hand

#외부에서 상대경로로 호출한 경우 상대 경로 처리
from pathlib import Path
FILE = Path(__file__).resolve()  # 현재 파일의 절대 경로를 얻음
ROOT = FILE.parents[0]  # YOLO의 루트 디렉토리를 설정
if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT))  # ROOT 디렉토리를 시스템 경로에 추가
ROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # ROOT를 현재 작업 디렉토리에 대한 상대 경로로 설정

 
#이미지 버퍼를 작접 받도록 함 
def detect_pose_buffer(model, oriImg) :

    candidate, subset = model(oriImg)
    canvas = copy.deepcopy(oriImg)
    canvas = util.draw_bodypose(canvas, candidate, subset)
   
    return canvas
 
# yolo 쪽에서 미리 모델을 로드 
def load_pose_model() :
    print("get openpose model")
    body_estimation = Body(ROOT / 'model/body_pose_model.pth', device = 'cuda')
    return body_estimation
 

 

 

yolov9 코드 수정 

 

bbox와 class를 감지하는 부분에 person 처리 하고 list에 좌표와 변환 이미지 저장 

                    #사람이면 opse detection, car 종류면 번호판 찾기 
                    if names[c] == 'person' :
                        poseTime = time.time()
                        person_xyxy, poseImg = draw_person_pose(pose_model, xyxy, imc, label=names[c], BGR=True)
 
                        person_list.append((person_xyxy, poseImg))
                        print(f"pose time : {time.time() - poseTime:.2f}")
                       

 

프레임 처리가 끝나면 openpose에서 처리한 이미지와 좌표 정보를 이용하여 프레임에 덮어쓰기

for person_xyxy, poseImg in person_list :
               # Load the cropped image with label
               #crop_img = cv2.imread(str(person_file))
               # Paste back to the original image
               #im0 = paste_back(im0, crop_img, person_xyxy) # 이미지 엎어 씌우기
               im0 = paste_back(im0, poseImg, person_xyxy) # 이미지 엎어 씌우기

 

#원본 파일에 openpose 결과를 overwite 하는 함수
def paste_back(im, crop_img, xyxy):
   
    im = im.copy() #ysyang 갑자기 read only 오류 발생해서 수정
   
    x1, y1, x2, y2 = map(int, xyxy)
   
    # Resize crop_img to fit the bounding box region in the original image
    height, width = y2 - y1, x2 - x1
    resized_crop_img = cv2.resize(crop_img, (width, height))

    # Make sure the dimensions match before pasting
    if im[y1:y2, x1:x2].shape == resized_crop_img.shape:
        im[y1:y2, x1:x2] = resized_crop_img
    else:
        print(f"Error: Shape mismatch. Original region shape: {im[y1:y2, x1:x2].shape}, Crop shape: {resized_crop_img.shape}")

    return im  

 

간단하게 저정도만 추가해도 잘 돌아갑니다. 

 

 

반응형