Coding4Fun: 赤ちゃん大好き! Web カメラと動作検出

赤ちゃん大好き! Web カメラと動作検出

Scott Hanselman
Corillian Corporation

April 7, 2006
日本語版最終更新日 2006 年 6 月 14 日

要約: 久々の「Some Assembly Required」コラムの第 7 回です。今回は、Scott Hanselman が Andrew Kirillov の Motion Detector (英語) を、彼の許可を得て .NET 2.0 と ClickOnce に拡張します。また、ネットワーク トラフィックの追跡とちょっとした直感を使って、AirLink AIC250 Network Camera の特性に対処しています。この記事には英語のページへのリンクも含まれています。

AIC250 Network Camera

「Some Assembly Required」コラムをしばらくお休みしていましたが、ただいま戻りました。皆さんが私のことを忘れていなければよいのですが。ちょうど 3 か月前、私と妻の間に男の子 (名前は "Zenzo" です) が生まれ、今も彼の世話に手を焼いています。Zenzo がほとんど一晩中眠っていてくれるようになったので、仕事に復帰しました。やがて、このコラムを定期的に執筆するリズムも戻ってくるでしょう。これからも、.NET に接続できる機器に関する皆さんのご提案をお待ちしています。

私は仕事中も Zenzo を見守っていたかったので、たくさんの方法を検討しました。MSN Messenger などのチャット ツールに組み込まれているビデオ チャットも使えますが、コンピュータ室に赤ちゃんを連れ込むのは何かと不都合ですし、ファイアウォールを通過してビデオを動作させる作業には多くの問題が発生します。また、ビデオ チャットを使用する場合は、双方が会話に参加しなければなりません。家にいる妻に電話をかけ、彼女に何も作業をさせることなく遠隔地からカメラを起動できれば、妻にもわかりやすく説明でき、その解決策に対する WAF (妻が出費に OK を出す条件) も増えるでしょう。

内部に独自のネットワーク接続が用意されていて、コンピュータに接続する必要のないカメラを希望していました。つまり、何らかの内部 Web サーバーが組み込まれているカメラです。

また、両親が 2 時間ほどの郊外に住んでいて、ときどき孫の顔を見たいと思うでしょうから、彼らにとってもできるだけ簡単な方法を用意しようと考えました。最後に、Zenzo が眠っている間に手足をバタバタさせていることをカメラが検出できるならば、ある種の動作モニタを接続するのもよいかもしれません。

たくさんのネットワーク カメラを見て回りましたが、価格を優先させなければならない事情から、AIC250 Network Camera に決めました。私は 99 米ドルの Wired Ethernet バージョンを購入しましたが、インターネット上では同じカメラのワイヤレス バージョンが同じく 99 米ドルで手に入ります。

カメラの設定は簡単です。コンセントに差し込むと、DHCP 経由で IP アドレスを取得します。このカメラには、ブラウザから設定を管理できる Web サーバーが統合されています。また、Web ベースのインターフェイスにビデオ ストリームを表示する方法も注目です。このカメラには、Java アプレットまたは ActiveX コントロールという 2 つの選択肢があります。これらのウィジェットは、カメラのファームウェアに組み込まれています。このファームウェアはソフトウェアを使用して製造元から直接更新できます。ビデオを正式に表示できるのは、これら 2 つのウィジェットのいずれかを使用した Web インターフェイスのみです。

ハッカー タイプの私としては、基になるビデオ ストリームにアクセスしたいと考えました。組み込む必要があるすべてのソフトウェアがカメラに含まれているのはすばらしいことですが、ストリームにアクセスできるといろいろとおもしろいことができます。

MotionJPEG

Web インターフェイスにログインし、組み込みの Java アプレットを使用してビデオを観たときのネットワーク トラフィックを確認するために、Simon Fell の TCPTrace (英語) を使いました。すると、トラフィックは一連の JPEG で構成されていることがわかりました。つまり、HTTP GET が延々と行われていました。接続を開いたまま、許容最大速度 (1 秒間に 10 ~ 15 フレーム) で JPEG が送信されます。このようなトラフィックはこれまで見たことがありません。カメラからは AVI ファイルや MPEG ファイルが生成されると思っていました。その後、JPEG を送信するのが最も安くビデオを生成できる方法だとわかりました。この方法であれば、ビデオ コーデック (圧縮プログラム/圧縮解除プログラム) のライセンスが不要だからです。Google で少し検索すると、この技法は MotionJPEG または MJPEG と呼ばれていることがわかりました。

