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アプリを作っていると、こういうことにしょっちゅうぶつかるなぁ。

crouton on chromebook でポート開放方法

crouton on chromebookのポート開放で悩んだのでメモしておく。


cronton上にhttpsサーバ立てて、動作確認することが多いのですが、ポート開放のやり方がよくわからず今まではchromebookスマホをUSBケーブルで接続してポートフォワードしていた。
いちいちケーブル接続して、ポートフォワード設定してが面倒になってきたのと、正月休みが重なったので、重い腰を上げて調べてみた。

最初はubuntuなどでポート開放する要領でファイルに記載するのかと思って、/etc/sysconfig/iptablesとか探したけどファイル自体がない。なんでないかよくわからないけどserviceコマンドすらない。さてどうしようかとググってたらchromiumOSでポート開放している記載がある記事を発見

qiita.com

iptablesで直接追加できるようだ。
でもこれって、動的だから毎回入力しないと行けないんだろうなぁ・・・

追加方法

INBOUND TCP:8080を追加してみた

追加する前の一覧

chronos@localhost /etc $ sudo iptables -nL
Chain INPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:34191
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:44369
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:40089
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:39177
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:36233
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:37658
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:43905
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:45392
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     udp  --  0.0.0.0/0            224.0.0.251          udp dpt:5353
ACCEPT     udp  --  0.0.0.0/0            239.255.255.250      udp dpt:1900

Chain FORWARD (policy DROP)
target     prot opt source               destination         
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            mark match 0x1
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           

Chain OUTPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            state NEW,RELATED,ESTABLISHED
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           

追加コマンド

sudo iptables -I INPUT -p tcp --dport 8080 -j ACCEPT

追加したあとの一覧

chronos@localhost /etc $ sudo iptables -nL
Chain INPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8080
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:34191
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:44369
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:40089
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:39177
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:36233
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:37658
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:43905
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:45392
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     udp  --  0.0.0.0/0            224.0.0.251          udp dpt:5353
ACCEPT     udp  --  0.0.0.0/0            239.255.255.250      udp dpt:1900

Chain FORWARD (policy DROP)
target     prot opt source               destination         
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            mark match 0x1
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           

Chain OUTPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            state NEW,RELATED,ESTABLISHED
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           

Chain INPUT に「tcp dpt:8080」 が追加されていますね。

よしよし

8thwallweb アカウント作成方法

今年は8thWallWebを使ってWebARの作品をいくつか作ってみましたが、来年は8thWallWebのWebARハンズオンをする予定なので、初学者向けはじめの一歩的な情報をまとめとこうと思い、ブログを書いています。

8thWallWebとは?

iOS/Androidどちらでも動作するクロスプラットフォームでWebARを提供する、Javascriptライブラリです。

8th Wall - Products

上のリンクから動画および実際に動作するWebアプリがありますので、一度お試しください。

作ったもの

昔からつくりたかったのですが、WebARのマイクラ的なものを作ってみました。8thwallにもtweetしていただき、ちょっと盛り上がりました。

スマートフォンで下記URLにアクセスすると遊べます。

CraftBlocks by @sgidon: 8th Wall Web: A-FRAME

アカウント作成

8thwallwebを利用するには、8thwallの無料アカウントが必要となります。上で紹介した8thwallwebのページからアクセスできます。

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

アクセスすると以下のような画面となります。各項目へ入力していきます。
Initial Workspace Typeは "Web Developer" を選択しておきましょう。

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

"Get Started"を選択後、一番下まで移動して"I Agree"を選択します。

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

"I Agree" を押したあとは電話番号認証です。"Japan(日本)"を選択して携帯電話番号を入力します。0から始まる11桁を入力し、"Verify via SMS"を選択します。
先頭に+81をつけたり、0を外さなくても良さそうです。

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

SMSで通知された6桁の番号を入力して"Verify"を選択します。

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

おめでとうございます。これでアカウント作成されました。
コンソール画面からAPI KEYの発行などが可能となります。

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

登録したメールアドレスに、チュートリアルなどの情報が送られます。
もう一度コンソール画面へアクセスするには、以下のURLからアクセスしましょう。

8th Wall Console

以上

deeplearn.jsのplaygroundで遊んでみる。SqueezeNetのソース理解

最近、おやつをガムに変えてみたところ、1か月で4キロほど痩せてしまった。
いままでどんだけおやつ食べてたんだと反省。

deeplearn.jsの勉強にと、とりあえず簡単そうなところから入ってみる。

playground

環境設定とかで躓きたくないので、とりあえずデモのPlayGroundで遊んでみる。
ここだと、サーバとか用意せず書いたコードをそのまま動かすことができるようだ。

deeplearnjs.org

SqueezeNet

playgroupndのリンクに「SqueezeNet - basic usage」とあったので、これ元にいじくって遊んでみよう。 その前に、SqueezeNetってなんだ?

