ProcessingとRubyの連携

ProcessingはMacWindowsLinuxで簡単に動かせて、どの環境でも同じコードで一番楽にUSBカメラを扱えるので愛用してたんだけど
Javaなのでいろいろネットワークまわりの所に俺が慣れてない。


今回はwebcamで撮影して簡単な画像処理をして、httpでform-multipartで画像をpostするかんたんなアプリを作りたかったけど挫折したのでメモしておく。

試したもの

action-coding

http://code.google.com/p/action-coding/
JRubyでprocessingを使う。
processing最新版だとcore.jarの位置が変わったぽい。
aco.rbの54行目で/Application/Processing.app/Contents/Resources/Java/core.jarを参照するように修正したら動いた。

include_class 'processing.video.Capture'

しても見つからないとエラーがでて挫折。

Ruby-Processing

https://github.com/jashkenas/ruby-processing/wiki
CRubyからどういうわけかProcessingが使えるようになる、rp5というコマンドラインツールがインストールされる。
rp5は動くけど、Rubyのライブラリと混在させて書くとダメ。
例えば

require 'open-uri'
puts open('http://shokai.org').read

というのを混ぜるとMacだとブラウザでwebページを開いてしまって挫折。

JMFを使う

http://www.oracle.com/technetwork/java/javase/tech/index-jsp-140239.html
JMFでJavaからカメラが使える。という事はJRubyからカメラが使える。
i-sightが認識されなくて挫折。
JMF自体はMac版が無いが、linux版をダウンロードしてきてjmf.jarを /Library/Java/Extensions/ の中にコピーすれば使える。

まだ

apache-mime4jとhttpmimeを使う

たぶん http://shokai.org/blog/archives/5401 の下の方で書いたのを、processingからやればできる。けどなんか・・パッケージ管理システムが無いだけでこんなにやる気が無くなるものとは。


最終的に、

Processingでカメラの画像を定期的にcamera.jpgに保存して

import processing.video.*;

Capture camera;

void setup(){
  size(320, 240); // width, height
  camera =  new Capture(this, width, height, 1); // 1fps
}

void draw(){
  image(camera, 0, 0);
  saveFrame("camera.jpg"); // capture
  delay(1000);
}

void captureEvent(Capture camera){
  camera.read();
}


アップロード先に合わせたアップロードライブラリ作って
lib/upload_client.rb

#!/usr/bin/env ruby
require 'rubygems'
require 'uri'
require 'json'
require 'httpclient'

class UploadError < Exception
end

module UploadClient
  
  def self.upload(filename, url)
    c = HTTPClient.new
    res = nil

    open(filename){|file|
      postdata = {
        'file' => file
      }
      res = c.post_content(url, postdata)
    }

    begin
      res = JSON.parse(res)
      raise UploadError.new res['error'] if res['error']
      raise UploadError.new 'file size error' if res['size'].to_i != File.size(filename)
      return res['url'] if res['url']
    rescue => e
      throw e
    end
    raise UploadError.new 'unknown error'

  end

end


これでcamera.jpgの更新日時を定期的に監視してアップロードするようにした・・
uploader.rb

#!/usr/bin/env ruby
require 'rubygems'
require 'yaml'
require 'ArgsParser'
require 'FileUtils'
require File.dirname(__FILE__)+'/lib/upload_client'

$KCODE = 'u'

parser = ArgsParser.parser
parser.bind(:loop, :l, 'do loop')
parser.bind(:file, :f, 'upload file')
parser.bind(:interval, :i, 'uplaod interval : default 5 (sec)')
parser.bind(:url, :u, 'upload URL')
parser.bind(:help, :h, 'show help')
first, params = parser.parse(ARGV)

params[:interval] = 5 unless params[:interval]

unless parser.has_params([:file, :url]) or parser.has_param(:help)
  puts parser.help
  exit
end

p params

f = params[:file]
unless File.exists? f
  STDERR.puts 'upload file not exists'
  exit 1
end

last_mtime = 0
loop do
  if File.mtime(f).to_i != last_mtime
    last_mtime = File.mtime(f).to_i
    tmp_file = File.dirname(__FILE__)+'/tmp'
    FileUtils.cp(f, tmp_file)
    begin
      url = UploadClient::upload(tmp_file, params[:url])
    rescue => e
      STDERR.puts e
      url = nil
    rescue UploadError => e
      STDERR.puts e
      url = nil
    end
    puts "[#{last_mtime}] #{url}" if url
  end
  break unless params[:loop]
  sleep params[:interval].to_i
end