プログラミングの備忘録

プログラムをつくる過程を残すもの

processingの備忘録 -AR-

こんにちは。
今回は、processingで「AR」を扱ってみます。


かなり前にですがニコニコ動画でARを使って初音ミクをなでたりしている動画を見つけて、そのころからものづくりが好きだったのでいつかまねしてみたいなと思っていました。
(いわゆる「ニコニコ技術部」に属する動画は結構ハマって見ていました。)

当時は出会っていませんでしたが、今はprocessingという手段があります。
それっぽいこともできるのではないかということで、やってみることにしました。


目次


ARとは

ARは「Augmented Reality」の略で、「拡張現実」という意味です。

おそらく誰しも一度くらいは触れたことがある概念かと思います。

特定のマークを検出したらその場所に対応する図を表示する、というのが単純な機構ですが、「Pokèmon GO」とか「Pikmin Bloom」といった現実世界に仮想世界のキャラクターを表示するものや、実際に着なくても試着できるシステムなどもそれにあたります。

少し古いですが、『電脳コイル』というアニメでもARの技術が見られます。
(自分が科学に興味を持ったきっかけといっても良いくらいのもので、機会があればぜひ見てみてください。)


似た概念に「VRVirtual Reality、拡張現実)」がありますが、こちらは仮想世界に入り込むようなものなので、仮想世界を現実世界に持ってくるARとは逆のようなものであるといえると思います。


processingでAR

ライブラリのインストール

processingでARを扱えるようにするには2つのライブラリが必要です。
カメラ用の「Video」とAR用の「NyARToolkit」です。


「Video」に関しては過去に扱っているので、詳細はそちらを参照してください。

taq.hatenadiary.jp


「NyARToolKit」についても他のライブラリと同様に「ライブラリのインポート」からインストールできます。

GitHubのページもあるので、そちらからダウンロードしてProcessingのフォルダ内の「libraries」に入れることでも可能です。

github.com


使うときは、コードの始めに

import processing.video.*; //「Video」のインポート
import jp.nyatla.nyar4psg.*; //「NyARToolkit」のインポート

と書いておけばOKです。


カメラを使えるようにする

「Video」のところで挙げた記事を読めばカメラは使えるようになると思いますが、一応こちらにも簡単に書いておきます。

import processing.video.*;

Capture cam;
String[] cameras = Capture.list();

void setup(){
    size(640, 480);
    
    for(int i = 0; i < cameras.length; i ++){ //使用可能なカメラを羅列
        println("[" + i + "] " + cameras[i]);
    }

    cam = new Capture(this, cameras[1]); //カメラ1を使用
    cam.start();
}

void draw(){
    if(cam.available() == true){
        cam.read(); //カメラの映像を読み込む
    }
    image(cam, 0, 0); //映像を表示
}


自分の環境では、

[0] FJ Camera
[1] UCAM-C0220F

という2つのカメラがあり、そのうちのcameras[1]のUCAM-C0220Fを使うことにしています。


ARで表示してみる

では、ライブラリに付属しているサンプルを実行して、どのように書くかを見てみます。

GitHubのページのreadmeにある「サンプル実行(ARマーカ)」に従い、「pattHiro.pdf」を印刷した後、以下のコード(simpleLite.pde)を実行してみました。

/**
 * NyARToolkit for proce55ing/3.0.5
 * (c)2008-2017 nyatla
 * airmail(at)ebony.plala.or.jp
 * 
 * 最も短いARToolKitのコードです。
 * Hiroマーカの上に立方体を表示します。
 * 全ての設定ファイルとマーカファイルはスケッチディレクトリのlibraries/nyar4psg/dataにあります。
 * 
 * This sketch is shortest sample.
 * The sketch shows cube on the marker of "patt.hiro".
 * Any pattern and configuration files are found in libraries/nyar4psg/data inside your sketchbook folder. 
*/
import processing.video.*;
import jp.nyatla.nyar4psg.*;