https://www.semiconportal.com/archive/contribution/applications/170418-neurochip5-2.html

うーん、わからん。
わかったのは、ネットワークモデルの一種だということと、正確性より処理応答性を重視していることくらい。 使う分にはわからなくてもよいので、とりあえず表面をサラッと理解してみる。

ソースの理解

javascriptのソースを少しづつ理解していこう。

画像情報の読み込み

const catImage = document.getElementById('cat');

HTMLのIMGタグの画像情報を取り込んでいる。右下の猫の画像がそれ。
なんか細長い猫だなと思ったら、元画像は 240x217 で、IMGタグ内で 227x227 に変更している。 なんでやねんって、IMGタグのwidth x heightを240x217に変更して[RUN]を実行したらエラーになった。

Error: The output # of columns (119.5) must be an integer. Change the stride and/or zero pad parameters

どうやらSqueezeNetに読み込ませる画像は227x227出ないといけないようだ。

SqueezeNetのロード処理

const math = new dl.NDArrayMathGPU();
// squeezenet is loaded from https://unpkg.com/deeplearn-squeezenet
const squeezeNet = new squeezenet.SqueezeNet(math);
await squeezeNet.load();

SqueezeNetの何かを読み込んでいるらしい。
いきなり「dl」と出てきているが、これはplaygroundがデフォルトでdeeplean.jsを読み込んでいるため使えている。(だと思う)。 次の「NDArrayMathGPU()」は多次元配列をGPUで処理するものという理解(であっているのか?)

以下にdeeplearn.jsのチュートリアルを日本語訳されたサイトがあった。とても参考になる。

http://tensorflow.classcat.com/2017/08/20/deeplearn-js-tutorials-introduction/

イメージデータの変換

// Load the image into an NDArray from the HTMLImageElement.
const image = dl.Array3D.fromPixels(catImage);

何か変換しているのはわかるが、なんだろうこれ?よくわからないので「console.log()」で吐き出す。

console.log(catImage);
 ↓
[object HTMLImageElement]
console.log(image)
 ↓
Tensor dtype: int32 rank: 3 shape: [227,227,3] values: [[[255, 255, 255], [255, 255, 255], [255, 255, 255], ..., [255, 255, 255], [255, 255, 255], [255, 255, 255]], [[255, 255, 255], [255, 255, 255], [255, 255, 255], ..., [255, 255, 255], [255, 255, 255], [255, 255, 255]], [[255, 255, 255], [255, 255, 255], [255, 255, 255], ..., [255, 255, 255], [255, 255, 255], [255, 255, 255]], ... [[169, 193, 180], [165, 193, 178], [161, 196, 176], ..., [40 , 39 , 35 ], [55 , 51 , 48 ], [37 , 34 , 29 ]], [[150, 188, 173], [152, 190, 175], [158, 192, 175], ..., [27 , 26 , 22 ], [33 , 29 , 26 ], [30 , 27 , 22 ]], [[157, 194, 187], [164, 197, 186], [165, 198, 181], ..., [35 , 34 , 30 ], [49 , 44 , 40 ], [38 , 30 , 27 ]]]

IMGタグの画像情報をint32の3次元配列[227,227,3]に変換しているようだ。 画像が227x227なので、3はRGB情報だろう。

推論

// Predict through SqueezeNet.
const logits = squeezeNet.predict(image);

[Predict:予測] なので、たぶん推論しているんだろうな。 logitsって何だろう。

console.log(logits)
 ↓
Tensor dtype: float32 rank: 1 shape: [1000] values: [14.1952286, 12.3713379, 13.3236885, ..., 1.7310563, 17.3527832, 17.3972549]

float32の1次元配列[1000]が入っていた。よくわからない。

結果表示

// Convert the logits to a map of class to probability of the class.
const topClassesToProbs = await squeezeNet.getTopKClasses(logits, 10);
for (const className in topClassesToProbs) {
  console.log(
      `${topClassesToProbs[className].toFixed(5)}: ${className}`);
}

推論結果を出力。
squeezeNet.getTopKClassesの前にawaitが入っている。非同期処理を想定しているってことはそれなりに重い処理なのか?
想像するに、SqueezeNetには1000の答えがあり、上で求めた「logits」には、各答えの正解率が入っているのではないか?
「squeezeNet.getTopKClasses(logits, 10)」でそのうち上位10件を切り出している。そんなところだろう。

よくわからないこと

HTML部に

<script src='https://unpkg.com/deeplearn-squeezenet'></script>

とあるが、これはなんだ?削っても問題なく動くし。
アクセスするとよくわからないJavascriptソースが下りてきた。

まとめ

一番簡単そうなソースで遊んでみましたが、DeepLearningの知識がほとんどない自分には突っかかりまくりで大変でした。このソースを使って応用する場合、画像サイズは227x227にそろえることを覚えておこう。

