図形だけで初音ミクを描く【OpenCV】

# 0. はじめに
- OpenCVの図形を描画する関数を用いて，初音ミクを描きました．
![miku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/699b30c8-23e0-7d7f-2912-f09170ef2bc3.png)

※ この作品は<a href="http://piapro.jp/license/pcl/summary">ピアプロ・キャラクター・ライセンス</a>に基づいてクリプトン・フューチャー・メディア株式会社のキャラクター「初音ミク」を描いたものです
# 1. OpenCVの図形描画関数
- [こちら](https://note.nkmk.me/python-opencv-draw-function/)のサイトを参考にさせていただいております．

### 共通引数
- `img`：図形を描画する画像
- `pt1`：始点
- `pt2`：終点
- `center`：円の中心
- `color`：色（今回はカラー画像で(Blue, Green, Red)で指定する）
- `thickness`：線の太さ，長方形や円の場合は負の値を指定することで塗りつぶされる（デフォルト：1）
- `lineType`：線のタイプ（デフォルト：8連結）
- `shift`：座標と半径の値の小数点以下の桁を表すビット数（デフォルト：0）

### 図形を描画するための画像を用意する
```Python
# OpenCVのインポート
import cv2
# 画像をNumPyの配列ndarrayとして読み込んで操作するためにインポート
import numpy as np

# np.full((縦のサイズ, 横のサイズ, 3), (Blue, Green, Red), 
# 符号なし8ビット整数型)で図形を描画するための画像を作成
img = np.full((100, 100, 3), (211, 211, 211), dtype=np.uint8)

# image.pngという名前で出力する
cv2.imwrite('image.png', img)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/cec0f804-5c70-d1ff-94e2-39c4f0d7d2a9.png)

### 線分：`cv2.line()`
```Python
# cv2.line(img, pt1, pt2, color, thickness, lineType, shift)
cv2.line(img, (10, 10), (90, 90), (0, 0, 255))
cv2.line(img, (90, 10), (10, 90), (0, 255, 0), thickness=3, lineType=cv2.LINE_AA)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/aaf26561-efc8-f113-2de7-8eede9c2f28c.png)

### 矢印：`cv2.arrowedLine()`
```Python
# cv2.arrowedLine(img, pt1, pt2, color, thickness, lineType, shift, tipLength)
# tipLength：矢の先の部分の長さ，全体の長さに対する比で表す（デフォルト：0.1）
cv2.arrowedLine(img, (10, 10), (90, 90), (255, 0, 0), thickness=5)
cv2.arrowedLine(img, (90, 10), (10, 90), (0, 255, 255), tipLength=0.5)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/2bf6cd76-7d2f-700a-6503-4c7d2b490b70.png)

### 長方形：`cv2.rectangle()`
```Python
# cv2.rectangle(img, pt1, pt2, color, thickness, lineType, shift)
cv2.rectangle(img, (10, 10), (90, 40), (255, 255, 0), thickness=-1)
cv2.rectangle(img, (10, 60), (90, 90), (255, 0, 255))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/a45e9cb1-530c-0e18-fc3d-a97992f85924.png)

### 円：`cv2.circle()`
```Python
# cv2.circle(img, center, radius, color, thickness, lineType, shift)
# radius：円の半径
cv2.circle(img, (25, 25), 15, (0, 0, 255), thickness=-1)
cv2.circle(img, (65, 65), 20, (0, 255, 0), thickness=5, lineType=cv2.LINE_4)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/b2287f68-3ec8-5935-cb0a-47278281e12a.png)


### 楕円：`cv2.ellipse()`
```Python
# cv2.ellipse(img, box, color, thickness, lineType)
# box(center, axes, angle)：centerは(x, y)，axesは(横方向直径, 縦方向直径)，
# angleはx軸方向を0度として時計回りに回転角度を指定する
cv2.ellipse(img, ((20, 35), (20, 50), 0), (255, 0, 0), thickness=-1)
cv2.ellipse(img, ((65, 65), (30, 60), 45), (0, 255, 255), thickness=2, lineType=cv2.LINE_AA)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/b1ab4a83-f3be-52f7-9f60-346a749f4931.png)

### 円弧：`cv2.ellipse()`

