Tensorflow.jsのhandposeをA-Frameに実装してみた

最近技術メモはGithubのプライベートに書き込んでいるのでBlogには何もアウトプットできていないすぎどんです。
すこしはアウトプットしていこうと一念発起。

GoogleがMediapipeで作成したFacemeshとHandposeをTensorflow.jsのライブラリとして公開していたので、それを試したことを記事にしようと思います。

Face and hand tracking in the browser with MediaPipe and TensorFlow.js

Tensorflow.jsでMediapipeのFacemeshおよびHandposeをjsライブラリ化して公開していた。 以下の記事です。

blog.tensorflow.org

Handposeに興味があってみていると・・・

returns twenty-one 3-dimensional landmarks locating features within each hand.

どうやら3D位置情報を返すようだ。それならA-Frameに実装してハンドトラッキングできるんじゃないかと思って実装してみた。

ライブラリの場所

Tensorflow.jsのHandposeは以下のGithubにて公開されている。

github.com

Readmeのソースおよび、デモソースを参考に実装を行う。

Mediapipeとは?

HandposeはMediapipeという仕組みを使って構築されている。 その前に、Mediapipeとは何なのかを少しご紹介します。

github.com

MediaPipe is a framework for building multimodal (eg. video, audio, any time series data), cross platform (i.e Android, iOS, web, edge devices) applied ML pipelines. With MediaPipe, a perception pipeline can be built as a graph of modular components, including, for instance, inference models (e.g., TensorFlow, TFLite) and media processing functions.

(Google翻訳)↓

MediaPipeは、マルチモーダル(ビデオ、オーディオ、任意の時系列データなど)、クロスプラットフォーム(つまり、AndroidiOS、Web、エッジデバイス)を適用したMLパイプラインを構築するためのフレームワークです。 MediaPipeを使用すると、知覚パイプラインを、たとえば推論モデル(TensorFlow、TFLiteなど)やメディア処理機能など​​のモジュールコンポーネントのグラフとして構築できます。

日本語で解説しているサイトをいくつか紹介します。

webbigdata.jp

note.com

www.techceed-inc.com

Handposeに関するMediapipeのGithubは以下

github.com

以前、Andorid アプリで試しましたが、かなりの精度でした。

デモサイト

A-Frameで実装したものは、glitch.com上で公開しています。

torpid-fanatical-nail.glitch.me

起動直後は重くて固まりますが、しばらくすると動き出すと思います。

ソース説明

Tensorflow.jsのHandposeは以下のGithubにて公開されています。

github.com

Readmeのソースおよび、デモソースを参考に実装を行っています。

以下、部分的にソースコードの説明をします。

Video Streamの取得

      window.onload = function() {
        navigator.mediaDevices = navigator.mediaDevices || ((navigator.mozGetUserMedia || navigator.webkitGetUserMedia) ? {
           getUserMedia: function(c) {
             return new Promise(function(y, n) {
               (navigator.mozGetUserMedia ||
                navigator.webkitGetUserMedia).call(navigator, c, y, n);
             });
           }
        } : null);

        if (!navigator.mediaDevices) {
          console.log("getUserMedia() not supported.");
          return;
        }

        // Prefer camera resolution nearest to 1280x720.

        var constraints = { audio: true, video: { width: 1280, height: 720 } };

        navigator.mediaDevices.getUserMedia(constraints)
        .then(function(stream) {
          var video = document.querySelector('video');
          video.srcObject = stream
          video.onloadedmetadata = function(e) {
            video.play();
            main();
          };
        })
        .catch(function(err) {
          console.log(err.name + ": " + err.message);
        });

      }

Handposeの推論で使用するカメラ動画Streamを取得する処理で、一般的な実装かと思います。

onload のタイミングで、video streamを取得しています。
video.onloadedmetadata のタイミングで main() を呼んでいて、この main() が次で説明するHandposeの推論およびA-Frameへ表示する関数となります。