Capture cam;
String[] cameras = Capture.list();
MultiMarker nya;

void setup() {
  size(640,480,P3D);
  colorMode(RGB, 100);
  println(MultiMarker.VERSION);
  cam=new Capture(this,cameras[1]);
  nya=new MultiMarker(this,width,height,"../../data/camera_para.dat",NyAR4PsgConfig.CONFIG_PSG);
  nya.addARMarker("../../data/patt.hiro",80);
  cam.start();
}

void draw() {
  if (cam.available() !=true) {
      return;
  }
  cam.read();
  nya.detect(cam);
  background(0);
  nya.drawBackground(cam);
  if((!nya.isExist(0))){
    return;
  }
  nya.beginTransform(0);
  fill(0,0,255);
  translate(0,0,20);
  box(40);
  nya.endTransform();
}

カメラの都合上一部変えたところはありますが、他は同じです。


実行すると、マーカー部分に以下のような青い立方体が描かれました。

すごく簡単です。これがライブラリの力。


中身を見てみます。

void setup() {
  size(640,480,P3D);
  colorMode(RGB, 100);
  println(MultiMarker.VERSION);
  cam=new Capture(this,cameras[1]);
  nya=new MultiMarker(this,width,height,"../../data/camera_para.dat",NyAR4PsgConfig.CONFIG_PSG);
  nya.addARMarker("../../data/patt.hiro",80);
  cam.start();
}

println(MultiMarker.VERSION);でNyARToolkitのバージョンを表示

nya=new MultiMarker以下ではカメラの校正などが行われているそうです。
(決まり文句)

nya.addARMarker以下で使うARマーカーを指定
(2個目の引数はマーカーの大きさを指し、今回の場合は80 mmという意味)


void draw() {
  if (cam.available() !=true) {
      return;
  }
  cam.read();
  nya.detect(cam);
  background(0);
  nya.drawBackground(cam);
  if(!nya.isExist(0)){
    return;
  }
  nya.beginTransform(0);
  fill(0,0,255);
  translate(0,0,20);
  box(40);
  nya.endTransform();
}

nya.detect(cam);で映像からマーカーを検出

nya.drawBackground(cam);で映像を背景に設定

if(!nya.isExist(0))以下でマーカー[0]が画面内に無ければreturn

nya.beginTransform(0);からnya.endTransform();の間で描画したい図の設定
nya.beginTransform()の引数でマーカーの番号を指定)


以上、サンプルを使って試してみました。

図の描画はprocessingの処理と同じ描き方だったので、これまでに3Dでつくったものもそのまま表示できます。
(自分で書いたコードで実行するときは、「simpleLite.pde」と同じ所に置いてある「data」フォルダの中身も移すことを忘れずに。でないとカメラの校正ができず、マーカーのデータも無いということになってしまい、動きません。)

processingの備忘録 -3D- - プログラミングの備忘録


processingの備忘録 -電子軌道- - プログラミングの備忘録


おまけ

以上でprocessingにおけるARの基本はつかめたので、おまけとしてさらに発展させてみます。

3Dモデルを表示させる

processingでも絵は描けますが、モデリングソフトには劣るかと思います。
自分でつくった、あるいは配布されている3DモデルをARで表示させたいというときもあるでしょう。

processingでは「PShape」型でobjファイルを取り込めるようなので、それを使えば外部ファイルを読み込ませて表示させることができそうです。


ロケット

Processingのサンプル内にはobjファイルを読み込んで表示するものがあり、そこに3Dデータもあるのでそれを使って試してみます。
(自分の場合は、Processingのフォルダ内「modes\java\examples\Basics\Shape\LoadDisplayOBJ」の「data」内にありました。)

import processing.video.*;
import jp.nyatla.nyar4psg.*;

Capture cam;
String[] cameras = Capture.list();
MultiMarker marker;
PShape obj; //objファイルのデータを入れておく変数

