Raspberry Pi 4B にWEBカメラをつなげて静止画を連続撮影してタイムラプス動画を生成する


#動機と背景
先日引越をして、新居のマンションからの景色が良い感じなので、ベランダからの風景でタイムラプス動画をつくりたいな、っておもいました。
オンライン飲み会用に買ったWEBカメラが余っている（このごろオンライン飲み会してない）ので、カメラもあるしちょうど良い。
というわけで、まずは静止画を連続撮影するところから始めます。

#参考にした記事など

https://qiita.com/nara256/items/e4dd4f36df449a0a82e2

https://uepon.hatenadiary.com/entry/2017/01/22/170349

#大まかな手順
1. WEBカメラをUSB接続して認識されていることを確認
1. WEBカメラの動作確認
1. 最初の1枚目の撮影
1. 連番で撮影
1. 放置して撮影

#手順
## 1. WEBカメラを接続して認識されていることを確認
WEBカメラをRaspberryPi本体のUSBコネクタに挿入します。挿入したら、lsusbコマンドでWEBカメラが認識されていることを確認します。

```
$ lsusb
Bus 002 Device 002: ID 04bb:0176 I-O Data Device, Inc. 
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 046d:082c Logitech, Inc. HD Webcam C615
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
```

WEBカメラらしきデバイスが認識されています。

## 2. WEBカメラの動作確認
WEBカメラの動作確認をおこないます。guvcviewというアプリを使います。

```
$ sudo apt install guvcview
```

インストールできたら、アプリを起動します。

```
 $ guvcview
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126400/a9668355-1662-0748-6834-afca3f5cf57f.png)

画面が見切れてしまっていますが、WEBカメラで撮影した画像が表示できました。WEBカメラ自体は正常に動作しているようです。

## 3. 最初の1枚の撮影
コマンド fswebcam を使って撮影します。必要となるパッケージがインストールされてない場合は、インストールします。

```
$ sudo apt install fswebcam v4l-utils
```

いちおう、WEBカメラのデバイスファイル名を確認しておきます。

```
$ v4l2-ctl --list-devices
bcm2835-codec-decode (platform:bcm2835-codec):
	/dev/video10
	/dev/video11
	/dev/video12

bcm2835-isp (platform:bcm2835-isp):
	/dev/video13
	/dev/video14
	/dev/video15
	/dev/video16

HD Webcam C615 (usb-0000:01:00.0-1.4):
	/dev/video0
	/dev/video1
```
WEBカメラのデバイスが /dev/video0, /dev/video1 として認識されているようです。


撮影してみます。(撮影の前に、gucviewerは終了しておきましょう）

```
$ fswebcam -r 1920x1080 test.jpg
--- Opening /dev/video0...
Trying source module v4l2...
/dev/video0 opened.
No input was specified, using the first.
--- Capturing frame...
Captured frame in 0.00 seconds.
--- Processing captured image...
Writing JPEG image to 'test.jpg'.
```


撮影終了です。ファイルを開いてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126400/e464c054-e8fd-96b3-5ec3-131aa6784510.png)

真っ暗になってしまいました。
こういうときは、空撮影(-S x)とかディレイ(-D x)を設定すると良いようです。

```
$ fswebcam  -r 1600x900 -D 5 -S 1 test2.jpg
--- Opening /dev/video0...
Trying source module v4l2...
/dev/video0 opened.
No input was specified, using the first.
Adjusting resolution from 1600x900 to 1600x896.
Delaying 5 seconds.
--- Capturing frame...
Skipping frame...
Capturing 1 frames...
Captured 2 frames in 0.06 seconds.
--- Processing captured image...
Writing JPEG image to 'test2.jpg'.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126400/d99d4d36-8a54-e030-2346-bde39a0a26b8.png)

とりあえず、撮影できたっぽいです。

なんだか画質がちょと変ですね。まあ、夜だし、安いカメラだから仕方ないですね。こういうときは、-Sオプションの値を調整します(10-60くらい？)。-S オプションの指定のぶんだけ撮影フレームをスキップしますが、この間にオートフォーカスとか露出補正とかが動くようです。

