INDEX(各項目ごとの目次)

[HOME]  [Processing関係]  [Arduino関係]  [マテリアル関係]  [秋葉原工作マップ]

2008年用ですが、部分的に内容を更新しています(2010/06/14)。
また、[建築農業工作ゼミ2009-2010]とも連動していますので、そちらにも幾つかサンプルがあります。
:

ラベル processing の投稿を表示しています。 すべての投稿を表示
ラベル processing の投稿を表示しています。 すべての投稿を表示

10/17/2008

Arduino+Xbee Shield/Processing+XBee Explorer USB

Arduinoにはワイヤレス通信するためのXbee Shieldがあります。今回はArduinoのXbee Shieldのサイトを参考に簡単な通信実験から始めたいと思います。Xbee Shiledを装着したArduino基板が2個必要になります。


(Xbee Shiledを装着したArduino基板)

注意しなければいけないことは、Arduino基板にXbee Shiledを装着したままプログラムをアップロードする際、Xbee Shiled上にある二つのジャンパピン(二つの間には「XBEE/USB」と表示されています)を「USB」側に差し替えなければいけないことです(3本のピンのうち、USB側のピンと中央のピンの2本のピンに差し込まれている状態になります)。
尚、アップロード後は「XBEE」側のピンと中央のピンの2本のピンに差し込まれている状態に戻してください。


「二つのArduino間での通信」
以下では、Xbee Shieldのサイトに従って、LEDの点滅実験を行います。一方のXbee Shiledを装着したArduino基板から、一秒おきに'H'か'L'の文字を送信し、受信したもう一方のXbee Shiledを装着したArduino基板の13番ピンに接続されたLEDが点滅する内容です。基本的には、通常のシリアル通信のプログラムと同じような内容になります。

送信側Arduinoのプログラム:
void setup(){
  //シリアル通信開始
  Serial.begin(9600);
}

void loop(){
  Serial.print('H');//「H」を送信(点灯)
  delay(1000);      //1秒待つ
  Serial.print('L');//「L」を送信(消灯)
  delay(1000);      //1秒待つ
}

送信側は一方的に'H'と'L'を1秒間隔で送信するだけです。受信側は、送信側からのデータが届いたら読み込みをし、データが「H」であれば13番ピンをHIGHで出力し、そうでなければLOWで出力するプログラムになります。

受信側Arduinoのプログラム:
int val;//受信データ用の変数を用意

void setup(){
  //シリアル通信開始
  Serial.begin(9600);
  //13ピンをデジタル出力に設定
  pinMode(13,OUTPUT);
}

void loop(){
  if(Serial.available()>0){ //データが来たら
    val=Serial.read();    //データを読み込み、valへ代入
  }
  if(val=='H'){           //valが「H」の場合
    digitalWrite(13,HIGH);//点灯
  }else{                  //valが「L」の場合
    digitalWrite(13,LOW); //消灯
  }
}

乾電池などの外部電源でArduinoを作動させれば、それぞれスタンドアロンで通信し合います。Decimilaなどの旧型のArduino基板の場合、外部電源を使うにはArduino基板についている「USB/EXT」のジャンパピンを「EXT」側に差し替える必要があります(プログラムをアップロードする際には、パソコンとUSB接続し、ジャンパピンも「USB」側に差し替えて下さい)。



「コンピュータに接続したXbeeと通信」:
コンピュータにXbeeを接続し、Arduinoとワイヤレス通信するには以下のようになります。
Sparkfunで販売されている「XBee Explorer USB」を使うと簡単にコンピュータとUSB接続でき、Processingからワイヤレスにシリアル通信が可能になります。


(左:「XBee Explorer USB」、右:Xbeeを装着した状態)

以下では、先ほどの受信用のXbee Shieldを装着したArduino基板に対して、Xbeeモジュールを装着した「XBee Explorer USB」を通して、Processingからワイヤレスでシリアル通信してみます。
まず、「XBee Explorer USB」のシリアルポートを調べてみます。

import processing.serial.*;
println(Serial.list());

「XBee Explorer USB」をコンピュータに接続し、上記の二行のプログラムをランさせれば、「XBee Explorer USB」のシリアルポートが出力されるはずです。


「Processingのプログラム」
受信側となるArduinoのプログラムは先ほどと同じものを使い、送信側となるProcessingだけのプログラムを以下に書きます。マウスボタンを押したら点灯、放したら消灯する内容とします。

//シリアル通信ライブラリを取り入れる
import processing.serial.*;
//ポートのインスタンス
Serial port;

void setup(){
  //「XBee Explorer USB」のシリアルポート設定
  port=new Serial(this,"/dev/tty.usbserial-A8003VXd",9600);
}
void draw(){
  //特になし  
}

void mousePressed(){//マウスボタンを押したら
  port.write('H');  //「H」を送信(点灯)
}

void mouseReleased(){//マウスボタンを放したら
  port.write('L');   //「L」を送信(消灯)
}

プログラムの内容はこれまでのシリアル通信と同じなので、ポートの設定、接続やジャンパピンの差し替えなどを間違わなければ特に問題はないと思います。



「Xbeeモジュールの設定/ATコマンド」
XbeeにATコマンドを送ることで、Xbeeモジュール自体の設定を確認したり変更することができます。ATコマンドをXbeeモジュールに送信するには、MacOSXなら「ZTerm」など、Windowsなら「ハイパーターミナル」や「Tera Term」などのターミナルアプリケーションで入力すると便利です(「ZTerm」と「ハイパーターミナル」の設定や使い方については、「Arduino-Processing BlueTooth通信+曲げセンサ」にも、説明があります)。

コンフィグレーションモードに入るには、
+++

プラスを3回入力し待機します(リターンキーは押さない)。そうすれば、
OK

という返事が返ってきます(ATコマンドモードでは、「+++」を押したあと1秒後に「OK」が返答され、さらに10秒以内に次のコマンドを送信しなければコマンドモードが自動的に終了してしまうので、返答がない場合は再度「+++」を押す必要があります)。さらに続けて、
ATID(リターンキーを押して送信)

と押せば、デフォルトの状態であれば、
3332

という数値が返ってきます。「3332」という数値は、そのモジュールのネットワークIDであり、このネットワークIDを変更すれば同じネットワークIDを共有しているXbeeモジュール間だけでの通信が可能になります(ひとつのモジュールだけでなく、相手になるモジュールも変える必要があります)。
設定されている通信速度を確認するには、
ATBD(リターンキーを押して送信)

と押せば、デフォルトであれば、
3

という数値が返ってきます。通信速度はそれぞれ
0:1200 bps
1:2400 bps
2:4800 bps
3:9600 bps
4:19200 bps
5:38400 bps
6:57600 bps
7:115200 bps

なので、デフォルトの「3」は9600 bpsということになります。
ATBD4(リターンキーを押して送信)

と打てば(「ATBD」の後に「4」を付け加える)、通信速度は「4」(19200)に変更されます(「OK」という返答がきます)。再度、
ATBD(リターンキーを押して送信)

と打てば、変更した内容を確認できます(この場合「4」という数値が返ってきます)。ただし、この場合電源が切れると設定内容は消えてしまいます。電源を切っても設定内容が戻らないようにするには、
ATWR(リターンキーを押して送信)

を変更後すぐに送信します(「OK」という返答がきます)。あるいは、
ATBD4,WR(リターンキーを押して送信)

という感じで、コマンドを複数合成して送信することもできます(「OK OK」という二つ分の返答がきます)。デフォルトの状態に戻すには、
ATRE(リターンキーを押して送信)

になるのですが、「WR」を付け加えていないので再度電源を入れたときには前回の状態に戻ってしまいます。
ATRE,WR(リターンキーを押して送信)

とすれば、電源を入れ直してもデフォルト状態は保持されます。
尚、コマンドモードから出るには、
ATCN(リターンキーを押して送信)

になります。
デフォルトでは、
ID:3332(ネットワークID)
CH:0x0C(チャンネル)
MY:0(そのモジュールのアドレス)
SH: (シリアルナンバー上位32ビット/モジュールごとに異なる)
SL: (シリアルナンバー下位32ビット/モジュールごとに異なる)
DH:0(送信先アドレス上位32ビット)
DL:0(送信先アドレス下位32ビット)
BD:3(通信速度:9600 bps)

に設定されており、ネットワーク、チャンネル、モジュールのアドレス、送信先アドレスがそれぞれ同じであるため、どのモジュール間でも通信可能です。逆に、ネットワークIDを変更してしまえば、他のモジュール群から干渉を受けずに通信し合うことも可能になります。あるいは、二つのモジュール間で送信先を互いに設定してしまえば、そのアドレスのモジュールだけとの通信が可能になります。そのためには、IDとCHは共有しておき、それぞれのモジュールアドレスを個別に設定しておきます。
ATMY1111,WR(リターンキーを押して送信)
OK OK(Xbeeからの返信)

とすれば、このモジュールの「MY」は「1111」に設定されたことになります(「WR」を付け加えたので、電源を落としても変更内容は記憶されます)。さらに送信先のアドレスを「2222」に設定するには、「DL」を「2222」に「DH」を「0」にします。
ATDL2222,DH0,WR(リターンキーを押して送信)
OK OK OK(Xbeeからの返信)

もう一方のほうも設定する必要があるので、「MY」を「2222」、「DL」を「1111」、「DH」を「0」にします。
ATMY2222,DL1111,DH0,WR(リターンキーを押して送信)
OK OK OK OK(Xbeeからの返信)

とします。こうすることで、「1111」のモジュールと「2222」のモジュールが互いに送信先を特定して通信し合うことができます。

Arduino Xbee Shieldサイトの説明によれば、「DH」を「0」、「DL」を「FFFF」に設定すれば、そのモジュールからの通信は、その他のすべてのモジュールによって受信可能になります。
また、送信先アドレス(上位ビットDHと下位ビットDL)が「FFFF」より大きい値(つまり「DH」が「0」以外の数値に設定したとき)、その「DH」と「DL」が相手モジュールの「SH」と「SL」に等しければ、相手モジュールのみに受信させることが可能になります。ただし、この場合も、ネットワークIDとチャンネルは同じでなければなりません。


「Ztermの設定」
以下は、MacOSXで「ZTerm」を使ったときの画面です。


ATコマンドを打つ画面。



メニューバー>Dial>Directory...をクリックすれば、上画面が現れます。「New」を押せば、新たな接続先を追加する画面(下画像)がでてきます。



「Service Name:」の欄に適当な名前を入れます。「Local Echo」にはチェックをいれておきます。チェックを外すと、自分の打った文字は画面に現れないので、チェックを入れておいた方がいいでしょう。Xbeeがデフォルト状態であれば、その他の項目は上画面のようになります。メニューバー>Setting>Connection...をクリックすることで、再度この画面が現れます。



メニューバー>Setting>Terminal...をクリックすれば、上画面が現れます。ここでは「Auto Line Feed」にチェックをいれておきます。チェックを外すと、ATコマンドが改行されなくなるので、チェックをいれておいたほうがいいでしょう。恐らく、MacOSXの改行コードは、「CR(キャリッジリターン:行頭に戻る)」だけなので、「LF(ラインフィード:次の行に移る)」も付け加えないと改行されなくなるからでしょう。

Zigbee開発ハンドブック (実践入門ネットワーク)
鄭 立
リックテレコム
売り上げランキング: 9866

DESIGNING ZIGBEE NETWORKS AND TRANSCEIVERS: The Complete Guide for Rf/Wireless Engineers
Shanin Farahani
Newnes
売り上げランキング: 21994

10/12/2008

Processing FileChooser2

