[OpenCV]ArUcoマーカー検出のサンプルスクリプト

## はじめに

OpenCVのARマーカーのライブラリArUcoのPython3サンプルスクリプトです

## サンプルスクリプト

ソースをgithubにおきました。

https://github.com/zgw426/OpenCV_SamplesForMyself


## 参考リンク

- [[Open CV] OpenCVの描画機能](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_gui/py_drawing_functions/py_drawing_functions.html)


## 準備

#### 環境

Windows10,Python 3.8.6 を使いました。
ストリーミングを使う場合は、カメラモジュールを付けた`Raspberry Pi 3 model B`を使いました。
ラズパイにはMJPEG-Streamerでストリーミングができるようにしています。

#### OpenCVインストール

Python3 が使える環境なら以下のコマンドでインストールすればOpenCVが使えるはずです。

```
$ pip install opencv-contrib-python
```

#### ARマーカー画像の作成

以下スクリプトを実行するとARマーカーが出力されます(`ar.png`)
[github](https://github.com/zgw426/OpenCV_SamplesForMyself)のopcv_outputARmark02.pyを実行すると他のマーカーも出力できます。

```python:opcv_outputARmark01.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import cv2
aruco = cv2.aruco
dictionary = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)

def arGenerator():
    fileName = "ar.png"
    # 0: ID番号，150x150ピクセル
    generator = aruco.drawMarker(dictionary, 0, 150)
    cv2.imwrite(fileName, generator)
    img = cv2.imread(fileName)

arGenerator()
```

スクリプトの実行コマンド

```
$ python opcv_outputARmark01.py
```

出力されるマーカー`ar.png`です。

<img width=220 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/bdaf4c33-c2d7-2318-1ec6-7fa14c52866d.png" />

## サンプルスクリプトの概要

githubに格納したデータの概要です。

|ファイル名|検出対象|描画|出力|
|---|---|---|---|
|opcv_outputARmark01.py|－|ARマーカー|画像|
|opcv_outputARmark02.py|－|ARマーカー|画像|
|opcvCapImg01_drawID.py|画像|ID||
|opcvCapImg02_drawText.py|画像|文字||
|opcvCapImg03_drawLine.py|画像|線||
|opcvCapImg04_drawRectangle.py|画像|四角形||
|opcvCapImg05_drawCircle.py|画像|円||
|opcvCapImg06_drawPolylines.py|画像|多角形||
|opcvCapImg07_drawIMG.py|画像|画像||
|opcvCapImg08_drawAxis.py|画像|3D軸||
|opcvCapVideo01_drawID.py|動画|ID|－|
|opcvCapVideo02_drawID_outMP4.py|動画|ID|動画|
|opcvCapVideo03_drawRectangle_outMP4.py|動画|四角形|動画|
|opcvCapVideo04_drawText_outMP4.py|動画|文字|動画|
|opcvCapVideo05_drawAxis_outMP4.py|動画|3D軸|動画|
|opcvCapVideo06_drawImg_outMP4.py|動画|画像|動画|
|opcvCapVideo07_drawVideo_outMP4.py|動画|動画|動画|
|(dummy)test-mapping.mp4____.txt|－|－|－|
|(dummy)test-video.mp4____.txt|－|－|－|
|test-img01.png|－|－|－|
|test-img02.png|－|－|－|
|test-img03.png|－|－|－|
|test-mapping.png|－|－|－|

- カラム説明
    - 検出対象
        - ARマーカーを検出する対象
        - 画像
            - PNG形式の画像を使用します
        - 動画
            - 以下3種類の形式を含みます
                - <img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/ec1dcb1f-db5d-262c-ccff-1a93f1059d09.png" />
                    - MP4形式の動画
                    - ストリーミング(ラズパイ＋カメラ)
                    - カメラデバイス（WindowsPC＋カメラ）
    - 描画
        - 検出したARマーカーに付与する情報の種類
        - (ID, 文字, 画像, 動画 など)
    - 出力
        - 実行結果をファイル出力する形式
        - (画像, 動画)
- ダミーファイル
    - mp4ファイルの変わりにダミーのテキストファイルを格納している
    - (dummy)test-mapping.mp4____.txt
        - `test-mapping.mp4`のダミーファイル
        - https://youtu.be/S-h031SBLaQ
    - (dummy)test-video.mp4____.txt
        - `test-video.mp4`のダミーファイル
        - https://youtu.be/qlqU_y5hu0k
    - 動画データはこちらでダウンロードできます
        - https://note.com/agw/n/na2f22f876228
- 画像ファイル
    - ARマーカー検出用の画像
        - test-img01.png
            - <img width=220 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/769b6562-4c8b-22eb-2703-7e99c8f36765.png" />
        - test-img02.png
            - <img width=220 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/f2b77f0a-3039-39cb-e873-7882987273e0.png" />
        - test-img03.png
            - <img width=220 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/16542d3d-9598-4c5f-fb24-94d1ecdc7b58.png" />
    - マッピング用の画像
        - test-mapping.png
            - <img width=220 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/972fd0b2-b11f-5f5a-e492-c792f6171514.png" />

## 画像からマーカーを検出するスクリプト

#### [IMG-01] IDを描画:opcv_outputARmark01.py

`aruco.drawDetectedMarkers(img, corners, ids, (0,255,0))`で検出した全てのARマーカーに対し、IDとマーカーの4隅を頂点とした四角形を描画します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/00cc8a92-0b7d-a378-c808-adc16d544b5f.png)