Handposeの推論実施

      async function main() {
        // Load the MediaPipe handpose model assets.
        const model = await handpose.load();

        // Pass in a video stream to the model to obtain 
        // a prediction from the MediaPipe graph.
        const video = document.querySelector("video");
        // let hands = await model.estimateHands(video);

        // Each hand object contains a `landmarks` property,
        // which is an array of 21 3-D landmarks.
        async function handTracking() {
          let hands = await model.estimateHands(video);
          let handEntity = document.querySelector('#hand');
          let spList = document.querySelectorAll('.sp');
          spList.forEach(sp => sp.parentNode.removeChild(sp));
          
          if (hands.length > 0) {
            hands.forEach(hand => {
              hand.landmarks.forEach(landmark => {
                let sp = document.createElement("a-sphere");
                let landmarkX = landmark[0];
                let landmarkY = landmark[1];
                let landmarkZ = landmark[2] * 2.5;
                sp.setAttribute("position", `${landmarkX} ${landmarkY} ${landmarkZ}`);
                sp.className = "sp";
                sp.setAttribute("color", "black");
                sp.setAttribute("scale", "10 10 10");
                handEntity.appendChild(sp);
              })
            });
          }
          requestAnimationFrame(handTracking);
        };
        
        handTracking();
      }

await handpose.load(); でHandposeモデルを取得しています。
取得したモデルを使って async function handTracking 内の await model.estimateHands(video); で推論実行し、手の情報を取得します。

手の情報を取得した後は、A-Frameで表示する処理となります。毎回削除追加しているので無駄に負荷をかけちゃっています。

リアルタイム更新を行うため、requestAnimationFrame(handTracking); で関数を再帰的に呼び出して手の情報は更新しています。

ポイントは、let landmarkZ = landmark[2] * 2.5; で、縦横方向の位置情報に対して奥行き方向の位置情報が弱かったので増幅をかけています。

HTML の構成

  <body>
    <video id="video" style="-webkit-transform: scaleX(-1);"></video>
    <a-scene background="color: #FAFAFA" stats>
      <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" shadow></a-box>
      <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E" shadow></a-sphere>
      <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D" shadow></a-cylinder>
      <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow></a-plane>
      <a-entity id="hand" position="5 5 -3" rotation="0 0 180" scale="0.01 0.01 0.01"></a-entity>
    </a-scene>
  </body>

videoタグにカメラ画像を出力しています。その際、style="-webkit-transform: scaleX(-1);で左右反転させています。左右反転はawait model.estimateHands(video);の引数でも実現できるようです。

id="hand" の a-entityがあります。async function handTracking() で取得した手の情報からa-sphereを作成していましたが、それはこのa-entityの子要素として配置されます。

手の調整として、async function handTracking() 内では手の形状調整を行い、<a-entity id="hand"~では、手そのものの位置、回転、サイズを調整しているイメージです。

まとめ

簡単ですがTensorflow.jsのライブラリとして公開された、MediapipeのHandposeをA-Frameへの実装を説明しました。こんなにも簡単にハンドトラッキングができるとは思いませんでした。

Mediapipeは、ほかにも様々なことができるようです。昨日公開されたBlogでは、3D Object Detectionができるようです。これもTensorflow.jsでライブラリ公開されたら試してみたいですね。

ai.googleblog.com

XR + AIは今後も目が離せないです。

水耕栽培始めました

水耕栽培始めました!

先日、水耕栽培をされている人の話を聞く機会があり、そんなに簡単ならやってみるかと思って始めてみました。

 

芽が出た!

いきなりですが、いままさに芽が出て双葉がすくすく成長しているところです。

植物を育てるのなんて小学校のアサガオ以来、こんなに感動するとは思ってなかった。

植えたのは冬でも育つらしい小松菜。

大きくなって、バター醤油でおいしくなるんだよ〜。

f:id:sgi-don:20191108224017p:plain

芽が出た!

と、いきなりすっ飛ばしましたが、ここまでの経緯を少しメモしていこうと思います。

 

本を読んで見る

水耕栽培の話を聞いたのはいいけど、最初はどこから初めていいやら。

すなおに相談してみると「この本いいよ」と言われたのがこの本

 

100円グッズで水耕菜園-伊藤-龍三

 

amazonで買おうと思ったけど、メルカリでみたら激安だった。

 

栽培の過程

栽培の方法は大きく以下のようになるようだ

 

 1. 種まき

 2. 発芽

 3. 水耕トレイへ移す

 4. 水(液肥)を切らさないよう、温度に注意して育てる

 5. 収穫

 

思ったより簡単そうだ。土を使わないので汚れないし、

 

装備を揃える

お勧めの本通り、100均ですべて揃えてみた。

 

買ったもの

 

 ・バーミキュライト

 ・水耕トレイ

 ・お茶パック

 ・小松菜の種

 ・ほうれん草の種

 

