4.2K Views
September 25, 21
スライド概要
2021/09/25に開催したハンズオン資料
可視化技術や人間計測/空間計測技術を活用した問題解決に関する研究開発。 ARコンテンツ作成勉強会(tryAR)を主催。
AI x OpenCV x WebAR SelfieSegmentationを使ってみよう
もろもろのダウンロード http://arfukuoka.lolipop.jp/selfie_segmen tation/sample.zip
自己紹介 氏名:吉永崇(Takashi Yoshinaga) 専門:ARを用いた医療支援や運動計測 Volumetric Video コミュニティ:ARコンテンツ作成勉強会 主催
ARコンテンツ作成勉強会の紹介 2013年5月に勉強会をスタート。 ARコンテンツの作り方をハンズオン形式で学ぶ 人数は5~10名程度の少人数で実施 参加条件はAR/VRに興味がある人(知識不要) 各地で開催 (福岡、熊本、宮崎、長崎、大分、 鹿児島、山口、広島、札幌、関東)
Twitterと勉強会ページで情報を発信しています @AR_Fukuoka Googleで「AR勉強会」で検索
ハッシュタグ #AR_Fukuoka
本題に入ります
本日のゴール https://youtu.be/Lj64eMZeYVE MediaPipeのSelfieSegmentationとOpenCVによる画像処理で遊ぶ
テンプレートの複製 https://glitch.com/~selfie-seg-template GET STARTED
テンプレートの複製 Remix Your Own
テンプレートの確認 index.htmlをクリックし、コードが表示されることを確認 index.html
テンプレートの確認 index.htmlをクリックし、コードが表示されることを確認 エディタ プレビュー
テンプレートの確認 index.htmlをクリックし、コードが表示されることを確認 プレビューを閉じる
ハンズオンの手順 1. テンプレートの概要を解説 2. SelfieSegmentation 3. OpenCVを使った画像処理 4. 結果の統合
ハンズオンの手順 1. テンプレートの概要を解説 2. SelfieSegmentation 3. OpenCVを使った画像処理 4. 結果の統合
テンプレートの確認 Lesson01
テンプレートの確認 ライブラリの 読み込み MediaPipeや OpenCVでの 処理を記述 (今日のメイン) 描画領域等
テンプレートの確認 描画領域等
HTMLの記述の解説
<!--Webカメラの映像を取得-->
<video id="input_video" style="position:absolute; "></video>
<!--最終結果の表示に使用-->
<canvas id="output_canvas" style="position:absolute;"></canvas>
<!--OpenCV用の画像作成や途中経過の表示に使用-->
<canvas id="opencv_canvas" style="position:absolute;"></canvas>
opencv_canvas
output_canvas
input_video
input_video
output_canvas
input_video
テンプレートの確認 ライブラリの 読み込み
ライブラリ読み込みの解説
<!--① OpenCVの読み込み (Selfie Segmentationのみの利用なら不要)-->
<script src="https://docs.opencv.org/3.4.1/opencv.js"></script>
<!--② カメラをmediapipeで簡単に利用するためのツール-->
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js"
crossorigin="anonymous"></script>
<!--③ selfie segmentationの読み込み-->
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/
selfie_segmentation.js" crossorigin="anonymous"></script>
OpenCV
Camera Utils
Selfie Segmentation
テンプレートの確認 MediaPipeや OpenCVでの 処理を記述 (今日のメイン)
テンプレートの確認
テンプレートの確認 変数宣言 初期化 描画領域/カメラ/ Segmentation 認識結果の利用
テンプレートの確認 変数宣言
テンプレートの確認 初期化 描画領域/カメラ/ Segmentation
javascriptを用いた初期化
window.onload = function() {
videoElm = document.getElementById('input_video'); //ビデオ要素の取得
canvasElm = document.getElementById('output_canvas'); //表示用のCanvasを取得
canvasCtx = canvasElm.getContext('2d’); //Canvas描画に関する情報にアクセス
//Segmentationを使用するための関連ファイルの取得と初期化
let selfieSegmentation = new SelfieSegmentation({locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/${file}`;
}});
//Segmentationで使う学習モデルを選択
selfieSegmentation.setOptions({ modelSelection: 0, });
//Segmentation結果を処理する関数を登録
selfieSegmentation.onResults(onResults);
//カメラの初期化
let camera= new Camera(videoElm, {
onFrame: async () => {
await selfieSegmentation.send({image: videoElm});
},
width: 640, height: 360
});
//カメラ動作開始
camera.start();
};
function onResults(results) {/*Segmentationの結果を利用する*/ };
javascriptを用いた初期化
window.onload = function() {
videoElm = document.getElementById('input_video'); //ビデオ要素の取得
canvasElm = document.getElementById('output_canvas'); //表示用のCanvasを取得
canvasCtx = canvasElm.getContext('2d’); //Canvas描画に関する情報にアクセス
//Segmentationを使用するための関連ファイルの取得と初期化
let selfieSegmentation = new SelfieSegmentation({locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/${file}`;
}});
//Segmentationで使う学習モデルを選択
selfieSegmentation.setOptions({ modelSelection: 0, });
//Segmentation結果を処理する関数を登録
selfieSegmentation.onResults(onResults);
//カメラの初期化
let camera= new Camera(videoElm, {
onFrame: async () => {
await selfieSegmentation.send({image: videoElm});
},
width: 640, height: 360
});
//カメラ動作開始
camera.start();
};
function onResults(results) {/*Segmentationの結果を利用する*/ };
javascriptを用いた初期化
window.onload = function() {
videoElm = document.getElementById('input_video'); //ビデオ要素の取得
canvasElm = document.getElementById('output_canvas'); //表示用のCanvasを取得
canvasCtx = canvasElm.getContext('2d’); //Canvas描画に関する情報にアクセス
//Segmentationを使用するための関連ファイルの取得と初期化
let selfieSegmentation = new SelfieSegmentation({locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/${file}`;
}});
//Segmentationで使う学習モデルを選択
selfieSegmentation.setOptions({ modelSelection: 0, });
//Segmentation結果を処理する関数を登録
selfieSegmentation.onResults(onResults);
//カメラの初期化
これの実現に使用
let
camera= new Camera(videoElm, {
onFrame: async () => {
await selfieSegmentation.send({image: videoElm});
詳細は後ほど実装
},
width: 640, height: 360
});
//カメラ動作開始
camera.start();
};
function onResults(results) {/*Segmentationの結果を利用する*/ };
javascriptを用いた初期化
window.onload = function() {
videoElm = document.getElementById('input_video'); //ビデオ要素の取得
canvasElm = document.getElementById('output_canvas'); //表示用のCanvasを取得
canvasCtx = canvasElm.getContext('2d’); //Canvas描画に関する情報にアクセス
//Segmentationを使用するための関連ファイルの取得と初期化
let selfieSegmentation = new SelfieSegmentation({locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/${file}`;
}});
//Segmentationで使う学習モデルを選択
selfieSegmentation.setOptions({ modelSelection: 0, });
//Segmentation結果を処理する関数を登録
videoElmの映像を
selfieSegmentation.onResults(onResults);
selfieSegmentationに渡す
//カメラの初期化
let camera= new Camera(videoElm, {
onFrame: async () => {
await selfieSegmentation.send({image: videoElm});
},
width: 640, height: 360
});
画像サイズはあまり大きくない
//カメラ動作開始
ように設定(for OpenCV)
camera.start();
};
function onResults(results) {/*Segmentationの結果を利用する*/ };
ハンズオンの手順 1. テンプレートの概要を解説 2. SelfieSegmentation 3. OpenCVを使った画像処理 4. 結果の統合
テンプレートの確認 認識結果の利用
結果画像の表示 //Segmentationの結果を利用する function onResults(results) { //canvasのサイズを設定 if(!initialized){ initialized=true; //canvasのサイズは入力画像の2倍 (お好きなサイズでどうぞ) canvasElm.width=results.image.width*2; canvasElm.height=results.image.height*2; } canvasCtx.save(); //描画内容をクリア canvasCtx.clearRect(0, 0, canvasElm.width, canvasElm.height); //カメラ画像(results.image)をcanvasのサイズに引き伸ばして描画 canvasCtx.drawImage(results.image, 0, 0, canvasElm.width, canvasElm.height); canvasCtx.restore(); }; Lesson02
動作確認 ①Show ②In a New Window
動作確認
Segmentation結果の表示 //Segmentationの結果を利用する function onResults(results) { //canvasのサイズを設定 if(!initialized){ initialized=true; //canvasのサイズは入力画像の2倍 (お好きなサイズでどうぞ) canvasElm.width=results.image.width*2; canvasElm.height=results.image.height*2; } imageを canvasCtx.save(); segmentationMaskに変更 //描画内容をクリア canvasCtx.clearRect(0, 0, canvasElm.width, canvasElm.height); //マスク画像をcanvasのサイズに引き伸ばして描画 canvasCtx.drawImage(results.segmentationMask, 0, 0, canvasElm.width, canvasElm.height); canvasCtx.restore(); }; Lesson03
動作確認 人物領域は色がつく 背景領域は透過
人物領域にカメラ画像を描画
//canvasのサイズを設定
if(!initialized){
initialized=true;
canvasElm.width=results.image.width*2;
canvasElm.height=results.image.height*2;
}
canvasCtx.save();
//描画内容をクリア
canvasCtx.clearRect(0, 0, canvasElm.width, canvasElm.height);
//マスク画像をcanvasのサイズに引き伸ばして描画
canvasCtx.drawImage(results.segmentationMask, 0, 0,
canvasElm.width, canvasElm.height);
//不透明な領域に書き込み許可
canvasCtx.globalCompositeOperation = 'source-in’;
//カメラ画像を描画
canvasCtx.drawImage(results.image, 0, 0,
canvasElm.width, canvasElm.height);
canvasCtx.restore();
Lesson04
動作確認 人物領域のみ書き込まれる 背景領域は透過
背景の塗りつぶし canvasCtx.save(); //描画内容をクリア canvasCtx.clearRect(0, 0, canvasElm.width, canvasElm.height); //マスク画像をcanvasのサイズに引き伸ばして描画 canvasCtx.drawImage(results.segmentationMask, 0, 0, canvasElm.width, canvasElm.height); //不透明な領域に書き込み許可 canvasCtx.globalCompositeOperation = 'source-in'; //カメラ画像を描画 canvasCtx.drawImage(results.image, 0, 0, canvasElm.width, canvasElm.height); //透明な領域に書き込み許可 canvasCtx.globalCompositeOperation = 'destination-atop'; //背景色を緑(#00FF00)に設定し、canvasと同サイズの長方形(Rectangle)を描画 canvasCtx.fillStyle = '#00FF00'; canvasCtx.fillRect(0, 0, canvasElm.width, canvasElm.height); canvasCtx.restore(); Lesson05
動作確認
塗りつぶしとカメラ画像の入れ替え canvasCtx.save(); //描画内容をクリア canvasCtx.clearRect(0, 0, canvasElm.width, canvasElm.height); //マスク画像をcanvasのサイズに引き伸ばして描画 canvasCtx.drawImage(results.segmentationMask, 0, 0, canvasElm.width, canvasElm.height); //不透明な領域に書き込み許可 canvasCtx.globalCompositeOperation = 'source-in'; //カメラ画像を描画 canvasCtx.drawImage(results.image, 0, 0, canvasElm.width, canvasElm.height); //透明な領域に書き込み許可 canvasCtx.globalCompositeOperation = 'destination-atop'; //背景色を緑(#00FF00)に設定し、canvasと同サイズの長方形(Rectangle)を描画 canvasCtx.fillStyle = '#00FF00'; canvasCtx.fillRect(0, 0, canvasElm.width, canvasElm.height); canvasCtx.restore(); Lesson06
塗りつぶしとカメラ画像の入れ替え canvasCtx.save(); //描画内容をクリア canvasCtx.clearRect(0, 0, canvasElm.width, canvasElm.height); //マスク画像をcanvasのサイズに引き伸ばして描画 canvasCtx.drawImage(results.segmentationMask, 0, 0, canvasElm.width, canvasElm.height); //不透明な領域に書き込み許可 canvasCtx.globalCompositeOperation = 'source-in'; //背景色を緑(#00FF00)に設定し、canvasと同サイズの長方形(Rectangle)を描画 canvasCtx.fillStyle = '#00FF00'; canvasCtx.fillRect(0, 0, canvasElm.width, canvasElm.height); //透明な領域に書き込み許可 canvasCtx.globalCompositeOperation = 'destination-atop'; //カメラ画像を描画 canvasCtx.drawImage(results.image, 0, 0, canvasElm.width, canvasElm.height); canvasCtx.restore(); Lesson06
動作確認
次のステップ ここに画像処理結果を描画
ハンズオンの手順 1. テンプレートの概要を解説 2. SelfieSegmentation 3. OpenCVを使った画像処理 4. 結果の統合
変数の追加
変数の追加 let videoElm ; let canvasElm; let canvasCtx; let cvCanvasElm; //OpenCVで使う画像を表示 let cvCanvasCtx; //cVCanvasElmの描画に関する情報 let initialized=false; //初期化 window.onload = function() { //ビデオ要素の取得 videoElm = document.getElementById('input_video'); //表示用のCanvasを取得 canvasElm = document.getElementById('output_canvas'); //Canvas描画に関する情報にアクセス canvasCtx = canvasElm.getContext('2d'); //opencv処理結果表示用のCanvasと描画に関する情報を取得 cvCanvasElm = document.getElementById('opencv_canvas'); cvCanvasCtx= cvCanvasElm.getContext('2d’); /*スペーつの都合により省略*/ } Lesson07
OpenCVと連携する準備 認識結果の利用
OpenCVと連携する準備 //Segmentationの結果を利用する function onResults(results) { if(!initialized){ initialized=true; //canvasのサイズは入力画像の2倍 (お好きなサイズでどうぞ) canvasElm.width=results.image.width*2; canvasElm.height=results.image.height*2; //入力画像と同じサイズのcanvasを用意 cvCanvasElm.width=results.image.width; cvCanvasElm.height=results.image.height; } /*スペースの都合により省略*/ }; Lesson08
OpenCVと連携する準備 function onResults(results) { if(!initialized){ initialized=true; //canvasのサイズは入力画像の2倍 (お好きなサイズでどうぞ) canvasElm.width=results.image.width*2; canvasElm.height=results.image.height*2; //入力画像と同じサイズのcanvasを用意 cvCanvasElm.width=results.image.width; cvCanvasElm.height=results.image.height; } //OpenCVを使った画像処理を行う関数 cvFilter(results); canvasCtx.save(); /*スペースの都合により省略*/ canvasCtx.restore(); }; //OpenCVを使った画像処理を行う関数の実装 function cvFilter(results){ }; Lesson09
画像の表示 function cvFilter(results){ //入力画像をOpenCV用のcanvasに表示 cvCanvasCtx.drawImage(results.image, 0, 0); } Lesson10
OpenCVの処理結果を表示 function cvFilter(results){ //入力画像をOpenCV用のcanvasに表示 cvCanvasCtx.drawImage(results.image, 0, 0); //cvCanvasElmでの表示画像をOpenCVに渡す(Mat形式) let src = cv.imread(cvCanvasElm); //処理結果を格納するMatを作成 let dst = new cv.Mat(); //カラー画像をグレーススケール画像に変換 cv.cvtColor(src, dst, cv.COLOR_RGB2GRAY, 0); //canvasでの表示のためカラー形式に戻す(見た目はグレーのまま) cv.cvtColor(dst, dst, cv.COLOR_GRAY2RGBA); //Matをcanvasで描画できるフォーマットに変換 let imgData = new ImageData( new Uint8ClampedArray(dst.data, dst.cols, dst.rows), dst.cols ,dst.rows); //画像を描画 cvCanvasCtx.putImageData(imgData,0,0); //Matのメモリを解放 src.delete(); dst.delete(); } Lesson11
結果 グレーに変換された画像が表示される
エッジ検出 //入力画像をOpenCV用のcanvasに表示 cvCanvasCtx.drawImage(results.image, 0, 0); //cvCanvasElmでの表示画像をOpenCVに渡す(Mat形式) let src = cv.imread(cvCanvasElm); //処理結果を格納するMatを作成 let dst = new cv.Mat(); //カラー画像をグレーススケール画像に変換 cv.cvtColor(src, dst, cv.COLOR_RGB2GRAY, 0); //Cannyフィルタを用いたエッジ検出 cv.Canny(dst, dst, 50, 90, 3, false); //canvasでの表示のためカラー形式に戻す(見た目はグレーのまま) cv.cvtColor(dst, dst, cv.COLOR_GRAY2RGBA); //Matをcanvasで描画できるフォーマットに変換 let imgData = new ImageData( new Uint8ClampedArray(dst.data, dst.cols, dst.rows), dst.cols ,dst.rows); //画像を描画 cvCanvasCtx.putImageData(imgData,0,0); //Matのメモリを解放 src.delete(); dst.delete(); Lesson12
動作確認 線画が表示される
Canny Filter cv.Canny(source, result, minVal, maxVal, 3, false); 隣接ピクセルとの濃淡差をチェックし、ある値(=maxVal)以上ならエッジとみなす。 ただし、少しでも下回ったらエッジと判定されなくなると連続性が低下するので、 隣接するピクセルがエッジと判断され、かつminVal以上なら引き続きエッジとする。 隣のピクセルとの濃淡差 255 0 https://docs.opencv.org/3.4.15/d7/de1/tutorial_js_canny.html
白黒反転 //cvCanvasElmでの表示画像をOpenCVに渡す(Mat形式) let src = cv.imread(cvCanvasElm); //処理結果を格納するMatを作成 let dst = new cv.Mat(); //カラー画像をグレーススケール画像に変換 cv.cvtColor(src, dst, cv.COLOR_RGB2GRAY, 0); //Cannyフィルタを用いたエッジ検出 cv.Canny(dst, dst, 50, 90, 3, false); //白黒反転 cv.bitwise_not(dst,dst); //canvasでの表示のためカラー形式に戻す(見た目はグレーのまま) cv.cvtColor(dst, dst, cv.COLOR_GRAY2RGBA); //Matをcanvasで描画できるフォーマットに変換 let imgData = new ImageData( new Uint8ClampedArray(dst.data, dst.cols, dst.rows), dst.cols ,dst.rows); //画像を描画 cvCanvasCtx.putImageData(imgData,0,0); //Matのメモリを解放 src.delete(); dst.delete(); Lesson13
動作確認 白黒反転した線画が表示される
ハンズオンの手順 1. テンプレートの概要を解説 2. SelfieSegmentation 3. OpenCVを使った画像処理 4. 結果の統合
画像処理結果を人物領域に描き込む
function onResults(results) {
if(!initialized){
initialized=true;
canvasElm.width=results.image.width*2;
canvasElm.height=results.image.height*2;
cvCanvasElm.width=results.image.width;
cvCanvasElm.height=results.image.height;
}
cvFilter(results);
canvasCtx.save();
canvasCtx.clearRect(0, 0, canvasElm.width, canvasElm.height);
canvasCtx.drawImage(results.segmentationMask, 0, 0,
canvasElm.width, canvasElm.height);
//不透明な領域に書き込み許可
canvasCtx.globalCompositeOperation = 'source-in';
//canvasCtx.fillStyle = '#00FF00';
//canvasCtx.fillRect(0, 0, canvasElm.width, canvasElm.height);
塗りつぶしに関する2行をコメントアウト
結果画像の表示
function onResults(results) {
if(!initialized){
initialized=true;
canvasElm.width=results.image.width*2;
canvasElm.height=results.image.height*2;
cvCanvasElm.width=results.image.width;
cvCanvasElm.height=results.image.height;
}
cvFilter(results);
canvasCtx.save();
canvasCtx.clearRect(0, 0, canvasElm.width, canvasElm.height);
canvasCtx.drawImage(results.segmentationMask, 0, 0,
canvasElm.width, canvasElm.height);
//不透明な領域に書き込み許可
canvasCtx.globalCompositeOperation = 'source-in';
//canvasCtx.fillStyle = '#00FF00’;
//canvasCtx.fillRect(0, 0, canvasElm.width, canvasElm.height);
//OpenCV用のcanvasの表示内容を書き込む
canvasCtx.drawImage(cvCanvasElm, 0, 0,
canvasElm.width, canvasElm.height);
Lesson14
動作確認 白黒反転した線画が表示される
OpenCVの領域の不可視化 <canvas id="opencv_canvas" style="position:absolute;display:none;"></canvas> Lesson15
参考 公式ページ https://google.github.io/mediapipe/solutions/selfie_s egmentation globalCompositeOperation https://developer.mozilla.org/enUS/docs/Web/API/CanvasRenderingContext2D/global CompositeOperation drawImage https://developer.mozilla.org/enUS/docs/Web/API/CanvasRenderingContext2D/drawI mage