```Python
# cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color, thickness, lineType, shift)
# axes：(横方向半径, 縦方向半径)
# startAngle：円弧の開始角度
# endAngle：終了角度
cv2.ellipse(img, (25, 50), (10, 25), 0, 0, 270, (255, 255, 0))
cv2.ellipse(img, (75, 50), (10, 25), 30, 0, 270, (255, 0, 255), thickness=-1)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/efc3acc7-0af5-e87e-a9b3-e2ab77c21c29.png)

# 2. 初音ミクを描く
### 背景
```Python
import cv2
import numpy as np

img = np.full((500, 500, 3), (211, 211, 211), dtype=np.uint8)

cv2.imwrite("miku.png", img)
```
![miku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/8de729e2-d990-0507-35b3-7c2d12f02457.png)


### 顔と眉
```Python
# 顔
cv2.circle(img, (250, 280), 150, (230, 240, 255), thickness=-1)

# 左眉
cv2.ellipse(img, (180, 270), (30, 10),   0, 210, 330, (0, 0, 0)) 
# 右眉
cv2.ellipse(img, (320, 270), (30, 10),   0, 210, 330, (0, 0, 0)) 
```
![miku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/23f39e1c-73ad-776c-765b-95de25b33942.png)


### 髪
```Python
# 髪土台
cv2.ellipse(img, (250, 175), ( 90, 150),  90,  90, 270, (118, 123,   1), thickness=-1)

# 前髪中央左
cv2.ellipse(img, (170, 270), (110, 110), 260,   0,  40, (118, 123,   1), thickness=-1)
# 前髪中央右
cv2.ellipse(img, (260, 160), ( 60, 150), 170, 270, 360, (118, 123,   1), thickness=-1)
# 前髪中央右調整
cv2.ellipse(img, (290, 180), ( 30, 140), 180, 270, 360, (230, 240, 255), thickness=-1)

# 触覚左
cv2.ellipse(img, (130, 160), ( 60, 220), 180, 270, 360, (118, 123,   1), thickness=-1)
# 触覚左調整
cv2.ellipse(img, (135, 200), ( 20, 180), 180, 270, 360, (230, 240, 255), thickness=-1)
# 前髪左
cv2.ellipse(img, (170, 180), ( 60, 120), 200, 270, 360, (118, 123,   1), thickness=-1)
# 触覚右
cv2.ellipse(img, (370, 160), ( 60, 220),   0,   0,  90, (118, 123,   1), thickness=-1)
# 触覚右調整
cv2.ellipse(img, (365, 200), ( 20, 180),   0,   0,  90, (230, 240, 255), thickness=-1)
# 前髪右
cv2.ellipse(img, (240, 175), (100, 220), 310,   0,  90, (118, 123,   1), thickness=-1)

# ツインテール左根元
cv2.ellipse(img, (150, 200), (100, 100), 120,   0, 180, (118, 123,   1), thickness=-1)
# ツインテール左毛先
cv2.ellipse(img, ( 80, 340), (180,  80), 100,   0, 180, (118, 123,   1), thickness=-1)
# ツインテール右根元
cv2.ellipse(img, (350, 200), (100, 100), 120,  90, 270, (118, 123,   1), thickness=-1)
# ツインテール右毛先
cv2.ellipse(img, (420, 340), (180,  80), 260,   0, 180, (118, 123,   1), thickness=-1)

# 触覚左影
cv2.ellipse(img, (105, 260), ( 20,  90),   0,  90, 270, ( 53,  53,  23), thickness=-1)
# ツインテール左影
cv2.ellipse(img, ( 80, 340), (120,  20), 100,   0, 180, ( 53,  53,  23), thickness=-1)
# 触覚左影調整
cv2.ellipse(img, (110, 260), ( 20,  90),   0,  90, 270, (118, 123,   1), thickness=-1)
# 触覚右影
cv2.ellipse(img, (395, 260), ( 20,  90), 180,  90, 270, ( 53,  53,  23), thickness=-1)
# ツインテール右影
cv2.ellipse(img, (420, 340), (120,  20), 260,   0, 180, ( 53,  53,  23), thickness=-1)
# 触覚右影調整
cv2.ellipse(img, (390, 260), ( 20,  90), 180,  90, 270, (118, 123,   1), thickness=-1)
```
![miku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/229655a5-ba5a-baa3-3982-9ab3cdf6f4b3.png)

### 口と頬
```Python
# 口
cv2.ellipse(img, (250, 350), ( 15,  15),   0,  40, 140, (  0,   0,   0)) 

