Laser Tag 2.0 でのSnowLeopardの問題を解決しました

中西研の北原です。

Graffiti Research Lab » L.A.S.E.R. Tag で紹介されているLaser Tagというレーザポインタでプロジェクターが投影している先に落書きが出来るアプリケーションがあります。しかし、このLaserTagが作られてからSnowLeopardにバージョンアップしたり、LaserTagが依存しているOpenFrameworksが大幅に変わるなどでSnowLeopardでカメラを使った最にクラッシュしてしまう問題がありました。

Theodore Watson – Laser Tag 2.0 – How To – Download and Source Code のコメントに紹介されていたSnowLeopardの修正版の挙動も本家と少し違うように感じられたので修正したものを

http://web.sfc.keio.ac.jp/~soh335/download/LaserTag2002SnowLeopard.zip

にアップロードしました。

変更点としては、OpenFrameworksのバージョン、0.61を利用しつつ同胞されてるOpenCVやXMLを扱うアドオンがもとのLaserTagの時と大分変わってしまっているので、0.61で動くように昔のアドオンを変更して利用するというふうにしました。

Processingで射影変換(ホモグラフィ)

中西研の関口です。4年生になりました。気づいたら22歳。最近、疲れが抜けません。

さて、L.A.S.E.R. Tagをいじる機会があり、現在、P5用に移植してる真っ最中です。その中で重要な機能であるキャリブレーションの実装について今回はメモ程度に残しておきます。

L.A.S.E.R. Tagでは、読み込んだカメラ画像に対しキャリブレーションを施して正確にポインタと投影面が重なるように設定が可能となっています。これによって、斜めからカメラで落書き面を捉えても、正確に落書きすることが可能となっているんですね。

今回は、これとおんなじようなことを、Processingでやろうというわけです。

多分、L.A.S.E.R. TagではOpenCVを使ってキャリブレーションしてる気がするのですが、ProcessingにはOpenCVのライブラリがあるのですが、キャリブレーションする機能は使えないようだったので、結局頑張って計算することにしました。

