ラズベリーパイで作る省電力ライブカメラ(前編)~ ラズベリーパイ研究室

Raspberry Pi でのライブカメラの記事というと、大抵は MJPG-streamer を使ったライブストリーミングの記事になるようですね。ですが、この MJPG-streamer、結構処理が重いようです。Raspberry Pi は省電力が売りなので、あまり重いライブカメラは Pi っぽくないと思います。

そこで、ここでは出来るだけ処理の軽いライブカメラを作ってみようと思います。

おススメ!記事
Raspberry Pi 用「HAL」で、
カップラーメン・タイマーを作ってみよう!
ラズパイDIYの決定版! ソケットサーバー「HAL」をご紹介します。

※誠に申し訳ありませんがこちらの電子工作のコンテンツは弊社の実験制作例となっております。十分な安全が保障されているわけではないため、参照や実施は自己責任となってしまいますのでご注意ください。

省電力のライブカメラを作ろう

Raspberry Pi にはカメラ端子があり、別売のカメラモジュールを接続することで簡単に撮影を行うことができます。

Raspberry Pi 用赤外線カメラモジュール

このカメラモジュールで、ライブカメラを作ってみたいと思います。

なお、上記は赤外線カメラです。普通のカメラモジュールとの違いは赤外線フィルタが無い事のようです。このため、普通の撮影をすると少し色味が変です。また、赤外線カメラといっても何もせずに暗闇で撮影ができるわけではなく、暗闇で撮影するには十分な量の赤外線光を照射出来るだけのLEDシステムなどが別途必要です。ですから、綺麗な撮影をしたいのであれば通常のカメラモジュールを購入して下さい。今回の省電力ライブカメラ構築にあたってはどちらでも問題ありません。

私が赤外線カメラを好んで買っているのは、なんとなく普通のカメラよりも性能お得感があるからです。ただの自己満足なので気にしないでください。決して悪いことに使うためではないですよ。本当ですよ。

MJPG-streamer を使わないライブカメラ

検索で上位に来る Raspberry Pi でのライブカメラの記事というと、大抵は MJPG-streamer を使ったライブストリーミングの記事になるようですね。ですが、この MJPG-streamer、結構処理が重いようです。Raspberry Pi は省電力が売りなので、あまり重いライブカメラは Pi っぽくないと思います。

そこで、ここでは出来るだけ処理の軽いライブカメラを作ってみようと思います。

軽いライブカメラとしては、ストリーミングを捨てます。省電力設計の Raspberry Pi には、ライブエンコーディングは少々荷が重すぎます。

ではどうするかというと、HTTPで連続静止画配信を行います。インターネット回線が弱かった時代のライブカメラはこうした手法が用いられていました。

このため、WEBサーバーの Apache とPHP をインストールして下さい。

ApacheとPHPのインストール

※勿論、WEB サーバーは Apache でなくても構わないし、PHP ではない別の使い慣れたプログラム実行環境でも構いません。

インストールが終わったら、カメラモジュールを Raspberry Pi に接続します。

カメラモジュールを接続

カメラモジュールを接続したら、テスト撮影をしてみましょう。ムービーではなく、静止画撮影なので、raspistill コマンドです。

sudo raspistill -o test.jpg -t 1 -w 640 -h 480 -e jpg -q 70 -rot 180

640✕480 pixels、クオリティ 70 の JPEG 画像を生成しています。 -rot 180 は、カメラの向きが天地逆さまなので映像を 180度回転させています。オブションの詳細は Jun's Homepage に詳しく纏めて頂いてあるので参照して下さい。感謝。

上記コマンドを実行すると、現在のディレクトリに test.jpg というファイルが生成されているはずです。きちんと撮影出来ているか、ファイルを開いて確認してみてください。

Raspberry Pi のシェル画面で画像を確認するには、fbi(linux framebuffer imageviewer)を利用すると良いようです。fbi のインストールには、[コラム] 第5回『Raspberry Pi専用カメラモジュールで遊ぼう part 1』 をご参照下さい。

テスト撮影ができたら、このコマンドを実行する PHP プログラムを書きましょう。ファイルの場所はひとまず、Apache のドキュメントルート、/var/www/html 内です。

sudo vi /var/www/html/still.php

※この still というファイル名、もし可能であれば別のファイル名にしてみてください。あなたがうっかり Apache サーバーをインターネットに公開してしまった場合、この記事を読んでいることを推測されてあなたの部屋が覗かれるという事をかなり防いでくれます。もちろん、適切なアクセス制限をしてあれば問題無いですし、してなければファイル名を変えたところで絶対に覗かれないわけではありません。ご不安でしたら、使わない時はカメラを取り外すか、何かで覆っておきましょう。

/var/www/htm/still.php

<?php
exec("sudo raspistill -o test.jpg -t 1 -w 640 -h 480 -e jpg -q 70 -n -rot 180");

$data = file_get_contents("test.jpg");

echo "data:image/jpeg;base64," . base64_encode($data);

exec コマンドは、PHP から外部プログラムを実行するコマンドです。先ほどの raspistill コマンドを実行することで、still.php と同じ階層に test.jpg ファイルが生成されます。

file_get_contents() は、指定したファイルの内容を PHP で読み込むコマンドです。これで、書き出された test.jpg ファイルの内容が読み込まれます。