void setup(){
    size(640, 480, P3D);
    
    for(int i = 0; i < cameras.length; i ++){
        println("[" + i + "] " + cameras[i]);
    }

    cam = new Capture(this, cameras[1]);
    cam.start();
    
    marker = new MultiMarker(this, width, height, "data/camera_para.dat", NyAR4PsgConfig.CONFIG_PSG);
    marker.addARMarker("data/patt.hiro", 80);
    
    obj = loadShape("rocket.obj"); //objファイルの読み込み
}

void draw(){
    if(cam.available() == false){
        return;
    }
    cam.read();
    marker.detect(cam);
    marker.drawBackground(cam);
    
    if(marker.isExist(0) == true){
        marker.beginTransform(0);
        rotateX(PI/2); //なぜか横たわるので90度回転させる
        scale(0.25); //大きさの調整
        shape(obj); //objファイル内のモデルを表示
        marker.endTransform();
    }
}


「data」内にはobj、mtl、pngの3つのファイルがあったかと思いますが、それぞれ3Dモデルそのもの、モデルの表面の設定、モデル表面の柄、のような役割になっています。

obj、mtlはテキストとして読むこともできるので、一度は中身を見てみるとわかりやすいかと思います。


初音ミク

記事の始めでも書きましたがやはり初音ミクを出したいということで、「ニコニ立体」からTda初音ミクさんをお借りさせていただきました。

3d.nicovideo.jp


初音ミクは大抵がMMDモデルでpmdやpmxといった拡張子になっているので、これをobjに変換する必要があります。

調べてみると、モデリングソフト「Blender」で変換できるようですが、デフォルトではpmdやpmxは読み込めないのでアドオンが必要です。

以下からダウンロードし、「mmd_tools」をBlenderのインストール先の「Blender Foundation」→「Blender 3.1」→「3.1」→「scripts」→「addons」に入れます。

github.com

これだけでは機能しないので、さらにBlender内で「Edit」タブから「Preferences」を選び、「Add-ons」で「mmd」などと検索して出た「mmd_tools」の左側にあるチェックボックスを押し、左下のボタンから「Save Preferences」を押します。

これで「mmd_tools」が機能し、pmdやpmxを読み込めるようになりました。


Blender内の「File」タブから「Import」→「MikuMikuDance Model」を選び、読み込みたいpmdまたはpmxファイルを選べば読み込めます。

このまま「File」タブから「Import」→「Wavefront」でエクスポートすればobjファイルとmtlファイルが出力されるので、これらを先のロケットの例のようにすれば表示できます。

が、変なものがついてモコモコしています。
(モデルの表示部分にlights();を加えると凹凸が目立って見やすくなると思います。下の画像は追加したコードで実行したものです。)

この解決に苦戦しましたが、どうやら非表示になっている部分がエクスポート時に出てきてしまっているようでした。
なので、これを消していけばOKです。

今回のTda式ミクでは、Blender上の右上にあるウィンドウでファイルを漁ると「rigitbodies」というものがあり、この中身を表示してみると先程のモコモコが出てきました。

ということでこれらを消し、再びエクスポートすると…


ここでさらに問題があり、見てお分かりの通り、色がありません。
Blenderではモデルデータは取り出せてもその表面の絵までは取り出せないようです。

ではどうやって絵をつけるか、なかなか苦戦したのですが、結論としては自力でやるしかないということになりました。

ダウンロードしたMMDデータには「tex」フォルダがあり、そこに絵(テクスチャ)が入っているのですが、Blenderでモデルの一部分と対応する絵とを関連付けていく必要があるということです。

ここは独自の方法というか、全くBlenderに触れたことがないながらにやっているので正しいかはわかりませんが、具体的には、Blender上の右上のウィンドウから最後が「_mesh」となったものを探して選択し、右下のウィンドウから「Material Properties」を選び、「body_skin」や「body_cloth」など全てに対して以下の操作を行います。

  • Surface」を「MMDShaderDev」から「Principled BSDF」に変更

  • 「Base Color」を「Image Texture」に変更

  • その下の「Open」から絵を選ぶ