```
$ fswebcam  -r 1600x900 -D 5 -S 60 test2.jpg
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126400/c5b0b523-b4d7-2363-7474-2a6e5ca7870e.png)

すこしマシになりましたね。

ところで、下の方に帯のようなものが表示されますが、これは　--no-banner オプションで消せます。

```
$ fswebcam  -r 1600x900 -D 5 -S 60 --no-banner test2.jpg
```


## 4.連番で撮影
最初の１枚が出来たので、連続で撮影できるようにします。ファイル名を連番にしておけば、並べるのに苦労しなさそうです。

保存するディレクトリを作っておきましょう。

```
$ mkdir -p timelapse/pic
$ cd timelapse
$ pwd
/home/pi/timelapse
```

ダブらない連番を生成する事を考えます。1秒間に1枚以上の撮影はしないこと、わかりやすさなどから日付時刻から生成することにしました。
dateコマンドを使って連番の文字列を生成します。

```
$ date '+%Y%m%d%H%M%S'
20210703235547
```

これをもとに、シェルスクリプトを書きます。

```
$ gedit timelapse_pic.sh 
```

timelapse_pic.sh の内容

```
#!/bin/sh

filename=`date '+%Y%m%d%H%M%S'`.jpg
fswebcam  -r 1600x900 -D 5 -S 60 --no-banner pic/$filename
```

実行パーミッションを付与します。

```
$ chmod a+x timelapse_pic.sh 
$ ls -l timelapse_pic.sh 
-rwxr-xr-x 1 pi pi 91  7月  4 00:02 timelapse_pic.sh
```

何回か実行してみます。

```
$ ./timelapse_pic.sh 
```

ファイルが出来たか確認します。

```
$ ls ./pic/
20210704000915.jpg  20210704000922.jpg  20210704000933.jpg  20210704000942.jpg
```

## 5.放置して撮影
放置して撮影することを考えます。
放置して撮影するには、いくつかの方法があります。例えば、pythonやシェルスクリプトで while(1) delay(60); みたいな感じでプログラムを組む、もしくは、cronで一定期間ごとに実行、などがあります。
前者だと、プログラムが止まると記録されなくなってしまって困ります。cronは、一定時間ごとにコマンドを叩いてくれるので、コマンドが上手く動かなかったとしても被害が最小限で済みそうです。
このため、cronで一定時間事に実行することにします。とりあえず、1分ごとの実行を目指してみます。

cronで実行するとき、ファイルのパスなどに気をつける必要があります。実行するスクリプトの指定、スクリプト内のファイル指定は、全てフルパスにしておきます。
このため、スクリプトと写真の保存ディレクトリのパスを確認します。

```
$ pwd
/home/pi/timelapse
$ ls
pic  timelapse_pic.sh
```

というわけで、スクリプトのパスは /home/pi/timelapse/timelapse_pic.sh、データの保存先は、/home/pi/timelapse/pic です。シェルスクリプトを書き換えておきます。

```
#!/bin/sh

filename=`date '+%Y%m%d%H%M%S'`.jpg
fswebcam  -r 1600x900 -D 5 -S 60 --no-banner  /home/pi/timelapse/pic/$filename
```

cronの設定を書き換えます。

```
$ crontab -e
```

1分ごとに実行するので、次のような記述になります。

```
*/1 * * * * /home/pi/timelapse/timelapse_pic.sh
```

cronが動いているか確認します。

```
 $ service cron status