以前「Processing FileChooser/ファイル選択画面の表示」でJava Swingを用いましたが、Processing 146以降からselectInput()によって、ファイル選択画面を通して任意の場所にあるファイルを読み込むことが簡単にできるようになりました。
以下は、「f」キーを押すとselectInput()で、ファイル選択画面を表示し、コンピュータの任意の場所にある画像を表示するサンプルです。表示された画像は、ドラッグすることで位置を変えられるようにしてあります。

//画像用インスタンス用意
PImage img;

//現在選択中のファイルパスの変数
String currentPath=null;
//画像配置座標の変数
int x,y;
//画像配置座標とクリック座標の差分の変数
int dx,dy;

void setup(){
  //画面サイズ設定
  size(600,400);
}

void draw(){
  //背景描画(黒)
  background(0);
  //現在選択中のファイルパスが空ではないとき
  if(currentPath!=null){
    //画像描画
    image(img,x,y);
  }
}

//クリックしたら
void mousePressed(){
  //画像配置座標とクリック座標の差分を求めておく
  dx=x-mouseX;
  dy=y-mouseY;
}

//ドラッグ中
void mouseDragged(){
  //マウス座標に差分座標を加えた値を画像配置座標とする
  x=mouseX+dx;
  y=mouseY+dy;
}

//キーを押した場合
void keyPressed(){
  //「f」キーなら
  if(key=='f'){
    //ファイル選択画面を表示し選択したファイルパス取得
    String loadPath = selectInput();
    //ファイルパスが空の場合
    if (loadPath == null) {
      //「ファイルが選ばれてない」メッセージを出力
      println("No file was selected...");
      //ファイルパスを前回のファイルパスにする
      loadPath=currentPath;
    } 
    else {//ファイルパスが選択された場合
      //ファイルパスのドット以降の文字列を取得(拡張子名を取得)
      String ext = loadPath.substring(loadPath.indexOf('.') + 1);
      //拡張子が「jpg」または「png」なら
      if(ext.equals("jpg") || ext.equals("png")){
        //選択ファイルパスの画像を取り込み
        img = loadImage(loadPath);
        //現在選択中のファイルパスを更新
        currentPath=loadPath;
        //現在選択中のファイルパスを出力
        println(currentPath);
      }else{//拡張子が「jpg」または「png」ではないとき
        //「画像ファイルではない」と出力
        println("Not image file.");
      }      
    }
  }
}


ファイル選択画面上で選択したファイルのパスを取得したら、substring()によってパスの文字列末尾に含まれる拡張子を調べます。その際、indexOf()を使うことで、パスの文字列に含まれる「.」を手掛かりに、パスの文字列末尾の拡張子を抜き出します(indexOf()は、括弧内に入れた文字が文字列中の何番目にあるかを教えてくれます)。
今回は、拡張子が「jpg」か「png」であれば、loadImage()で画像を取り込み、次回のためにファイルパスを記憶させておきます。それ以外の拡張子の場合は、"Not image file."だけを出力します。

selectInput()以外に、フォルダを選択するためのselectFolder()や、保存先を指定するselectOutput()も加えられています。

----------------------------------
以下は頂いたコメントに対するサンプルです。
import java.awt.*;

void setup() {
  size(200,200);
}

void draw(){  
}

void fileSelected(File selection) {
  if (selection == null) {
    println("Window was closed or the user hit cancel.");
  } else {
    println("User selected " + selection.getAbsolutePath());
  }
}

void mousePressed(){
  FileDialog fd = new FileDialog(new Frame(),"Choose a file");
  fd.setDirectory("/Users/username/Desktop");//ここのディレクトリを任意に変えてください。
  fd.setVisible(true);
  if(fd.getFile()==null){
    println("Canceled");
  }else{
    println("FILE NAME:"+fd.getFile());
    println("DIRECTORY:"+fd.getDirectory());
  }
}

10/11/2008

Processing Webカメラ/定点記録画像

身体の動作などを連続写真として記録するために、Webカメラを用いインデックス番号をつけて画像保存する方法です。設定したフレームレートで撮影画像を順番に保存していきます。以下は、フレームレート2の速度(0.5秒/フレーム)で処理するサンプルです。sキーで連続写真の画像を0.5秒おきに保存し、eキーで保存を終了、cキーでカメラセッティング画面に切り替わります。
videoライブラリの基本的な使い方は「Processing Video(Webカメラ)」を参照して下さい。

*Windowsの場合、そのままの設定ではこのVideoライブラリを使用することができません。WinVDIG 1.0.1をインストールする必要があります。


//ライブラリの取り込み
import processing.video.*;
Capture myCapture;

//記録開始用のフラグ
boolean start=false;
//記録画像インデックス用変数
int num=0;

void setup() {
//画面サイズ設定
size(320, 240);
//キャプチャする映像の設定(2フレーム/秒)
myCapture = new Capture(this, width, height, 2);
//ループのフレームレート(2フレーム/秒)
frameRate(2);
}

void draw() {
//映像を画面に配置
image(myCapture, 0, 0);
if(start){
//記録中の目印表示
rect(0,0,10,10);
//記録画像インデックス名(jpgで保存)
String s="image_"+num+".jpg";
//画像を保存
save(s);
//インデックス番号を更新
num++;
}
}

//映像の読み込み
void captureEvent(Capture myCapture) {
myCapture.read();
}

void keyPressed(){
//sキーで画像記録開始
if(key=='s'){
start=true;
}
//eキーで記録終了
if(key=='e'){
start=false;
}
//cキーでカメラセッティング
if(key=='c'){
myCapture.settings();
}
}


連続写真の画像は、撮影された数だけスケッチフォルダのなかに保存されます。

(スケッチフォルダの中にインデックス番号を含んだ保存名で保存される/MacOSXの場合)

上図の場合、「image_0.jpg」から「image_10.jpg」までの合計11枚の画像が保存されています。フレームレートは2なので、約5.5秒間撮影(連続保存)したことになります。

関連:
Processing Video (Webカメラ)」--Webカメラの使い方/映像にフィルタをかけて表示。
Arduino+Processing マトリクスLED+Webカメラ」--Webカメラ映像をマトリクスLEDに映す。
Processing Webカメラを光センサとして使う」--点光源で画面内に線を描く。
Processing Webカメラ/カラートラッキング」--Webカメラを使い、色を手がかりに物体を追いかける。
Processing Webカメラ/モーショントラッキング」--Webカメラを使って動体検知する。

10/04/2008

Processing QRコード/2次元コード

Processingのライブラリには、「QRCode」というものがあります。QRコードとは、以下のような2次元的なマトリクスを利用したコードです。バーコードよりは情報量が多く、様々な場面に用いられています。


このブログのURL情報が含まれたQRコード(上画像:qrcode.png)

Processingの「QRCode」ライブラリを用いることで、QRコードに含まれた情報を解読することができます。逆に、任意の情報のQRコードを生成するには以下のようなサイトで行うことができます。

http://qrcode.kaywa.com
http://qr.quel.jp

QRCode」ライブラリでは、上記サイトなどで直接生成したQRコード以外にも、Webカメラやデジタルカメラで撮影したQRコード(紙上に印刷したQRコード)を認識/解読することができます。尚、このライブラリを使うには「QRCode」ライブラリサイトからライブラリをダウンロード+インストールする必要があります。サイトの説明によれば、以下のようなコードでQRコードを解読することができます。予めQRコードの画像を用意して、スケッチフォルダ内dataフォルダに入れておいて下さい。
以下のサンプルは、上にあるQRコード(qrcode.png)を使って解読するコードです。無事QRコードが解読されれば、link()によって、自動的にこのブログのURLへジャンプするようになっています。


//ライブラリを取り入れる
import pqrcode.*;

//インスタンス名
Decoder decoder;

void setup() {
//オブジェクトの生成
decoder = new Decoder(this);
//イメージのロード
PImage img = loadImage("qrcode.png");
//イメージの解読
decoder.decodeImage(img);
}

void decoderEvent(Decoder decoder) {
//解読結果をテキストとして取り出す
String statusMsg = decoder.getDecodedString();
//テキストを画面に出力
println(statusMsg);
//テキストに書かれているURLへ移動
link(statusMsg, "_new");
}



QRCode」ライブラリのサイトには、Webカメラから撮影したQRコードを読み込んで解読するサンプルがあります。

http://www.shiffman.net/p5/pqrcode_files/Pqrcode_example.zip

このサンプルでは、「スペース」キーでカメラからのQRコードを画像として読み込み解読します。解読されたテキストは画面に文字として表示されます。「f」キーで、既に用意されているテスト用の画像を読み込んで解読します(「http://www.shiffman.net」と表示されるはずです)。「s」キーで、カメラセッティングの画面に切り替わります。
カメラでQRコードを撮影するときに、ピントがずれていると認識できないこともあるので、カメラを調整する必要があるかもしれません。

以下では、デスクトップ上あるいはその他の場所に保存してあるQRコードをファイルチューザーで選び、解読する実験をしてみます。ファイルチューザーについては、前回のブログ「Processing FileChooser/ファイル選択画面の表示」を参照して下さい。
マウスを押したら、ファイルチューザーのダイアログ画面が現れ、任意のQRコードを選択し、解読結果をProcessingのコンソールに表示します。

//JavaのSwingを取り込む
import javax.swing.*;

import pqrcode.*;

//解読用インスタンスを用意
Decoder decoder;

//画像インスタンスを用意
PImage pimage;

//選択ファイル名を用意し
//ファイル名を空にしておく
String getFile = null;

void setup(){
//とりあえず表示画面を400角に設定
size(400,400);
//解読用オブジェクトの生成
decoder = new Decoder(this);
//背景(黒)
background(0);
}

void draw(){
//選択ファイル名が空でないとき
if(getFile != null){
//ファイルを取り込む
fileLoader();
}
}

//マウスを押したら
void mousePressed(){
//選択ファイル取得処理
getFile = getFileName();
}

//ファイルを取り込むファンクション
void fileLoader(){
//選択ファイル名のドット以降の文字列を取得
String ext = getFile.substring(getFile.lastIndexOf('.') + 1);
//その文字列を小文字にする
ext.toLowerCase();
//文字列末尾がjpg,png,gif,tgaのいずれかであれば
if(ext.equals("jpg") || ext.equals("png") || ext.equals("gif") || ext.equals("tga")){
//選択ファイル名のイメージを取り込む
pimage = loadImage(getFile);
//背景(黒)
background(0);
//イメージ表示
image(pimage, 0, 0, pimage.width, pimage.height);
//イメージの解読
decoder.decodeImage(pimage);
}
//選択ファイルパスを空に戻す
getFile = null;
}

//ファイル選択画面、選択ファイル名取得の処理
String getFileName(){
//処理タイミングの設定
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
//ファイル選択画面表示
JFileChooser fc = new JFileChooser();
int returnVal = fc.showOpenDialog(null);
//「開く」ボタンが押された場合
if (returnVal == JFileChooser.APPROVE_OPTION) {
//選択ファイル取得
File file = fc.getSelectedFile();
//選択ファイルのパス取得
getFile = file.getPath();
}
}
//上記以外の場合
catch (Exception e) {
//エラー出力
e.printStackTrace();
}
}
}
);
//選択ファイルパス取得
return getFile;
}

void decoderEvent(Decoder decoder) {
//解読結果をテキストとして取り出す
String statusMsg = decoder.getDecodedString();
//テキストを画面に出力
println(statusMsg);
}



以下は、授業内で実験した内容です。
数値の情報(100など)を含んだQRコードを生成し、プログラムによって解読された文字列としての数値を整数型の数値に変換します。その数値をArduinoへシリアル通信で送信し、analogWrite()でLEDの輝度やモータの速度あるいはサーボの回転角度などに反映させる内容です。いくつかのQRコードを作成し、ファイルチューザーで選択したQRコードを入れ替わりで送ります。

//シリアル通信ライブラリを取り込む
import processing.serial.*;
//シリアル通信インスタンス
Serial port;