最低限、これだけでも良さそう

 

バーミキュライトは、栽培クッション。土みたいに使うけど土じゃない。"ひる石"という鉱石を高温で焼いて10倍以上に膨らませた材質で、とても軽くて通気性・保水性に優れているだと。

 

水耕トレイは、トレイとザルが一緒になったもの。水耕なのでトレイに水が溜まって、その上のザルで栽培します。あまり大きいと持ち運びが大変出し、そこそこの大きさにしてみた。

 

お茶パックにバーミキュライトを入れて、そこに種を撒きます。最初はスポンジを考えていたんですが、どうせバーミキュライトに植え替えるので最初からでもいいんじゃ?とおもって、バーミキュライト直で種を撒いています。

 

小松菜は、11月初旬まで種植えOK。ほうれん草は11月中旬までOKなので買ってみた。(福岡の場合)。11月に入って急に寒くなったし、種植えは何回できるかな・・・

 

他には

 

 ・霧吹き

 ・プラスチックコップ

 ・プラスチックスプーン

 ・アルミホイル

 

霧吹きは発芽するまでの間に水分を切らさないよう一日1回使っています。

 

プラスチックコップは、苗が育ってきたら安定のためにバーミキュライトお茶パックを植えかえる予定です。

 

プラスチックスプーンはバーミキュライトを扱うように買ってみました。おかげで手が汚れない。

 

アルミホイルは、藻がつかないように根元を覆うらしい。

 

種植直後はこんな感じです。

f:id:sgi-don:20191109011928p:plain

種植直後

家にあったもの

ちょうど家ゴミに卵パックがあったのでピンときた。これを発芽までの苗床にしようかと。

 

f:id:sgi-don:20191109011821p:plain

 

ちょうどいい感じ。

お茶パック、卵パックどちらも水でヒタヒタになるくらいたっぷり水を与えて放置。

 

芽が出た!

室内の窓際に3〜4日放置してたら、芽が出た!

f:id:sgi-don:20191109012154p:plain

芽が出た(お茶パック)

f:id:sgi-don:20191109012126p:plain

芽が出た(卵パック)

卵パック側はとても順調。お茶パック側は根っこが植向きに伸びていたりとあれって感じ。パックの壁が高すぎ(バーミキュライトが少なすぎ)なので光が届いていないのからかな?あとで間引こう。

 

という感じで、水耕栽培を知って、装備を整えて、双葉が出るまでのお話を書いてみました。

これから水耕トレイにうつして、液肥を作って、育てていきます。

 

Sony Xperia Z Ultraのバッテリー交換

またバッテリー交換?

先日 スマートウォッチの Moto360 1st バッテリー交換をしました。

バッテリーも2~3日ほど持つようになり毎日つけています。

「それ、Apple Watch?」と聞かれることも多くなりました。スマートウォッチに対する一般認知度は5年前と変わっていないようです。Googleさん頑張ってほしいです。

sgi-don.hatenablog.com

 

同じようなことは続くもので、今回は Sony Xperia Z Ultra のバッテリー交換をします。

というのも、子供に使わせていたのですが、気が付くと背面がこんな感じに膨らんでて、やばい状態になっていました。

こんなことは初めてだったのですが、バッテリーって使い方や充電頻度などでこのように膨らんでしまうようですね。このままだと発火や爆発の可能性が高いのでなんとか対応したいと思います。

f:id:sgi-don:20191026113335p:plain

バッテリー膨張

Sony Xperia Z Ultra ってなに?

「片手におさまる 大画面 世界最薄、防水対応 約6.4インチフルHDタブレット

というキャッチコピーだったようです。2013年ごろに発売されたAndroid端末です。画面サイズが6.4インチと、とにかくでかいやつです。

f:id:sgi-don:20191026112638p:plain

ZUltra と iPhone5Sの比較

一時期はこれをメイン端末として電話としても使っていました。タブレット端末で電話している感じになりますので、かなり異端です。当時はやっていたiPhone5Sがこのサイズなので、、、これで電話していたときの周りからの目線が痛かったですw

 

スペック的にもSnapdragon800だったり、防水対応だったりと何かと重宝するやつなので子供の調べもの、ゲーム端末として活用していました。あと、SIMフリー化しているので、格安SIM刺してカーナビ替わりにもしていました。

 