MotionJPEG (英語) は "見事な" 技法だと思います。これは、馴染みがある MPEG (Motion Picture Experts Group) ビデオのことではありません。MotionJPEG は、JPEG イメージが連続するストリームです。公式の仕様はなく、実装はさまざまです。

ビデオ用の "MotionJPEG" または "MJPEG" といった標準はありません。さまざまなベンダがビデオ シーケンスの個々のフレームに JPEG を適用し、それを "M-JPEG" と呼びました。JPEG は、自然のままの実在する景色を表すフルカラー画像またはグレースケール画像を圧縮するためにデザインされました。

HTTP 経由の MotionJPEG では、Content-Type ヘッダーの "multipart/x-mixed-replace" を構成可能な境界と併用します。つまり、ストリームが複数の部分 (multipart) で構成され、新しいフレームがそれぞれ前のフレームを置き換え (x-mixed-replace) ます。このカメラでは、次のような HTTP ヘッダーを送信します。

Content-Type: multipart/x-mixed-replace;=--video boundary--

境界には、サーバーが選択できる任意の文字列を指定できます。この場合は、直感的にわかる "--video boundary--" を指定します。境界の後には、その部分の実際の Content-Type (この場合は image/jpeg) を含むヘッダーを追加指定します。

.NET 2.0 と組み込みの WebClient ライブラリを使用して、パーサーとレンダラの最初から作成し始めました。バッファにデータを読み取り、境界を超えることを監視するコードは非常に簡単です。次に System.Drawing ライブラリを使用して、JPEG を読み取り、それらを受け取った順に表示します。しかし、このようなコードが既に記述されているライブラリがあるかもしれないと考えました。そのとき、Andrew Kirillov動作検出に関する CodeProject の資料 (英語) を見つけました。

Andrew の MJPEG 処理は、実際は彼のコードにとってそれほど重要な部分ではありませんでした。以下で説明する動作検出アルゴリズムは、彼の .NET 1.1 プロジェクトがモデルになっていますが、興味深いのはプラグ可能な VideoSource の実装でした。

Andrew の実装

Andrew は、IVideoSource というインターフェイスを作成しました。彼は、具体的な実装をたくさん含めています。私が必要とした MJPEGSourcem もその 1 つです。

  
    public interface IVideoSource
{
  event CameraEventHandler NewFrame;
  string VideoSource{get; set;}
  string Login{get; set;}
  string Password{get; set;}
  int BytesReceived{get;}
  object UserData{get; set;}
  bool Running{get;}
  void Start();
  void SignalToStop();
  void WaitForStop();
  void Stop();
  
}

Andrew の実装の中で最も見事だったのは、私が思いも付かなかった CameraEventHandler です。

  
    public delegate void CameraEventHandler(object sender, CameraEventArgs e);
public class CameraEventArgs : EventArgs
{
  private System.Drawing.Bitmap bmp;
  public CameraEventArgs(System.Drawing.Bitmap bmp)
  {
    this.bmp = bmp;
  }
  public System.Drawing.Bitmap Bitmap
  {
    get { return bmp; }
  }
}

VideoSource は、検出した各フレームを、イベント内にパッケージ化されたビットマップとしてスローします。これは一見効率が悪いようですが、このイベントが 1 秒間に起動する回数は 30 回を超える可能性は低い (通常は 5 ~ 10 回です) ことに注意してください。また、ビットマップへの参照をスローしているので、ビットマップのコピーがシステム間を移動するというわけではありません。このようにイベントを使用することにより、インターフェイスを整理したり、IVideoSource の実装と表示を選択するフォームを分離したりできるだけでなく、処理後のフィルタと動作検出アルゴリズムを挿入することもできます。

AIC250 の特性への対処

