狠狠撸
Submit Search
第19回厂翱滨尝水曜セミナー:搁骋叠-顿カメラで础搁空间に入り込もう
?
2 likes
?
857 views
T
Takashi Yoshinaga
Follow
Kinectで取得したPoint Cloudを使ったARコンテンツ作成ハンズオンの資料
Read less
Read more
1 of 55
More Related Content
第19回厂翱滨尝水曜セミナー:搁骋叠-顿カメラで础搁空间に入り込もう
1.
第19回SOIL水曜セミナー RGB-DカメラでAR空間に入り込もう
2.
自己紹介 氏名:吉永崇(Takashi Yoshinaga) 所属:九州先端科学技術研究所(ISIT) 専門:AR/VRを用いた情報可視化と各種計測 コミュニティ:ARコンテンツ作成勉強会 主催
3.
ARコンテンツ作成勉強会の紹介 ? 2013年5月に勉強会をスタート ? ARコンテンツの作り方をハンズオン形式で学ぶ ?
人数は5~10名程度の少人数で実施 ? 参加条件はAR/VRに興味がある人(知識不要) ? 各地で開催 (福岡、熊本、宮崎、長崎、大分、 鹿児島、山口、広島、関東)
4.
Twitterと勉強会ページで情報を発信しています #AR_Fukuoka Googleで「AR勉強会」で検索
5.
SRP Open Innovation
Lab主催 SOIL水曜セミナー 今回のイベントについて 登録よろしく! https://soil.connpass.com/
6.
セットアップ ? Kinect for
Windows SDK v1.8 https://www.microsoft.com/en-us/download/details.aspx?id=40278 ? Processing 2.2.1 https://processing.org/download/ Processingを起動後、下記を解凍したフォルダを Processingのlibrariesフォルダに配置 (Windowsの場合:C:?Users?YOU?Documents?Processing? libraries) ? SimpleOpenNI 1.96 https://code.google.com/archive/p/simple-openni/downloads ? NyARToolKit (nyar4psg.zip) https://github.com/nyatla/NyARToolkit-for-Processing/releases 下記を解凍してデスクトップ等のわかりやすいところに配置 https://drive.google.com/file/d/1ahzAzp_s5gqg5WbfJkF5JSv5v87qm KZv/view?usp=sharing
7.
本日作成するコンテンツ Kinectで取得した点群データ(Point Cloud)をリアルタイムにAR表示 https://youtu.be/WeorvV9vZh8
8.
Kinectとは ? Kinect(キネクト)とはMicrosoft社のゲーム機、XBOX360 専用のジェスチャー入力コントローラとして発売された機器 ? カラー画像?深度(Depth)情報?身体の認識等が可能 深度センサ(赤外線プロジェクタ+赤外線カメラ) カラーカメラ
9.
Kinectを用いたコンテンツ開発 ↑ カラー画像+距離画像+3D計測 人体認識 → とC#
or C++を使えば開発可能 結構敷居が高い を使うと比較的簡単に開発可能
10.
processingとは ? processingとは、電子アートとビジュアルデザインのための プログラミング言語であり、統合開発環境 ? 視覚的な表示を簡単に行えるため初心者のプログラミング 学習にも向いている。文法はJavaとほぼ同じ。
11.
ARToolKit、NyARToolKit ? ARが一般に認知されるきっかけとなったライブラリ ? カメラと正方形のマーカーを使用してCGを重畳 ?
もともとはC/C++向けに開発 ? processing対応の派生版ライブラリNyARToolKit 画像の取得 ? 二値化等の画像処理 ? マーカの検出 位置?姿勢計算 CGを重畳 (引用元: http://www.youtube.com/watch?v=TqGAqAFlGg0) (D.Wagner et al. Computer Vision Winter Workshop, 2007)
12.
準備 KinectとPCを接続しておきましょう
13.
processingを起動 実行 テキストエディタ ここにソースコードを記述 保存 【起動】 ? processing のアイコンを クリック 【保存】 ? 保存ボタンを クリック ?
Sample1と して保存
14.
processingでウィンドウを表示させよう //setup関数内で初期設定 void setup(){ size(256,256); //ウィンドウ内部のサイズ background(200,200,200);
//ウィンドウ内部の色 } デフォルトでは色をRGBで表現(それぞれ0~255)
15.
基本図形を描画しよう void setup(){ /*省略(前ページ参照)*/ } //描画のための関数 void draw(){ //枠線の色 stroke(255,0,0); //枠線の太さ(pixel) strokeWeight(10); //塗りつぶしの色 fill(0,255,255); //四角形(rectangle)を描画 //rect(x座標,y座標,横幅,縦幅) rect(10,10,50,50); } (x,y)=(10,10) 枠線の色:(255,0,0)=赤 枠線の太さ:10px 塗り色: (0,255,255)=シアン
16.
他の図形も描いてみよう void draw(){ //枠線の色 stroke(255,0,0); //枠線の太さ(pixel) strokeWeight(10); //塗りつぶしの色 fill(0,255,255); //四角形(rectangle)を描画 //rect(x座標,y座標,横幅,縦幅) rect(10,10,50,50); } 塗りつぶした円を描画 自由な多角形(三角形)を描画
17.
頂点を指定して図形を描画 【三角形】 strokeWeight(2); //枠線の幅 beginShape(TRIANGLES); vertex(70, 10); vertex(70,
60); vertex(150, 60); endShape(); (70,10) (70,60) (150,60) 【点(塗りつぶし円)】 strokeWeight(60); //点の幅(=円の直径) beginShape(POINTS); vertex(35,100); //点の中心位置(x,y) endShape(); 60 60 (35,100) 詳細 https://processing.org/reference/beginShape_.html
18.
次のプロジェクトの作成 保存新規作成 【新規作成】 ? 新規ボタンをクリック ?
Sample1の内容を コピー&ペースト ? draw関数内を消す 【保存】 ? 保存ボタンをクリック ? Sample2として保存
19.
void draw(){ stroke(255,0,0); //赤で塗りつぶし strokeWeight(30);
//直径をセット beginShape(POINTS); for(int x=0; x<5; x+=1){ vertex(40*x, 0); //円 } endShape(); } for文を用いた繰り返し処理 (1/2) x=0 x=1 x=2 x=3 x=4 40 (=40*1) 80 (=40*2)
20.
for文を用いた繰り返し処理 (2/2) void draw(){ stroke(255,0,0);
//赤で塗りつぶし strokeWeight(30); //直径をセット beginShape(POINTS); for(int y=0; y<5; y+=1){ for(int x=0; x<5; x+=1){ vertex(40*x, 0); //円 } } endShape(); } vertex(40*x, 40*y); y → 0 y → 1 y → 2 y → 3 y → 4
21.
描画位置をずらそう void draw(){ translate(20,20); //全体をx,y方向に20ずつ移動 stroke(255,0,0); strokeWeight(30); beginShape(POINTS); for(int
y=0; y<5; y+=1){ for(int x=0; x<5; x+=1){ vertex(40*x, 40*y); } } endShape(); }
22.
void draw(){ translate(20,20); //全体をx,y方向に20ずつ移動 stroke(255,0,0); strokeWeight(30); beginShape(POINTS); for(int
y=0; y<5; y+=1){ for(int x=0; x<5; x+=1){ vertex(40*x, 40*y); } } endShape(); } 1個おきに描画しよう for(int y=0; y<5; y+=1){ for(int x=0; x<5; x+=2){ vertex(40*x, 40*y); } } for(int y=0; y<5; y+=2){ for(int x=0; x<5; x+=2){ vertex(40*x, 40*y); } }
23.
次のプロジェクトの作成 保存新規作成 【新規作成】 ? 新規ボタンをクリック ?
Sample2の内容を コピー&ペースト ? setup関数とdraw関数 の内側のコードを消す 【保存】 ? 保存ボタンをクリック ? Sample3として保存
24.
Kinectを起動させよう import SimpleOpenNI.*; //SimpleOpenNIを読み込む SimpleOpenNI
kinect; //Kinectを扱うための変数 void setup(){ //OpenNIおよびKinectを初期化 kinect = new SimpleOpenNI(this); if(kinect.isInit() == false){ exit(); return; } }
25.
確認 赤外線の照射が始まる場合も エラーが出なければOK
26.
NyARToolKitを使う準備 事前にDLしたSampleフォルダ内のcamera_para.dat, patt.sample1をSample3フォルダにコピー&ペースト 【NyARToolKit】 【Sample3】
27.
ARToolKitで画像を表示する準備 import SimpleOpenNI.*; import jp.nyatla.nyar4psg.*;
//ライブラリのインポート SimpleOpenNI kinect; MultiMarker ar; //ARToolKitの機能を使うための変数 void setup(){ size(640,480,P3D); /*OpenNIおよびKinectを初期化(省略)*/ ar = new MultiMarker( this, width, height, "camera_para.dat" ,NyAR4PsgConfig.CONFIG_PSG_PV221); } 640×480の領域に 3Dの描画を行う カメラの画角に関する情報画像サイズ
28.
ARToolKitでカメラ画像を表示する void draw(){ //Kinectの取得情報を更新 kinect.update(); //カラー画像を取得 PImage rgbImage
= kinect.rgbImage(); //ARToolKit提供の機能で画像を描画する ar.drawBackground(rgbImage); } void setup(){ /*OpenNIおよびKinectを初期化(省略)*/ ar = new MultiMarker(this, width, /*以下省略*/); kinect.enableRGB(); //カラー画像の取得をON }
29.
ARToolKitでマーカーを認識しよう (1/2) void setup(){ /*OpenNIおよびKinectを初期化(省略)*/ ar
= new MultiMarker(this, width, /*以下省略*/); //マーカーのデータを読み込む(サイズは80mm) ar.addARMarker("patt.sample1", 80); kinect.enableRGB(); } 【ARToolKitマーカーの基礎知識】 ? 正方形の枠と内側のパターン ? 回転対称パターンはNG ? ARToolKit(本家)をダウンロード すれば数種類のマーカが利用可能 ? マーカーデータの自作も可能
30.
ARToolKitでマーカーを認識しよう (2/2) void draw(){ kinect.update(); PImage
rgbImage = kinect.rgbImage(); ar.drawBackground(rgbImage); } ar.detect(rgbImage); //マーカーを検出 if(!ar.isExistMarker(0)){ return; //マーカーが存在しなければ戻る } ar.beginTransform(0); //マーカ0を原点にして描画 fill(0,0,255); box(40); //縦横高さが40mmの立方体 ar.endTransform();
31.
動作確認 マーカーに乗って いる感じがしない
32.
CGの位置を調整しよう ar.beginTransform(0); translate(0,0,20); fill(0,0,255); box(40); ar.endTransform(); ?boxを上に半分ずらす x Y Z (0,0,0) (0,0,20) 40mm
33.
距離(Depth)画像を取得しよう void setup(){ /*省略(Kinectの初期設定など)*/ kinect.enableRGB(); kinect.enableDepth(); //距離画像の利用をON } void
draw(){ kinect.update(); //PImage rgbImage = kinect.rgbImage(); PImage rgbImage = kinect.depthImage(); ar.drawBackground(rgbImage); /*以下省略*/ }
34.
動作確認 次はこれを3次元化する Kinectからの距離に 応じた濃淡
35.
その前に??? void setup(){ /*省略(Kinectの初期設定など)*/ kinect.enableRGB(); kinect.enableDepth(); //距離画像の利用をON } void
draw(){ kinect.update(); PImage rgbImage = kinect.rgbImage(); //PImage rgbImage = kinect.depthImage(); ar.drawBackground(rgbImage); /*以下省略*/ } 表示やマーカー認識に使用する画像をカラーに戻す このコードに戻す 削除
36.
距離画像を3次元情報に変換 void draw(){ kinect.update(); PImage rgbImage
= kinect.rgbImage(); //縦横(pixel)とDepthの組をx,y,z座標(mm)に変換 //PVectorの一次元配列に全データを記録 PVector[] realWorld = kinect.depthMapRealWorld(); /*以下省略*/ pixelと濃淡 変換 実空間
37.
距離画像とrealWorldとの対応付け P00 (x,y,z) P01 (x,y,z) P02 (x,y,z) P10 (x,y,z) P11 (x,y,z) P12 (x,y,z) P20 (x,y,z) P21 (x,y,z) P23 (x,y,z) 【ポイント】 ? 3次元座標は一次 元配列realWorld に格納されている ? 任意のu,vピクセル のx,y,zを知るには 計算が必要 P00
P01 P02 P10 P11 P12 P20 P21 P22 ??? 0 1 2 v 0 1 2 u realWorld [0] [1] [2] [3] [4] [5] [6] [7] [8] ??? v = 0 段目 v = 1 段目 v = 2 段目 ピクセルu,vでの3次元座標 → realWorld[v*画像の横幅 + u] 【距離画像】
38.
3Dデータを点で描画しよう (1/2) void draw(){ /*Kinectの画像取得や変数の宣言(省略)*/ /*マーカーの認識
(省略)*/ ar.beginTransform(0); // translate(0,0,20); fill(0,0,255); box(40); ar.endTransform(); } beginShape(POINTS); //ここで描画する点の3次元座標を指定 (次頁) endShape(); translate(0,0,20); fill(0,0,255); box(40);
39.
3Dデータを描画しよう (2/2) beginShape(POINTS); endShape(); //v:縦, u:横、それぞれ4pixelおきに3次元座標を取得 for(int
v=0; v<kinect.depthHeight(); v+=4){ for(int u=0; u<kinect.depthWidth(); u+=4){ int index = u + v * kinect.depthWidth(); PVector point = realWorld[index]; stroke(255,255,0); //描画色(黄色) if(point.z>0) { vertex(point.x, point.y, point.z); } } } X Y Z 黒画素(z=0)は 描画したくない
40.
動作確認 何も表示されない? 後ろに離れてマーカー を撮影すると黄色い点
41.
現状を把握しよう Kinectから数100mm先に計測対象がいる Kinectで計測した点を配置 (原点はマーカー) 【現プログラムのの問題点】 マーカーから大きく離れた 位置の点描をカメラ(Kinect)に 向けると画像面から近すぎる 点描の位置をマーカー原点 周辺にずらす必要がある 描画
42.
3Dデータを原点付近に移動させよう translate(0,0,-600); //600mm後ろに戻す (場合による) beginShape(POINTS); for(int
v=0; v<kinect.depthHeight(); v+=4){ for(int u=0; u<kinect.depthWidth(); u+=4){ int index = u + v * kinect.depthWidth(); PVector point = realWorld[index]; stroke(255,255,0); //描画色 if(point.z>0) { vertex(point.x, point.y, point.z); } } } endShape(); X Y Z
43.
動作確認 点の密度が少し高くなる マーカーの辺りに実寸大の自分が描画される(らしい)
44.
3Dデータを手乗りサイズにしよう scale(0.3); //サイズを3/10にする translate(0,0,-600); beginShape(POINTS); for(int v=0;
v<kinect.depthHeight(); v+=4){ for(int u=0; u<kinect.depthWidth(); u+=4){ int index = u + v * kinect.depthWidth(); PVector point = realWorld[index]; // stroke(255,255,0); //描画色 if(point.z>0) { vertex(point.x, point.y, point.z); } } }
45.
描画する点の大きさを整える strokeWeight(2); //描画サイズを2pixelにする scale(0.3); translate(0,0,-600); beginShape(POINTS); for(int v=0;
v<kinect.depthHeight(); v+=4){ /*以下描画の処理(省略)*/
46.
マーカーの上に立たせよう strokeWeight(2); scale(0.3); //x軸(1,0,0)を中心に90度(=π/2)回転 rotate(PI/2, 1, 0,
0); translate(0,0,-600); beginShape(POINTS); 回転
47.
背景のデータを除去:描画範囲の限定 beginShape(POINTS); for(int v=0; v<kinect.depthHeight();
v+=4){ for(int u=0; u<kinect.depthWidth(); u+=4){ int index = u + v * kinect.depthWidth(); PVector point = realWorld[index]; // stroke(255,255,0); if(point.z>0) { vertex(point.x, point.y, point.z); } } } endShape(); if(point.z>0 && point.z<1500) { vertex(point.x, point.y, point.z); } 除去 1500mm未満なら描画
48.
描画位置を上にずらす 移動 strokeWeight(2); scale(0.3); rotate(PI/2, 1, 0,
0); translate(0,0,-600); //600mm後ろに戻す (場合による) beginShape(POINTS); for(int v=0; v<kinect.depthHeight(); v+=4){ /*以下描画の処理(省略)*/ translate(0,300,-600); //300mm上に移動 (場合による)
49.
点描にカメラ画像の色を割り当てよう beginShape(POINTS); for(int v=0; v<kinect.depthHeight();
v+=4){ for(int u=0; u<kinect.depthWidth(); u+=4){ int index = u + v * kinect.depthWidth(); PVector point = realWorld[index]; // color pixelColor = rgbImage.pixels[index]; stroke(255,255,0); if(point.z>0) { vertex(point.x, point.y, point.z); } } } endShape(); stroke(pixelColor); //各ピクセルの色を指定
50.
動作確認 ちょっとズレる
51.
ずれが生じる理由:Kinect利用の注意点 Color Depth ? カラー用のカメラと奥行用の カメラは位置も画角も異なる →
各画像のピクセル座標 は一致しない
52.
カラー画像と距離画像のずれの補正 void setup(){ size(640, 480,
P3D); kinect = new SimpleOpenNI(this); if(kinect.isInit() == false){ exit(); return; } ar = new MultiMarker( this, width, height, "camera_para.dat"); ar.addARMarker("patt.sample1", 80); kinect.enableRGB(); kinect.enableDepth(); kinect.alternativeViewPointDepthToImage(); } これを追加するだけ
53.
完成
54.
【参考】 ? processingリファレンス https://processing.org/reference/ ? SimpleOpneNI公式サイト https://code.google.com/p/simple-openni/ ?
NyARToolKit公式サイト http://nyatla.jp/nyartoolkit/wp/ ? ARToolKit公式サイト(最新) http://artoolkit.org/