# 左頬
cv2.ellipse(img, ((160, 350), ( 60,  80), -25), (225, 225, 250), thickness=-1)
# 右頬
cv2.ellipse(img, ((340, 350), ( 60,  80),  25), (225, 225, 250), thickness=-1)
```
![miku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/cae38f88-33ef-7a05-6383-aea41119a863.png)

### 目
```Python
# 左目白
cv2.ellipse(img, ((180, 320), ( 60,  80),   0), (255, 255, 255), thickness=-1)
# 左目緑大
cv2.ellipse(img, ((185, 315), ( 48,  70),   0), (118, 123,   1), thickness=-1)
# 左目緑中
cv2.ellipse(img, ((190, 326), ( 40,  50),   0), (176, 162, 101), thickness=-1)
# 左目緑小
cv2.ellipse(img, ((193, 330), ( 30,  40),   0), (168, 184, 103), thickness=-1)
# 左目緑中央
cv2.ellipse(img, ((190, 310), ( 15,  20),   0), (118, 118,  73), thickness=-1)  
# 左目灰
cv2.ellipse(img, ((195, 340), ( 20,  20),   0), (169, 169, 169), thickness=-1)
# 左目光大
cv2.ellipse(img, ((190, 290), ( 10,  15), -20), (255, 255, 255), thickness=-1)
# 左目光小
cv2.ellipse(img, ((185, 290), ( 10,   5),   0), (255, 255, 255), thickness=-1)

# 左目線上
cv2.ellipse(img, (175, 300), ( 20,  40),  90, 120, 240, (  0,   0,   0), thickness=5) 
# 左目線下
cv2.ellipse(img, (175, 300), ( 20,  40),  60, 120, 190, (  0,   0,   0), thickness=5) 

# 右目白
cv2.ellipse(img, ((320, 320), ( 60,  80),   0), (255, 255, 255), thickness=-1)
# 右目緑大
cv2.ellipse(img, ((315, 315), ( 48,  70),   0), (118, 123,   1), thickness=-1)
# 右目緑中
cv2.ellipse(img, ((310, 326), ( 40,  50),   0), (176, 162, 101), thickness=-1)
# 右目緑小
cv2.ellipse(img, ((307, 330), ( 30,  40),   0), (168, 184, 103), thickness=-1)
# 右目緑中央
cv2.ellipse(img, ((310, 310), ( 15,  20),   0), (118, 118,  73), thickness=-1)
# 右目灰
cv2.ellipse(img, ((305, 340), ( 20,  20),   0), (169, 169, 169), thickness=-1)
# 右目光大
cv2.ellipse(img, ((320, 290), ( 10,  15), -20), (255, 255, 255), thickness=-1)
# 右目光小
cv2.ellipse(img, ((315, 290), ( 10,   5),   0), (255, 255, 255), thickness=-1)

# 右目線上
cv2.ellipse(img, (325, 300), ( 20,  40),  90, 120, 240, (  0,   0,   0), thickness=5) 
# 右目線下
cv2.ellipse(img, (325, 300), ( 20,  40), 120, 170, 250, (  0,   0,   0), thickness=5) 
```
![miku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/58d643bc-7713-9abe-b09c-37b742937c35.png)

### 髪飾り
```Python
# 左髪飾り左上黒
cv2.line(img, (150, 110), (110, 150), (  0,   0,   0), thickness=30)
# 左髪飾り左下黒
cv2.line(img, (130, 170), (110, 150), (  0,   0,   0), thickness=30)
# 左髪飾り左上赤
cv2.line(img, (150, 110), (110, 150), (  0,   0, 255), thickness=10)
# 左髪飾り左下赤
cv2.line(img, (130, 170), (110, 150), (  0,   0, 255), thickness=10)

# 右髪飾り左上黒
cv2.line(img, (370, 110), (330, 150), (  0,   0,   0), thickness=30)
# 右髪飾り左下黒
cv2.line(img, (370, 190), (330, 150), (  0,   0,   0), thickness=30)
# 右髪飾り右下黒
cv2.line(img, (370, 190), (390, 170), (  0,   0,   0), thickness=30)
# 右髪飾り左上赤
cv2.line(img, (370, 110), (330, 150), (  0,   0, 255), thickness=10)
# 右髪飾り左下赤
cv2.line(img, (370, 190), (330, 150), (  0,   0, 255), thickness=10)
# 右髪飾り右下赤
cv2.line(img, (370, 190), (390, 170), (  0,   0, 255), thickness=10)
```
![miku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/699b30c8-23e0-7d7f-2912-f09170ef2bc3.png)


# 3. 髪型と目の色を変えると
- 別のキャラクターになります

```Python
import cv2
import numpy as np
 