バッテリー交換作業

 

今回使った交換バッテリーはamazon で購入しました。

https://www.amazon.co.jp/gp/product/B0773C38K2

 

作業自体は、この動画がわかりやすかった。

www.youtube.com

 

Z Ultraは 背面にもガラスをつかっているようで、雑に取り外すと割れてしまうようです。慎重に、、、慎重に、、、

f:id:sgi-don:20191026133623p:plain

Zultra あっさり開腹

無事に開腹。動画に沿って作業すれば10分ほどであっさり作業完了です。

まとめ

作業手順として動画があるのがありがたかったです。

開腹に多少コツがいりますが、それさえこなしてしまえばあとはそんなに難しくなかったです。

 

moto360 1st もそうですが、4~5年前のデバイスがバッテリー交換するだけで現役で使えてしまうということは、ここ最近ハードウェア的な進化が停滞し始めていることなのかと感じています。最近スマートフォンの発表でワクワクしませんし。

それでは、スマートフォンの次に来るデバイスは?

nrealやMagicLeapOneなどのスカウター型デバイスが早く来ないかなと切に願っています。

Pixelbook Go を個人輸入代行で購入してみた 注文編

2019/10/15の夜中、madebygoogle で発表されましたね。

 

Google Pixelbook Go!!

 

8th m3 CPU/ 8GB / 64GB SSD / 13.3インチ 1080pディスプレイ / 1.06kg

これで649ドルって、Chromebookファンの僕としてはたまらないデバイスです。

eMMC ではなく SSD ってところが一番の決め手です。

 

直前までHP Chromebook X360 14を買おうと思っていたので、すっごい悩みました。

HP Chromebook X360 14もすっごい魅力的なデバイスで、国内販売しているので

購入後のサポートとかを考えると、HPやASUSで購入するのもありだなぁと。。。

 

ちょっと逸れますが、Chromebookは過去2台保有していましたが、どちらも

購入後にサポートのお世話になっています。運が悪かっただけかな?

 

ASUS C101pa

 → 初期不良: 右側のスピーカーが鳴らない。購入店にて交換。

ASUS C302ca

 → 画面の一部が黄ばんでしまう。1年保証内だったので無償修理

 

ノートPCなんて自分で直しようがないので、1年間保証ってのはとても大きいです。

 

個人輸入代行を利用してみる

んで、Pixelbook Go に戻ります。

 

発表された直後は、まだHP Chromebook X360 14を買う気だったので、

 

 「へぇ~、Googleさんすごいの出したなぁ。これもいいなぁ」

 

的な、なぜか余裕な気持ちだったのですが、1日2日たつにつれて、

 

 「お、これいいぞ」

 「求めてたのはこれじゃね?」

 「これ買わないと!」

 

と変わってきた。気づくの遅すぎますねw

 

Pixelbook Goは日本発売未定。US、Canada、UKのみの販売で直輸入不可

ということですので、さてどうやって買ったものかと調べてみましたら、

今の自分にピッタリの記事が。素敵です。

 

www.naenote.net

 

個人輸入代行が一番よさそう。

Google Store で Pixel2 を購入されている記事があったので同じように購入してみます。

www.naenote.net

 

 

Google Store

個人輸入代行は https://jp.edfa3ly.com/ を利用します。

edfa3ly は購入する商品ページのURLを張り付け、商品の詳しい情報を

入力することで購入するようです。ということでGoogle StoreのURLを

取得します。

f:id:sgi-don:20191018160752p:plain

Google Store - Pixelbook Go

日本のサイトでは売っていないので、国設定をアメリカにします。

https://store.google.com/ にアクセスし、一番下の日本国旗マークを選択して、

United States に変更します。

f:id:sgi-don:20191018161258p:plain

国設定を変更

 

 

さっそく注文

注文方法は割愛します。上記でPixel2を購入されていた記事を参考にしました。

注文画面は以下の通り。国際送料次第ですが、思ってたより安く済みそう。

 

f:id:sgi-don:20191018163131p:plain

注文画面

ポンポン進んで、支払いまで完了。

 

f:id:sgi-don:20191018164330p:plain

注文完了

あとは待つだけですね。

 

f:id:sgi-don:20191018164624p:plain

Pixelbook go Ship date

Google Store 自体の出荷が10月28日予定なので、それ以降になりますね。

11月上旬に届けばうれしいな。

 