動作検出アプリケーションを AIC250 カメラの MJPEG ソースに直接接続できることを期待していました。しかし、それは甘い考えでした。Andrew のコードの MJPEG 実装に問題はありませんでしたが、AIC250 埋め込みの Web サーバーの認証処理にいくつかの特性がありました。

綴りの間違った "Auther" (sic) HTTP ヘッダーは、自身を Camera Web Server/1.0 として報告するこの Web サーバーを Steven Wu が記述したことを示しています。

AIC250 カメラでは、HTTP 基本認証がサポートされています。管理用ホームページに初めてアクセスすると、HTTP 基本認証を使用して、名前とパスワードの入力が要求され、次のページに ActiveX コントロールまたは Java アプレットが表示されます。上記の TCP の調査では、JPEG のストリームが /mjpeg.cgi というエンドポイントへの HTTP GET の結果として取得されたことがわかりました。

ハリッハするよ拡大画像が表示され?す

(画像をクリックすると拡大表示されます。)

.NET を使用して /mjpeg.cgi を呼び出し、HTTP 基本認証を経由して名前とパスワードを渡せば、ストリームを取得できると考えていました。しかし、実際にはうまくいきませんでした。ただし、まず、プログラムからホームページに "アクセス" し、それから /mjpeg.cgi を要求すると、MJPEG ストリームが開始されました。このカメラでは、ユーザーがホームページにアクセスした後、次の要求で /mjpeg.cgi にアクセスすることを許可する内部フラグを設定するようです。これは、/mjpeg.cgi エンドポイントのコードが、単に JPEG を返し、他には何も返さないように記述されているからだと思います。この Web サーバーの作成者 (Steven Wu) は、/mjpeg.cgi に認証をサポートさせ、厳密に HTTP の仕様に準拠させることが困難だと判断し、値を提供しなかったのでしょう。彼らのユース ケースでは、私のようにビデオ ストリームに直接アクセスしようとするユーザーは想定されていませんでした。

次の問題は、HTTP 基本認証が、資格情報の送信を待機する前にまず認証チャレンジを待機することでした。この Web サーバーは、まず資格情報の送信を要求するようです。幸運にも、WebRequest クラスでは、認証チャレンジの前にまず認証資格情報を送信する PreAuthenticate プロパティがサポートされています。

私は "PreAuthentication" オプションと "Authentication with the Home Page" オプションをサポートするように MJPEGSource を変更したことで、本格的にスタートしました。

その他の特性 - UnsafeHeaderParsing

対処すべき特性はこれで終わりではありませんでした。.NET 1.1 から .NET 2.0 にアプリケーションをアップグレードすると、HttpWebRequest.GetResponse() の呼び出し中に例外を受け取るようになりました。しかし、これらの例外はあまり情報を提供してくれませんでした。変わったのは、1.1 から 2.0 にアップグレードしたことだけです。Google や MSDN などで詳しく調べた結果、Mike Flasko のブログ (英語) に辿り着きました。

「既定では、NET Framework は URI の解析に RFC 2616 を厳密に適用します。一部のサーバー応答には、禁止されているフィールドに制御文字が含まれていることで、System.Net.HttpWebRequest.GetResponse メソッドによって WebException がスローされる場合があります。この場合、useUnsafeHeaderParsing が true に設定されていれば、System.Net.HttpWebRequest.GetResponse から例外は返されません。ただし、アプリケーションがいくつかの形式の URI 解析攻撃を受けやすくなります。最善の解決策は、応答に制御文字が含まれないようにサーバーを変更することです。」

もちろん、このカメラに組み込まれた Steven Wu の Web サーバーが変更されることはないので、次のような app.config を含める以外に選択の余地はありませんでした。

  <?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.net>
    <settings>
      <httpWebRequest useUnsafeHeaderParsing="true" />
    </settings>
  </system.net>
</configuration>

動作検出

Andrew は、4 つの異なる動作検出アルゴリズムを含めています。これらのアルゴリズムは、CodeProject の資料 (英語) で詳しく説明されています。彼のアルゴリズムは、ビデオの処理よりも画像の処理に重点を置いています。これが、各フレームをビットマップとしてスローし、最後のフレームを保存して現在のフレームと比較している理由です。すべてのビデオ ソースを一連のビットマップに "正規化" するので、さまざまなビデオ ソースに接続できるだけでなく、任意の動作検出アルゴリズムを任意のビデオ ソースに適用できます。

