【OpenCV】カメラ映像の1フレーム前と今のフレームで類似点を結ぶ【Python】

![Screenshot from 2022-10-27 23-53-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/710848/239be067-2bd5-9346-aa70-90c101fa65c9.png)
動画のリンクはこの記事の最下部からどうぞ！
# 開発経緯
画像解析で前のフレームと今のフレームを比較して、2Dカメラ一つで立体的な空間データを取るための下準備として今回のプログラムを開発しました。OpenCVを勉強し始めてまだ1ヶ月程度なのでプログラムが非効率かもしれません。有志の方、ぜひ改善プログラムをコメントでご指摘いただけると幸いです。Python使用歴は2022/4から始めたので今で6ヶ月です。

# 開発環境
* Ubuntu 20.04 LTS
* Python 3.8.10
* Opencv-python 4.6.0

# 必要モジュール
* Opencv-python
* numpy

# プログラムを書く：カメラの下準備編

今回はプログラムは一つのファイルで完結するので適当に好きなディレクトリに **main.py**を作ってください。
まずモジュールをインポートします。
``` Python
import cv2
import numpy as np
```
次にカメラからの映像をPythonを使って扱っていきます。Pythonでカメラの映像を取得する下準備をします。
``` Python
# 続けて書く
~~~
WIDTH = 800
HEIGHT = 600
FPS = 30
VIDEO_TARGET = 1 #カメラID
```
### VIDEO_TARGETという変数について
この変数の数字はパソコンの環境によって変わります。私の場合はDellのパソコンで1を設定することで内蔵カメラを指定することができました。友達のパソコンでは0を指定することで内蔵カメラを指定することができていたので、ご自身のパソコンに応じて**柔軟に対応**してください。
**スマートフォン**のカメラを使いたい場合は、Iriunを使うことをおすすめします。私もつかっています。
[Iriunダウンロードページ](https://iriun.com/)
Iriunをスマホにもダウンロードして正常に接続できたらVIDEO_TARGETの変数の数字を2や3に変えてみてください。

# プログラムを書く：カメラの映像を加工しよう編
```Python
cap_file = cv2.VideoCapture(VIDEO_TARGET)
count = 1
cap_file.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('H','2','6','4'))
cap_file.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
cap_file.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)
cap_file.set(cv2.CAP_PROP_FPS, FPS)
```

上のプログラムはカメラのデータをOpencvで処理しやすいように、さっき書いた変数をもとに解像度やフレームレートを変えます。

### フレームごとに処理させる
```Python
#前のフレーム
previous_frame = []


#繰り返しのためのwhile文
while True:
    print('あの一瞬（とき）は戻ってこない...',count)
    #カメラからの画像取得
    ret, frame = cap_file.read()
    
    orb = cv2.ORB_create()
    gray = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
    canny = cv2.Canny(gray,5,50)
    
    height = canny.shape[0]
    width = canny.shape[1]
    center = (int(width/2),int(height/2))

    angle = 0
    scale = 1.0
    trans = cv2.getRotationMatrix2D(center,angle,scale)
    image2 = cv2.warpAffine(frame,trans,(width,height))

    previous_frame.append(image2)

    img_src1 = image2
    img_src2 = previous_frame[count - 1]
    img_dst = img_src1


    keypoint1,descript1 = orb.detectAndCompute(img_src1,None) #現在のフレームの写真
    keypoint2,descript2 = orb.detectAndCompute(img_src2,None) #前のフレームの写真をゲット
    bf = cv2.BFMatcher(cv2.NORM_HAMMING,crossCheck=True)
    matches = bf.match(descript1,descript2)
    matches = sorted(matches,key=lambda x:x.distance)
    img3 = cv2.drawMatches(img_src1, keypoint1, img_src2, keypoint2, matches, img_dst, flags=2)
    
    #カメラの画像の出力
    cv2.imshow('camera' , img3)
    
    #繰り返し分から抜けるためのif文
    cv2.waitKey(1)
    cv2.destroyAllWindows()
    
    count +=1
#メモリを解放して終了するためのコマンド
cap.release()
cv2.destroyAllWindows()
```

まず、orb = cv2.ORB_create()でORB抽出器を初期化します。
cannyでエッジ検出をしています。
BFMatcherやdrawMatchesで特徴点を線で結びます。
## ちょっと小ネタ:類似点検知で的確な角度とは
今、私が書いたプログラムでは**AKAZE**というものを利用しました。実はこれ前バージョン**KAZE**の高速版なんですね。
てことは２つ兄弟関係という捉え方でいいのかな..?
とても興味深い記事を発見しました。

![Screenshot from 2022-10-28 00-23-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/710848/e47424b7-1a60-7a51-3a16-8b321dd90025.png)


> 引用　[三次元変形を受けた画像に対する
マッチング手法の比較](https://www.lab.kochi-tech.ac.jp/yoshilab/thesis/1150295.pdf)

この画像は論文資料の一部です。論文の内容は写真一枚に対する類似点検出についてですが、本記事のように写真２枚を扱うときでも同様のことが言えるかもしれないと思いました。
つまり、一つのカメラだけ３０度向かせて、もう一つのカメラは角度を変えないという方法です。
精度がぐんと上がるかもしれません。

ちなみに下の画像は両カメラ45度内側に傾けて実験して見た結果です。
回転角90度となるわけですが、全く類似点検出をしていません。（あえてここでは類似点の本数を2本に限定しています）
![mosaic_20221028003842.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/710848/ec20a1a6-4e60-57a6-dae2-4a1575cbeacb.png)
普通にパソコンの画面にTwitterうつってたからモザイク処理をしたら逆になんかね、ちょっと（苦笑）
パソコン周り汚いですが、耐えてください！
### 結論 30度にせよ！！！

## サンプル映像

https://youtu.be/pM362ZzdUaw

以上で今回の記事はおしまいです。お疲れ様でした。