//JavaのSwingを取り込む
import javax.swing.*;
import pqrcode.*;
//解読用インスタンスを用意
Decoder decoder;
//画像インスタンスを用意
PImage pimage;
String getFile = null;

void setup(){
//とりあえず表示画面を400角に設定
size(400,400);
//解読用オブジェクトの生成
decoder = new Decoder(this);
background(0);

//シリアルポートの設定
port = new Serial(this, "/dev/tty.usbserial-A4001Kjl", 9600);
}

void draw(){
//選択ファイル名が空でないとき
if(getFile != null){
//ファイルを取り込む
fileLoader();
}
}

void mousePressed(){
getFile = getFileName();
}

void fileLoader(){
//選択ファイル名のドット以降の文字列を取得
String ext = getFile.substring(getFile.lastIndexOf('.') + 1);
//その文字列を小文字にする
ext.toLowerCase();
//文字列末尾がjpg,png,gif,tgaのいずれかであれば
if(ext.equals("jpg") || ext.equals("png") || ext.equals("gif") || ext.equals("tga")){
//選択ファイル名のイメージを取り込む
pimage = loadImage(getFile);
//背景(黒)
background(0);
//イメージ表示
image(pimage, 0, 0, pimage.width, pimage.height);
//イメージの解読
decoder.decodeImage(pimage);
}
//選択ファイルパスを空に戻す
getFile = null;
}

//ファイル選択画面、選択ファイル名取得の処理
String getFileName(){
//処理タイミングの設定
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
//ファイル選択画面表示
JFileChooser fc = new JFileChooser();
int returnVal = fc.showOpenDialog(null);
//「開く」ボタンが押された場合
if (returnVal == JFileChooser.APPROVE_OPTION) {
//選択ファイル取得
File file = fc.getSelectedFile();
//選択ファイルのパス取得
getFile = file.getPath();
}
}
//上記以外の場合
catch (Exception e) {
//エラー出力
e.printStackTrace();
}
}
}
);
//選択ファイルパス取得
return getFile;
}

void decoderEvent(Decoder decoder) {
//解読結果をテキストとして取り出す
String statusMsg = decoder.getDecodedString();
//テキストを画面に出力
println(statusMsg);

//解読した文字列を整数値に変換
int val=int(statusMsg);
//シリアル通信で送信
port.write(val);
}



Arduino側は、11番ピン(PWMピン)にLEDを接続し、輝度が変化する内容であれば以下のようになります。


int val;

void setup(){
//シリアル通信開始
Serial.begin(9600);
}

void loop(){
//受信データがひとつ届いたら
if(Serial.available()>0){
//受信データ読み込み
val=Serial.read();
}
//11番ピンをアナログ出力する
analogWrite(11,val);
}



QRコードで、「100,20,35,180」のように、幾つかの数値をコンマ(デリミタ)で区切り連続した数値の文字列を作成し、解読した結果として送信すれば、複数の数値データをArduinoへ送信することもできます。
この場合、Processingのプログラムの最後の部分にあるvoid decoderEvent(){...}の部分を以下のようにします。

void decoderEvent(Decoder decoder) {
String statusMsg = decoder.getDecodedString();
int[] data=int(split(statusMsg,',');
for(int i=0;i<data.length;i++){
port.write(val);
}
}

解読される文字列は

statusMsg = "100,20,35,180"

なので、
この文字列statusMsgをsplit()に代入し「,」コンマを区切り記号として複数の文字列を含んだ配列に変換します。同時にその文字列群をint()で括ることで、文字列データを整数値データに変換し、

data = {100,20,35,180}

という整数値の配列になります。data.lengthによって配列dataに何個のデータが含まれているか確認し(この場合4個、data[0]からdata[3]まで)、for()文でデータ数の分だけport.write()で繰り返しシリアル通信で送信します(4回分送信)。Arduino側で、順番にこれらの数値を受け取ることで、LEDの輝度調整のプログラムであれば、連続した値をもとに変化する輝度調整が可能になります。

応用的な使い方として、LEDの点灯やサーボの回転角度などの連続する動きのデータをQRコードで幾つか作成しておき、Webカメラを通して解読させる度に異なる動作をさせたり、マトリクスLEDなどに文字や模様として表示させたりできます。

10/03/2008

Processing FileChooser/ファイル選択画面の表示

通常Processingでは、プログラム上で使われる画像データや音源データなどは、スケッチフォルダ内のdataフォルダ内に入れておく必要があります。ProcessingはJavaでつくられているため、JavaのGUIライブラリであるSwingを使うことで、ファイルチューザーのダイアログ画面(ファイル選択画面)を表示し、パソコン上にある任意のファイルを選択し開くことができます(尚、この方法はProcessing/Hacks/filechooserで紹介されています)。
関連:「Processing FileChooser2」(Processing 146以降/selectInput()の使い方)


(Windows XPのファイルチューザー画面/ファイル選択ダイアログ)

dataフォルダ内のデータだけでなく、デスクトップ上にある画像データなどを読み込むことができるので、入れ替わりで画像表示させるときなどに便利です。
以下は、クリックするとファイルチューザー(ファイル選択画面)というダイアログ画面が現れ、コンピュータ上の任意の場所にある画像ファイル(jpeg、png、gif、tga)を取り込んで表示するサンプルです。


//JavaのSwingを取り込む
import javax.swing.*;

//画像インスタンスを用意
PImage pimage;

//選択ファイルを用意し
//ファイルを空にしておく
String getFile = null;

void setup(){
//とりあえず表示画面を400角に設定
size(400,400);
}

void draw(){
//選択ファイル名が空でないとき
if(getFile != null){
//ファイルを取り込む
fileLoader();
}
}

//マウスを押したら
void mousePressed(){
//選択ファイル取得処理
getFile = getFileName();
}

//ファイルを取り込むファンクション
void fileLoader(){
//選択ファイルパスのドット以降の文字列を取得
String ext = getFile.substring(getFile.lastIndexOf('.') + 1);
//その文字列を小文字にする
ext.toLowerCase();
//文字列末尾がjpg,png,gif,tgaのいずれかであれば
if(ext.equals("jpg") || ext.equals("png") || ext.equals("gif") || ext.equals("tga")){
//選択ファイルパスの画像を取り込む
pimage = loadImage(getFile);
//イメージ表示
image(pimage, 0, 0, pimage.width, pimage.height);
}
//選択ファイルパスを空に戻す
getFile = null;
}

//ファイル選択画面、選択ファイルパス取得の処理
String getFileName(){
//処理タイミングの設定
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
//ファイル選択画面表示
JFileChooser fc = new JFileChooser();
int returnVal = fc.showOpenDialog(null);
//「開く」ボタンが押された場合
if (returnVal == JFileChooser.APPROVE_OPTION) {
//選択ファイル取得
File file = fc.getSelectedFile();
//選択ファイルのパス取得
getFile = file.getPath();
}
}
//上記以外の場合
catch (Exception e) {
//エラー出力
e.printStackTrace();
}
}
}
);
//選択ファイルパス取得
return getFile;
}


選択ファイルが画像ファイルとして相応しいものであるかどうかを

if(ext.equals("jpg") || ext.equals("png") || ext.equals("gif") || ext.equals("tga")){...}

によって判別しています。選択したファイル名の「.」ドット以降が、「jpg」、「png」、「gif」、「tga」であれば、読み込み可能な画像フォーマットとして処理されます。以下の

String getFileName(){...}

以降はJavaのSwingによるファイルチューザーを呼び出して選択ファイルまでのパスを得るコードなので、そのままコピー&ペーストしても構わないでしょう。
PImageでは、jpg、png、gif、tgaの4種類が読み込み可能ですが、この部分を音源のファイルフォーマットに指定し、PImageのかわりにサウンドライブラリを使えば、音源の選択/読み込み/再生も可能になります。
ファイルチューザー画面を用いて、Processingのサウンドライブラリである「Ess」による音源再生のプログラムを以下に書きます。画面中央の白い正方形をクリックすれば、音源選択の画面が現れます。今回利用できる音源のフォーマットは、「wav」だけとします。dataフォルダの中にある音源だけでなく、iTuneなどの音楽ライブラリの中から曲を選ぶこともできるはずです。白い正方形以外の周辺の場所をクリックすれば、再度音源が再生されます。尚、「Ess」ライブラリをダウンロード+インストールしておく必要があります。また、曲などの大きな音源データの場合は、ProcessingのメニューバーからPreferencesあるいは環境設定で、メモリーを増やしておく必要があります(「Increase maximum available memory to [ ]MB」という欄にチェックを入れ、データ量に相当するメモリー数を記入して下さい)。


//Essサウンドライブラリの取り込み
import krister.Ess.*;
//音源インスタンスの用意
AudioChannel mySample;

//JavaのSwingライブラリの取り込み
import javax.swing.*;

String getFile = null;

void setup(){
size(200,200);
//Ess使用開始
Ess.start(this);
background(100,100,30);
rectMode(CORNER);
}

void draw(){
//画面中央白い正方形の描画
rect(width/2-25,height/2-25,50,50);
if(getFile != null){
fileLoader();
}
}

void mousePressed(){
//クリックの箇所が白い正方形以内なら
if(mouseX>width/2-25 && mouseX<width/2-25+50 && mouseY>height/2-25 && mouseY<height/2-25+50){
//選択ファイル所得処理
getFile = getFileName();
}
else{//白い正方形以外の箇所をクリックしたら
//音源ファイルが空ではないとき
if(mySample!=null){
//音源再生
mySample.play();
}
}
}

void fileLoader(){
String ext = getFile.substring(getFile.lastIndexOf('.') + 1);
ext.toLowerCase();
//選択したファイルが「wav」フォーマットなら
if(ext.equals("wav") ){
//音源ファイルの指定
mySample=new AudioChannel(getFile);
//音源再生
mySample.play();
}
getFile = null;
}

//ファイル選択画面、選択ファイルパス取得の処理
String getFileName(){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
JFileChooser fc = new JFileChooser();
int returnVal = fc.showOpenDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
getFile = file.getPath();
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
);
return getFile;
}

//Ess使用停止
public void stop(){
Ess.stop();
super.stop();
}

9/26/2008

Processing Webカメラを光センサとして使う

今回は、パソコンに接続したWebカメラを光センサとして使う応用実験を行います。点光源(LEDなど)を空間内で動かし、その軌跡をProcessingの画面上に描画してみたいと思います(身体にLEDなどの点光源をつけて、腕を動かしたり歩いたりすれば、身体の動きを連続的に描画/記録することができます)。
Processingでは、videoライブラリを用いてWebカメラを通してキャプチャし、キャプチャした画面のピクセルをひとつずつ読み込んで、設定した明るさ以上のピクセルを選択します。選択したピクセルのみを別の色で表示するプログラムになります。
以下のプログラムでは、カメラからキャプチャした画像の各ピクセルの明るさ(0~255)を調べ、そのピクセルの明るさが254以上であれば、画面上に赤で表示する内容になります。クリックすれば、黒で塗りつぶして画面をリセットすることにします。
videoライブラリの基本的な使い方は「Processing Video(Webカメラ)」を参照して下さい。

*Windowsの場合、そのままの設定ではこのVideoライブラリを使用することができません。WinVDIG 1.0.1をインストールする必要があります。

//ライブラリを取り込む
import processing.video.*;

//オブジェクトの用意
Capture video;

//画面サイズの変数と値
int w=320;
int h=240;

void setup() {
  size(w, h);
  video = new Capture(this, w, h, 30);
  //背景を黒にしておく
  background(0);
  video.start();//Processing2.0以上の場合はこの行が必要
}