#### [IMG-02] 文字を描画:opcvCapImg02_drawText.py

`cv2.putText(・・・)`で文字を描画します。サンプルではマーカーの4隅の座標を描画しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/d280f304-2db2-e99e-807c-9ccba094a451.png)


#### [IMG-03] 線を描画:opcvCapImg03_drawLine.py

`cv2.line(・・・)`で直線を描画します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/a4f70b99-cad2-f0d7-13e1-8777f83570f5.png)


#### [IMG-04] 四角形を描画:opcvCapImg04_drawRectangle.py

`cv2.rectangle(・・・)`で四角形を描画します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/8a9c2e30-7154-bd0d-db2c-5da12446cfaf.png)

#### [IMG-05] 円を描画:opcvCapImg05_drawCircle.py

`cv2.circle(・・・)`で円を描画します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/058ef7ac-d434-fdeb-8f4a-fe0d6ac7dcb1.png)

#### [IMG-06] 多角形を描画:opcvCapImg06_drawPolylines_outImg.py

`cv2.polylines(・・・)`で多角形を描画します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/940f607f-90be-503c-eba8-98cd5c721035.png)

#### [IMG-07] 画像を描画


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/49f18e29-0281-a41d-2653-f3fda2ded1c8.png)

#### [IMG-08] 3D軸を描画

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/6a918c53-e98a-3da5-06a3-891d9e398096.png)


## 動画からマーカーを検出するスクリプト

※動画 [`test-video.mp`](https://youtu.be/qlqU_y5hu0k)を使用した実行結果を紹介します。

<img width=330 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/bdfbaf9d-7147-3f76-bed8-0f9fc533edb3.png" />


#### [VIDEO-01] IDを描画:opcvCapVideo01_drawID.py

マーカーを検出しIDを描画します。他の動画系のサンプルと違い結果を動画出力しません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/65a263fa-1a1f-7219-44e3-6ff99d9142e1.png)
https://youtu.be/XVpjjGrzeAw

#### [VIDEO-02] IDを描画し動画ファイルを出力:opcvCapVideo02_drawID_outMP4.py

[VIDEO-01]と同様に、マーカーを検出しIDを描画します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/65a263fa-1a1f-7219-44e3-6ff99d9142e1.png)
https://youtu.be/XVpjjGrzeAw

#### [VIDEO-03] 四角形を描画し動画ファイルを出力:opcvCapVideo03_drawRectangle_outMP4.py

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/d6436375-dc16-ecd3-dcd9-bc67fc31f8b9.png)
https://youtu.be/eyuDafmZQ28

#### [VIDEO-04] 文字を描画し動画ファイルを出力:opcvCapVideo04_drawText_outMP4.py

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/108b79db-53b8-3b94-71e2-52165fde3df8.png)
https://youtu.be/zTJSGT5dQ_g

#### [VIDEO-05] 3D軸を描画し動画ファイルを出力:opcvCapVideo05_drawAxis_outMP4.py

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/e1cf56d0-1796-a488-72f0-f1a25578d9de.png)
https://youtu.be/nyaPeCBZ2hQ

