OpenCV와 Python으로 영상 채팅을 더욱 즐겁게 해보세요

소개



오랜만에 파이썬 기사를 써 보았습니다. 아마도 최근에는 직장에서 비공개로 화상 채팅을 많이 사용했을 것입니다. 이번에는 Python과 OpenCV를 사용하여 화상 채팅을 더욱 즐겁게 해주는 해킹을 해보자.

만드는 것



모처럼의 화상 채팅이므로 평소의 자신과는 다른 모습이 되고 싶다고 누구나가 생각할 것입니다. 이번에는 다음과 같은 느낌으로 "손을 잡으면 마법진이 나온다"와 같은 스냅 카메라 되돌아갑니다.



손 감지



손 감지에는 OpenCV에서 지원하는 계단식 분류기를 사용합니다. 자세한 내용은 이 기사을 참조하십시오. 간단히 설명하면 물체 검출을 해주는 라이브러리입니다. OpenCV의 도입 방법에 대해서는, OS에 따라서 다르고 이미 많은 기사가 나돌고 있으므로 여기에서는 생략하겠습니다.
OpenCV에서의 캐스케이드 분류기는 「얼굴 검출」이 메이저라고 생각합니다. 이 캐스케이드 분류기의 모델을 스스로 조립하는 것은 몹시 힘든 작업이 되기 때문에 이쪽 학습된 모델 을 공식보다 빌렸습니다.

마법진의 표시에 대해서



마법진의 표시는 단순히 검출한 손에 마법진을 씌울 뿐입니다만, 그것이라고 투과해 주지 않기 때문에 여기 를 참고로 하면서 마스크를 사용한 화상 합성을 실장했습니다.

구현



코드는 다음과 같은 느낌입니다. 평소 파이썬은 별로 쓰지 않으므로 약간의 더러움은 용서해주십시오.
import cv2
import numpy as np
import random

def masking(background, foreground, size):
    gray = cv2.cvtColor(foreground, cv2.COLOR_BGR2GRAY)
    _, binary = cv2.threshold(gray, 90, 255, cv2.THRESH_BINARY_INV)
    _, contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    mask = np.zeros_like(foreground)
    cv2.drawContours(mask, contours, -1, color=(255, 255, 255), thickness=-1)
    roi = background[size[0]:size[2], size[1]:size[3], :]
    result = np.where(mask==255, foreground, roi)
    return result

def HandDetector(img):
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img_rgb = clahe.apply(img_rgb)
    hand_rects = hand_cascade.detectMultiScale(img_rgb, 1.1, 5)
    if len(hand_rects) > 0:
        for i, (x,y,w,h) in enumerate(hand_rects):
            size = [y-h//2, x-w//2, y+h//2*3, x+w//2*3]
            if i == 0 and size[0] > 0 and size[1] > 0 and size[2] < img.shape[0] and size[3] < img.shape[1]:
                magicsquare = cv2.imread(magic)
                hand = img[size[0]:size[2], size[1]:size[3]]
                magicsquare = cv2.resize(magicsquare, (hand.shape[0], hand.shape[1]))
                magicsquare = masking(img, magicsquare, size)
                img[size[0]:size[2], size[1]:size[3]] = magicsquare
                center = np.array([x+w//2, y+h//2])
    return img

cascade_path = "./data/aGest.xml" # 手の検出用モデルの読み込み
hand_cascade = cv2.CascadeClassifier(cascade_path)
clahe = cv2.createCLAHE(clipLimit=1.5, tileGridSize=(8,8))
cap = cv2.VideoCapture(1)

magic = './魔法陣の画像のPath.jpg'

while True:
    ret, frame = cap.read()
    frame = cv2.flip(frame, 1)
    height, width, channels = frame.shape
    frame = HandDetector(frame)
    cv2.namedWindow('img', cv2.WINDOW_NORMAL)
    cv2.imshow('img', frame)
    c = cv2.waitKey(1)
    if c == 27:#ESC
        break
cap.release()
cv2.destroyAllWindows()
HandDetector 로 손의 검출, masking 로 마법진을 투과시키도록 합성시키는 것 같은 느낌입니다. 다음은 imgshow에서 실제로 합성한 영상을 표시시킵니다.



이런 느낌으로 할 수 있었습니다! 마법진은 어딘가의 자유의 것을 집어 왔습니다.

화상 채팅에서 사용해보기



모처럼 만들었으므로 이것을 실제 영상 채팅에서도 사용하고 싶네요. 이번에는 간단을 위해 SplitCam을 사용하여 imgshow에서 출력한 영상을 가상 디바이스로 출력했습니다. 그런 다음 비디오 채팅 도구에 입력했습니다 (이번에는 주제 Zoom 사용). 아래에서는 간단히 SplitCam의 절차를 설명합니다.Media Layers라는 조작 영역에 "+"버튼이 있으므로 버튼을 클릭하면 다음과 같은 풀다운 메뉴가 나타납니다. 그대로Screenshare를 선택합니다. 그러면 풀다운 메뉴가 다시 나오므로 imgAdd합니다 (위의 코드를 그대로 실행한 경우).



그러자 이런 식으로 Python으로 출력한 영상이 캡처되었습니다.



이대로 Zoom을 열면 카메라의 선택 메뉴에서 SplitCam Video Driver를 선택할 수 있게 되었으므로 그것을 선택합니다. 그러자 Zoom에게 Python의 영상을 낼 수 있었습니다!



사이고에게



이번 코드를 일단 gist에도 공개해 둡니다. 이미지나 학습된 모델은 픽업이므로 각자 찾아보세요. 학습된 모델의 링크를 여기에 다시 게시합니다.
단지 이 손의 검출입니다만, 구의 형태 밖에 검출할 수 없기 때문에 좀 더 바리에이션이 있는 손의 화상을 검출시키고 싶네요. 그렇게 되면 TensorFlow 에 오랜만에 손을 내게 될 것 같다.

좋은 웹페이지 즐겨찾기