こうすることでモデルと対応する絵が関連付けられるので、objやmtlと同じフォルダに選んだ絵を入れておけば色がつきます。


これをエクスポートしたものについて同様に実行しましたが、マーカーが映ると固まってしまい、確認することができませんでした。
(今のところは絵がついたことで重くなって固まったと考えています。)

windowsパソコンなら「3Dビューワー」なるものがあるのでobjファイルを開けますが、そこでは色がついていたのでおそらく合っていると思います。


おまけのおまけ

初音ミクでは重くてダメだったので、はちゅねミクくらいならいけるのではないかと思ってやってみました。

モデルはこちらからお借りしました。

piapro.jp


表示はできましたが、『ぼくのなつやすみ』の32日目みたいになって正しく表示されませんでした。
3Dビューワーでは正しく表示されているんですが…


絵を指定する画像ファイルが「tga」だったのですが、これが悪さをしているのではと思い「png」に変換し、mtlファイルもpngファイルを指定するように変えてみました。

うまくいきました。


参考


複数のマーカーを使う

マーカーが複数欲しいときもあるでしょう。

import processing.video.*;
import jp.nyatla.nyar4psg.*;

Capture cam;
String[] cameras = Capture.list();
MultiMarker marker;
int hiro, kanji; //マーカーの種類(0, 1, 2, ...という値が対応)

void setup(){
    size(640, 480, P3D);
    
    for(int i = 0; i < cameras.length; i ++){
        println("[" + i + "] " + cameras[i]);
    }

    cam = new Capture(this, cameras[1]);
    cam.start();
    
    marker = new MultiMarker(this, width, height, "../data/camera_para.dat", NyAR4PsgConfig.CONFIG_PSG);
    hiro = marker.addARMarker("../data/patt.hiro", 80); //「patt.hiro」のマーカーをマーカー[0]に
    kanji = marker.addARMarker("../data/patt.kanji", 80); //「patt.kannji」のマーカーをマーカー[1]に
}

void draw(){
    if(cam.available() == false){
        return;
    }
    cam.read();
    marker.detect(cam);
    marker.drawBackground(cam);
    
    if(marker.isExist(hiro) == true){ //マーカー[0]が画面内にあれば
        marker.beginTransform(hiro);
        fill(0, 0, 255);
        translate(0, 0, 20);
        box(40); //立方体を表示
        marker.endTransform();
    }
    
    if(marker.isExist(kanji) == true){ //マーカー[1]が画面内にあれば
        marker.beginTransform(kanji);
        fill(0, 0, 255);
        translate(0, 0, 20);
        sphere(40); //球を表示
        marker.endTransform();
    }
}


「data」フォルダには「patt.hiro」と「patt.kannji」という2つのマーカーがあるので、それぞれが立方体と球を表示するように設定しています。


参考


マーカーを自作して使う

四角いマーカーがあると見た目が悪いとか、そもそもマーカーの数が足りないというときもあるでしょう。

白黒の四角いマーカー

これまで使ってきた、マーカーマーカーしたものです。

ライブラリのフォルダ内の「data」にある「NyIdMarkerCard_00_09」にID 0~9のものが載っているのでそれを使っても良いですし、「NyARIdMarkerFormat」にはマーカーの仕組みが書いてあるので、それを参考に自作しても良いです。

便利なサイトもあり、「ID」の欄に使いたいマーカーのIDを入れれば生成してくれます。
(MODELはどちらでも同様に反応しました。)

sixwish.jp

マーカーの仕組みについては省略します。
(読んでも理解できなかったとかということでは決してない。決して…)


実際に使うときは…

import processing.video.*;
import jp.nyatla.nyar4psg.*;