img = np.full((500, 500, 3), (211, 211, 211), dtype=np.uint8)

# 左髪後ろ中央
cv2.ellipse(img, (160, 300), ( 30, 120),  20,   0,  90, ( 50, 148, 188), thickness=-1)
# 左髪後ろ左
cv2.ellipse(img, (160, 300), ( 30, 120),  40,   0,  90, ( 50, 148, 188), thickness=-1)
# 左髪後ろ右
cv2.ellipse(img, (150, 310), ( 30, 120),   0,   0,  90, ( 50, 148, 188), thickness=-1)

# 右髪後ろ中央
cv2.ellipse(img, (340, 300), ( 30, 120), 340,  90, 180, ( 50, 148, 188), thickness=-1)
# 右髪後ろ右
cv2.ellipse(img, (340, 300), ( 30, 120), 320,  90, 180, ( 50, 148, 188), thickness=-1)
# 右髪後ろ左
cv2.ellipse(img, (350, 310), ( 30, 120),   0,  90, 180, ( 50, 148, 188), thickness=-1)

# 顔
cv2.circle(img, (250, 280), 150, (230, 240, 255), thickness=-1)

# 左眉
cv2.ellipse(img, (180, 270), (30, 10),   0, 210, 330, (0, 0, 0)) 
# 右眉
cv2.ellipse(img, (320, 270), (30, 10),   0, 210, 330, (0, 0, 0)) 

# 髪土台
cv2.ellipse(img, (250, 175), ( 90, 150),  90,  90, 270, (  0, 186, 255), thickness=-1)
# 前髪中央左
cv2.ellipse(img, (240, 250), (110, 110), 220,   0,  40, (  0, 186, 255), thickness=-1)
# 前髪中央右
cv2.ellipse(img, (260, 130), ( 60, 150), 140, 270, 360, (  0, 186, 255), thickness=-1)
# 前髪中央右調整
cv2.ellipse(img, (300, 170), ( 30, 140), 140, 270, 360, (230, 240, 255), thickness=-1)

# 触覚左
cv2.ellipse(img, (130, 260), ( 60, 130),   0,  90, 270, (  0, 186, 255), thickness=-1)
# 触覚左調整
cv2.ellipse(img, (135, 200), ( 20, 180), 180, 270, 360, (230, 240, 255), thickness=-1)
# 前髪左
cv2.ellipse(img, (170, 180), ( 60, 120), 200, 270, 360, (  0, 186, 255), thickness=-1)
# 触覚左影
cv2.ellipse(img, (125, 300), ( 10,  70),   0,  90, 270, ( 36, 106, 134), thickness=-1)
# 触覚右
cv2.ellipse(img, (370, 260), ( 60, 130),   0, 270, 450, (  0, 186, 255), thickness=-1)
# 触覚右調整
cv2.ellipse(img, (365, 200), ( 20, 180),   0,   0,  90, (230, 240, 255), thickness=-1)
# 前髪右
cv2.ellipse(img, (370, 155), ( 20, 220),   0,   0,  90, (  0, 186, 255), thickness=-1)
cv2.ellipse(img, (310, 210), ( 70,  70),  35, 180, 360, (  0, 186, 255), thickness=-1)
# 触覚右影
cv2.ellipse(img, (375, 300), ( 10,  70), 180,  90, 270, ( 36, 106, 134), thickness=-1)

# 口
cv2.ellipse(img, (250, 350), ( 15,  15),   0,  40, 140, (  0,   0,   0)) 

# 左頬
cv2.ellipse(img, ((160, 350), ( 60,  80), -25), (225, 225, 250), thickness=-1)
# 右頬
cv2.ellipse(img, ((340, 350), ( 60,  80),  25), (225, 225, 250), thickness=-1)