● cron.service - Regular background program processing daemon
   Loaded: loaded (/lib/systemd/system/cron.service; enabled; vendor preset: ena
   Active: active (running) since Sat 2021-07-03 22:50:29 JST; 1h 40min ago
     Docs: man:cron(8)
 Main PID: 448 (cron)
    Tasks: 1 (limit: 4241)
   CGroup: /system.slice/cron.service
```

"Active"と書いてあるので、OKそうです。

cronの設定を有効化するために、cronを再起動します。

```
 $ sudo service cron restart
 $
```

メッセージが何も出てこないので、成功したということでしょう。
時間が経てば、ファイルが出来てくるはずなので、しばらく待って ls してみます。

```
$ pwd
/home/pi/timelapse/pic
$ ls
20210704003401.jpg  20210704003501.jpg
$ ls
20210704003401.jpg  20210704003601.jpg  20210704003801.jpg
20210704003501.jpg  20210704003701.jpg
```


順調に増えています。ファイル名をみても1分ごとにファイルが生成されているのが分かります。

ちなみに、生成されたファイルのパーミッションは、ちゃんとpiになっています。

```
$ whoami 
pi
$ ls -l
合計 11260
-rw-r--r-- 1 pi pi 1058970  7月  4 00:34 20210704003401.jpg
-rw-r--r-- 1 pi pi 1050662  7月  4 00:35 20210704003501.jpg
-rw-r--r-- 1 pi pi 1055547  7月  4 00:36 20210704003601.jpg
-rw-r--r-- 1 pi pi 1055736  7月  4 00:37 20210704003701.jpg
-rw-r--r-- 1 pi pi 1043745  7月  4 00:38 20210704003801.jpg
-rw-r--r-- 1 pi pi 1057062  7月  4 00:39 20210704003901.jpg
-rw-r--r-- 1 pi pi 1044439  7月  4 00:40 20210704004001.jpg
-rw-r--r-- 1 pi pi 1028871  7月  4 00:41 20210704004101.jpg
-rw-r--r-- 1 pi pi 1037192  7月  4 00:42 20210704004201.jpg
-rw-r--r-- 1 pi pi 1033181  7月  4 00:43 20210704004301.jpg
-rw-r--r-- 1 pi pi 1042085  7月  4 00:44 20210704004401.jpg
```

いちおう、できあがったファイルを確認してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126400/df4da4c5-9b88-ac8d-f003-f0c7535e13cb.png)

画質はともかく、ファイルが生成されていることが分かりました。


## 6.タイムラプス動画を生成したい

動画の素材となる静止画が得られたので、これらを「つなげて」動画を生成します。

動画の生成にはffmpegを使用します。
ffmpegは、指定したディレクトリ内にある連番のファイルを読み込ませると、その順番にフレームを連結して動画にしてくれるようです。

たとえば、"0001.jpg", "0002.jpg", "9999.jpg"といった感じです。
先ほど生成したファイルは、"yyyymmddhhmmddss.jpg"の形式だったので、ファイル名を変える必要があります。

動画の生成も、cronで一定時間ごとに実行します。１日１回生成すればよいでしょう。

準備として、ディレクトリをいくつかに分けることにします。

* /home/pi/timelapse/pic --- その日1日撮影した写真を保存する
* /home/pi/timelapse/pic2 -- 前日までの写真の蓄積
* /home/pi/timelapse/mov --- 動画を生成するための作業ディレクトリ

動画を生成するのは、１日１回。cronで実行したいです。次の仕事をやらせるシェルスクリプトを組みます。

* ./pic/ ディレクトリにあるjpgファイルを./pic2/ディレクトリにコピー
* ./pic2/ディレクトリにあるjpgファイルを連番に直して./mov/ディレクトリにコピー
* ./mov/ディレクトリにある連番jpgファイルを連結して動画ファイルを生成

これらの仕事をするシェルスクリプト "makemovie.sh" の内容は以下の通りです。cronから起動するのでパスの類いは絶対パスで記述しておきます。

```
#!/bin/sh

rm /home/pi/timelapse/mov/*
mv /home/pi/timelapse/pic/*.jpg /home/pi/timelapse/pic2/ 
find /home/pi/timelapse/pic2/ -name '*.jpg' | sort -n | awk 'BEGIN{ a=0 }{ printf "cp \"%s\" /home/pi/timelapse/mov/%06d.jpg\n", $0,a++}' | bash
ffmpeg -f image2 -r 30 -i /home/pi/timelapse/mov/%06d.jpg -an -vcodec libx264 -pix_fmt yuv420p /home/pi/timelapse/mov/output.mp4

```


これを1日1回起動するようにcrontabを設定します。

```
$crontab -e

*/1 * * * * /home/pi/timelapse/timelapse_pic.sh
0   0 * * * /home/pi/timelapse/makemovie.sh

```

動画が生成されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126400/07754acd-01d8-849c-771d-7d890e81ead1.png)





以上