そもそも、こういった平面の変換は射影変換と呼ばれていて、任意の4点が定まれば、一意にパラメータが定めることができます。(詳しくは、http://www.teu.ac.jp/clab/kondo/research/cadcgtext/Chap5/Chap503.html

ということで、キャプチャした画像から任意の4点を選択し、そこからパラメータを計算して表示すればなんとなくOKっぽいです。パラメータの計算は連立方程式をとけば出てきます。
homographyMatrix
ただ、あんまりProcessingにないライブラリやらを使いたくなかったので、今回はMatrixを使わずに計算することにしました。この際、AS3で同じことをしている人を発見したので、大いにパクリました。というかむしろ、AS3をP5に移植したといって過言ではないです。(参考:Homography | HIDIHO!

ということで、ソースコードです。

[java]
import JMyron.*;
JMyron jmyron;

PImage capture;
PImage convert;
boolean dragged = false;
int selector = 0;
final static int CAP_WIDTH = 320;
final static int CAP_HEIGHT = 240;
Point[] points = {
  new Point(),new Point(),new Point(),new Point()};

void setup(){
  size(CAP_WIDTH*3,CAP_HEIGHT);
  jmyron = new JMyron();
  jmyron.start(CAP_WIDTH,CAP_HEIGHT);  
  capture = new PImage(CAP_WIDTH,CAP_HEIGHT);
  convert = new PImage(CAP_WIDTH,CAP_HEIGHT);
  points[0].setPoint(0,0);
  points[1].setPoint(320,0);
  points[2].setPoint(320,240);
  points[3].setPoint(0,240);
}
void mousePressed(){
  if(dragged == false){
    for(int i=0; i < 4; i++){
      if(points[i].x -10 < mouseX && points[i].x + 10>mouseX){
        if(points[i].y -10 < mouseY && points[i].y + 10>mouseY){
          dragged = true;
          selector = i;
        }
      } 
    } 
  }
}
void mouseDragged(){
  if(dragged){
    if(mouseX > 0 && mouseX < CAP_WIDTH){
      points[selector].setPoint(mouseX,points[selector].y);
    }
    if(mouseY > 0 && mouseY < CAP_HEIGHT){
      points[selector].setPoint(points[selector].x,mouseY);

    }
  }
}
void mouseReleased(){
  dragged = false; 
}
void draw(){

  jmyron.update();
  int[] jImage = jmyron.image();
  capture.loadPixels();
  for(int i=0;i < capture.width*capture.height; i++){
    capture.pixels[i] = jImage[i]; 
  }
  capture.updatePixels();
  image(capture,0,0);
  ellipseMode(CENTER);
  noFill();
  strokeWeight(1);
  for(int i=0; i < 4; i++){
    stroke(255*(i%2),64*(i%4),0);

    ellipse(points[i].x,points[i].y,10,10);
    line(points[i%4].x,points[i%4].y,points[(i+1)%4].x,points[(i+1)%4].y);
  }
  convert = setTransform(capture,CAP_WIDTH,CAP_HEIGHT,points[0],points[1],points[2],points[3]);
  image(convert,CAP_WIDTH,0);

}
PImage setTransform(PImage pimg,
int destWidth, int destHeight,
Point p0, Point p1,
Point p2, Point p3){
  if ( p0 == null || p1 == null || p2 == null || p3 == null ) return null;
  float[] system = new float[8];
  system = getSystem(p0,p1,p2,p3);
  PImage target = new PImage(destWidth,destHeight);

  int i,j,x,y,u,v;
  Point p;

  for(i = 0; i

processingのcaptureを使えばいいんですが、ちょっと都合上JMyronを使わせて頂きました。と言っても、Captureでも特に問題はありません。その場合は、captureをPImageからCaptureに変えればすんなり動く気がします。

実行結果はこんな感じです。

homography

このように、ポスターが長方形ぴったりに収まるように表示されるようになりました。

これで、カメラの位置に関係なく色々することができますね!

それでわ。

whiteglovetracking

中西研の北原です。

ネタ自体は古いものなのですが、whiteglovetrackingというプロジェクトが2007年からあります。これはマイケル・ジャクソンのBillie Jeanでの白い手袋をトラッキングしようという試みで、トラッキングの方法がとにかく毎フレームごとに四角の位置を記録してデータとして残してみたというものです。このサイト自体が2007年ということと、this is itが公開されてからも少し経っているという微妙感はいなめませんが、面白そうなのでやってみました。

“whiteglovetracking” の続きを読む

Javaで自作のボタンを作る

メディア技術基礎最終課題の参考にしていただきたいと思い…

まずは、簡単な自作ボタンの作成例です。JPanelを拡張し、MouseListenerを実装することで作ることにします。
JavaSwingには、すでにJButtonという部品が用意されていますが、自分の作りたいモノのイメージに合ったボタンを自作した cheap bounce house for saleいと思う人も多いのではないでしょうか。ボタンを自作したところで機能的にはなんら変わりはなく、そのため本質的な部分ではないのかもしれませんが…。

ソースコードは以下の2つです。

[java]
/************************************************************************************
* MainFrame.java
* JFrameの設定をして、自作したOriginalIconクラスをその上に配置する。
* JFrameに配置する際、直接レイアウトするとうまくいかないため、土台となるJPanelを用意しその上に配置。
* 詳しくは、http://www.javadrive.jp/tutorial/jpanel/index5.html
************************************************************************************/

import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class MainFrame {

private JFrame frame;

MainFrame(){

//JFrameのインスタンス作成
frame = new JFrame(“OriginalIcon”);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200, 200);

//お決まりのウインドウ設定
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.addWindowListener(new WindowAdapter(){
public void windowClosed(WindowEvent e) {
System.exit(0);
}
});

//自作したOriginalIconクラスをインスタンス化
OriginalIcon iconImg = new OriginalIcon();
//OriginalIconのインスタンスをのせるJPanelを用意する
JPanel base = new JPanel();
base.add(iconImg);
frame.getContentPane().add(base, BorderLayout.SOUTH);
frame.setVisible(true);

}

public static void main(String[] args){
MainFrame frame = new MainFrame();
}

}    end of MainFrame
[/java]

[java]
/************************************************************************************
* OriginalIcon.java
* JPanelを拡張し、MouseListenerを実装する。
* 用意した2枚の画像を読み込み、マウスに反応させる。
************************************************************************************/

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class OriginalIcon extends JPanel implements MouseListener{

private BufferedImage icon1;
private BufferedImage icon2;
private boolean mouse_enter;

public OriginalIcon() {
this.setPreferredSize(new Dimension(75, 75));
this.addMouseListener(this);
mouse_enter = false;

////////////////////////////////////////////////////////////////////////
// 画像読み込み関連処理
////////////////////////////////////////////////////////////////////////
File imgfile1 = new File(“img/naka-lab_a.gif”);
File imgfile2 = new File(“img/naka-lab_b.gif”);
try {
icon1 = ImageIO.read(imgfile1);
icon2 = ImageIO.read(imgfile2);
} catch (IOException e) { e.printStackTrace(); }

}

////////////////////////////////////////////////////////////////////////
// paintメソッドをオーバーライド
////////////////////////////////////////////////////////////////////////
public void paint(Graphics g) {
g.clearRect(0, 0, this.getWidth(), this.getHeight());
g.drawImage(icon1, 0, 0, this);
if(mouse_enter == true)    g.drawImage(icon2, 0, 0, this);
g.dispose();
}

////////////////////////////////////////////////////////////////////////
// マウスリスナー関連
////////////////////////////////////////////////////////////////////////
public void mouseClicked(MouseEvent e) {
System.out.println(“Click!”);
}
public void mouseEntered(MouseEvent e) {
mouse_enter = true;
System.out.println(“Enter!”);
System.out.println(“mouse_enter: ” + mouse_enter);
repaint();
}
public void mouseExited(MouseEvent e) {
mouse_enter = false;
System.out.println(“Exit!”);
System.out.println(“mouse_enter: ” + mouse_enter);
repaint();
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}

}    end of OriginalIcon
[/java]

eclispeの画像、実行画面を2つ

次は、簡単な画像ビューワーの例です。Flickrの画像を使いますが、URLを決め打ちしています。アイコン部分と画像表示部分の関係性がその方が分かりやすいと思うからです。ですが、この方法(URL決め打ち)では最終課題で良い評価はいただけないでしょう。
ソースコードは以下の3つです。

[java]
/************************************************************************************
* SimplePhotoViewerMain.java
* JFrameの設定をして、自作したIconPanelクラスとPhotoPanelクラスをその上に配置する。
* PhotoPanelクラスは特殊な方法でインスタンスを生成(シングルトンパターン)する。
* JFrameに配置する際、直接レイアウトするとうまくいかないため、土台となるJPanelを用意しその上に配置。
* 詳しくは→http://www.javadrive.jp/tutorial/jpanel/index5.html
* Flickrの画像はURLを決め打ちでIconPanelインスタンスに渡す( ※※※ 最終課題ではこの方法は不可! ※※※ )。
************************************************************************************/

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class SimplePhotoViewerMain {

private JFrame frame;

SimplePhotoViewerMain(){

//JFrameのインスタンス作成
frame = new JFrame(“SimplePhotoViewer”);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(640, 480);

//お決まりのウインドウ設定
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.addWindowListener(new WindowAdapter(){
public void windowClosed(WindowEvent e) {
System.exit(0);
}
});

//自分で定義したメソッドを呼ぶ
this.setIconImage();
//PhotoPanelクラスのインスタンスを配置
frame.getContentPane().add(PhotoPanel.getInstance(), BorderLayout.CENTER);    //※シングルトンパターン
frame.setVisible(true);

}

////////////////////////////////////////////////////////////////////////
// 自作したIconPanelクラスのインスタンスを8コ作り、画面下部に配置。
// 個々のインスタンスにはIconPanelクラスで定義してあるsetURLメソッド用い、
// 画像のURL(Flickr)を渡す。
// レイアウトはGridLayoutで設定
//(参考:http://www.javadrive.jp/tutorial/gridlayout/index.html)
////////////////////////////////////////////////////////////////////////
private void setIconImage() {
IconPanel[] iconImg = new IconPanel[8];
JPanel bar = new JPanel();
JPanel barComp = new JPanel();
bar.setLayout(new GridLayout(1, 8, 3, 0));
for(int i = 0; i  < iconImg.length; i++) {
iconImg[i] = new IconPanel();
bar.add(iconImg[i]);
}
barComp.add(bar);
iconImg[0].setURL(“http://farm4.static.flickr.com/3502/3728985417_b60ec4c584”);
iconImg[1].setURL(“http://farm3.static.flickr.com/2626/3729767622_6d5f06d694”);
iconImg[2].setURL(“http://farm4.static.flickr.com/3246/3731935380_2fef26490b”);
iconImg[3].setURL(“http://farm3.static.flickr.com/2548/3730278390_e789b31cfe”);
iconImg[4].setURL(“http://farm3.static.flickr.com/2482/3730292862_fafea11f70”);
iconImg[5].setURL(“http://farm3.static.flickr.com/2629/3728938115_c1315e5c86”);
iconImg[6].setURL(“http://farm3.static.flickr.com/2430/3728941305_ef2be9ae69”);
iconImg[7].setURL(“http://farm3.static.flickr.com/2602/3728939335_4601509898”);
frame.getContentPane().add(barComp, BorderLayout.SOUTH);
}

public static void main(String[] args){
SimplePhotoViewerMain frame = new SimplePhotoViewerMain();
}

}    end of SimplePhotoViewerMain
[/java]

[java]
/************************************************************************************
* IconPanel.java
* JPanelを拡張し、MouseListenerを実装する。
* マウスカーソルによって、画像の色を変える。
* またクリックされたらPhotoPanelインスタンスに画像のURL(文字列)を渡す。
************************************************************************************/

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageProducer;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.GrayFilter;
import javax.swing.JPanel;

public class IconPanel extends JPanel implements MouseListener{

private BufferedImage img;
private String imgURL;
private boolean color;

public IconPanel() {
this.setPreferredSize(new Dimension(75, 75));
this.addMouseListener(this);
color = false;
}

////////////////////////////////////////////////////////////////////////
// paintメソッドをオーバーライド
////////////////////////////////////////////////////////////////////////
public void paint(Graphics g) {
//画像のグレイスケール化処理
GrayFilter grayFilter = new GrayFilter(true, 15);
ImageProducer ipG = new FilteredImageSource(img.getSource(), grayFilter);
Image grayImg = createImage(ipG);
g.drawImage(grayImg, 0, 0, this);
//このクラスのインスタンス(画像)の上にマウスカーソルが入ったら、カラー画像を表示
if(color == true)    g.drawImage(img, 0, 0, this);
g.dispose();
}

////////////////////////////////////////////////////////////////////////
// Mainクラスから画像のURL(文字列)を引数として受け取り、その文字列に”_s.jpg”を
// 追加した上でURLとして読み込み、画像をとってくる。
////////////////////////////////////////////////////////////////////////
public void setURL(String str) {
imgURL = str;
URL url = null;
try {
url = new URL(imgURL + “_s.jpg”);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
img = ImageIO.read(url);
} catch (IOException e) { e.printStackTrace(); }
}

////////////////////////////////////////////////////////////////////////
// マウスリスナー関連
////////////////////////////////////////////////////////////////////////
public void mouseClicked(MouseEvent e) {
System.out.println(“Click!”);
PhotoPanel.getInstance().setImage(imgURL);        //※シングルトンパターン
}
public void mouseEntered(MouseEvent e) {
color = true;
}
public void mouseExited(MouseEvent e) {
color = false;
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}

}    end of IconPanel
[/java]

[java]
/************************************************************************************
* PhotoPanel.java
* JPanelを拡張。
* シングルトンパターンにならい、自身のインスタンスをprivate/staticで宣言し、
* 他のクラスからはgetInstance()メソッドによって、インスタンスを取得させるようにする。
* IconPanelから画像のURL(文字列)を受け取り、画像を読み込み表示する。
************************************************************************************/

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class PhotoPanel extends JPanel{

//自身のインスタンスをprivate/staticで生成。
private static PhotoPanel instance = new PhotoPanel();

private URL url;
private BufferedImage img;

private PhotoPanel(){
url = null;
}

////////////////////////////////////////////////////////////////////////
// paintメソッドをオーバーライド
////////////////////////////////////////////////////////////////////////
public void paint(Graphics g) {
g.clearRect(0, 0, this.getWidth(), this.getHeight());
if( url != null) {
g.drawImage(img, this.getWidth()/2 – img.getWidth()/2, this.getHeight()/2 – img.getHeight()/2, this);
}
g.dispose();
}

////////////////////////////////////////////////////////////////////////
// IconPanelクラスから画像のURL(文字列)を引数として受け取り、その文字列に”_m.jpg”を
// 追加した上でURLとして読み込み、画像をとってくる。
////////////////////////////////////////////////////////////////////////
public void setImage(String str) {
try {
url = new URL(str + “_m.jpg”);
} catch (MalformedURLException e) { e.printStackTrace(); }
try {
img = ImageIO.read(url);
} catch (IOException e) { e.printStackTrace(); }
this.repaint();
}

//このクラスのインスタンスはメソッドで生成させるようにする。
public static PhotoPanel getInstance() {
return instance;
}

}    end of PhotoPanel
[/java]
IconPanelクラスは基本的に自作ボタンのOriginalIconクラスと同じです。
(ということは、OriginalIconクラスを継承してIconPanelクラスを作った方が良かったかも…。)
主な変更点は、
・画像の場所がローカルでなくなったことに対する対処、
・画像を表示するPhotoPanelクラスとの連携、
・マウスカーソルによるアイコンの画像変化を2枚ではなく1枚の画像に画像処理フィルターを用いることで表現するようにした
です。
はじめに簡単なソースコードを記述してから、目的の機能を付け加えていくとプログラミングの見通しが良くなるかもしれません。

rubyによるマルコフ連鎖とvimのデバッグ操作

中西研の北原です。前回はrubyのライブラリであるnokogiri, mechanize, ruby-growlを使いました。今回はnokogiriを使いさらに形態素解析エンジンであるMeCabを利用して簡単なマルコフ連鎖による文章の再構築を行ってみることにします。また僕はvimを使っているのでそのあたりにも触れる予定です。
“rubyによるマルコフ連鎖とvimのデバッグ操作” の続きを読む

RubyとTwitter

大森です。さんざん出ている小ネタです。

rubyを使ってTwitterコメントを送るというものです。
どうやら ruby twitter gem を使えば簡単にできそうです。

インストールも
$ sudo gem install twitter
で簡単です。

ソースはこんな感じです

require 'rubygems'
gem 'twitter'
require 'twitter'

message = "hoge"
httpauth = Twitter::HTTPAuth.new('username', 'Passward')
base = Twitter::Base.new(httpauth)
base.update(message)

こうなりました

Flexとあわせてイタズラすることを考えています。しかしSFCサーバとrubyは相性悪い…
また報告します

体育状況をrubyで取得する

中西研の北原です。SFCでは体育を30回もとらないと卒業出来ません。しかし体育は予約制なので運がわるいと抽選から漏れてしまいます。しかし、予約を解除する人は毎回でるので、うまく予約したいものです。そこで前々から興味があったrubyで作られているパーサのnokogiriを使ってgrowlに体育の状況を通知させてみました。
“体育状況をrubyで取得する” の続きを読む

eclipseのinstall

北原です。今回は授業でeclipseを使うのでインストール方法について書きたいと思います。

http://www.eclipse.org/downloads/index.php

ダウンロードページは上記です。幾つか種類がありますが、Eclipse IDE for Java Developersを選んでダウンロードして下さい。
macの場合はダウンロードをしたものを解凍しアプリケーションフォルダにいれれば大丈夫です。

30A230D5309A30EA30B130FC30B730E730F3
Uploaded with plasq‘s Skitch!

次にwindowsの方の場合です。windowsの場合はこちらのサイトを参考にするとよいようです。

http://www.site-cooler.com/java/eclipse/

AS3で画像を任意の基準点で回転させる

中西研の関口です。春休みですね。春休みと言えば勉強を疎かにしてついつい趣味に走ってしまう学徒も多いのではないでしょうか。

そんなこんなで、昨晩から腕時計の絵を書くという、不毛な作業をしていました。そんな作業の鍔際に、ふと、TeraClockという有名なASで簡単に時計が作れる便利なライブラリがあるので、Flashで実際に動かそうという、また不毛な作業を思い立ち、”勉強がてら”、腕時計をFlashに移植することにしました。

絵の用意は案外サクッと出来たので、いよいよAS3で時計を作るのですが…。

“AS3で画像を任意の基準点で回転させる” の続きを読む