2009年7月24日金曜日

Processingを使ってWebカメラを監視カメラにする

このブログ記事をはてなブックマークに追加

Processingvideoライブラリを利用して、手持ちのWebカメラを防犯・監視カメラにしてみることにした。監視カメラとして、以下の2点を満たすこととする。

  • 動くものを捕らえたときだけ写真を残す。
  • 写真は指定のメールアドレスに直ちに送信される。

今回、コードを書くに当たって、Webカメラ関連については建築発明工作ゼミ2008、メール送信についてはE-mail Processingを参考にさせてもらった。特に建築発明工作ゼミ2008については他にもProcessing関連の有益な情報があって重宝する。

因みに、今回用いたWebカメラはずいぶん前に購入したLogitechのQuickCam for Notebooks Proだ。多分、大抵のWebカメラで問題なく動作すると思う。

それでは、早速作成してみる。

下準備

まず、ProcessingでWebカメラを利用するには、QuickTimeが必要になるので予め用意しておく。また、WindowsについてはQuickTimeでWebカメラを利用するためのVDIG (QuickTime-compatible video digitizer)をインストールする必要がある。このとき、最新バージョン(1.04/1.05)では動作時にエラーが起こるようなので、バージョンは1.01を用意すること。

次に、Processingでメール送信を行うために、JavaMailJavaBeans Activation Framework (JAF)をダウンロードする。JavaMailからmailapi.jarとsmtp.jar、JAFからactivation.jarを取り出す。そして、Processingの作業フォルダ(コーディングしているファイルの存在するフォルダ)にcodeという名前のフォルダを作成し、その中にその3つのjarファイルをコピーする。

これで下準備は完了だ。

コーディング

ライブラリについては、ビデオ関連でprocessing.video、電子メール関連でjavax.mailをインポートする。setup関数でvideoオブジェクトを作成し、draw関数で映像を表示する。今回は320×240の解像度に設定した。次にカメラの映像から、以前の画像と現在の画像との差をピクセルごとで比較し、その差が許容値(torelance)以上であれば動体とし、それらすべてのピクセルを数え上げる。そして、その動体として認識されたピクセルの数が一定数(今回は100に設定)以上であれば写真を撮る。フレームごとに写真を撮ってしまうと膨大なファイル数になってしまうので、シャッター間隔は最小で5秒とした。また、写真を保存するフォルダはPATH変数に設定しておく。

次に、撮った写真をメールで送信するために、sendmail関数を実装し、ファイル添付についてはMIMEを利用した。今回のコードではGmailのSMTPサーバで送信するので、認証のためのアカウントとパスワードを予め設定しておく必要がある。もし、自前でSMTPサーバを用意できるのであれば、認証の必要はない。その場合、

props.put("mail.smtp.port", PORT); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.starttls.enable", "true"); Session session = Session.getDefaultInstance(props, new Auth(FROM, PASS));

を、

Session session = Session.getDefaultInstance(props,null);

に変更する。また、Authクラスも、PORT/PASS変数も必要なくなる。

今回のコーディングは結構面白かった。メールの送信で若干もたつくけど、まあ実用にはなると思う。それにしても、こんなに簡単に、短いコードでWebカメラを監視カメラに仕立て上げることができるとは、Processingの可能性を再認識させられた。



watching_camera.pde

import processing.video.*; import javax.mail.*; String SMTP = "smtp.gmail.com"; String FROM = "your_account@gmail.com"; String TO = "your@mail.address"; // Gmail Authentication String PORT = "587"; // or "465" String PASS = "your_password"; // 画像フォルダ. String PATH = "C:/your_folder/Processing/watching_camera/"; // 映像オブジェクト. Capture video; int W = 320; int H = 240; color[] ex_color = new color[W * H]; int tolerance = 50; // 許容値. float timer = 0; void setup() { size(W, H); video = new Capture(this, W, H, 30); // 30fps noStroke(); } void draw() { if (video.available()) { background(0); video.read(); set(0, 0, video); loadPixels(); int num_pixels = 0; for (int i = 0;i < W * H; i++) { // 前回と現在のピクセルの差. float diff_R = abs(red(ex_color[i]) - red(video.pixels[i])); float diff_G = abs(green(ex_color[i]) - green(video.pixels[i])); float diff_B = abs(blue(ex_color[i]) - blue(video.pixels[i])); // 許容値以上の差があれば動体. if(diff_R > tolerance && diff_G > tolerance && diff_B > tolerance) num_pixels++; // 現在のピクセルを保存. ex_color[i] = video.pixels[i]; } // 撮影間隔は5秒以上. 差のあるピクセルの数が100以上あれば撮影. if (millis() - timer > 5000 && num_pixels > 100) { DecimalFormat formatter = new DecimalFormat("00"); String date = str(year()); date += formatter.format(month()); date += formatter.format(day()); date += formatter.format(hour()); date += formatter.format(minute()); date += formatter.format(second()); String filename = "img" + date + ".jpg"; save(PATH + filename); timer = millis(); println("shot: " + date); // 画像ファイル添付のメールを送信. sendmail(date, filename, PATH); } } } void sendmail(String date, String filename, String path) { Properties props = System.getProperties(); props.put("mail.transport.protocol", "smtp"); props.put("mail.smtp.host", SMTP); // Gmail Authentication props.put("mail.smtp.port", PORT); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.starttls.enable", "true"); Session session = Session.getDefaultInstance(props, new Auth(FROM, PASS)); MimeMessage mimeMessage = new MimeMessage(session); try { mimeMessage.setFrom(new InternetAddress(FROM)); mimeMessage.setRecipients(Message.RecipientType.TO, TO); mimeMessage.setSubject("SHOT: " + date, "iso-2022-jp"); Multipart mp = new MimeMultipart(); MimeBodyPart mbp; // メール本文. mbp = new MimeBodyPart(); mbp.setText("撮影日時: " + date, "iso-2022-jp"); mbp.setHeader("Content-Type", "text/plain"); mp.addBodyPart(mbp); // ファイル添付. mbp = new MimeBodyPart(); FileDataSource fds = new FileDataSource(path + filename); DataHandler imgdh = new DataHandler(fds); mbp.setDataHandler(imgdh); mbp.setFileName(MimeUtility.encodeWord(filename)); mp.addBodyPart(mbp); mimeMessage.setContent(mp); mimeMessage.setSentDate(new Date()); // メール送信. Transport.send(mimeMessage); } catch (Exception e) { e.printStackTrace(); } }

Auth.pde

import javax.mail.Authenticator; import javax.mail.PasswordAuthentication; public class Auth extends Authenticator { private String username, password; public Auth(String username_, String password_) { super(); username = username_; password = password_; } public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } }

1 コメント:

Blogger さんのコメント...

You could be qualified to get a $1,000 Amazon Gift Card.