2021/09/10
ラズパイ(Raspberry Pi Zero W)を使って、あさがおの咲くところを定点カメラで撮影してみました。
仕組み・概要
Raspberry Pi Zero WにカメラモジュールVer1.3を取り付けて撮影。撮影した画像をサーバにアップロードして、タイムラプス風にして見られるようにしてみました。
◎概要
- ラズパイで撮影、WiFi経由で即時サーバへアップロード。
- 撮影は2分間隔、crontabで定時実行の処理。
- 保存先はレンタルサーバとし、DBにテーブルを新規に構築。
- サーバ側へはPOSTメソッドで画像をアップロード。
- 画像保存と同時に、DBに時間・ファイル名をインサート。
- JavaScriptで画像を連続して表示し、タイムラプス風にスライドショーで表示。
実際の撮影画像はこのようになります。咲くシーンのみ抽出しました。
クライアント側プログラム
ハードウェア | Raspberry Pi Zero W |
---|---|
OS | Raspbian Buster with desktop:September 2019 |
クライアント側プログラム(asagao.sh)では、2つの処理をします。撮影と撮影画像をサーバにPOSTメソッドでアップロードです。2つの処理を1つのシェルで実行するようにしました。raspistillで撮影、2020-08-15_0600.jpgのようなファイル名で保存されます。curlでサーバを指定し撮影画像をアップロードしてます。
asagao.sh
#!/bin/bash
DATE=$(date +"%Y-%m-%d_%H%M")
raspistill -rot 90 -w 600 -h 400 -o /home/pi/asagao/$DATE.jpg
curl -X POST -F upload_file=@/home/pi/asagao/$DATE.jpg https://レンタルサーバのドメイン/asagao/post.php
このプログラムをあさがおの撮影時間に合わせて4時から11時まで2分間隔で動作するように、crontabに設定します。
crontab*/2 4-10 * * * /home/pi/asagao.sh
サーバ側 DBの準備
サーバは、DBが利用できる環境であれば、レンタルサーバ/クラウドサーバ/ローカルのサーバなんでも大丈夫です。今回はレンタルサーバのMySQLにimagesというテーブルを新たに作りました。テーブル構造は以下の通りです。
テーブル作成コマンド
CREATE TABLE `images` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`datetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`filename` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
)DEFAULT CHARSET=utf8mb4;
データが格納されている状況
サーバ側 表示プログラム
サーバ側プログラムはphpで作成しました。画像をPOSTメソッドで受け取るプログラム(post.php)と、JavaScriptでスライドショーを表示するプログラム(index.php)の2つです。
データベースへは、datetimeへ$timeを、filenameへ環境変数から取得のファイル名$_FILES[‘upload_file’][‘name’]の2項目をインサート。imagesディレクトリにファイルを保存します。
post.php<?php
$dsn = "mysql:host=localhost; dbname=mydatabase; charset=utf8";
$username = "myusername";
$password = "mypassword";
try {
$dbh = new PDO($dsn, $username, $password);
} catch (PDOException $e) {
echo $e->getMessage();
}
$time = date("Y-m-d H:i:s");
$sql = "INSERT INTO images (id,datetime,filename) VALUES (NULL, :time, :name)";
$stmt = $dbh->prepare($sql);
$stmt->bindValue(':time', $time, PDO::PARAM_STR);
$stmt->bindValue(':name', $_FILES['upload_file']['name'], PDO::PARAM_STR);
if (is_uploaded_file($_FILES['upload_file']['tmp_name'])) {//ファイルが選択されていれば$imageにファイル名を代入
move_uploaded_file($_FILES['upload_file']['tmp_name'], './images/'.$_FILES['upload_file']['name']);//imagesディレクトリにファイル保存
$message = '画像をアップロードしました';
$stmt->execute();
}
?>
<?php if (isset($_POST['upload'])): ?>
<p><?php echo $message; ?></p>
<?php else: ?>
<form method="post" enctype="multipart/form-data">
<p>アップロード画像</p>
<input type="file" name="upload_file">
<input type="submit" name="upload" value="送信">
</form>
<?php endif;?>
JavaScriptで画像の一覧を取得するために、class=”slides”で指定しました。またスライドショーとは別に、アップロードされた画像を表形式で表示するようにしてみました。スライドショー画像右上に表示される時刻は、CSSのセレクタ”ul.slides li p”で指定し表示しています。
index.php<?php
$dsn = "mysql:host=localhost; dbname=mydatabase; charset=utf8";
$username = "myusername";
$password = "mypassword";
?>
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>定点カメラ あさがお スライドショー</title>
<style>
ul.slides li {
display:none;
position:relative;
list-style:none;
}
ul.slides li img {
width:600px;
}
ul.slides li p {
position:absolute;
top:1em;
left:25em;
color:white;
font-size:1.2em;
}
#slide_speed {
transform: rotateY(180deg);
}
</style>
</head>
<body>
<ul class="slides">
<?php
try {
$dbh = new PDO($dsn, $username, $password);
// データベースからレコードを取得
$sql = "SELECT * FROM `images` WHERE `datetime` >= '2020-08-14 18:00:00'";
$stmt = $dbh->prepare($sql);
$stmt->execute();
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
?>
<li><img src = "./images/<?=$row['filename'] ?>"><p><?=substr($row['datetime'],11,5) ?></p>
<?php
} }catch (PDOException $e) {
echo $e->getMessage();
}
?>
</ul>
<p>速度(1000-50msec):<input type="range" id="slide_speed" value="1000" min="50" max="1000"></p>
<hr>
<p><a href=index.php>top</a></p>
<hr>
<table width="400" cellspacing="0" cellpadding="5" border="1">
<tr align="center">
<th>ID</th>
<th>DateTime</th>
<th>画像</th>
</tr>
<?php
try {
$dbh = new PDO($dsn, $username, $password);
// データベースからレコードを取得
$sql = "SELECT * FROM `images` WHERE `datetime` >= '2020-08-14 18:00:00' ORDER BY `id` DESC";
$stmt = $dbh->prepare($sql);
$stmt->execute();
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
?>
<tr align="center">
<td><?=$row['id'] ?></td>
<td><?=$row['datetime'] ?></td>
<td><a href="./images/<?=$row['filename'] ?>"><img src = "./images/<?=$row['filename'] ?>" width="100"></a></td>
</tr>
<?php
} }catch (PDOException $e) {
echo $e->getMessage();
}
?>
</table>
<script>
//スライド用のliタグを全て取得
var slides = document.getElementsByClassName('slides')[0].getElementsByTagName('li');
//スライド表示用の関数を呼び出す(引数はスライドの切り替え時間)
viewSlide();
function viewSlide(slide_no = -1)
{
//現在のスライドを消す
if (slides[slide_no]) {
slides[slide_no].style.display = 'none';
}
//スライド番号をカウントアップ
slide_no++;
if (slides[slide_no]) {
//次のスライドを表示
slides[slide_no].style.display = 'block';
} else {
//次のスライドがなければ最初のスライドを表示
slides[0].style.display = 'block';
slide_no = 0;
}
var msec = document.getElementById('slide_speed').value;
setTimeout(function(){viewSlide(slide_no);}, msec);
}
</script>
</body>
</html>
サーバへのプログラム配置は以下の通りです。
root/ ├ asagao/ ├ images/ ├ post.php └ index.php
画像表示のJavaScriptは下記のサイトを参考にさせていただきました。
タイムラプス風というか、パラパラ漫画風に表示できるJavaScriptの画像ビューアを色々とさがしました。Viewer.jsとかSwiper.jsとか。でも、参考サイトのものが軽量でシンプル、速度変更もできて一番適していました。
実行結果
別ページに遷移します。全体で50MBと少し重いページです。撮影時間7時間、200枚以上の写真をパラパラ漫画風に1分半で再生します。
※上述のindex.phpで生成されたページを、サンプル用に静的なHTMLファイルに加工しました。
日が差す頃に咲き始め、途中青色から紫色に変化し、そして10時過ぎたころにしぼみ始める様子がよくわかります。撮影実行中は、2分間隔で画像が追加され、更新されていきます。
設置
7尺の脚立の上に、カメラ用三脚を設置。2m以上の高さから撮影。前日夕方に咲きそうなものを見つけてセットしておきました。
通信は無線ですが、電源は家の中から電源延長ケーブルで伸ばしました。ラズパイの消費電力を考えれば、携帯電話充電用のモバイルバッテリーでもよかったかもしれません。
カメラモジュールと一緒に格納できる自作ケース。ほこりと水滴から守ります。
三脚へ取り付けるため、1/4インチねじが入るナットを付けました。カメラと同じ三脚が使えて便利です。
あとがき
タイムラプス風の撮影であれば、今どきのスマホアプリやデジカメの機能でも可能です。遠隔地にあるものをリアルタイムにタイムラプス風スライドショーで見えたら面白いなと思ってやってみました。
最低限のやりたいことは成功しました。撮影したスライドショーをみて、色んなものに応用できるなって思ってます。今回のように、遠隔地の植物の観察はもちろん、撮影スパンを変えて、建築中のビルとか、解体中の建物とか、季節の変動とか変化の分かるものだと面白そうです。
参考にしていただけたら幸いです。