void draw() {
  //画面のピクセルをロードしておく
  loadPixels();

  //カメラ画像のピクセルをひとつずつ調べる
  for(int i=0;i<w*h;i++){
    //ピクセルが254以上の明るさの場合
    if(brightness(video.pixels[i])>=254){
      //選択されたピクセルを赤にする
      pixels[i]=color(255,0,0);
    }   
  }

  //ピクセル表示更新
  updatePixels();
}

//キャプチャ画面の読み込み
void captureEvent(Capture video) {
  video.read();
}

//マウスボタンを押したら
void mousePressed(){
  //背景を黒にする
  background(0);
}


LEDなどの点光源をカメラに対して動かせば、以下のような画像ができあがります。赤いピクセル部分が、動かした点光源の軌跡です。




webカメラからの映像と合成(オーバーレイ)するには、以下のようになります。ここでは、webカメラからの映像を左右鏡像反転しています(カメラによっては、セッティング画面で鏡像にできるものもあります)。カメラからの映像を鏡像にすることで、点光源を右に動かせば画面内の点光源も右に動くようになります。マウスボタンを押せば画面を黒にリセット、「s」キーを押すと画面内の映像をjpeg画像で保存、「t」キーを押すとカメラセッティング画面になります。
import processing.video.*;
Capture video;
int w=320;
int h=240;

//軌跡用の配列を用意しておく
int[] pix=new int[w*h];

void setup() {
  size(w, h);
  video = new Capture(this, w, h, 30);
  //軌跡用配列の値をすべてゼロにしておく
  for(int i=0;i<w*h;i++){
    pix[i]=0;
  }
  video.start();//Processing2.0以上の場合はこの行が必要
}

void draw() {
  loadPixels();
  for(int i=0;i<w*h;i++){
    if(brightness(video.pixels[i])>=254){
      //ピクセルを鏡像反転するための計算
      //配列に値を記憶しておく
      int a=i/w;
      pix[w-i%w+a*w-1]=255;//選択されたピクセルだけを255にする
    }
    //配列からの値を画面ピクセルへ代入
    //選択されたピクセルを緑で画面表示    
    pixels[i]=color(0,pix[i],0);
  }
  updatePixels();

  //合成するカメラ映像の処理
  tint(255,128);//透明度128(50%)
  scale(-1.0, 1.0);//左右反転(鏡像)
  image(video, -w, 0);//映像出力
}

//カメラ映像読み込み
void captureEvent(Capture video) {
  video.read();
}

//マウスボタンを押すとリセット(黒へ)
void mousePressed(){
  for(int i=0;i<w*h;i++){
    pix[i]=0;
  }
}

int num=0;//保存画像インデックスの変数

void keyPressed(){
  //sキーを押すと(jpeg画像で保存)
  if(key=='s'){
    String s="image_" + num + ".jpg";//保存ファイル名
    save(s);//画像保存
    num++;//保存画像インデックスを+1しておく
  }
  //tキーを押すと
  if(key=='t'){
    video.settings();//カメラセッティング画面表示
  }
}

「s」キーを押すことで画面をjpegフォーマットで保存できるようにsave()を用います。画像は、save()の括弧内の指定したファイル名でスケッチフォルダ内に保存されます。インデックス用の変数numを用意し、image_0.jpg、image_1.jpg、image_2.jpg...というように、保存名にはインデックス番号がつくようにします。
「t」キーを押せばsettings()によって、カメラのセッティング画面が現れます。カメラの露出やコントラストなどの設定が「オート/自動」になっている場合があるので、できれば「マニュアル/手動」に切り替えて、それぞれを固定値にしたほうが、選択するピクセルの明るさが変化せずに済むのでいいでしょう。


上画像:合成/重ね合わせられた映像
映像内の手にはボタン電池に接続されたLEDが点灯しています。
この画像は、プログラム中にある「s」キーを押して画像保存したものです。


ドライバなしですぐにコンピュータに接続可能(UVC対応)なWebカメラとして以下のようなものがあります。
Macintosh/Windows兼用です。
      

関連:
Processing Video (Webカメラ)」--Webカメラの使い方/映像にフィルタをかけて表示。
Arduino+Processing マトリクスLED+Webカメラ」--Webカメラ映像をマトリクスLEDに映す。
Processing Webカメラ/定点記録画像」--Webカメラ映像を0.5秒おきに画像保存(JPEG)する。
Processing Webカメラ/カラートラッキング」--Webカメラを使い、色を手がかりに物体を追いかける。
Processing Webカメラ/モーショントラッキング」--Webカメラを使って動体検知する。

9/14/2008

Processing 緊急モーションセンサー(Mac)

Appleの2005年以降のPowerBook/iBook/MacBook(ノート型)には、緊急モーションセンサーが内蔵されています。緊急モーションセンサーは、ハードディスクを保護するために、あやまってコンピュータを落としたときの衝撃を感知します。
Processingには、この緊急モーションセンサーから値を得る「Sudden Motion Sensorライブラリ」があります。緊急モーションセンサーでは、以前扱った「加速度センサ」のように、コンピュータ本体を傾けたり衝撃を与えたりすることによって変化する値(XYZ軸に対する3つの値)が得られます。



Sudden Motion Sensorライブラリのサイトに従えば、3つの値は以下の方法で読み込むことが可能になります。尚、ライブラリをダウンロード+インストールする必要があります。

//ライブラリを取り込む
import sms.*;

void setup() {
//画面サイズをとりあえず200角
size(200,200);
}

void draw() {
//3つの値を読み込み、配列に代入
int[] vals = Unimotion.getSMSArray();
//3つの値を出力
println(vals[0] + " " + vals[1] + " " + vals[2]);
}

上記プログラムによって出力された値は以下のようになりました(機種や状況によって多少誤差が含まれるかもしれません)。

水平時 x: 0, y: -3, z:56
左90度 x: 52, y: -3, z: 6
右90度 x:-51, y: -3, z: 5
前90度 x: 1, y: 49, z: 6
後90度 x: 1, y:-54, z: 5

xの振り幅:103(-51から52までの±51.5)
yの振り幅:103(-54から49までの±51.5)

になります。それぞれの振り幅からxとyの中点(水平時の値)を新たに求めると、

xの中点:(-51+52)/2=0.5
yの中点:(-54+49)/2=-2.5

になります。これら中点の値をオフセット値として用いることにします。つまり、計測された値からオフセット値を差し引いて角度の計算をすることになります。以前の「Arduino 加速度センサ」のときと同様に、出力値からそれぞれの角度を求める式を用意します。それぞれの角度をradX、radY、オフセット値をoffsetX、offsetY、読み取り値をx、yとすると、

//atan2()で求める場合
radX=atan2((x-offsetX),sqrt(51.5*51.5-(x-offsetX)*(x-offsetX)))
radY=atan2((y-offsetY),sqrt(51.5*51.5-(y-offsetY)*(y-offsetY)))

//またはacos()、asin()で求める場合
radX=asin((x-offsetX)/51.5)
radY=acos((y-offsetY)/51.5)

になります。式中の51.5は加速度1G(重力)の時の値です(機種によっては256くらいのときもあります)。

以上の式を使って、コンピュータ本体を傾けることでProcessing画面上の3Dモデルを動かしてみます。
3Dモデルに関しては、前回の記事「Processing 3Dモデル/OBJ Loader」のものを使うことにします。コンピュータを傾けた方向に、画面内の3Dモデルも同様に傾く内容とします。
スケッチフォルダ内にdataフォルダを作成し、3Dモデルのデータを入れておいて下さい(3Dデータは、ここからダウンロードできます/.objファイルと.mtlファイルの二つが必要です)。


(コンピュータを傾けた時の3Dモデル/前回ブログの3Dモデルを使用)


//ライブラリのインポート
import sms.*;
import saito.objloader.*;

//モデルのオブジェクトを用意
OBJModel model;

void setup() {
//3D画面サイズ設定
size(400,400,P3D);
//モデルのオブジェクトを生成
model=new OBJModel(this);
//3Dデータ読み込み
model.load("macbook.obj");
//ワイヤーフレームなし
noStroke();
}

void draw() {
//3つの値を読み込み、配列に代入
int[] vals = Unimotion.getSMSArray();
//println(vals[0] + " " + vals[1] + " " + vals[2]);

//背景描画
background(50);
//直線光の設定
directionalLight(200, 200, 200, -1, 1, -1);
//環境光の設定
ambientLight(200, 200, 200);

//3Dモデルの位置座標設定
translate(width/2,height*2/3,0);

//オフセット値
float offsetX=-2.5;
float offsetY=0.5;
//角度の計算:atan2()で求める場合
float radX=-atan2(vals[1]-offsetX,sqrt(51.5*51.5-(vals[1]-offsetX)*(vals[1]-offsetX)));
float radY=-atan2(vals[0]-offsetY,sqrt(51.5*51.5-(vals[0]-offsetY)*(vals[0]-offsetY)));
//またはacos()、asin()で求める場合
//float radX=asin((vals[1]-offsetX)/51.5);
//float radY=acos((vals[0]-offsetY)/51.5);

//回転角度
rotateX(radX+PI/2);
rotateY(radY);

//三角形分割で面を生成する
model.drawMode(TRIANGLES);
//スケール(200倍)
scale(200);
//モデル描画
model.draw();
}

画面内の3D座標は、
左:-X
右:+X
上:-Y
下:+Y
後:-Z
前:+Z
という関係になります。
緊急モーションセンサーのX軸は、Processing上のY軸に対応しているので、vals[0]の値を3DモデルのY軸回転角度へ代入し、val[1]の値は3DモデルのX軸回転角度へ代入します。表示上90度X軸に対してずれていたので、rotateX(radX+PI/2)というようにradXにPI/2(90度)足しておきました。
directionalLight()は太陽光のような直線光であり、括弧内の数値については、最初の3つがRGBで光の色を指定、最後の3つが(0,0,0)の原点を基準に光の向きを設定することになります。
ambientLight()は環境光であり、光の向きはなく、空間全体を明るくしたり暗くしたりし(あるいは色を変える)、RGBの3つの数値で指定します。


もう一つのサンプルとして、コンピュータ自体をコントローラとして傾けて、水平面上のボールを転がすプログラムをしてみます。今回は、setup(){...}内で、一度緊急モーションセンサーから値を読み込み、それらをオフセット値として使うことにします。こうすることで、コンピュータを平らな場所においてプログラムを開始したときの状態(オフセット値)を記憶させておくことができます。ellipse()で擬似的な影を地面に落とすことで立体感がでるようにします。


(コンピュータの傾きに合わせてボールが転がる)


//ライブラリのインポート
import sms.*;

//ボールの座標用変数
float xPos,yPos;

//オフセット用変数
float xOffset;
float yOffset;

void setup() {
//3D画面設定
size(400,400,P3D);
//ワイヤーフレームなし
noStroke();
//水平状態の読み込み
int[] vals = Unimotion.getSMSArray();
//読み込み値をオフセット値に設定する
xOffset=vals[0];
yOffset=vals[1];
}

void draw() {
//モーションセンサーからの読み込み
int[] vals = Unimotion.getSMSArray();
//背景色
background(220);
//画面上半分の塗色
fill(50);
//画面上半分の矩形
rect(0,0,width,height/2);

//ボールの速度の計算
float xSpeed=vals[0]-xOffset;
float ySpeed=vals[1]-yOffset;
//ボール移動量の計算
xPos+=-xSpeed;
yPos+=ySpeed;
//ボール位置の設定
translate(width/2+xPos,height*4/5,-200+yPos);

//影の塗色
fill(50);
//影の座標と大きさ
ellipse(-25,30,80,80/5);

//直線光の設定
directionalLight(255, 255, 255, -1, 1, 0);
//ボールの塗色
fill(255,100,50);
//ボール描画
sphere(30);
}