以上

GoogleTango + DeepLearnJS + WebARで物体認識

寒くてバイクに3か月乗っていなかったら、案の定バッテリーがあがってた。
近くのバイク屋さんまで汗だくになりながら押して持っていき、見事復活!
今年はいろいろなところに行って写真を撮りまくろうと思っています。

今回作ったのは、スマホで物体認識した結果をARで表示するWebアプリです。一度作ってたのですが、AI部分(物体認識部)をクラウドを利用していたため、レスポンスが遅いなぁと感じていました。

こんなのです。

youtu.be

この時は、GCE(n1-highmem-8[vCPU x 8、メモリ 52 GB])上にTensorFlowを配置してぶん回したのですがこのレスポンス。遅い要因としては、

  • 解析画像リサイズしていない
  • ネットワーク転送
  • GCEでGPUを使っていない。

これらを改善した版を今回作ってみました。

youtu.be

だいぶ早くなっていますね。
大きく変わったところは2点。

  • 取得した画像をリサイズして解析している。
  • 物体検知をクラウド上ではなくブラウザ上で行っている。

使っている技術

多種多様な技術を使っています。
(Web業界って使う技術が多すぎてついていけない・・・)

GoogleTango

モーショントラッキング、エリアラーニング、Depthの取得をスマホでできるという画期的なデバイス
広角レンズと赤外線カメラで前方4メーター四方の物体の位置を認識することができます。
バイスASUS ZenfoneARをつかっています。

developers.google.com

残念ながら今の時代に早すぎたようで、ARCoreに置き換わるべく2018年03月でプロジェクト終了・・・

DeeplearnJS

Googleが提供しているJavascriptで動作するDeeplearning環境。WebGL経由でGPGPUを使用しており思った以上に高速に動作します。

deeplearnjs.org

WebAR

Web上でARを実現する手段。

github.com

A-Frame

WebでVR/ARを実装する際、タグだけで実装できちゃうライブラリ。

aframe.io

加えて、WebARを簡単に実装できるA-frameコンポーネントを使用しています。

github.com

使い方

以下に公開しています。残念ながらWebAR対応機種でかつWebAR専用ブラウザでないと動作しないです。

whispering-salesman.glitch.me

真ん中のカーソルが緑となっている状態で検知可能です。ARCore/ARKitの場合はまずは床面検知しないと緑にならないかと思います。
検知可能状態(緑)で画面タッチすると物体検知を実行し、解析結果をARで表示します。
端末をシェイクすると、AR表示を全削除します。

実装説明

ソースはGithubに公開しています。

github.com

index.html

HEADER部、BODY部、SCRIPT部に分けてポイントを説明します。

HEADER

A-Frameライブラリを一部修正しています。そのままではcanvasから画像取得ができなかったためです。
以下に詳細を記載したページがあります。

mementoo.info

BODY

a-assets内のmixinはAR表示Entityのテンプレートです。
AR表示EntityはtextGroupのEntity配下に追加登録しています。
raycasterとmarkについては、上で紹介したAFrame-ARを参考にしてください。
inference-canvasはよくわかりません。物体検知の実装をDeepLearnJSのWebCam Imagenetから取得した際の名残です。多分BODY句になくても動作する。

SCRIPT

「./dist/bundle.js」を読み込みます。ここに他のJS(DeeplearnJSとか、main.js)が入っています。
raycasterについては、上で紹介したAFrame-ARを参考にしてください。
shakeについては、Shake.jsを参考にしてください。

main.js

物体検知実装はDeepLearnJSのimagenetを持ってきただけですので、そちらを参考にするのがよいかと思います。

async function ready()

各種データ準備。

window.addEventListener('click', (env) => ...

画面表示しているCanvasとは別のCanvasを用意してリサイズ処理を行っています。
squeezeNetに食わせる画像のサイズがなぜか227x227固定だったので(それ以外だとエラーとなる)ため、227x227のResizeCanvasになっています。

function detection()

  1. リサイズ後画像の物体検知
  2. 解析結果から物体名の取得
  3. AR表示

の流れです。正直物体検知のところはサンプルそのまま持ってきているのでよくわかりません。
AR表示部は、A-FrameのTextコンポーネントを使っています。それにグルグル回るアニメーションをつけたうえで、index.htmlのBODY句にあったtextGroupエンティティに追加していきます。

まとめ

「[センシング/IoT → AI解析 → AR/VR表示] on WEB on モバイル」という流れは今後主流になると勝手に思っていますので、今回この流れで実装できたのでかなりテンション上がっています。
AIについてはまだまだ勉強が必要ですが、学習済みモデルを利用するだけなら難しくないですね。DeepLearnJSはTensorFlowの学習済みモデルをインポートできるようですので、どんどん活用していこうと思います。

以上です。