狠狠撸

狠狠撸Share a Scribd company logo
第19回SOIL水曜セミナー
RGB-DカメラでAR空間に入り込もう
自己紹介
氏名:吉永崇(Takashi Yoshinaga)
所属:九州先端科学技術研究所(ISIT)
専門:AR/VRを用いた情報可視化と各種計測
コミュニティ:ARコンテンツ作成勉強会 主催
ARコンテンツ作成勉強会の紹介
? 2013年5月に勉強会をスタート
? ARコンテンツの作り方をハンズオン形式で学ぶ
? 人数は5~10名程度の少人数で実施
? 参加条件はAR/VRに興味がある人(知識不要)
? 各地で開催 (福岡、熊本、宮崎、長崎、大分、 鹿児島、山口、広島、関東)
Twitterと勉強会ページで情報を発信しています
#AR_Fukuoka Googleで「AR勉強会」で検索
SRP Open Innovation Lab主催
SOIL水曜セミナー
今回のイベントについて
登録よろしく! https://soil.connpass.com/
セットアップ
? 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
本日作成するコンテンツ
Kinectで取得した点群データ(Point Cloud)をリアルタイムにAR表示
https://youtu.be/WeorvV9vZh8
Kinectとは
? Kinect(キネクト)とはMicrosoft社のゲーム機、XBOX360
専用のジェスチャー入力コントローラとして発売された機器
? カラー画像?深度(Depth)情報?身体の認識等が可能
深度センサ(赤外線プロジェクタ+赤外線カメラ)
カラーカメラ
Kinectを用いたコンテンツ開発
↑ カラー画像+距離画像+3D計測
人体認識 →
とC# or C++を使えば開発可能
結構敷居が高い を使うと比較的簡単に開発可能
processingとは
? processingとは、電子アートとビジュアルデザインのための
プログラミング言語であり、統合開発環境
? 視覚的な表示を簡単に行えるため初心者のプログラミング
学習にも向いている。文法はJavaとほぼ同じ。
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)
準備
KinectとPCを接続しておきましょう
processingを起動
実行
テキストエディタ
ここにソースコードを記述
保存
【起動】
? processing
のアイコンを
クリック
【保存】
? 保存ボタンを
クリック
? Sample1と
して保存
processingでウィンドウを表示させよう
//setup関数内で初期設定
void setup(){
size(256,256); //ウィンドウ内部のサイズ
background(200,200,200); //ウィンドウ内部の色
}
デフォルトでは色をRGBで表現(それぞれ0~255)
基本図形を描画しよう
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)=シアン
他の図形も描いてみよう
void draw(){
//枠線の色
stroke(255,0,0);
//枠線の太さ(pixel)
strokeWeight(10);
//塗りつぶしの色
fill(0,255,255);
//四角形(rectangle)を描画
//rect(x座標,y座標,横幅,縦幅)
rect(10,10,50,50);
}
塗りつぶした円を描画
自由な多角形(三角形)を描画
頂点を指定して図形を描画
【三角形】
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
次のプロジェクトの作成
保存新規作成 【新規作成】
? 新規ボタンをクリック
? Sample1の内容を
コピー&ペースト
? draw関数内を消す
【保存】
? 保存ボタンをクリック
? Sample2として保存
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)
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
描画位置をずらそう
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();
}
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);
}
}
次のプロジェクトの作成
保存新規作成 【新規作成】
? 新規ボタンをクリック
? Sample2の内容を
コピー&ペースト
? setup関数とdraw関数
の内側のコードを消す
【保存】
? 保存ボタンをクリック
? Sample3として保存
Kinectを起動させよう
import SimpleOpenNI.*; //SimpleOpenNIを読み込む
SimpleOpenNI kinect; //Kinectを扱うための変数
void setup(){
//OpenNIおよびKinectを初期化
kinect = new SimpleOpenNI(this);
if(kinect.isInit() == false){
exit();
return;
}
}
確認
赤外線の照射が始まる場合も
エラーが出なければOK
NyARToolKitを使う準備
事前にDLしたSampleフォルダ内のcamera_para.dat,
patt.sample1をSample3フォルダにコピー&ペースト
【NyARToolKit】 【Sample3】
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の描画を行う
カメラの画角に関する情報画像サイズ
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
}
ARToolKitでマーカーを認識しよう (1/2)
void setup(){
/*OpenNIおよびKinectを初期化(省略)*/
ar = new MultiMarker(this, width, /*以下省略*/);
//マーカーのデータを読み込む(サイズは80mm)
ar.addARMarker("patt.sample1", 80);
kinect.enableRGB();
}
【ARToolKitマーカーの基礎知識】
? 正方形の枠と内側のパターン
? 回転対称パターンはNG
? ARToolKit(本家)をダウンロード
すれば数種類のマーカが利用可能
? マーカーデータの自作も可能
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();
動作確認
マーカーに乗って
いる感じがしない
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
距離(Depth)画像を取得しよう
void setup(){
/*省略(Kinectの初期設定など)*/
kinect.enableRGB();
kinect.enableDepth(); //距離画像の利用をON
}
void draw(){
kinect.update();
//PImage rgbImage = kinect.rgbImage();
PImage rgbImage = kinect.depthImage();
ar.drawBackground(rgbImage);
/*以下省略*/
}
動作確認
次はこれを3次元化する
Kinectからの距離に
応じた濃淡
その前に???
void setup(){
/*省略(Kinectの初期設定など)*/
kinect.enableRGB();
kinect.enableDepth(); //距離画像の利用をON
}
void draw(){
kinect.update();
PImage rgbImage = kinect.rgbImage();
//PImage rgbImage = kinect.depthImage();
ar.drawBackground(rgbImage);
/*以下省略*/
}
表示やマーカー認識に使用する画像をカラーに戻す
このコードに戻す
削除
距離画像を3次元情報に変換
void draw(){
kinect.update();
PImage rgbImage = kinect.rgbImage();
//縦横(pixel)とDepthの組をx,y,z座標(mm)に変換
//PVectorの一次元配列に全データを記録
PVector[] realWorld = kinect.depthMapRealWorld();
/*以下省略*/
pixelと濃淡
変換
実空間
距離画像と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]
【距離画像】
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);
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)は
描画したくない
動作確認
何も表示されない?
後ろに離れてマーカー
を撮影すると黄色い点
現状を把握しよう
Kinectから数100mm先に計測対象がいる Kinectで計測した点を配置 (原点はマーカー)
【現プログラムのの問題点】
マーカーから大きく離れた
位置の点描をカメラ(Kinect)に
向けると画像面から近すぎる
点描の位置をマーカー原点
周辺にずらす必要がある
描画
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
動作確認
点の密度が少し高くなる
マーカーの辺りに実寸大の自分が描画される(らしい)
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);
}
}
}
描画する点の大きさを整える
strokeWeight(2); //描画サイズを2pixelにする
scale(0.3);
translate(0,0,-600);
beginShape(POINTS);
for(int v=0; v<kinect.depthHeight(); v+=4){
/*以下描画の処理(省略)*/
マーカーの上に立たせよう
strokeWeight(2);
scale(0.3);
//x軸(1,0,0)を中心に90度(=π/2)回転
rotate(PI/2, 1, 0, 0);
translate(0,0,-600);
beginShape(POINTS);
回転
背景のデータを除去:描画範囲の限定
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未満なら描画
描画位置を上にずらす
移動
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上に移動 (場合による)
点描にカメラ画像の色を割り当てよう
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); //各ピクセルの色を指定
動作確認
ちょっとズレる
ずれが生じる理由:Kinect利用の注意点
Color Depth
? カラー用のカメラと奥行用の
カメラは位置も画角も異なる
→ 各画像のピクセル座標
は一致しない
カラー画像と距離画像のずれの補正
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();
}
これを追加するだけ
完成
【参考】
? processingリファレンス
https://processing.org/reference/
? SimpleOpneNI公式サイト
https://code.google.com/p/simple-openni/
? NyARToolKit公式サイト
http://nyatla.jp/nyartoolkit/wp/
? ARToolKit公式サイト(最新)
http://artoolkit.org/
第19回厂翱滨尝水曜セミナー:搁骋叠-顿カメラで础搁空间に入り込もう

More Related Content

第19回厂翱滨尝水曜セミナー:搁骋叠-顿カメラで础搁空间に入り込もう