緊急モーションセンサーの読み取り値からオフセット値を差し引いた値を、そのままボールのスピードに反映させています。読み取られたY方向の値は、画面内のZ座標(前後の軸)に対応するので、yPosをtranslate()のZ軸に代入してあります。実際に動かしてみて、向きが逆であったり、座標軸がきちんと対応していない場合は、値にマイナスを掛けたり、代入先を入れ替えたりして調整してみて下さい。
ボールの位置設定のためのtranslate()は、まずX座標をwidth/2で左右中央、Y座標をheight*4/5で画面上下4/5の位置、Z座標を-200奥とした座標を基準とし、その基準の座標に変化する値となるxPosとyPosを追加して、最終的な位置を決定しています。
疑似の影として用いたellipse()は2D用の図形でありX座標とY座標しか設定できませんが、translate()以後に挿入してあるので、translate()のZ軸の値に合わせて、前後に動きつつ大きさも変化します(2D図形は、3D空間上ではZ座標値が0の位置に配置されており、translate()以後に書いた2D図形は、Z軸の値を変化させれば、見た目の大きさや位置も影響を受けて変化します)。同様に、directionalLight()に対しても、2D図形をdirectionalLight()以前に書けば、光の影響を受けませんが、directionalLight()以後に2D図形を書くと、光の影響を受けて図形自体に陰影がつきます。そのため、directionalLight()は、プログラムの冒頭の方に書かずに、ボール描画の直前(2D図形描画以降)に書いておきます。

9/11/2008

Processing 3Dモデル/OBJ Loader

Processingのライブラリには、3Dモデリングソフトで制作した3Dモデルを読み込むOBJ Loaderライブラリがあります。OBJ Loaderでは、拡張子が「.obj」の3Dモデルを扱うことができます。3Dモデリングソフトがあれば、制作した3Dモデルを「.obj」フォーマットで書き出して、3DデータをOBJ Loaderで読み込みます。読み込みに使用する3Dデータは、スケッチフォルダ内に入れておきます。
また、モデリングが面倒であれば、インターネットからフリーの3Dデータを検索しダウンロードして利用する方法もあります。この場合「.obj」フォーマットのデータでなければならないのですが、それ以外のフォーマットであっても、一旦3Dモデリングソフトで読み込んで「.obj」フォーマットにして書き出せば利用可能です。最近はフリーの3Dモデリングソフトも多く存在するので、ダウンロード/インストールしてすぐに使うことができるはずです(ブログページ右側にもフリーのモデリングソフトのリストがあります)。

参考3Dモデリングソフト(フリーウェア):
Blender (Win,Mac)
Maya 2010体験版(30日)(Win,Mac)
Rhino (Mac用ベータ版/要登録)
Metasequoia/LE (Win)
DoGA (Win)
CB Model Pro (Win,Mac)
SketchUp (Win,Mac)
SketchyPhysics (Win):SketchUp物理演算プラグイン
trueSpace7 (Win)
CoCreate (Win/要登録)

今回はインターネット上から無料の3Dモデルのデータをダウンロードし、Processing上に表示してみたいと思います。Turbo Squidという3Dモデルのデータライブラリのサイトから無料の3Dモデルを探し出し、以下の3Dモデルを利用してみたいと思います(データをダウンロードするにはTurbo Squidに登録する必要があります)。

http://www.turbosquid.com/3d-models/max-apple-macbook/391534

このモデルは拡張子が「.3DS」なので3ds Max用のデータです。フリーウェアのBlenderという3Dモデリングソフトで、この「.3DS」フォーマットのデータを読み込み(File>Import>.3ds)、「.obj」フォーマットで書き出して(File>Export.obj)利用してみたいと思います(「.obj」に変換したファイルのリンクはこの記事の最後にあります)。

はじめてのBlender (I・O BOOKS)
山崎 聡
工学社
売り上げランキング: 6131


そのまま読み込むと、各パーツの位置が少しずれていたので、修正して「.obj」フォーマットで書き出すことにしました。「.obj」フォーマットを選択して書き出すと、「~.obj」と「~.mtl」という二つのファイルが出来上がります。「~.obj」はポリゴンの座標についてのデータであり、「~.mtl」は色や材質、テクスチャなどのデータが含まれています。以下のプログラムで、3Dデータを読み込んでみます(「~.obj」ファイルと「~.mtl」ファイルは、スケッチフォルダ内に入れておいてください)。

//ライブラリのインポート
import saito.objloader.*;

//モデルのオブジェクトを用意
OBJModel model;

void setup() {
//3D用の画面設定
size(400,400,P3D);
//モデルのオブジェクトを生成
model=new OBJModel(this);
//objファイルの読み込み
model.load("macbook.obj");
//ワイヤーフレームなし
noStroke();
}

void draw(){
//背景描画
background(50);
//直線光の設定
directionalLight(200, 200, 200, -1, 1, -1);
//環境光の設定
ambientLight(200, 200, 200);

//3Dモデルの位置設定
translate(width/2,height/2,0);
//スケール設定(200倍)
scale(200);

//マウス入力で回転させる
rotateX(PI*mouseY/height);

//三角形分割で面を生成する
model.drawMode(TRIANGLES);
//3Dモデルの描画
model.draw();
}


Blenderでは、細かな出力設定はせずに、そのまま「.3DS」データを読み込み(File>Import>.3ds)、「.obj」フォーマットで書き出す(File>Export>.obj)ことにします(書き出す際には、予め画面上で3DモデルをSelectしておく必要があります)。表示されるスケールが小さすぎたので、scale()で200倍の大きさに変換しています。そのまま表示させると、3Dモデルの色が真っ黒のままだったので、多少の修正が必要になります。



色などの内容を確かめるために「.mtl」ファイルをテキストエディタなどで開くと、以下のような内容が記述されていることが分かります。

# Blender3D MTL File: macbook.blend
# Material Count: 2
newmtl Screen
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.084706 0.084706 0.084706
Ks 0.449020 0.449020 0.449020
Ni 1.000000
d 1.000000
illum 2

newmtl Plastic
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.800000 0.800000 0.800000
Ks 0.449020 0.449020 0.449020
Ni 1.000000
d 1.000000
illum 2

このデータには、ScreenとPlasticという名前の二つの材質についての数値が含まれており、以下のような内訳になります。
# コメント
newmtl 材料名
Ns 輝度
Ka 環境色
Kd 拡散色
Ks 反射色
Ni 光の屈折率
d  アルファ値
illum 0:照明なし、1:反射ハイライトなし、2:Ksの値で反射ハイライトあり

恐らくKaの値がそれぞれ0.000000, 0.000000, 0.000000なので真っ黒な状態になったのでしょう。それぞれの値を以下のように変えます。

ScreenのKaの値を
Ka 0.1 0.01 0.01

PlasticのKaの値を
Ka 0.7 0.7 0.7

上記三つの値はRGB(赤,緑,青)に対応しています。
それぞれの値を変更すると、ScreenのKaは少し赤みがかった黒、PlasticのKaは白に近いグレーになります。
先ほどのプログラムで再度描画させれば、以下のようにかたちが認識できるようになるはずです。


マウス上下で3Dモデルも上下に回転します。
見えない場合はこちらへ


Processingでは、box()sphere()vertex()などの基本的な3D描画のコマンドはありますが、複雑な3Dモデルを制作する際には、モデリングソフトを利用して取り込んだ方がいいでしょう。あるいは、3Dモデルのデータもインターネット上に数多く存在しているので、検索すれば相応しいものが見つかるかもしれません。
尚、上記プログラムで使用したmacbook.objとmacbook.mtlのファイルは、ここからダウンロードできます。


3Dキャラクタアニメーション Blender(DVD付)
トニー・マレン
アスキー
売り上げランキング: 118200

8/30/2008

Arduino タッチパネル(4線式)4-wire touch panel

*Some are written in English at the moment(sorry not all of them...)
This example shows how to use a touch panel/screen with an Arduino board plus a serial communication to a Processing program, which draws where to touch on the touch panel.
This is not about a multi-touch function, only single point on the touch panel can be detected.

今回は、4線式のタッチパネルをArduino基盤に接続し操作実験してみたいと思います。
タッチパネルには、4線式や5線式という比較的簡単な構造になっているものがあります。今回使うタッチパネルは、指先やペン先で触れた一点の位置(X座標値とY座標値)を検出可能にするものです(複数の点を同時検出可能なマルチタッチではありません)。
基本的には、X座標に2線、Y座標に2線あり、合計4線あります。手順としては、まずX座標を検出、そしてY座標を検出というように別々(交互)に行います。タッチパネル自体がX座標用とY座標用に対応した2層の抵抗になっており、X座標(横方向)だけで考えれば、タッチパネルの左端に0V、右端に5Vを接続しておいて、指先で触れた箇所で分圧される仕組みになっています。つまり、X座標の左に行くほど0Vに近く、中心に触れれば約2.5V、右に行くほど5Vに近い電圧が読み取れることになります。X座標を読み取る際には、使用していないY座標の2線のうちの1本を使います。X座標を検出したら、検出対象をY座標に切り替えて同様の方法で検出を続けます。下の図では、タッチパネルの2層あるうちのX座標用の層を押せば、下にあるY座標用の層と接触し、その地点での分圧された電圧の値をY座標用の層から読み込むことができます。
There are two layers of conductive films on a 4-wire touch panel, the one for x-coordinate and the other for y-coordinate. Each layer is connected to GND and 5V on the egdes. Pressing the x-coordinate layer with your finger, then the x-coordinate layer will contact to the other layer(y-coordinate layer) underneath. At this moment the voltage is devided at the point where the two layers are touching, and the devided valtage can be read from the edge of the y-coordinate layer(the y-coordinate layer is working as a conductive film for the x-coordinate film at this time).



Arduino基盤との接続は、以下のようにアナログ入力の0〜3番ピンに接続することにします(X座標用に0番ピンと1番ピン、Y座標用に2番ピンと3番ピンを使用)。通常タッチパネルなどの薄型の機器にはFPC(フレキシブルプリント基板)/FFC(フレキシブルフラットケーブル)の端子がついています。Arduino基盤からのワイヤーと接続するためにはFFC用コネクタを介して接続します(直接ハンダ付けできないので)。
X座標を計測中にはyLowの端子からX座標の分圧された電圧を読み取るので(Y座標を計測中にはxLowの端子で読み取る)、タッチパネル上に何も触れていない時は、xLow端子に0Vが接続されるようにするため、プルダウン抵抗を接続しておきます(xLowやxHighとyHighにもプルダウン抵抗をつけておきます)。


Needs a pull-down resistor for each wire from the touch panel.

処理の手順は、まずX座標(横方向)の検出を行う際には、アナログ入力の「0番ピンと1番ピン」を「14番ピンと15番ピン」としてデジタル出力に切り替え、14番ピンを0V(LOW)、15番ピンを5V(HIGH)で出力しておきます。そして、Y座標用のアナログ入力2番ピンを通してanalogRead()で値を読み込みます。読み込まれた値は、X座標の値になります。このとき、Y座標用のアナログ入力「2番ピン」がデジタル出力にならないように「16番ピン」として予めデジタル入力にしておきます。「3番ピン」も同様に「17番ピン」としてデジタル入力にしておきます。
Y座標(縦方向)の検出の際には、アナログ入力の「2番ピンと3番ピン」を「16番ピンと17番ピン」としてデジタル出力に切り替え、16番ピンを0V(LOW)、17番ピンを5V(HIGH)で出力しておきます。X座標用であった「14番ピンと15番ピン」をデジタル入力に切り替えておいてから、アナログ入力「0番ピン」を通してY座標の値をanalogRead()で読み込みます。このように1ループのなかで、X座標とY座標の検出処理を順番に行い、それぞれの座標値を得ます。
Basically using the analog pins 0,1,2,3 on an Arduino board, those pins can be both analogRead pins and digitalWrite pins(in this case:pin numbers are 14,15,16,17) depending on setting.
First, to read an x-coordinate value, set the pin14 as 0V(LOW) and the pin15 as 5V(HIGH), then the rest of the pins(either of 16 and 17) can be analogRead pins. Next, to read a y-coordinate value, set the pin16 as 0V(LOW) and the pin16 as 5V(HIGH), then read the value from either of the pin14 and the pin15. Reading the both value one after another then send them through a serial communication to Processing.