経過をBlogにアップします。

 

-----

2019/10/19 追伸

 

注文がキャンセルされてしまいました。

 

f:id:sgi-don:20191019210549p:plain

Pixelbook GOの注文がキャンセルされてしまった

Google が注文数量を制限しているようで購入できなかったようです。

そうかー。残念だけど仕方がないっすね。

 

エドファリーさん、ご対応ありがとうございました。

 

moto360 を復活させてみた

moto360とは、Google Android WearOSを搭載したスマートウォッチです。

 

www.motorola.co.jp

 

スマートウォッチは様々ありますが、この無骨なデザイン!好きです。

 

発売されたのが2014年ということでバッテリーがヘタッてて3時間くらい

しか持たない状態で放置していました。

それ以外は特に問題なく使えていたのでもったいないぁと思いつつ。

 

んで、バッテリーって交換できないかなと思ってたら、すでにされている方が!

 

focus-position.com

 

む、かなり難易度高そうだが、やらない手はない。

バッテリーはebayのここで買いました。

 

www.ebay.com

 

手順は上記ブログに貼ってある動画がわかりやすかった。

 


Moto 360 v1 Battery Replacement (the easy way)

 

動画を見ながら、バラバラにしてみる。

 

f:id:sgi-don:20191014013752j:plain

moto360をバラバラにしてみる。

 

交換するバッテリー比較

×が書いてある方がもともと入っていたバッテリーです。

型番は、、、WX30 SNN5951Aってやつかな。同一で安心しました。

 

f:id:sgi-don:20191014013842j:plain

バッテリーの違い

ばらした逆の手順でくみ上げていって完成です。

 

f:id:sgi-don:20191014014147j:plain

完成

おぉ、この感じ、久しぶり。

 

今ではOSはAndroid Wear → WearOSに変わり、v2.0まで進んでいます。

moto360は昔のv1.6のままで追随できていませんが、時計として、

また、スマホの通知を受けるくらいであればまだまだ使えます。

 

しばらくはまたスマートウォッチのある生活を過ごせそうです。

 

Looking Glassに触ってきました。

福岡のARコンテンツ作成勉強会(#AR_Fukuoka) にて開催された、「はじめようLooking Glass」に参加してきました。

eventon.jp

Looking Glassって?

こういうものです。

lookingglassfactory.com

HMDやステレオカメラを使わず、裸眼で3D表現できるディスプレイです。さわってみましたが、ちゃんと3Dしてて、「そこにある」感じがすごい。3Dアバター嫁を召喚したいって思う人の気持ちがわかるような気がするw
実際に持ってわかったのですが、これ重いっす。透明部分がすべてガラスかなにかで詰まっていました。触れる前は、透明部分は水槽みたいな感じかと思っていたので「ぅ、重っ!」って言ってしまった。
コンテンツはUnityを使って作成し、PCに接続して外部ディスプレイとして利用します。接続はHDMIとUSB接続です。電源がUSBというのはお手軽ですね。

ハンズオン内容

ハンズオンの内容は講師の吉永さんがslideshareで公開されています。
初学者向けのハンズオンのため、Unityの使い方から始まって、Looking Glassにコンテンツを表示して、LeapMotionでインタラクションできるところまでの内容です。それをわかりやすく、ノンコーディングで実現しています。いつもながらすごい資料です。

www.slideshare.net

とりあえず表示してみる。

Leap Motionを使ったインタラクション

まとめ

Google Map(3D)を表示して、LeapMotionで操作(移動、Zoom)できたら面白いんじゃないかと思った。調べてみよう。

Webでスマホカメラを使おうとしてハマった話

機械学習のデータ収集のため、スマホで画像を取得&増幅してサーバへせっせと送るWebアプリを作っていたのですが、久しぶりに動かそうとすると

Failed to execute 'createObjectURL' on 'URL'

とかいって動かない。

なんだそりゃ?何も変えてないぞ。ソースは以下を参考にして組んだもの。

developer.mozilla.org

いろいろ調べてみると、原因は「Google Chrome」だった。

stackoverflow.com

Google Chromeでは、window.URL.createObjectURLは廃止予定だと。

実装方法

Chrome

video.srcObject = stream;

Chrome以外

video.src = window.URL.createObjectURL(stream);

Webアプリを作っていると、こういうことにしょっちゅうぶつかるなぁ。