【python】Mediapipe&OpenCV&OBSを使って、WEB会議でのフィルターを作った

# TL;DR

ソースコードは[こちら](https://github.com/YomamaBanana/Mediapipe-python/blob/main/src/face_filter_web.py)!

# はじめに

今回は以前の[この記事](https://qiita.com/YomamaBanana/items/4197c4f9ec26a05416ed)を使って、実際にWEB会議などで自前で実装したフィルターをかけた状態の画像を使ってみよう！

前のものに数行追加すればできる話ではあるが、今回は別のソフトが必要になってくるので、別途インストールが必要。

考え方としては画像処理を行った結果を仮想カメラに渡し、WEB会議ではその仮想カメラを利用すること。

# 手順

今回のフォルダ構成は以前の記事と同じで、プログラムの大まかな処理は下記の通り：

1. Configの読み込む
1. Mediapipeのインスタンス化
1. カメラからの画像取得
1. フィルター適用
1. **結果を仮想カメラへ送信する**

# OBSについて

OBSとはオーペンソースの仮想カメラのソフトで、ゲーム中継や動画配信によく使われている。使い方は様々だが、今回は「仮想カメラ」として利用する。

まずはダウンロードして、インストールしよう！

https://obsproject.com/

## ツール

OBSとpythonを連携させるため、pyvirtualcamというライブラリーを利用する。

https://letmaik.github.io/pyvirtualcam/#

まずはインストール

```shell
$ pip install pyvirtualcam
```

この記事は非常に参考になったので、ぜひご参考まで！

https://serverless.hateblo.jp/entry/2020/12/12/235007

# ソースコード

ソースコードはほとんど前回と同じだが、今回は仮想カメラを利用するので、それをクラス化しよう

```python
import pyvirtualcam

class VirtualCamera():
    def __init__(self, width, height, fps) -> None:
        self.v_cam = pyvirtualcam.Camera(width=width, height=height, fps=fps)

    def _send(self, image):
        self.v_cam.send(image)
        self.v_cam.sleep_until_next_frame()
```

次に既存カメラクラス（class Camera）の中に仮想カメラを起動しする。

```python
class Camera():
    # 画像サイズを同調させるため、予め横軸を定義する。
    def __init__(self, index, config, detector, width:int=1920, height:int=1080, fps:int=30) -> None:
        self.config = config
        self.detector = detector

        self.width = width
        self.height = height
        self.fps = fps

        self.start(index)

    def start(self, index) -> None:
        self.cap = cv2.VideoCapture(index)
        self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
        self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
        success, frame = self.cap.read()
        if not success:
            logger.error(f"Camera not successful: video input: {index}")
            sys.exit()
        self.mask = np.zeros((frame.shape[0], frame.shape[1], 3), dtype=np.uint8)
        
        #　仮想カメラを始動する。
        self.v_cam = VirtualCamera(width=self.width, height=self.height, fps=self.fps)

```

最後に画像処理の結果を送信する。

```python
    def capture(self) -> None:
        logger.info("Catpuring images from video input... (press 'q' to exit.)")
        while True: 
            _, frame = self.cap.read()
            frame.flags.writeable = False
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            self.detector(frame)
            
            frame.flags.writeable = True
            frame, mask = self.detector.post_processing(self.mask, self.config.yaml_cfg)
            mask = cv2.flip(mask, 1)
            
            # 結果を送信する。
            self.v_cam._send(frame)
            
            # マスクを表示する。
            mask = cv2.resize(mask, dsize=(400, 320))
            cv2.imshow("mask", cv2.cvtColor(mask, cv2.COLOR_BGR2RGB))
            
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
```

修正はこれだけ！

これでフィルターをかけた画像が仮想カメラへ送信され、あとは実際のWEB会議でその仮想カメラを選択するのみ！

詳細なやり方は割愛させていただくが、USBカメラを利用する時と同じである。

# デモ

WEB会議で仮想カメラ（OBSカメラ）を選択する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290515/abc5f41c-8b40-2510-c97f-a147d3b0fd93.png)

プログラムを実行する。

![face_filter_web.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290515/8c1b50b0-64e4-55a5-d824-586c2f37550b.gif)

# あとがき

今回は単純に前回のものに加え、仮想カメラを利用することで、WEB会議などで自前のフィルターを使えるようになった。

ぜひ既存するものを活かして、身の回りのことをスマートかつ**面白くしよう**！