[Python, OpenCV]動画から検出した円をぴえんに変換する

**動画を作りました。よろしければご視聴ください。**
**https://youtu.be/4xfmPCFUplU**

**Githubのリポジトリも作ってもらいました。このページでもソースコードは全文公開していますが、よろしければこちらもどうぞ。**
**https://github.com/y-taka-avant/makePienFromCircle**

ぴえんという顔文字をご存じでしょうか。
2019年ぐらいからJKやJCの間で流行りはじめた顔文字で、2020年には上半期のインスタ流行語大賞に選ばれたらしいです。
実際に見てもらった方が早いですね。

<img width="110" alt="002・pien.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/668610/e2fa378e-2bb0-e35d-d75f-767a49fc0915.png">

いつからか誰かがこの顔文字と一緒に「ぴえん」という単語を使い始めて、それが定着してこの顔文字自体がぴえんと呼ばれるようになった・・・らしいです。

なんというかカルト的な人気を博したこのぴえん🥺ですが、この顔をした八頭身に追いかけられるようなゲームが出たり、「ぴえんこえてぱおん」というまた新しいよくわからない単語が登場したりしています。

さて、今回はこのぴえんをPythonとOpenCVを使ってカメラの映像に表示したいと思います。
まだどちらも触って1,2ヶ月程度なのでおかしな部分があるかもしれませんが、よろしくお願いします。

#開発環境
- macOS Catalina(使っているのはMacBookPro Mid 2014)
- Python 3.7.7
- numpy 1.18.5
- OpenCV 3.4.2

Anacondaで仮想環境を作成しています。
カメラはeMeetのNovaを使っています。

#ソース
```python:pien.py
# -*- coding: utf-8 -*-
import cv2
import numpy as np

#カメラの取得
cap = cv2.VideoCapture(1)

while True:
    ret, frame = cap.read()
    #ハフ変換用にリサイズし、グレーカラーに変更
    frame = cv2.resize(frame, dsize=(640, 480))
    gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
    gray = cv2.GaussianBlur(gray, (33, 33), 1)

    #ハフ変換による円形の抽出
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 60, param1=10, param2=85, minRadius=1, maxRadius=80)

    #変換結果がある場合にぴえんの描写を行う
    if circles is not None:
        circles = np.uint16(np.around(circles))
        for i in circles[0,:]:
            #i[0]=x座標 i[1]=y座標 i[2]=半径
            x = i[0]; y = i[1]; r = i[2]
            #cv2.circleは円形 cv2.ellipseは楕円や楕円弧
            #輪郭
            cv2.circle(frame, (x, y), r, (0, 215, 255), -1)
            #眉毛右
            cv2.ellipse(frame, (x+int(r*2/3), y-int(r*2/3)), (int(r/3), int(r/4)), 0, 90, 165, (19, 69, 139), 3)
            #眉毛左
            cv2.ellipse(frame, (x-int(r*2/3), y-int(r*2/3)), (int(r/3), int(r/4)), 0, 15, 90, (19, 69, 139), 3)
            #涙右
            cv2.circle(frame, (x+int(r*2/5), y), int(r*3/10), (255, 255, 255), -1)
            #涙左
            cv2.circle(frame, (x-int(r*2/5), y), int(r*3/10), (255, 255, 255), -1)
            #黒目右
            cv2.circle(frame, (x+int(r*2/5), y-int(r/15)), int(r*3/10), (0, 0, 0), -1)
            #黒目左
            cv2.circle(frame, (x-int(r*2/5), y-int(r/15)), int(r*3/10), (0, 0, 0), -1)
            #白目大右
            cv2.ellipse(frame, ((x+int(r/3), y-int(r/6)), (int(r/3), int(r/4)), 135), (255, 255, 255), -1)
            #白目大左
            cv2.ellipse(frame, ((x-int(r/2), y-int(r/6)), (int(r/3), int(r/4)), 135), (255, 255, 255), -1)
            #白目小右
            cv2.ellipse(frame, ((x+int(r/2), y), (int(r/10), int(r/15)), 135), (255, 255, 255), -1)
            #白目小左
            cv2.ellipse(frame, ((x-int(r/3), y), (int(r/10), int(r/15)), 135), (255, 255, 255), -1)
            #口
            cv2.ellipse(frame, (x, y+r), (int(r/4), int(r/2)), 0, 245, 295, (19, 69, 139), 5)

    #ウィンドウにカメラの映像を表示 この時点でframeにはぴえんが描写されている
    cv2.imshow("frame", frame)

    #qキーを押したら終了する
    if cv2.waitKey(1)&0xff == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()
```
コードの説明はそのままコメントで残しました。
重要なのは中程のハフ変換による円形の検出部分と、検出した円の座標・半径をもとにぴえんを作成する部分です。
ぴえんは画像を貼り付けているわけではなく、全て円と線で描画しているので、どんな大きさにも対応できるようになっています。
文字列も描写できるので🥺をそのまま使えないかと思ったのですがダメでした。

#やってみた結果
仮想環境をconda activateした上でpien.pyを実行すると、カメラからの映像がウィンドウで表示されるかと思います。
そこでカメラを丸いものに向けてみましょう。

![001・pien.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/668610/057d52ce-cf41-2a27-249e-3d36353c74f0.gif)

ぴえんになりました🥺
しっかり大きさが違ってもそれに合わせて変換されていますね。
今回は円を検出しているので、円の中に何が入っているかは特に関係ありません。時計でもキャップでもメガネでもぴえんになります。
コードを丸くすればそれもぴえんになります。
ちなみに瞳もぴえんになりました。(🥺ω🥺)みたいな感じです。

OpenCVを使えば円だけでなく例えば人の顔も検出できます。
つまりカメラに移った顔を全て🥺にすることができるということです。
そこら辺はそのうち試してみたいと思います。