# 左目白
cv2.ellipse(img, ((180, 320), ( 60,  80),   0), (255, 255, 255), thickness=-1)
# 左目青大
cv2.ellipse(img, ((185, 315), ( 48,  70),   0), (128,  77,   0), thickness=-1)
# 左目青中
cv2.ellipse(img, ((190, 326), ( 40,  50),   0), (186, 118,   0), thickness=-1)
# 左目青小
cv2.ellipse(img, ((193, 330), ( 30,  40),   0), (255, 162,   0), thickness=-1)
# 左目青中央
cv2.ellipse(img, ((190, 310), ( 15,  20),   0), (147,  84,   0), thickness=-1)  
# 左目灰
cv2.ellipse(img, ((195, 340), ( 20,  20),   0), (169, 169, 169), thickness=-1)
# 左目光大
cv2.ellipse(img, ((190, 290), ( 10,  15), -20), (255, 255, 255), thickness=-1)
# 左目光小
cv2.ellipse(img, ((185, 290), ( 10,   5),   0), (255, 255, 255), thickness=-1)

# 左目線上
cv2.ellipse(img, (175, 300), ( 20,  40),  90, 120, 240, (  0,   0,   0), thickness=5) 
# 左目線下
cv2.ellipse(img, (175, 300), ( 20,  40),  60, 120, 190, (  0,   0,   0), thickness=5) 

# 右目白
cv2.ellipse(img, ((320, 320), (  60,  80),   0), (255, 255, 255), thickness=-1)
# 右目青大
cv2.ellipse(img, ((315, 315), (  48,  70),   0), (128,  77,   0), thickness=-1)
# 右目青中
cv2.ellipse(img, ((310, 326), (  40,  50),   0), (186, 118,   0), thickness=-1)
# 右目青小
cv2.ellipse(img, ((307, 330), (  30,  40),   0), (255, 162,   0), thickness=-1) 
# 右目青中央
cv2.ellipse(img, ((310, 310), (  15,  20),   0), (147,  84,   0), thickness=-1)
# 右目灰
cv2.ellipse(img, ((305, 340), (  20,  20),   0), (169, 169, 169), thickness=-1)
# 右目光大
cv2.ellipse(img, ((320, 290), (  10,  15), -20), (255, 255, 255), thickness=-1)
# 右目光小
cv2.ellipse(img, ((315, 290), (  10,   5),   0), (255, 255, 255), thickness=-1)   

# 右目線上
cv2.ellipse(img, (325, 300), ( 20,  40),  90, 120, 240, (  0,   0,   0), thickness=5)
# 右目線下
cv2.ellipse(img, (325, 300), ( 20,  40), 120, 170, 250, (  0,   0,   0), thickness=5) 

# ピン留め左上
cv2.line(img, (150, 230), (140, 220), (255, 255, 255), thickness=10)
# ピン留め左下
cv2.line(img, (140, 245), (130, 235), (255, 255, 255), thickness=10)
# ピン留め右上
cv2.line(img, (260, 210), (270, 200), (255, 255, 255), thickness=10)
# ピン留め右下
cv2.line(img, (300, 240), (310, 230), (255, 255, 255), thickness=10)

# カチューシャ左
cv2.ellipse(img, (145,  160), ( 100,  20),  315,   180,  360, (255, 255, 255), thickness=-1) 
# カチューシャ右
cv2.ellipse(img, (355,  160), ( 100,  20),   45,   180,  360, (255, 255, 255), thickness=-1) 
# リボン左内側
cv2.circle(img, (190, 100),  50, (255, 255, 255), thickness=-1)
# リボン左外側
cv2.ellipse(img, (160,  60), ( 80,  70),  90,   0,  90, (255, 255, 255), thickness=-1) 
# リボン右内側
cv2.circle(img, (310, 100),  50, (255, 255, 255), thickness=-1)
# リボン右外側
cv2.ellipse(img, (340,  60), ( 80,  70), 180, 180, 270, (255, 255, 255), thickness=-1) 
# リボン中心
cv2.rectangle(img, (240,  80), (260, 120), (255, 255, 255), thickness=-1)

cv2.imwrite("rin.png", img)
```
![rin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/1b47976e-dac6-c9dc-37c2-92e80ddafabf.png)

※ この作品は<a href="http://piapro.jp/license/pcl/summary">ピアプロ・キャラクター・ライセンス</a>に基づいてクリプトン・フューチャー・メディア株式会社のキャラクター「鏡音リン」を描いたものです