Python, OpenCVを使ってPCのカメラからタイムラプスを撮影する


# はじめに
アリを飼うことにしました。クロオオアリです。
家のPCでアリたちの様子をタイムラプス撮影したいと思いました。
一定時間ごとに写真を撮影し、最後に撮影した写真をつなげることでタイムラプス撮影を行うことができたため、その方法をまとめました。
プログラミング初心者なので、勉強も兼ねて初歩的な内容も書いています。

# 方法
Python 3.7.3
Anaconda Prompt
PC付属のwebカメラを使用

事前にコマンドプロンプト上で次のように打ち込み、OpenCVをインストールする必要があります。

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

# コード

``` python:timelaps.py
import cv2
import glob
import os
import shutil
import time
from datetime import datetime

### もろもろの初期設定

date = datetime.now().strftime("%Y%m%d_%H%M%S")
if not os.path.exists(date):
    os.mkdir(date)   # 画像保存用のフォルダ作製

# とりあえずwaiting_time秒待ってから撮影をスタートさせる
capture_interval = 0.5 # 画像取得間隔（秒）
waiting_time = 0
print('Recording will be started in {0} seconds'.format(waiting_time))
time.sleep(waiting_time)
print('Start')

### 画像の撮影
def capture():
    cap = cv2.VideoCapture(0) # 任意のカメラ番号に変更する。1台だけならカメラ番号は0。
    while True: # capture_interval秒ごとに画像の読み込みおよび保存を行う。
        ret, frame = cap.read() # カメラからキャプチャされた画像をframeとして読み込む
        cv2.imshow("camera", frame) # frameを画面に表示。なぜかこいつを残しておかないとenterで操作を止められない。
        k = cv2.waitKey(1)&0xff # キー入力を待つ。引数は入力待ち時間。
        # カレントディレクトリ内にある「img」フォルダに「(date).jpg」というファイル名でファイルを保存
        date_time = datetime.now().strftime("%Y%m%d%H%M%S")
        path = "./{0}/".format(date) + date_time + ".jpg"
        cv2.imwrite(path, frame) # 画像をフォルダへ保存

        # エンターキーを押したら撮影終了
        if k == 13:
            break 
        time.sleep(capture_interval)
    cap.release()
    cv2.destroyAllWindows()

### 画像のタイムラプス化
def timelaps():
    images = sorted(glob.glob('{0}/*.jpg'.format(date))) # 撮影した画像の読み込み。
    print("画像の総枚数{0}".format(len(images)))

    if len(images) < 30: #FPS設定
        frame_rate = 2  
    else:
        frame_rate = len(images)/30

    width = 640
    height = 480
    fourcc = cv2.VideoWriter_fourcc('m','p','4','v') # 動画のコーデックをmp指定。（ちょっと違うが）動画の拡張子を決める、    
    video = cv2.VideoWriter('{0}.mp4'.format(date), fourcc, frame_rate, (width, height)) # 作成する動画の情報を指定（ファイル名、拡張子、FPS、動画サイズ）。

    print("動画変換中...")
    
    for i in range(len(images)):
        # 画像を読み込む
        img = cv2.imread(images[i])
        # 画像のサイズを合わせる。
        img = cv2.resize(img,(width,height))
        video.write(img) 
    
    video.release()
    print("動画変換完了")

def capture_delete():
    shutil.rmtree(date)

if __name__ == '__main__':
    start = time.time()

    capture()
    timelaps()
    capture_delete()

    elapsed_time = time.time() - start
    print ("処理にかかった時間は:{0}".format(elapsed_time) + "[sec]")
```
##コードの解説
### 概要
1. 一定間隔で写真撮影し、フォルダに保存
2. フォルダに保存した画像をつなげた動画（タイムラプス）を作製、保存
3. タイムラプスの材料となった大量の写真をフォルダごと削除
4. 最後に、所要時間を報告。