//デジタル出力用ピン番号の定義:the digital output pins
#define xLow  14
#define xHigh 15
#define yLow  16
#define yHigh 17

void setup(){
  //シリアル通信開始:start serial communication
  Serial.begin(9600);
}

void loop(){
  //X座標用端子をデジタル出力に設定し、それぞれをLOWとHIGHで出力しておく
  //set the both x-coordinate pins as digital output:one is Low the other is HIGH  
  pinMode(xLow,OUTPUT);
  pinMode(xHigh,OUTPUT);
  digitalWrite(xLow,LOW);
  digitalWrite(xHigh,HIGH);

  //Y座標用端子をLOWにしておく:the both y-coordinate pins are set to be LOW
  digitalWrite(yLow,LOW);
  digitalWrite(yHigh,LOW);

  //Y座標用端子をデジタル入力に設定:change the y-coordinate pins as digital input
  pinMode(yLow,INPUT);
  pinMode(yHigh,INPUT);
  delay(10);

  //アナログ入力2番ピン(yLowピン)で読み込み
  //read analog pin2(yLow pin) to get an x-coordinate value
  int x=analogRead(2);
  
  //Y座標用端子をデジタル出力に設定し、それぞれをLOWとHIGHで出力しておく
  //set the both y-coordinate pins as digital output:one is Low the other is HIGH 
  pinMode(yLow,OUTPUT);
  pinMode(yHigh,OUTPUT);
  digitalWrite(yLow,LOW);
  digitalWrite(yHigh,HIGH);

  //X座標用端子をLOWにしておく:the both x-coordinate pins are set to be LOW
  digitalWrite(xLow,LOW);
  digitalWrite(xHigh,LOW);

  //X座標用端子をデジタル入力に設定:change the x-coordinate pins as digital input
  pinMode(xLow,INPUT);
  pinMode(xHigh,INPUT);
  delay(10);

  //アナログ入力0番ピン(xLowピン)で読み込み
  //read analog pin0(xLow pin) to get an y-coordinate value
  int y=analogRead(0);

  if(Serial.available()>0){
    //文字列でシリアル通信:send the values as a DEC format with a delimiter
    Serial.print(x,DEC);   //X座標:x-coordinate
    Serial.print(",");     //デリミタ:delimiter
    Serial.println(y,DEC); //Y座標:y-coordinate

    //合図用信号読み込みでバッファを空にする
    //read a handshake signal from Processing and clear the buffer
    Serial.read();
  }
}

今回実験で用いたタッチパネルは、12.1インチのサイズ(横:縦=4:3)であり、指先で触れた位置が
パネル左端: 70 :a minimum value when touching the left edge of the touch panel
パネル右端:781 :a maxmum value when touching the right edge of the touch panel
パネル上端: 81 :a minimum value when touching the upper edge of the touch panel
パネル下端:822 :a maxmum value when touching the lower edge of the touch panel
の値として検出されました。パネルに触れないときには、プルダウン抵抗により0が出力されます(以下Processingのプログラムでは、XとYの読み取り値が10以上のときタッチしていることとして判別しています)。
In this example, I used a 12.1-inch(width:height=4:3) touch panel.
When touching each edge of the touch panel, the values are like the above.
When not touching the touch panel, you can read zero value from the analog pins because of the pull-down resistors.