#### [VIDEO-06] 画像を描画し動画ファイルを出力:opcvCapVideo06_drawImg_outMP4.py

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/2f4e57c5-0f40-c24d-0359-ffaa6df97a1d.png)
https://youtu.be/1aR9-r7BFjQ


#### [VIDEO-07] 動画を描画し動画ファイルを出力:opcvCapVideo07_drawVideo_outMP4.py

検出したマーカーに動画ファイル(MP4)を描画します。出力した動画には、マーカーに合わせて描画した動画が含まれない(?)らしく、出力した動画には正しく描画されませんでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/9651263e-e5e9-4765-6887-4392e4eb0366.png)
https://youtu.be/jqJlxl5lzkI



## カメラデバイスやストリーミングを使用する場合

<img width=330 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/c4b60c62-6338-4593-000e-049958c889cb.png" />

`cv2.VideoCapture(・・・)`は、カメラデバイスやMJPEG-Streamerにも対応しています。
`cv2.VideoCapture(0)`でカメラデバイスを、`cv2.VideoCapture("http://{IP Address}:8090/?action=stream")`でストリーミングを指定します。

以下の図のようにストリーミングに対しマーカー検出/描画ができるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/ac4972b3-f684-3c98-0863-463d83197201.png)


[ここ](https://github.com/zgw426/OpenCV_SamplesForMyself)の動画系のサンプルスクリプトで、カメラデバイス、ストリーミングが使用できることは確認しました。

[ここ](https://github.com/zgw426/OpenCV_SamplesForMyself)のスクリプトでカメラデバイスやストリーミングを使う例を紹介します。`opcvCapVideo07_drawVideo_outMP4.py`だと、以下に抜粋した変数`targetVideo`,`outputVideo`の値を変更すれば使用できます。

```python:opcvCapVideo07_drawVideo_outMP4.py
# --- 抜粋 --- #

#targetVideo = 0 # カメラデバイス
#targetVideo = "test-video.mp4" # 動画
#targetVideo = "http://{IP Address}:8090/?action=stream" # MJPG-Streamer
targetVideo = "test-video.mp4"

#mappingVideo= 0 # カメラデバイス
mappingVideo= "test-mapping.mp4" # 動画
#mappingVideo= "http://{IP Address}:8090/?action=stream" # MJPG-Streamer
outputVideo= "editV07.mp4"

# --- 抜粋 --- #
```

例）ストリーミングからマーカーを検出し画像を描画した場合のサンプル

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">2021年、最初の作品は&quot;合わせ鏡AR&quot;です。<a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://twitter.com/hashtag/opencv?src=hash&amp;ref_src=twsrc%5Etfw">#opencv</a> <a href="https://t.co/F1kZA6JtyT">pic.twitter.com/F1kZA6JtyT</a></p>&mdash; suo-takefumi (@zsipparu) <a href="https://twitter.com/zsipparu/status/1344926099579359238?ref_src=twsrc%5Etfw">January 1, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

例）ストリーミングからマーカーを検出し動画(MP4)を描画した場合のサンプル

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">&quot;合わせ鏡AR&quot; の動画バージョン<br>牛がキョロキョロしてます。<a href="https://twitter.com/hashtag/%E4%B8%91?src=hash&amp;ref_src=twsrc%5Etfw">#丑</a> <a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://twitter.com/hashtag/opencv?src=hash&amp;ref_src=twsrc%5Etfw">#opencv</a> <a href="https://twitter.com/hashtag/AR?src=hash&amp;ref_src=twsrc%5Etfw">#AR</a> <a href="https://t.co/FPn5ZWqvO1">pic.twitter.com/FPn5ZWqvO1</a></p>&mdash; suo-takefumi (@zsipparu) <a href="https://twitter.com/zsipparu/status/1344949614772768769?ref_src=twsrc%5Etfw">January 1, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## 参考：ストリーミングサーバーの構築

MJPG-Streamerのインストールはこちらを参考にしました。
https://qiita.com/suo-takefumi/items/2ae5527869dc13d038a9

## まとめ

OpenCVの使い方を調べたのでその情報をまとめました。
OpenGLだと3Dオブジェクトも扱えるらしいので、そっちも調べようと思います。