More Related Content
はじめようARCore:自己位置推定・平面検出・FaceTracking OpenCV/ARCore/Unityで作る塗り絵AR Immersal を活用した AR クラウドなシステム開発とハンズオン! 第4回UE4勉強会 in 大阪 UE4でのチーム製作 AI x WebAR: MediaPipeのハンドトラッキングを使ってみよう 待望のUE4新機能 ナイアガラでプログラマブルVFX UE4の攻略方法を伝授! より効率よく楽しく学ぶ ための鉄則について UE4のシーケンサーをもっともっと使いこなそう!最新情報・Tipsをご紹介! What's hot (20)
Unreal Engine 5 早期アクセスの注目機能総おさらい Part 2 UE4 コリジョン検証 -HitとOverlapイベントが発生する条件について- SceneCapture2Dを使って壁の向こうを見る -気になるあの娘の部屋の壁- HoloLens2とMeta QuestではじめるWebXR UE4.14.0 Forward Shadingのエンジン改造でセルシェードやってみた 大規模タイトルにおけるエフェクトマテリアル運用 (SQEX大阪: 林武尊様) #UE4DD Unity道場京都スペシャル トゥーンシェーディングとノンフォトリアリスティック風絵づくり入門_ Unityネットワーク通信の基盤である「RPC」について、意外と知られていないボトルネックと、その対策法 NiagaraでサクッとMorphEffectを作ろう Unreal Engine 5 早期アクセスの注目機能総おさらい Part 1 【Unity道場 2月】シェーダを書けるプログラマになろう A-Frameで始めるWebXRとハンドトラッキング (HoloLens2/Oculus Quest対応) UE4を使った映像制作 (UE4 Character Art Dive Online) Similar to OpenCVでつくろうARスタンプアプリ in 熊本 (20)
OpenCVとARCoreで作るスタンプAR in 宮崎 ARコンテンツ作成勉強会 in 宮崎: はじめようARCore AI x OpenCV x WebAR: Selfie Segmentationを使ってみよう エンジニアカフェ1周年イベント:WebAR/VR開発入門 はじめようARCore: Motion Tracking & Image Tracking編 第19回SOIL水曜セミナー:RGB-DカメラでAR空間に入り込もう HTMLを書くだけで誰でも簡単!A-FrameではじめるWeb AR/VR A-Frameで始めるOculus Quest対応WebVR AI x WebAR! MediaPipeの顔認識を使ってみよう! More from Takashi Yoshinaga (20)
【準備編】OculusQuest/HoloLens2対応WebXR開発 ARコンテンツ作成勉強会( #AR_Fukuoka )紹介 iPad LiDARでエンジニアカフェを3Dスキャン 【準備編!】HoloLens 2/Oculus Quest対応WebXRハンズオン Holo-SDKハンズオン:はじめようヘッドトラッキングを用いた3D表現 FUKUOKA Engineers Day 2021 発表資料:AR Fukuoka & HoloBox紹介 Voxon Photonics VX1で遊んでみた AI x WebXR: フェイストラッキングを用いた擬似3D表現を解説! iPad LiDARで作ってみた in AR Fukuoka 忘年会2020 MRTKで始めるAR開発 (HoloLens 1 and 2, ARCore, ARkit) OSC2020 Fukuoka: インストールいらず、WebAR入門 Oculus Quest 1&2 開発のはじめの一歩 with A-Frame WebVR AI x WebAR MediaPipeの顔認識を使ってみよう! in 織りなすラボ Spatial Copy & Paste @HoloLensゆるっとLT会 ノンプログラミングで始めるAR (HoloLens 2 / ARCore / ARKit) 開発 with MRTK OpenCVでつくろうARスタンプアプリ in 熊本
- 10. OpenCV Plus Unity
画像処理ライブラリの定番としてお馴染みのOpenCVのUnity版
C#版のOpenCVSharpをベースにUnityに対応させたアセット
Windows/Mac/Android/iOSに対応。しかも無料!
- 101. 準備:まずは画面全体をキャプチャしてみる
//UIが張り付けられたCanvas
public GameObject canvas;
//プレビュー領域
public RawImage preview;
//キャプチャ領域を保持
UnityEngine.Rect capRect;
//キャプチャ画像を保持
Texture2D capTexture;
void Start () {
int w = Screen.width;
int h = Screen.height;
//原点(0,0)から画面の縦横の長さまでをキャプチャ領域とする
capRect = new UnityEngine.Rect(0, 0, w, h);
//画面サイズの空画像を作成
capTexture =
new Texture2D(w, h, TextureFormat.RGBA32, false);
//capTextureをプレビュー領域に貼り付け
preview.material.mainTexture = capTexture;
}
width
height
(0,0)
- 102. 画像処理用の関数
void Start () {
/*省略:前ページで記述した初期化*/
}
IEnumerator ImageProcessing()
{
canvas.SetActive(false);//Canvas上のUIを一時的に消す
yield return new WaitForEndOfFrame();//フレーム終了を待つ
capTexture.ReadPixels(capRect, 0, 0);//キャプチャ開始
capTexture.Apply();//各画素の色をテクスチャに反映
canvas.SetActive(true);//Canvas上のUIを再表示
}
public void StartCV()
{
StartCoroutine(ImageProcessing());//コルーチンの実行
}
ここに記述
- 116. 二値化
void CreateImages()
{
capTexture.ReadPixels(capRect, 0, 0);
capTexture.Apply();
//Texure2DをMatに変換
bgraMat = OpenCvSharp.Unity.TextureToMat(capTexture);
//カラー画像をグレースケール(濃淡)画像に変換
binMat = bgraMat.CvtColor(ColorConversionCodes.BGRA2GRAY);
//大津の方法で二値化。結果を白黒反転
binMat = binMat.Threshold(100, 255, ThresholdTypes.Otsu);
//あとで色を変えられるようにカラー(BGR)に変換
bgraMat = binMat.CvtColor(ColorConversionCodes.GRAY2BGRA);
}
bgraMat binMat(GrayScale) binMat (Binarized) bgraMat (B=G=R)
- 122. 黒画素に色を付ける (1/2)
void SetColor(Texture2D texture)
{
//Matが初期化されていない場合は何もしない
if (bgraMat == null || binMat == null) { return; }
unsafe
{
//各Matのピクセル情報の配列(ポインタ)を取得
byte* bgraPtr = bgraMat.DataPointer;
byte* binPtr = binMat.DataPointer;
//全ピクセル数を算出
int pixelCount = binMat.Width * binMat.Height;
//各ピクセルを参照して黒画素なら色を塗る
for (int i = 0; i < pixelCount; i++)
{
}
}
OpenCvSharp.Unity.MatToTexture(bgraMat, texture);
}
後ほど処理を記述
- 124. この次の作業
void SetColor(Texture2D texture)
{
//Matが初期化されていない場合は何もしない
if (bgraMat == null || binMat == null) { return; }
unsafe
{
//各Matのピクセル情報の配列(ポインタ)を取得
byte* bgraPtr = bgraMat.DataPointer;
byte* binPtr = binMat.DataPointer;
//全ピクセル数を算出
int pixelCount = binMat.Width * binMat.Height;
//各ピクセルを参照して黒画素なら色を塗る
for (int i = 0; i < pixelCount; i++)
{
}
}
OpenCvSharp.Unity.MatToTexture(bgraMat, texture);
}
これから処理を記述
- 125. 黒画素に色を付ける (2/2)
byte* bgraPtr = bgraMat.DataPointer;
byte* binPtr = binMat.DataPointer;
int pixelCount = binMat.Width * binMat.Height;
for (int i = 0; i < pixelCount; i++)
{
//白黒画像のi番目に相当するBGRAのデータの位置
int bgraPos = i * 4;
//白かったら無視(あとで透過させる)
if (binPtr[i] == 255)
{
}
//黒かったら色を塗る
else
{
bgraPtr[bgraPos] = 255; //B
bgraPtr[bgraPos+1] = 0; //G
bgraPtr[bgraPos+2] = 0; //R
}
}
- 127. 色の配列を作成
//14色の色情報
byte[,] colors = { { 255, 255, 255 },{ 18, 0, 230 },
{ 0, 152, 243 }, { 0, 241, 255 }, { 31, 195, 143 },
{ 68, 153, 0 }, { 150, 158, 0 }, { 233, 160, 0 },
{ 183, 104, 0 }, { 136, 32, 29 }, { 131, 7, 146 },
{ 127, 0, 228 }, { 79, 0, 229 }, { 0, 0, 0 } };
//何番目の色かを表す変数 (colNo=0~13)
int colorNo = 0;
void Start()
{
int w = Screen.width;
int h = Screen.height;
/*資料のスペースの都合により省略*/
}
①color.txtからコピペ
②自分で書く
- 128. 黒画素の色の切り替え(1/3)
void SetColor(Texture2D texture)
{
//Matが初期化されていない場合は何もしない
if (bgraMat == null || binMat == null) { return; }
unsafe
{
//各Matのピクセル情報の配列(ポインタ)を取得
byte* bgraPtr = bgraMat.DataPointer;
byte* binPtr = binMat.DataPointer;
//全ピクセル数を算出
int pixelCount = binMat.Width * binMat.Height;
//各ピクセルをチェックして黒かったら色を付ける
for (int i = 0; i < pixelCount; i++)
{
}
}
OpenCvSharp.Unity.MatToTexture(bgraMat, texture);
}
ここで色を指定 (次のページで解説)
- 129. 黒画素の色の切り替え(2/3)
for (int i = 0; i < pixelCount; i++)
{
//白黒画像のi番目に相当するBGRAのデータの位置
int bgraPos = i * 4;
//白かったら無視(あとで透過にする)
if (binPtr[i] == 255)
{
}
//黒かったら色を付ける
else
{
bgraPtr[bgraPos] = colors[colorNo, 0]; //B
bgraPtr[bgraPos + 1] = colors[colorNo, 1]; //G
bgraPtr[bgraPos + 2] = colors[colorNo, 2]; //R
}
}
- 130. 黒画素の色の切り替え(3/3)
public void ChangeColor()
{
colorNo++;
colorNo %= 14; //またはcolors.Length / 3;
SetColor(capTexture);
}
//色を変更する関数
void SetColor(Texture2D texture)
{
//Matが初期化されていない場合は何もしない
if (bgraMat == null || binMat == null) { return; }
unsafe
{
//各Matのピクセル情報の配列(ポインタ)を取得
byte* bgraPtr = bgraMat.DataPointer;
byte* binPtr = binMat.DataPointer;
/*資料のスペースの都合により以下省略*/
- 138. 白画素を透過させる(1/2)
void SetColor(Texture2D texture)
{
//Matが初期化されていない場合は何もしない
if (bgraMat == null || binMat == null) { return; }
unsafe
{
//各Matのピクセル情報の配列(ポインタ)を取得
byte* bgraPtr = bgraMat.DataPointer;
byte* binPtr = binMat.DataPointer;
//全ピクセル数を算出
int pixelCount = binMat.Width * binMat.Height;
//各ピクセルに対して透過/非透過の処理を行う
for (int i = 0; i < pixelCount; i++)
{
}
}
OpenCvSharp.Unity.MatToTexture(bgraMat, texture);
}
ここで色を指定 (次のページで解説)
- 139. 白画素を透過させる(2/2)
for (int i = 0; i < pixelCount; i++)
{
//白黒画像のi番目に相当するBGRAのデータの位置
int bgraPos = i * 4;
//白かったら透過
if (binPtr[i] == 255)
{
bgraPtr[bgraPos + 3] = 0;
}
//黒かったら非透過
else
{
bgraPtr[bgraPos] = colors[colorNo, 0]; //B
bgraPtr[bgraPos + 1] = colors[colorNo, 1]; //G
bgraPtr[bgraPos + 2] = colors[colorNo, 2]; //R
bgraPtr[bgraPos + 3] = 255;
}
}
- 152. スタンプの実サイズを計算
//カメラの情報を取得
Camera cam = Camera.main;
//画面左下のxy座標を3次元に変換(0.6m手前に置くとする)
Vector3 v1 =
cam.ViewportToWorldPoint(new Vector3(0, 0, 0.6f));
//画面右上のxy座標を3次元に変換
Vector3 v2 =
cam.ViewportToWorldPoint(new Vector3(1, 1, 0.6f));
//画面左上のxy座標を3次元に変換
Vector3 v3 =
cam.ViewportToWorldPoint(new Vector3(0, 1, 0.6f));
//キャプチャ領域の実空間でのサイズを計算
float w = Vector3.Distance(v2, v3);
float h = Vector3.Distance(v1, v3);
/*次のページに続く*/
(0,0)
(1,1)
(0,1)
v1
v2
v3
- 153. 空間にスタンプを固定
/*前のページに記述したコードからの続き*/
GameObject stamp = GameObject.Instantiate(original);
//オブジェクトの生成とカメラに対する位置・向き・サイズを設定
stamp.transform.parent = cam.transform;
stamp.transform.localPosition = new Vector3(0, 0, 0.6f);
stamp.transform.localRotation = Quaternion.identity;
stamp.transform.localScale = new Vector3(w, h, 1);
//上記で作ったオブジェクトに貼るテクスチャを作成
Texture2D stampTexture =
new Texture2D(capTexture.width, capTexture.height);
//色を塗り、そのあとテクスチャとして貼り付ける
SetColor(stampTexture);
stamp.GetComponent<Renderer>().material.mainTexture
= stampTexture;
//スタンプの原点をカメラではなくワールドに変更
stamp.transform.parent = null;
preview.enabled = false;