pythonのopencvでカメラ画像を定期的に取得する最短の方法

# 背景

opencv を用いて簡単にカメラ画像を取得する方法と，それを定期的に実行する方法を紹介する．ここでのカメラとは，USBカメラや内蔵カメラなどopencvで制御できるカメラに限定です．
設定などは，anaconda3系でpythonをお使いなのが前提の話です． windows, mac, linux のどのOSでも動作します． 

一眼レフなど高級なカメラでの自動制御を行いたい場合は， gphoto を使うのが must のようです．gphotoの対応機種は，http://www.gphoto.org/proj/libgphoto2/support.php にあって，かなりのものが対応しています． [一眼レフカメラをラズパイからコントロール](https://qiita.com/class2glass/items/ab09d7abed9db5249bd7) が参考になります．

## 環境設定

### opencv の install 

anaconda にはデフォルトでopencvが入ってないので，

``` bash:
conda install -c conda-forge opencv
``` 

を実行して入れます． 


### schedule 

これは， anaconda には入ってないので， pip でいれます．

``` python:
pip install schedule
``` 

なぜ anaconda 入ってないのか，，とかは開発のdetailに依存するので深追いは不要です．


# 一回だけカメラの画像を取得する方法

## コード

``` python:oneshot.py
#!/usr/bin/env python

import cv2

deviceid=0 # it depends on the order of USB connection. 
capture = cv2.VideoCapture(deviceid)

ret, frame = capture.read()
cv2.imwrite('test.jpg', frame)
```

- capture = cv2.VideoCapture(deviceid)でデバイスIDを取得． ただし，デバイスIDは，USBカメラの場合は，指した順番などで変わるのに注意する．普通は0番が内蔵，USBカメラが1番のはず．
- ret, frame = capture.read() この瞬間に画像データが frame に保存される．
- cv2.imwrite('test.jpg', frame) で，取得した画像をjpgに保存する．保存ファイル形式は拡張子で決まる．

OpenCVで画像を保存する方法などは，[Python, OpenCVで画像ファイルの読み込み、保存](https://note.nkmk.me/python-opencv-imread-imwrite/) が参考になります． エラーの判定や，グレースケールへの変更などは， [Mac内蔵カメラの動画をpythonでキャプチャ](https://qiita.com/seigot/items/9ae0dc3fed6d43bec828) が参考になります．



## 実行方法

oneshot.py があるディレクトリで， python に PATH が通っている環境で，

``` bash:
python oneshot.py
``` 

もしくは，

``` bash:
chmod +x oneshot.py
./oneshot.py
``` 

です． linux or mac であればターミナルを使う． windows はコマンドプロンプトでもよいけど， IDE的なアプリから実行の方が楽かもしれない．  


# スケジュールにしたがってカメラの画像を取得する方法

## コード

``` python:scheduled_shot.py
#!/usr/bin/env python

import cv2
import datetime
import schedule
import time

deviceid=0 # it depends on the order of USB connection. 
capture = cv2.VideoCapture(deviceid)

def job():
	ret, frame = capture.read()
	strdate=datetime.datetime.now().strftime('%Y%m%dT%H%M%S') 
	fname="image_" + strdate + ".jpg"
	cv2.imwrite(fname, frame) 
	print(fname + " is created.")

#do job every 10 seconds
schedule.every(1/6).minutes.do(job)

while True:
  schedule.run_pending()
  time.sleep(1)
```

今度は， 10秒に一回，画像を取得する． schedule の使い方は， [scheduleを使ってモジュールを定期実行させよう](https://qiita.com/dgkmtu/items/3bd3794b44a0aa03bfe3) が参考になります． 

while True という書き方はソフトウェアではあまり見かけないかもしれないですが，ハードウェアの制御では必携の書き方です．これがあることで，無限ループになって，例えば炊飯器であれば電源コードを指したらずっと動き続けるという機能が実装できます． ここでは， while True があることで，無限に10秒に一回画像を撮り続けることができます． 無限ループから脱出するには， cntl + c などでプログラムを強制的に終了する必要があります(この書き方の場合であって，一定時間すぎたら終了するようにタイマーを仕掛けることもできます．)． 
 

ファイル名が書き変わらないように，かつ，時系列にファイル名が並ぶように， datetime を用いてファイル名を生成する． [Pythonのdatetimeで日付や時間と文字列を変換](https://www.sejuku.net/blog/23606) を参考ください． 

連続取得の場合は，画像が取得できたら，ファイル容量を確認して，その容量だと，必要な容量がどのくらいか検討しましょう．気がついたら，diskの残りの容量がゼロということもよくありますので．

## 実行方法

``` bash:
python scheduled_shot.py
...
image_20201001T110144.jpg is created.
image_20201001T110154.jpg is created.
image_20201001T110204.jpg is created.
``` 

今後は，実行すると，10秒ごとに画像が保存される． 強制終了しかないので， cntl + c などでプログラムを強制的に終了されることで終了する． 


# そのほか
## 画像の閲覧

画像を python の imshow などで見ようとすると，最近の画像は容量がでかいので出力がかなり遅くなる． 画像は， png や jpeg に保存してから見るようにしよう．

## より高度な自動化へ

これだけだと使い勝手が悪い場合は， [PyQt5でGUIを作ろう！導入から使い方まで解説](https://www.sejuku.net/blog/75467)など，GUIを作ることも簡単にできる．ただし，安定性という観点からは，CUIで正しく動くのを作り込んでからがよい．

また，このようなプログラムでは，データ取得するプログラムはそれに専念させる方がよいです．カメラから画像を取得して保存するCPUやプログラムはデータをロストしないように専念してもらう．後段でデータを吸い上げたり，転送したり，加工するなど，2次的な操作は別のプログラムで行う．