### def capture() 以前
画像を保存するフォルダをmkdirにて作成します。フォルダ名は何でも構いません。今回は、2020年9月20日13時57分09秒に作成した場合「20200920_135709」という名前のフォルダが作成されます。同名のフォルダが既に存在するとos.mkdirのところがエラーを吐くので、同名のフォルダがない場合のみフォルダを作成するようにしています。
画像を取得する間隔（waiting_time）を設定します。動かしてからいきなり撮影が始まると、蟻より先に僕が映ってしまうので、waiting_time秒だけ待たせます。

### capture()
コード中のコメントを読んでいただけると、何をしているかわかると思います。撮影されたのが2020年9月20日12時34分56秒ならファイル名は「20200920123456」になります。容量を食いすぎるのがなんとなく怖いのでpngではなくjpgにしています。k == 13というのはエンターキーが押されることに相当します。保存先を指定するのにformat()メソッドがとても便利でした。

### timelaps()
まず、撮影した画像を読み込みます。sorted関数を使って数字の若い順（つまり撮影された順番）にソートされたリストを生成しています。が、ぶっちゃけ今回の方法なら

```
images = glob.glob('{0}/*.jpg'.format(date))
```

でも問題なく動きます。
次に1秒間に用いられる画像の枚数（FPS）を設定します。とりあえず長時間撮影しても30秒の動画に収まるようにしてみました。
動画の画面サイズを設定（画像のデフォルトが640*480なのでそれに合わせています。）したら動画を作っていきます。cv2.imread()でi番目の画像を読み込み、画像のサイズを動画用のサイズに整えた後に動画へ差し込んでいきます。これを全画像に対して繰り返すことで動画を作成します。

### capture_delete()
画像の保存用に作ったフォルダ（date）を削除します。
1行なのでわざわざ関数として定義しなくても良いのですが、なんとなく見栄えが良いので関数にしてみました。

# 結果
コマンドプロンプト上で実行すると、以下のような文章が表示されます。

```
Recording will be started in 0 seconds
Start
[ WARN:0] global C:\Users\appveyor\AppData\Local\Temp\1\pip-req-build-2b5g8ysb\opencv\modules\videoio\src\cap_msmf.cpp (435) `anonymous-namespace'::SourceReaderCB::~SourceReaderCB terminating async callback
画像の総枚数10
動画変換中...
動画変換完了
処理にかかった時間は:12.048035621643066[sec]
```

[WARN:0]のところはよく理解できませんでした。動いたので良しとしています。テキトーですね。
Pythonを動かしたディレクトリにおいて、動画のファイルが得られています。
蟻が届いてから、実際に撮影した動画を載せるつもりです。

[200925追記]蟻が届いたので撮影しました。
[201129追記]撮影した動画は以下のURLからご覧いただけます。アップロードするのを忘れておりました。
https://youtu.be/asf1edT4H_0

# おわりに
PCのカメラから写真を撮影し、撮影した写真をもとにタイムラプスを作成するプログラムについて、初心者向けに述べました。
ディレクトリ、for, whileなどの知識さえあればそんなに難しくないので、プログラミング初心者の方もぜひやってみてください。
また、勉強中の身ですので、ご指摘・コメント・アドバイス等ございましたら、いただけますと幸いです。

## 参考文献
作成にあたり参考にさせていただいたページを以下に示します。
#### format() メソッド
https://gammasoft.jp/blog/python-string-format/
#### OpenCV
OpenCV全般
http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_gui/py_image_display/py_image_display.html
http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_gui/py_video_display/py_video_display.html#display-video

画像のキャプチャ
http://rikoubou.hatenablog.com/entry/2019/03/07/153430

waitKey()関数
https://qrunch.net/@opqrstuvcut/entries/XZsZ0jUEX6RatMER?ref=qrunch
https://www.kishugiken.co.jp/cn/code02.html

画像から動画への変換
https://yusei-roadstar.hatenablog.com/entry/2019/11/29/174448
#### os, shutil
https://note.nkmk.me/python-os-remove-rmdir-removedirs-shutil-rmtree/

以上です。ここまで読んでくださりありがとうございました。