Capture cam;
String[] cameras = Capture.list();
MultiMarker marker;

void setup(){
    size(640, 480, P3D);
    
    for(int i = 0; i < cameras.length; i ++){
        println("[" + i + "] " + cameras[i]);
    }

    cam = new Capture(this, cameras[1]);
    cam.start();
    
    marker = new MultiMarker(this, width, height, "../data/camera_para.dat", NyAR4PsgConfig.CONFIG_PSG);
    int id = 0; //ID
    marker.addNyIdMarker(id, 80); //IDと大きさを指定してマーカーを追加
}

void draw(){
    if(cam.available() == false){
        return;
    }
    cam.read();
    marker.detect(cam);
    marker.drawBackground(cam);
    
    if(marker.isExist(0) == false){
        return;
    }
    
    marker.beginTransform(0);
    fill(0, 0, 255);
    translate(0, 0, 20);
    box(40);
    marker.endTransform();
}


上記以外のマーカー

「NFT(Natural Feature Tracking、自然特徴点追跡)」と呼ばれるもので、これまで使ってきた白黒の四角いマーカー以外でもマーカーとして使うことができます。


「NyARToolkit」のサンプルの中に「nftFilesGen」というものがあり、これを使うことで対象の特徴点を探し、マーカーとして使えるようにすることができます。

例えばこんな図をマーカーとして、メタンの分子模型を表示するようにしてみます。

「nftFilesGen」を起動し、上の画像を読み込ませます。

「source DPI」の部分には画像の解像度を入れるそうです。
今回は300 dpiでした。

その他はよく分からないので、とりあえずはデフォルトのままにしました。

「Make Feature Set」ボタンを押してしばらく待つと、以下のようになりました。

「Export」から「Make Feature Set files」を押して名前をつけると、「methane.fset」「methane.fset3」「methane.iset」という3つのファイルができました。
これがマーカーの特徴点が入ったファイルです。

これをスケッチフォルダに入れ、以下を実行すると…

import processing.video.*;
import jp.nyatla.nyar4psg.*;

Capture cam;
String[] cameras = Capture.list();
MultiNft nft;

void setup(){
    size(640, 480, P3D);
    
    for(int i = 0; i < cameras.length; i ++){
        println("[" + i + "] " + cameras[i]);
    }

    cam = new Capture(this, cameras[1]);
    cam.start();
    
    nft = new MultiNft(this, width, height, "data/NyARToolkit/camera_para.dat", NyAR4PsgConfig.CONFIG_PSG);
    nft.addNftTarget("methane", 80);
}

void draw(){
    if(cam.available() == false){
        return;
    }
    cam.read();
    nft.detect(cam);
    nft.drawBackground(cam);
    
    if(nft.isExist(0) == true){
        nft.beginTransform(0);
        scale(0.25);
        noStroke();
        
        fill(0);
        sphere(170);
        
        fill(0, 0, 255);
        pushMatrix();
        translate(0, 0, 109);
        sphere(120);
        popMatrix();
        
        pushMatrix();
        translate(0, 103, -35.5);
        sphere(120);
        popMatrix();
        
        pushMatrix();
        translate(88.7, -37.2, -35.5);
        sphere(120);
        popMatrix();
        
        pushMatrix();
        translate(-88.7, -37.2, -35.5);
        sphere(120);
        popMatrix();
        nft.endTransform();
    }
}

位置が少しずれてる気もしますがこれはtranslate()で調整できるので良しとして、構造式にカメラをかざすことでその分子の模型が表示される、というシステムができました。


参考


まとめ

以上、おまけが本編のような、相変わらず長い記事となりましたが、processingでARを扱えるようになりました。

今回はマーカーがあったら表示するというだけでしたが、モデルが動いたり、表示したモデルと何かしらインタラクションができるようにするのも面白そうだなと思っています。

最後まで読んでいただいてありがとうございました。