上記の画像は、右側にあるテディベアが糸で動かされたことを示しています。テディベアを囲む赤いボックスは、動作検出アルゴリズムが画像の変更部分を示すために追加したものです。

ClickOnce

これで、息子の部屋のカメラからの映像を表示できる WinForms 2.0 アプリケーションが完成しました。外出中や仕事中でもこのアプリケーションに接続できるように、ケーブル モデムの外部 IP アドレスからカメラの内部 IP アドレスにポートを転送しました。次の手順は、このアプリケーションを両親や私がどこからでも実行できるように ClickOnce アプリケーションにすることでした。

Visual Studio を使用すると、ClickOnce アプリケーションを非常に簡単に作成できます。Visual Studio のソリューション エクスプローラ内でプロジェクトを右クリックし、[プロパティ] をクリックします。まず [署名] タブをクリックし、テスト証明書を作成します (独自の署名用証明書がない場合)。この証明書は、Web サイトに発行される ClickOnce マニフェストへの署名に使用されます。次に [セキュリティ] タブをクリックし、[これは完全に信頼するアプリケーションです] をクリックします。独自の ClickOnce アプリケーションを作成する場合、アプリケーションの実行に必要なアクセス許可を厳密に指定できます。最後に、[発行] タブで発行場所と最終 URL を指定できます。私は publish フォルダを発行場所にし、コンテンツを Web サイトに手動でアップロードすることにしました (これは、私の支配欲が強いからです)。

プロジェクトを構成したら、Visual Studio コマンド ラインから "msbuild /target:publish" を実行して、ClickOnce アプリケーションをビルドおよび発行することもできます。

これについておもしろいエピソードがあります。私は ClickOnce と FireFox (英語) の問題点をブログに書いたのですが、ClickOnce チームのメンバが説明をブログに書いていました (英語) 。幸いこの問題は Framework の次のバージョンで修正されます。

これで家族の誰もが、家族用 Web サイトにアクセスし、クリック 1 つでこの .NET 2.0 WinForms アプリケーションを実行して、名前とパスワードを入力して AIC250 Network Camera に接続できます。Andrew の動作検出はおまけです。

まとめ

このプロジェクトには、拡張、追加、および強化できるおもしろい内容がたくさんあります。いくつか提案事項を次に示します。

  • アニメーション GIF、DivX、またはその他の形式を読み取る独自のビデオ ソースを追加する。

  • WIA (Windows Image Acquisition) デバイスに接続するビデオ ソースの実装をビルドする。

  • 他のネットワーク カメラに接続する。

  • 複数のフレームを比較する独自の動作検出アルゴリズムを作成し、Andrew のアルゴリズムと比べる。

  • 動作とその動作に対応するプラグインに対して起動するイベントを作成する。ライトを接続したり、電子メールを送ったり、いろいろ試してみてください。

  • "分割画面" 機能を使用して複数のカメラ (おそらく 4 台) をサポートするようにアプリケーションをする。

"一部アセンブリが必要" という言葉を恐れることなく、コーディングに励んでください。

このアプリケーションを拡張したら、ぜひそのソースをリリースしてください。すばらしいソースの拡張を許可してくれた Andrew Kirillov に改めて感謝します。そして私の家族にも感謝します。

Scott Hanselman は Corillian Corporation (eFinance 企業) の主任アーキテクトです。C、C++、VB、COM のソフトウェア開発を 13 年間経験し、最近では VB.NET と C# のソフトウェアも開発しています。Scott は Microsoft RD と Architecture MVP を兼ねていることに誇りを持っています。Bill Evjen と共同で『Professional ASP.NET 2.0』を執筆しました。この本は、BookPool.com および Amazon で入手できます。.NET、プログラミング、および Web サービスの "禅" に関する彼の見解を本人のブログ http://www.computerzen.com/ (英語) でご覧いただけます。

top of page Top of Page