それでは、座標値をProcessingへシリアル通信し、Processingの画面上に描画することにします。Processingへは、座標値を文字列として送信することにします(文字列のシリアル通信については「Arduino-Processing シリアル通信5」を参照して下さい。Processing側では、タッチパネル上の指先の動きに合わせて円が動くようにします。タッチパネルに触れている時といない時では円の色が変化するようにします。「s」キーを押してシリアル通信開始です。
The below Processing code is that:
a circle on the screen moves and follows where to touch on the touch panel,
the color of the circle changes depending on touching or not touching.
*Press 's' key to start the serial communication with the Arduino.

//シリアルライブラリを取り込む
import processing.serial.*;
//シリアル通信用インスタンスportを用意
Serial port;

//読み込み値の変数を用意:variables for data from the Arduino
int x,y;
//座標用変数を用意:variables for xy-coordinates to draw a circle on the screen
float xPos,yPos;

void setup(){
  //画面サイズ設定
  size(800,600);
  smooth();
  //シリアルポート設定
  port = new Serial(this,"/dev/tty.usbserial-A4001Kjl",9600);
  //「10」(ラインフィード)が来る度に
  //serialEvent()を作動させる
  port.bufferUntil(10);
  background(0);
  stroke(255); 
}

void draw(){
  //背景描画(黒)
  background(0);

  if(x>10 && y>10){//タッチしている時:when touching
    //塗り色を白にする
    fill(255);
    //読み取り値を座標にマッピングする
    xPos=map(x,70,781,0,width);
    yPos=map(y,81,822,0,height);
  }else{//タッチしていない時:when not touching
    //塗り色を黒にする
    fill(0);
  }

  //マッピングした座標を代入し円を描写:draw a circle
  ellipse(xPos,yPos,20,20);
}

//シリアル通信:serial communication
void serialEvent(Serial p){
  //文字列の変数stringDataを用意し「10」(ラインフィード)が来るまで読み込む
  String stringData=port.readStringUntil(10);

  //文字列データが空ではないとき
  if(stringData!=null){
    //文字列データに含まれる改行記号を取り除く
    stringData=trim(stringData);

    //整数型の配列data[]を用意し、
    //コンマ記号をもとに文字列データを区切って
    //配列data[]に整数化して入れておく
    int data[]=int(split(stringData,','));

    //配列data[]内のデータが2つなら、
    if(data.length==2){
      //最初のデータをxに代入
      x=data[0];
      //次のデータをyに代入
      y=data[1];

      //合図用データ送信:send a handshake signal
      port.write(65);
    }
  }  
}

//キー「s」が押されたら通信開始
void keyPressed(){
  if(key=='s'){
    //開始用データ送信:send a first handshake signal
    port.write(65);
  }
}

Arduinoから送られてくるXY座標値をmap()をつかってProcessingの画面上のXY座標値に対応させます。今回の場合、読み取りの最小値(70,81)を画面上の(0,0)に、読み取りの最大値(781,822)を(width=800,height=600)に対応させます。

通常タッチパネルはモニターと一体型になっていますが、タッチパネルだけを用いて透明で平面的な入力デバイスとして利用することも考えられます。仕組みはそれほど複雑ではないので、中古やジャンクのタッチスクリーンなどを分解して手に入れてもいいかもしれません。

以下のサイトでは、タッチパネルの構造や仕組みについて説明してあります。Other sites explaining about touch panels/touch screens.
グンゼ/タッチパネル製造
DMC/タッチパネル製造

また、以下のようなタッチパネル製品/部品などもあります。Shops selling touchpanel parts.
aitendo/タッチパネル部品販売
ストロベリーリナックス/PSP用タッチパネル
スイッチサイエンス/NintendoDSタッチスクリーン
Liquidware/Arduino TouchShield
Sparkfun/OLED+Touchscreen
iPodパーツショップ/iPhone 3G タッチスクリーン(部品)



Logitec USB対応15型タッチパネル LTP-15U
ロジテック (2004-10-31)
売り上げランキング: 13452

8/11/2008

Arduino+Processing マトリクスLED+Webカメラ

今回は、Webカメラから取り込んだ映像をArduinoに接続した8×8マトリクスLEDに映す実験を行います。まず、ProcessingでWebカメラからの映像を8×8ピクセルで取り込み、合計64個のピクセルの明るさの値(0〜255)を調べてから、その個々の値をシリアル通信でArduinoに送ります。Arduino側では、受け取った64個分の値をマトリクスLEDの個々の明るさに反映させます。Arduino基盤とマトリクスLEDとは、ICを使わず直結することにします(接続方法は「Arduino マトリクスLED1」を参照)。
Processingの画面では、マトリクスLEDの表示シミュレーション(モニタリング)を同時に行うことにします(前回行ったモザイク処理のような方法で赤い円を64個映し出すことにします)。


「Processingの画面(モニタリング)」

Processingのプログラム:

//ビデオライブラリを取り込む
import processing.video.*;
//キャプチャ用オブジェクトを用意
Capture video;

//シリアル通信ライブラリを取り込む
import processing.serial.*;
//シリアル通信オブジェクトを用意
Serial port;

//64個分のピクセル色の配列を用意
int[] pixelValue=new int[64];

//シリアル通信開始用フラグ
boolean start=false;

void setup(){
//画面を160角に設定
size(160,160);
//描画を滑らかにする
smooth();

//映像キャプチャの設定(幅8,高さ8ピクセル,フレームレート30)
video = new Capture(this, 8, 8, 30);

//ポートの設定
port=new Serial(this,"/dev/tty.usbserial-A40014iU",9600);

//外形線なし
noStroke();
}

void draw(){
//背景を黒で塗る
background(0);

//64個分のピクセルの処理
for(int i=0;i<64;i++){
//映像の各ピクセルの色の値を
//明るさの値に変換する
pixelValue[i]=int(brightness(video.pixels[i]));

//円の塗色(赤の値)に代入
fill(pixelValue[i],0,0);
//円を描画
ellipse((i%8)*20+10,(i/8)*20+10,15,15);

//値を送信
if(start){
port.write(pixelValue[i]);
}
}
}

//キャプチャ映像読み込み
void captureEvent(Capture video) {
video.read();
}

クリックでシリアル通信開始
void mousePressed(){
start=true;
}


Processingの方では、VideoライブラリSerialライブラリの二つを取り込む必要があります。マトリクスLEDが8×8の解像度なので、Webカメラから取り込む映像の解像度も8×8にしておきます(カメラ映像の横縦比は4:3なので、少し縦長の映像になってしまいます)。
*Windowsの場合、そのままの設定ではこのVideoライブラリを使用することができません。WinVDIG 1.0.1をインストールする必要があります。
「pixelValue[i]=int(brightness(video.pixels[i]))」では、まず映像の各ピクセルの色をpixels[]で読み込みます。pixels[]は、RGBの三色の値(三つの値)を含んでおり、brightness()で括ると明るさの値(一つの値)に変換されます(0〜255)。変換された値はfloat(小数)なのでint()で括って整数に変換しておきます。この値を、fill()の赤の値に代入し(緑と青は0)、LEDのような赤い円をellipse()を使って64個描画します。8×8を160×160の画面で表示しているので、20×20ピクセルのグリッド状に配置されます。ellipse()の直径はとりあえず15にしておきました。ellipse()のXとY座標は、「%」と「/」を使って計算します(X座標となる「(i%8)*20+10」は、iを8(横幅)で割った余りに20ピクセル掛けて、さらに10ピクセル足すことでellipse()が20×20のグリッドの中心に来るように位置調整しています)。最後に「port.write(pixelValue[i])」で、Arduinoへ各ピクセルの明るさの値を送信します。今回は画面をクリックしたらシリアル通信が開始されるようにしています。

次にArduinoの方に移ります。冒頭で書いたように、今回はマトリクスLEDを、ICを使わず直結します。個々のLEDはダイナミック点灯しているので、点灯時間の長さによって明るさを調整することになります。点灯時間が短ければ暗くなり、長くなれば明るくなります。つまり、Processingから送られて来た明るさの値(0〜255)を、個々のLEDの点灯時間に反映させるプログラムになるということです。マトリクスLEDとの接続方法や詳細については「Arduino マトリクスLED1」を参照してください。

Arduinoのプログラム:

//8x8の二次元配列を用意
byte matrix[8][8];

void setup(){
//出力ピンの設定、すべてオフにする
for(int i=2;i<=17;i++){
pinMode(i,OUTPUT);
digitalWrite(i,LOW);
}
//シリアル通信開始
Serial.begin(9600);
}

void loop(){
//シリアル通信(64個分のデータ)
if(Serial.available()>63){
for(int k=0;k<8;k++){
for(int l=0;l<8;l++){
//読み込んだ値を配列に代入
matrix[k][l]=Serial.read();
}
}
}

//各LEDの点灯制御
for(int i=2;i<=9;i++){
//列の点灯
digitalWrite(i,HIGH);

for(int j=10;j<=17;j++){
//行の点灯
digitalWrite(j,LOW);
//行の点灯継続時間
delayMicroseconds(1+matrix[i-2][j-10]);
//行の消灯
digitalWrite(j,HIGH);
//行の消灯継続時間
delayMicroseconds(256-matrix[i-2][j-10]);
}
//列の消灯
digitalWrite(i,LOW);
}
}


以前の「Arduino マトリクスLED1」とほぼプログラム内容は同じです。異なる部分は、シリアル通信と各LEDを点灯/消灯させる継続時間の部分です。用意する二次元配列は、boolean型ではなくbyte型(0~255の値なので)にしています。
シリアル通信上で干渉しないようにするため、Arduino基盤の0番ピンと1番ピンには何も接続しないことにしています(2〜17番ピンを使用)。シリアル通信では、Processingから送られてくる64個のデータをSerial.available()でカウントして、それぞれの値を予め用意しておいた二次元配列matrixに保存しておきます。送られてくるデータは0〜255(明るさの値)となります。そしてダイナミック点灯していく際に、明るさの値をdelayMicroseconds()に代入して、点灯継続時間と消灯継続時間に割り当てます。delayMicroseconds(0)としてしまうと、0マイクロ秒としては扱ってくれないので、delayMicroseconds()の括弧内に入れられる最小値は1にしてあります。最小1マイクロ秒の点灯時間かつ最大256マイクロ秒の消灯時間のときが最も暗くなるときです。その逆で、最大256マイクロ秒の点灯時間かつ最小1マイクロ秒の消灯時間のときが最も明るくなります。個々のLEDは257マイクロ秒ごとにダイナミック点灯していることになります。個々のLEDの点滅を300マイクロ秒程度にすると点滅しているようには見えないので、今回の257マイクロ秒周期で点滅させれば、ほぼ問題ないでしょう。

関連:
・「シリアル通信1〜5
・「Arduino マトリクスLED1
・「Processing Video(Webカメラ)

ロジクールストア(ウェブカメラカテゴリ)

8/09/2008

Processing Video (Webカメラ)

ProcessingのVideoライブラリを使用することで、パソコンに接続した(あるいは内蔵された)Webカメラからの映像を取り込むことができます。このライブラリを使用するには、パソコンにQuickTimeがインストールされている必要があるので、もしインストールされていない場合は、アップルのQuickTimeのサイトからダウンロード/インストールして下さい。
USB接続する外付けのWebカメラの場合は必要なドライバをメーカーのサイトなどからダウンロード/インストールして使用可能な状態にしておいて下さい。Webカメラがパソコンに内蔵されている場合は、そのまま使うことができるはずです。まずは、簡単な映像の取り込みかたから始めます。

*Windowsの場合、そのままの設定ではこのVideoライブラリを使用することができないことがあります。その場合、WinVDIG 1.0.1をインストールする必要があります。


//ライブラリの取り込み
import processing.video.*;
//キャプチャする映像のオブジェクトを用意
Capture myCapture;

void setup() {
size(200, 200);
//キャプチャする映像の設定
myCapture = new Capture(this, width, height, 30);
}

void draw() {
//映像を画面に配置
image(myCapture, 0, 0);
}

//映像の読み込み
void captureEvent(Capture myCapture) {
myCapture.read();
}

//クリックでカメラセッティング画面になる
void mousePressed(){
myCapture.settings();
}


上記プログラムでは、画面サイズが200×200の正方形であるため、Webカメラでキャプチャされた映像は縦長になってしまうかもしれません。通常画面の比率は4:3(横:縦)であるため、もし比率を合わせるのであれば、320×240(4:3)などに合わせてください。
Capture()によって、親オブジェクト(thisのままでよい)と映像の幅と高さ、フレームレートを設定できます。映像が滑らかに動かない場合は、フレームレートを下げるか、画像サイズを縮小して調整して下さい。
キャプチャされた映像は、毎フレームごとにimage()によって画面内に配置されます。image()の括弧内の数値は、配置するX座標、Y座標となります。上記プログラムでは0,0に設定されているので、左上角の座標を基準に映像が画面内に配置されます。
captureEvent()は、Webカメラから信号が送られてくる度に作動します。read()によって、毎回送られてくる映像信号を読み込んでいます。
今回は最後にクリックするとsettings()によって、カメラのセッティング画面(Webカメラ用の設定アプリケーション画面)が現れるようにしてあります。ここで、カメラの解像度やコントラストなどの設定を行うことができるはずです。

次に映像にフィルタをかけてみるプログラムをしてみます。
画像用のfilter()というコマンドがあり、
THRESHOLD 白黒ニ値化:0.0~1.0(黒〜白)/初期設定0.5
GRAY    グレースケール:パラメータなし
INVERT   反転色:パラメータなし
POSTERIZE ポスタライズ:2~255(各チャンネルの色数の限定)
BLUR    ぼかし:指定半径範囲でぼかす
それぞれをfilter()の括弧内に入れれば、映像にフィルタをかけることができます。フィルタの種類の後にパラメータを入れることで、フィルタのかかり具合を調整することができます。
複数のフィルタを重ねて使うこともできます。ただ、フィルタの種類(特にBLUR)によっては処理速度に影響がでる場合があるので、適宜フレームレートや画素数などを調整する必要がでてくるときがあります。
以下は、キャプチャした映像にぼかしをいれノイズを取払い、その後白黒でニ値化し、白黒の単純な映像に変換するサンプルです。尚、映像の比率は4:3にすることにします。


「フィルタ(ぼかし+白黒ニ値化)をかけた映像(上画像)」


//ライブラリの取り込み
import processing.video.*;
//キャプチャする映像のオブジェクトを用意
Capture myCapture;

void setup() {
size(320, 240);//比率4:3
//キャプチャする映像の設定
myCapture = new Capture(this, width, height, 30);
}

void draw() {
//映像を画面に配置
image(myCapture, 0, 0);
filter(BLUR,1.8);//ぼかし
filter(THRESHOLD,0.7);//白黒ニ値化
}

//映像の読み込み
void captureEvent(Capture myCapture) {
myCapture.read();
}


次に、映像のモザイク化のプログラムをしてみます。画面サイズを320×240とし、横を32分割、縦を24分割し、10×10のモザイク処理をすることにします。まずloadPixels()によって映像の全ピクセルを読み込み、pixels[]によって選択したピクセルの色を取得します。取得した色をもとにrect()でモザイクの矩形を描くことにします。モザイク化されるには、必要なキャプチャ映像は結果的に32×24の解像度があれば済むので、予め32×24のサイズでキャプチャすることにします。


「モザイク映像(上画像)」


//ライブラリの取り込み
import processing.video.*;
//キャプチャする映像のオブジェクトを用意
Capture myCapture;

void setup() {
size(320, 240);//比率4:3
//キャプチャする映像を32×24に設定
myCapture = new Capture(this, 32, 24, 30);
//noStroke(); //モザイクの黒枠をなしにする場合
}

void draw() {
//キャプチャ画像の各ピクセルの色を
//塗色に割当て、矩形を描く
for(int i=0;i<32*24;i++){
fill(myCapture.pixels[i]);
rect((i%32)*10,(i/32)*10,10,10);
}
}

//映像の読み込み
void captureEvent(Capture myCapture) {
myCapture.read();
}


最終的に32×24のモザイクになるので、キャプチャする画像も同様の解像度にしておけば、処理速度が遅くならなくて済みます。pixels[]によってキャプチャされた映像の各ピクセルの色を取得します。32×24なので合計768ピクセル分となります。rect()による矩形も768個必要になるので、まずfill()で塗色を指定してから描きます。pixel[i]のiは画面左上から数えていき最後は右下のピクセルの番号になります。pixel[i]のiは0~767(合計768個)までの連続した数値なので、「%」と「/」を使ってXとYの座標値に変換します。変換したXとYの座標値をrect()に代入することで、320×240の画面に10ピクセル単位で横方向と縦方向に32×24のモザイク矩形として配置されます。今回はnoStroke()を使っていないので、黒い枠のついたモザイク矩形にしています。


ドライバなしですぐにコンピュータに接続可能(UVC対応)なWebカメラとして以下のようなものがあります。
Macintosh/Windows兼用です。
      

関連:
Arduino+Processing マトリクスLED+Webカメラ」--Webカメラ映像をマトリクスLEDに映す。
Processing Webカメラを光センサとして使う」--点光源で画面内に線を描く。
Processing Webカメラ/定点記録画像」--Webカメラ映像を0.5秒おきに画像保存(JPEG)する。
Processing Webカメラ/カラートラッキング」--Webカメラを使い、色を手がかりに物体を追いかける。
Processing Webカメラ/モーショントラッキング」--Webカメラを使って動体検知する。

7/30/2008

Processing サウンド4/スクラッチ

前回までSoniaライブラリを使って、音源の再生、逆再生ならびにテンポ(スピード)変換をしました。これらを組み合わせると、スクラッチ(ターンテーブルやCDJ)のようなことができるので、応用としてプログラミングしてみたいと思います。マウスを上下(Y座標)にドラッグすることで、スクラッチすることにします。画面下方へドラッグすれば順方向へ、画面上方へドラッグすれば逆方向へ曲が再生されるようにします。そのとき、ドラッグした量に比例して再生される速度も変化するようにします。スクラッチ中やスクラッチを解除したときに、ターンテーブルの回転数の立ち上がりを少し鈍くするために、テンポの変化にフィルターをかけることにします。
*音源データは予め用意して、スケッチフォルダ内のdataフォルダ内にいれておいて下さい(方法については、「Processing サウンド1/Sonia」を参照)。


//ライブラリを取り込む
import pitaru.sonia_v2_9.*;

//順再生と逆再生のオブジェクトを用意
Sample tuneFW;
Sample tuneRV;
//音源データの長さの変数
int dataLength;
//順再生の再生位置の変数
int currentFrameFW;
//逆再生の再生位置の変数
int currentFrameRV;
//テンポの変数
float tempo=0;
//フィルター付きテンポの変数
float tempoFiltered;

void setup() {
size(200,200);
//Soniaの使用開始
Sonia.start(this);
//音源の指定
tuneFW = new Sample("music.wav");
//音源の全フレーム数を調べておく
dataLength=tuneFW.getNumFrames();

//逆再生用のデータを作成
tuneRV= new Sample(dataLength);
float[] forward=new float[dataLength];
tuneFW.read(forward);
float[] backward=new float[dataLength];
backward=reverse(forward);
tuneRV.write(backward);

//念のため、順再生/逆再生のフレームレートを調べる
println(tuneFW.getRate()+":"+tuneRV.getRate());

//順再生する
tuneFW.play();
//テンポを1.0倍(標準)にしておく
tempo = 1.0;
}

void draw(){
background(0);
//テンポに少し反応を鈍くするフィルターをかける
tempoFiltered+=(tempo-tempoFiltered)/4;
//テンポ設定
tuneFW.setSpeed(tempoFiltered);
tuneRV.setSpeed(tempoFiltered);

//それぞれの再生位置を取得する
if(tuneFW.isPlaying()){//順再生の時
//順再生の再生位置を記憶させておく
currentFrameFW = tuneFW.getCurrentFrame();
//逆再生の再生位置を全フレーム数から
//順再生の再生位置を差し引いて計算しておく
currentFrameRV = dataLength-currentFrameFW;
}
else{//逆再生の時
//逆再生の再生位置を記憶させておく
currentFrameRV = tuneRV.getCurrentFrame();
//順再生の再生位置を全フレーム数から
//逆再生の再生位置を差し引いて計算しておく
currentFrameFW = dataLength-currentFrameRV;
}
}

//ドラッグしたとき(スクラッチ中)
void mouseDragged(){
//前回と今回のマウスY座標値の差を求める
int difY=mouseY-pmouseY;

if(difY>=0){//差がプラスなら(下向きにドラッグした時)
//再生位置から順再生させる
tuneFW.play(currentFrameFW,dataLength);
//逆再生停止
tuneRV.stop();
}
else{//差がマイナスなら(上向きにドラッグした時)
//再生位置から逆再生させる
tuneRV.play(currentFrameRV,dataLength);
//順再生停止
tuneFW.stop();
}
//差の絶対値を4で割った値をテンポにする
tempo=abs(difY)/4;
}

//マウスを放した時(スクラッチ解除時)
void mouseReleased(){
//再生位置からテンポを1.0倍にして順再生させる
tempo=1.0;
tuneFW.play(currentFrameFW,dataLength);
//逆再生停止
tuneRV.stop();
}

public void stop(){
Sonia.stop();
super.stop();
}

ここをクリックでサンプルのサイトへ移動します。
*MacOSX(Intel)の場合、新たにJSynプラグインをインストールする必要があります。ここをクリックすると、インストール画面に移動します。移動先のページ上の「Click Here to Install JSyn Plugin」のボタンを押してインストールしてください。


スクラッチによって曲が行ったり来たりするので、順再生と逆再生の二つの音源を別々に用意し、交互に切り替えて再生させています(逆再生の音源のつくりかたは「Processing サウンド2/逆再生」を参照)。まずsetup(){...}内の「dataLength=tuneFW.getNumFrames()」によって、曲の全フレーム数を調べておきます。全フレーム数から順再生された分のフレーム数を差し引けば、次に逆再生のされるときの再生箇所が導きだされます。そのため、draw(){...}内で毎回現在順再生か逆再生かをisPlaying()によって判別し、もし順再生なら「currentFrameFW = tuneFW.getCurrentFrame()」で現在の再生位置を調べておき、同時に次に逆再生されるときの再生位置を「currentFrameRV = dataLength-currentFrameFW」で計算して記憶させておきます。
念のために、setup(){...}内の「println(tuneFW.getRate()+":"+tuneRV.getRate())」で、順再生と逆再生のサンプルレートを調べておきます。同じ値ならいいのですが、もし異なる場合は、順再生と逆再生の再生速度が変わってしまうので、draw(){...}内のsetSpeed()で片方のスピード値を半分にするなどしてください。
スクラッチは前回と今回のマウスのY座標の差分をテンポに反映させて再生させています。大きくドラッグすれば、早回しで再生されることになります。ただし音が早回りしすぎるときがあるので、「tempo=abs(difY)/4」というように、値を4で割ってスケールダウンしています(4以外の数値を入れて調整してみてください)。ここで得られたテンポの値は、draw(){...}内の「tempoFiltered+=(tempo-tempoFiltered)/4」に代入され、テンポの変化が少し鈍く反映されるようにフィルターをかけています。ここでも4で割っていますが、もう少し反応を鈍くさせたい場合は4より大きい値で割ってください(少し反応を鈍くさせたほうが、スクラッチらしい音になるので、そうしています)。abs()は絶対値に変換する関数です。最終的にテンポに代入される値はプラスなので、差分がマイナスの場合にプラスになるように調整しています。
マウスを放したときは、スクラッチを止めた時なので、もともとの順再生の状態に戻るようにし、逆再生は停止させます。その時のテンポも標準の再生スピードになるように1.0に戻しておきます。

関連:
Processing サウンド3/テンポ」--音源再生のテンポ変換をする
Processing サウンド2/逆再生」--逆再生の音源をつくる
Processing サウンド1/Sonia」--音源の再生/停止/ポーズする

今回のプログラムのサンプルサイトへのリンク

*MacOSX(Intel)の場合、新たにJSynプラグインをインストールする必要があります。Windowsの場合も、上記サンプルサイトをオンラインで視聴するためにプラグインが必要となります。ここをクリックすると、インストール画面に移動します。移動先のページ上の「Click Here to Install JSyn Plugin」のボタンを押してインストールしてください。

 iTunes Music Store(Japan)

7/28/2008

Processing サウンド3/テンポ

引き続きSoniaライブラリでの音の制御についてです。今回は曲や音源のスピード(テンポ)を変えてみたいと思います。Soniaでは、setRate()もしくはsetSpeed()で、容易に動的にテンポを変換できます。setRate()の場合は括弧内に0~88200までの値が入ります。その音源の基準のサンプルレートが44100であれば、22050を代入すると半分のスピードになり、88200で2倍のスピードになります。基準のサンプルレートを調べるにはgetRate()を用います。通常なら44100あたりに設定されていると思います。setSpeed()の場合は、0~2.0までの小数点が括弧内に入ります。0.5で半分のスピード、2.0で2倍のスピードになります。今回はsetSpeed()を用いて、マウスを上下(Y軸方向)にドラッグするとテンポが動的に変化するプログラムにします。

*音源データは、前回書いたようにスケッチフォルダ内にdataフォルダを作成し、その中に入れておいて下さい。
*プログラムをランさせた時に「Exception in thread "Thread-2" java.lang.OutOfMemoryError:...」のような赤文字のエラーが出たら、メモリーが足りないということなので、MacosXの場合はメニューバーのProcessing>環境設定を選択し(ウィンドウが現れる)、「Set maximum available memory to ... MB」の欄に256MBなど充分なメモリー(曲や音源のサイズ)を記入し、チェック欄にチェックを入れて下さい。Windowsの場合は、File>Preferencesを選択し、同様に充分なメモリー数を記入しチェックを入れて下さい。


//ライブラリを取り込む
import pitaru.sonia_v2_9.*;
//音源tune(名前は任意)を用意
Sample tune;

void setup() {
//とりあえず画面を200角に設定
size(200,200);
//Sonia開始
Sonia.start(this);
//括弧内に音源名を指定し設定する
tune = new Sample("music.wav");
//音源のサンプルレートを調べる
println(tune.getRate());
//音源の再生
tune.play();
}

void draw(){
//特になし
}

//マウスをドラッグしたとき
void mouseDragged(){
//マウスY座標値を0~2.0の範囲で割り当てる
float tempo=map(mouseY,0,height,0,2.0);
//テンポ(スピード)設定
tune.setSpeed(tempo);
}

//Soniaの使用停止
public void stop(){
Sonia.stop();
super.stop();
}


map()には、1つ目の値が元となるマウスY座標値を入れ、2つ目の値にはその最小値、3つ目がその最大値、4つ目は変換された後の最小値、5つ目が変換された後の最大値を入れます。変化する値をスケーリングしたりオフセットを設けたりして変換させるときに使うと便利です。setSpeed()は0~2.0の値が入るので、マウスY座標値を0~2.0の範囲で変換させるということになります。

関連:
Processing サウンド4/スクラッチ」--曲をスクラッチ演奏する
Processing サウンド2/逆再生」--逆再生の音源をつくる
Processing サウンド1/Sonia」--音源の再生/停止/ポーズする

*MacOSX(Intel)の場合、新たにJSynプラグインをインストールする必要があります。ここをクリックすると、インストール画面に移動します。移動先のページ上の「Click Here to Install JSyn Plugin」のボタンを押してインストールしてください。

 iTunes Music Store(Japan)

7/24/2008

Processing サウンド2/逆再生

前回は音源を用意し再生/停止(ポーズ)のプログラムをしました。今回もまた「Sonia」ライブラリを使用しますが、用意した音源を逆再生するプログラムをしてみます。前回少し触れたように、音源データはフレームレートの数値で再生箇所や経過時間などを制御できます。時間の経過に合わせて、連続する音のデータを次々と読み込んで再生していく仕組みになっています。逆再生の場合は、配列を用意し音源データの最終フレームから先頭フレームまでを読み込んで、逆方向に配置されたデータをつくり、それを再生させます。

クリックで順再生と逆再生が切り替えられるプログラムを以下に書きます。音源は予め用意して、sketchフォルダ内のdataフォルダ(なければ新規作成)にいれておいて下さい。
尚、プログラムをランさせた時に「Exception in thread "Thread-2" java.lang.OutOfMemoryError:...」のような赤文字のエラーが出たら、メモリーが足りないということなので、MacosXの場合はメニューバーのProcessing>環境設定を選択し(ウィンドウが現れる)、「Set maximum available memory to ... MB」の欄に256MBなど充分なメモリー(曲や音源のサイズ)を記入し、チェック欄にチェックを入れて下さい。Windowsの場合は、File>Preferencesを選択し、同様に充分なメモリー数を記入しチェックを入れて下さい。


//ライブラリの取り込み
import pitaru.sonia_v2_9.*;

//順再生/逆再生の二つのオブジェクトを用意
Sample forwardTune;
Sample reverseTune;

//順再生/逆再生の切替フラグを用意
//trueで順再生、falseで逆再生
boolean direction=true;

void setup() {
size(200,200);

Sonia.start(this);

//使う音源を指定する
forwardTune = new Sample("music.wav");
//音源データの全体の長さ(フレーム数)を取得する
int dataLength=forwardTune.getNumFrames();

//順再生データ用の配列を全データの長さ分だけ用意
float[] forwardArray=new float[dataLength];
//順再生データを配列として読み込む
forwardTune.read(forwardArray);

//逆再生データ用の配列を全データの長さ分だけ用意
float[] backwardArray=new float[dataLength];
//順再生データを逆配列に置き換える
backwardArray=reverse(forwardArray);

//逆再生音源を用意
reverseTune= new Sample(dataLength);
//逆再生データを逆再生音源として書き出す
reverseTune.write(backwardArray);

//まず順再生音源を再生
forwardTune.play();
}

void draw(){
//特になし
}

void mousePressed(){
if(direction==false){
direction=true;
reverseTune.stop();//逆再生停止
forwardTune.play();//順再生
}else{
direction=false;
forwardTune.stop();//順再生停止
reverseTune.play();//逆再生
}
}

public void stop(){
Sonia.stop();
super.stop();
}


手順としては、用意した音源をSample()で指定し、全体のフレーム数(全データの長さ)をgetNumFrames()で数えます。データを変換するために順再生と逆再生の二つの配列を用意します。全体のフレーム数だけ配列のデータが必要になります。read()を用いて配列として全データを読み込みます。読み込んだ全データをreverse()で逆方向の配列に置き換えます。置き換えられた配列をwrite()で書き出し、逆再生の音源として取り込みます。今回は使いませんでしたが、getCurrentFrame()を用いて、反転する箇所のフレーム数を記憶させておけば、クリックするごとに行ったり来たりするような再生が可能になります。

関連:
Processing サウンド4/スクラッチ」--曲をスクラッチ演奏する
Processing サウンド3/テンポ」--音源再生のテンポ変換をする
Processing サウンド1/Sonia」--音源の再生/停止/ポーズする

*MacOSX(Intel)の場合、新たにJSynプラグインをインストールする必要があります。ここをクリックすると、インストール画面に移動します。移動先のページ上の「Click Here to Install JSyn Plugin」のボタンを押してインストールしてください。

 iTunes Music Store(Japan)


[目次:Processing関係]  [HOMEへ戻る]  [目次:Arduino関係]