あとはこの内容を出力します。読み込まれた test.jpg の内容はバイナリデータのため、HTTP で簡単に通信を行うために Base64 という方式を用いてテキストデータにエンコードします。

Base64

先頭に付加している "data:image/jpeg;base64," は、そのデータが Base64 でエンコードされた JPEG ファイルであることを明示するための URL スキームという物です。

あとはこの出力を HTML の <img/> エレメントの src として指定するだけで、画像が表示されます。

sudo vi /var/www/html/camera1.html

/var/www/html/camera1.html

<html>
<head>
<title>MyCamera1</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
</script>
<script>
var temp = $("<img/>");
setInterval(function(){
$.ajax({
type: "get",
url: "still.php"
})
.done(function(result){
temp.on("load", function(){
$("#my-camera").attr("src", temp.attr("src"));
});
temp.attr("src", result);
})
}, 700);
</script>
</head>
<body>
<img id="my-camera" src="">
</body>
</html>

非同期通信を簡単に行うため、<head/> 内で google の CDN から jQuery ライブラリを読み込んでいます。

setInterval(function(){ }, 700);

は、700 ミリ秒毎(0.7秒毎)に、 { } 内のプログラムを実行する命令です。

$.ajax({url:"still.php"}).done()

で、先ほどの /var/www/html/still.php を非同期で呼び出しています。通信が成功すると、done() が実行され、result に still.php からの応答が格納されます。この応答をテンポラリオブジェクト temp = $("<img/>") で受け取り、画像全体の読み込みが終わったタイミングで <body/> 内の <img/> タグ($("#my-camera") )の src 属性にセットして画像を表示するわけです。

とても簡単なプログラムですね。

temp.on("load", function(){ $("#my-camera").attr("src", temp.attr("src")); });
temp.attr("src", result);

は、実際には実行順序が上下逆です。temp.on("load", function(){}) メソッドは、temp でロードイベント(読み込みが完了した事を検知するイベント)が発生した時に function(){} を実行するよう定義する命令です。

一方、temp.attr("src", result); は、temp のソースとして result(still.phpからの出力)を設定する命令です。

気持ち的には temp.attr("src", result); の方を先に書きたいところですが、もし仮にすさまじい速さでレスポンスが返されてロードイベントが一瞬で発生してしまった場合、temp.on("load", function(){}) がまだ定義されておらず、何も起きない、ということが発生しかねません。(HTTP通信では、まず起こりえないですが…)

そんなわけで、最初にロードイベントが起きた場合の定義をしてから、そのイベントが起こるであろう処理を記述するわけです。

余談ですが、Base64方式を用いたのはただの気まぐれです。適切な header を付加してバイナリデータをそのまま出力する方法もあります。その辺については、またの機会にご紹介します。Base64 は簡単にバイナリを扱える分、データ量は元の 4/3 倍になります。ただ、Base64 を知って欲しかっただけなので気にしないでください。

標準出力を使ったオンメモリでのプログラム

ところで Raspberry Pi のストレージは SD カードなのですが、かなり寿命は短いようです。SD カードの品質にもよるようですが、頻繁に読み書きを繰り返していると次第に劣化し、壊れてしまうようです。

ですから、ライブカメラのように頻繁に撮影を行う用途で常にファイルを書き出すのはちょっと危険ですね。

そこで、ファイルには書き出さず、全てメモリ内で賄ってしまいましょう。

raspistill の -o オプションは出力設定のオブションですが、これを -o - と指定することで、撮影した画像データをファイルではなく標準出力に出力することが出来ます。

そうしたら、この標準出力にあるデータをそのままブラウザに返してしまいましょう。

sudo vi /var/www/html/still.php

/var/www/htm/still.php の修正

<?php

ob_start();
system("sudo raspistill -o - -t 1 -w 640 -h 480 -e jpg -q 70 -n -rot 180");
$data = ob_get_contents();
ob_end_clean();

echo "data:image/jpeg;base64," . base64_encode($data);

ob_start() は、PHP の出力バッファリングをオンにする命令です。こうすることで、PHP からの出力をバッファに貯めることができます。

先程は exec() コマンドを使いましたが、今回は system() コマンドです。こちらはexec()コマンドと違い、実行した外部プログラムからの出力を標準出力に出力することができます。先ほどバッファをオンにしてあるので、このバッファに raspistill コマンドの出力である画像データが貯まります。

raspistill コマンドの実行が終わったら、欲しい画像データは全てバッファに溜まっているので、ob_get_contents() を使ってこれをデータとしてPHPで取得します。

画像データが取得できたら、ob_end_clean() でバッファリングを終了して綺麗に掃除します。

最後に、先ほどと同じように Base64 でエンコードし、URL スキームをつけてブラウザに出力して終わりです。

軽量かつお手軽なライブカメラのプログラム前編は以上です。topコマンドで見てみると、CPU使用率は多くても 4% くらいですね。(画像は RPi3)

cpu 使用率

後編では当サイトの製品、ソケットサーバー「HAL」 を使って、ネットワークにつながっている複数の Raspberry Pi に接続した カメラを一元管理してみたいと思います。

吉井和哉 - Shine and Eternity

ラズベリーパイで作る省電力ライブカメラ(後編)

この記事へのコメント

※現在コメントはMarkdown記法が強制です。>>Markdown の書き方


この記事に返信

このコメントに返信