よこやま日記・技術/ガジェット編

@hiroyky IT系エンジニアのまったりブログ

HololensでPhotoCapture画像をワールド座標の照準オブジェをもとにトリミング

f:id:hiroyky:20170226213437j:plain

作ったもの

前回,突貫ながらHololensのQRコードリーダを開発しました.

hiroyky.hatenablog.com

今回は,QRコードリーダに正方形の照準を加えました.

f:id:hiroyky:20170226210028j:plain

前回作ったQRコードリーダはAirTapするとHololensの視野全体をキャプチャし, QRコードの検出と解析を行うものでした.

視野角全体をキャプチャするため,画像のほとんどの部分は不要です. 特に,視野角にQRコードが2つ入っていると対応できなくなっていました.

そこで,Hololensに正方形の照準を設けて,キャプチャ下画像のうち照準内のみを切り出して処理するようにしました.

仕組み

MainCamera直下に照準としてQuad(以下QrSight)を設置します. f:id:hiroyky:20170226213714p:plain

PhotoCaputureを行なったときに,QrSightの4頂点のワールド座標をキャプチャ画像の座標系に変換し,囲まれた範囲を切り出します.

ソースコード

ソースコードは前回同様このリポジトリにあります.

github.com

特に今回は Assets/Scripts/PhotoInput.csを実装しました.

ポイント

QrSightの設置

MainCameraの直下にQuadオブジェクトを設置します.枠線のみの透過pngの画像をテクスチャとして割り当てます. これを照準として利用します. f:id:hiroyky:20170226215437p:plain

  • popsition: (x, y, z = 0,0,0)
  • scale: (x, y, z = 0.2,0.2,0.2)

QrSightの4頂点の座標

AirTapで画像キャプチャされたときにQrSightの4頂点の座標を求め,座標変換を行います.

まずは,QrSightの座標(中央座標)からscaleを手掛かりに頂点をそれぞれ求めます. 今回はこのようにQuadの各頂点の位置を求めましたが,もっと良いやり方があればご教授願いです.

そして,Camera.main.WorldToScreenPointメソッドで ワールド座標系から投影座標系に変換を行います.なおこの時にRayCastした座標を使うべきか否かは悩みどころで悩んでます.

        var position = QrSight.transform.position;
        var direction = QrSight.transform.forward;
        var scale = QrSight.transform.localScale;

        // ワールド座標系でのQR照準の座標を求めます.
        var leftTop = new Vector3(
                position.x - scale.x / 2,
                position.y + scale.y / 2,
                position.z);
        var rightTop = new Vector3(
                position.x + scale.x / 2,
                position.y + scale.y / 2,
                position.z);
        var rightBottom = new Vector3(
                position.x + scale.x / 2,
                position.y - scale.y / 2,
                position.z);
        var leftBottom = new Vector3(
                position.x - scale.x / 2,
                position.y - scale.y / 2,
                position.z);

#if false
        /* Rayを使うかどうかが悩みどころ;;*/
        RaycastHit leftTopHit, rightTopHit, leftBottomHit, rightBottomHit;
        Physics.Raycast(leftTop, direction, out leftTopHit);
        Physics.Raycast(rightTop, direction, out rightTopHit);
        Physics.Raycast(leftBottom, direction, out leftBottomHit);
        Physics.Raycast(rightBottom, direction, out rightBottomHit);

        // ワールド座標系を投影座標系に変換
        var leftTopScreen = Camera.main.WorldToScreenPoint(leftTopHit.point);
        var rightTopScreen = Camera.main.WorldToScreenPoint(rightTopHit.point);
        var leftBottomScreen = Camera.main.WorldToScreenPoint(leftBottomHit.point);
        var rightBottomScreen = Camera.main.WorldToScreenPoint(rightBottomHit.point);
#else
        // ワールド座標系を投影座標系に変換
        var leftTopScreen = Camera.main.WorldToScreenPoint(leftTop);
        var rightTopScreen = Camera.main.WorldToScreenPoint(rightTop);
        var leftBottomScreen = Camera.main.WorldToScreenPoint(leftBottom);
        var rightBottomScreen = Camera.main.WorldToScreenPoint(rightBottom);
#endif

これで投影座標系に変換できたため画像上のピクセルが求まったかと思いきやそうではなくて,キャプチャ画像の座標に変換する必要がありそうでした. 次のように,各頂点の座標をいったん正規化してCameraParametersの解像度を使って変換します.

        // 投影座標系を,PhotoCaptureが撮影する画像上での座標に変換
        int leftSide = (int)(leftTopScreen.x / (float)Camera.main.pixelWidth * cameraParameters.cameraResolutionWidth);
        int rightSide = (int)(rightTopScreen.x / (float)Camera.main.pixelWidth * cameraParameters.cameraResolutionWidth);
        int bottomSide = (int)(leftBottomScreen.y / (float)Camera.main.pixelHeight * cameraParameters.cameraResolutionHeight);
        int topSide = (int)(leftTopScreen.y / (float)Camera.main.pixelHeight * cameraParameters.cameraResolutionHeight);

これで,キャプチャ画像座標系でのQrSightの頂点座標が求まりました.ただ,ズレも目立つので調整する必要ありそうです(;‘∀’)

画像の変換・トリミング

キャプチャ画像座標系でのQrSightの頂点座標をもとに,画像を抜き出します. ここはOpenCVを使えばよいと思いますが今回はゴリゴリ書いてみました.

        byte[] dst = new byte[src.Count];
        for (int y = 0; y < cameraParameters.cameraResolutionHeight; ++y) {
            for (int x = 0; x < cameraParameters.cameraResolutionWidth; ++x) {
                int px = (y * cameraParameters.cameraResolutionWidth + x) * stride;
                if (x >= leftSide && x <= rightSide && y >= bottomSide && y <= topSide) {
                    for (int i = 0; i < stride; ++i) {
                        dst[px + i] = src[px + i];
                    }
                } else {
                }
            }
        }
// *一部,ブログ記事用に本体コードに変更を加えています.

まとめ

  • 前回開発したHololens Qrコードリーダに照準を付けてみました.
  • ワールド座標系をHololensのPhotoCaptureによるキャプチャ画像の座標系に変換しました.(間違ってるかも)
  • 変換した座標をもとにキャプチャ画像をトリミングしました.

HololensでQRコードリーダを作ってみた.

作ったもの

AirTapするとQRコードを読み込んで,その内容を空間に表示するHololensアプリケーションです.


Hololens Qr code reader

仕組み

QRコードの検出とデコードにZXing.Netを利用しました.

AirTapするとHololensのカメラの画像をZXingに入力して,デコードを行います. デコードで得られたQRコードの内容のカーソルの座標に表示します. カーソルの座標を3次元で得るためにSpatialMappingはあらかじめ行っています.

(まぁ,できあいの物をつなげただけと言われればそれまでです.)

ソースコード

今回開発したものはこちらで公開しています.

github.com

ポイント

WebCamとSpatialMappingを許可

HolotoolKit-Unityをインポート後,WebCamとSpatialMappingを許可します. f:id:hiroyky:20170223003953p:plain

HololensのWebCamは2017年2月23日現在,UnityEditor上でのデバッグには非対応のようです. 実際,UnityEditor上でWebCamを使用すると開発PCに接続しているWebCamの映像が入力されました. (これはこれでデバッグとして使えるかもしれませんが.)

インポートするZXingについて

HololensはUWPアプリケーションであるため,ZXingに含まれるzxing.winmdをプラグインとしてインポートします. インポート後,WSAPlayerでのみ使用するように設定されていることを確認します. f:id:hiroyky:20170223003403p:plainf:id:hiroyky:20170223003433p:plain

UnityEditorは非対応のため,プリプロセッサで分岐します.

    public string Decode(byte[] src, int width, int height) {
#if !UNITY_EDITOR
        Debug.Log("qr decoding...");
        ZXing.IBarcodeReader reader = new ZXing.BarcodeReader();
        var res = reader.Decode(src, width, height, ZXing.BitmapFormat.BGRA32);
        if (res == null) {
            return null;
        }
        return res.Text;
#else
        return "editor debugging...";
#endif
    }

.Net Framework,.Net Core, UWP, Xamarinなど最近いろいろありますが,この辺りを知っている人は難なくできそうでうですが,自分にとってはつまづきポイントだったため,記述しておきます;;

WebCamで撮影

Hololensで見ている景色を撮影する方法は Locatable camera in Unityで解説されています.

また,CameraParameterのhologramOpacityプロパティの値を0にすることでホログラムが映らなくなるはずなので,今回は0にします.

c.hologramOpacity = 0;

該当コード: HololensQrCodeReader/PhotoInput.cs at master · hiroyky/HololensQrCodeReader · GitHub

QRコードのデコード

QRコードの検出とデコードはもっぱらZXingのライブラリ任せです. byte配列にして渡しています.

該当コード: HololensQrCodeReader/QrDecoder.cs at master · hiroyky/HololensQrCodeReader · GitHub

QRコードの内容表示

QRコードを無事デコードできたら,その内容をTextMeshで表示します。 TextMeshの表示位置はカーソルの位置です。

まとめ

突貫で簡易ながらHololensでQRコードリーダを作ってみました.

MQTTでServer(Broker)も設置してRaspberry Pi 3とLinux機を通信させてみた。

概要

先日、RaspberryPi3にGroveの温湿度センサを取り付けて温湿度をREST APIとして提供する装置を作ってみました。

hiroyky.hatenablog.com

REST APIでも良いのですが、今回はMQTTというプロトコルを使って温度・湿度を送受信してみました。 MQTTについてはMQTT as a Service sango MQTTについて詳しく知る(外部)が参考になると思います。

目標

  1. Raspberry PIをPublisherとし、温湿度を送信すする。
  2. Linux上に構築したMQTT Server(Broker)を構築する。
  3. 別のLinuxマシンが温湿度を受信する。

RaspberryPiが送信した温湿度を、MQTT Serverを経由して別のLinuxマシンが受信します。

Raspberry Piではじめるおうちハック ~ラズパイとIoTでつくる未来の住まい~

Raspberry Piではじめるおうちハック ~ラズパイとIoTでつくる未来の住まい~

やってみた

MQTT Serverのセットアップ

まっ更なUbuntuにmosquittoをインストール。各OSごとの導入方法はDownloads | Mosquittoで紹介されています。 Ubuntu16.04では標準のパッケージリストにすでに含まれているようです。最新のパッケージが欲しい場合はリポジトリを追加する必要がありそうですが、このまま行きます。

$ sudo apt-get install mosquitto
$ sudo service mosquitto status
sudo: unable to resolve host mqtt-broker
● mosquitto.service - LSB: mosquitto MQTT v3.1 message broker
   Loaded: loaded (/etc/init.d/mosquitto; bad; vendor preset: enabled)
   Active: active (running) since Mon 2017-01-02 17:39:05 UTC; 19h ago
・・・

MQTTのおためし

MQTTのSubscriverをセットアップ

こちらもまっ更なUbuntuにインストール。mosquitto_clientsをインストールします。

$ sudo apt-get install mosquitto-clients

mosquitto_subで起動します。とりあえずトピック名はsampleでいきます。

$ mosquitto_sub -d -t sample -h <mosuqittoサーバのアドレス>
Client mosqsub/2193-mqtt-test2 sending CONNECT
Client mosqsub/2193-mqtt-test2 received CONNACK
Client mosqsub/2193-mqtt-test2 sending SUBSCRIBE (Mid: 1, Topic: sample, QoS: 0)
Client mosqsub/2193-mqtt-test2 received SUBACK
Subscribed (mid: 1): 0
Client mosqsub/2193-mqtt-test2 received PUBLISH (d0, q0, r0, m0, 'sample', ... (5 bytes))

MQTTのPublisherをセットアップ

温湿度センサのRaspberryPi3にインストール。mosquitto_clientsをインストールします。

$ sudo apt-get install mosquitto-clients

テキトーなメッセージを送ってみます。

$ mosquitto_pub -d -t sample -m "Hello" -h <mosquittoサーバのアドレス>

Subscriberの方にメッセージのHelloが標準出力されました。

MQTTのPublisherをPythonで実装

pythonインターフェイスの導入

ありがたいことにmosquittoのpythonインターフェイスが用意されているので導入します。

pypi.python.org

$ sudo pip install paho-mqtt

まぁこんな感じかな。grovepiからセンサの値を取得して、そのままMQTTで送信。 コード全体はこちらのリポジトリ このスクリプトをcronあたりで定期実行すればとりあえずOKと。

#!/usr/bin/python2.7
# -*- coding:utf-8 -*-

import paho.mqtt.publish
import grovepi

def parse_args():
・・・

def sensing():
    (temperature, humidity) = grovepi.dht(8, 0)
    return (temperature, humidity)

def publish(hostname, data):
    print data
    paho.mqtt.publish.multiple([data], hostname=hostname)

if __name__ == '__main__':
    args = parse_args()
    (temperature, humidity) = sensing()
    data = {
        'topic': args.topic,
        'payload':str({'temperature':temperature, 'humidity':humidity})
    }
    publish(args.hostname, data)

参考文献

Ubuntuでnvidiaドライバをインストールしたらログインループにorz..

概要

Ubuntu 16.04 LTSにnvidiaドライバをインストールしたらGUIでのログインができなくなってしまいました。 ログイン画面が表示されてパスワードを入力してログインを試みても、一瞬画面が黒くなってまた戻ってしまいます。

似た事例

同じ現象に悩まされた人はいるようです。そして解決方法もマチマチな感じ。

askubuntu.com

askubuntu.com

may46onez.hatenablog.com

www.rokudw.net

ちなみにこちらは動画で症状と解決方法を提案しています。ただ、自分はこの方法を試しても改善しませんでした。しかしながら、コストは大きくなのでとりあえず試してみるのがいいと思います。


How to fix ubuntu login loop [ quick tutorial ]

解決方法

自分の成功した解決策は次のaskubuntuに投稿された1つ目の投稿です。

askubuntu.com

ctrl + alt + f1を押してCUIでログイン。

$ sudo ubuntu-drivers list
$ sudo ubuntu-drivers autoinstall
$ sudo reboot

これでとりえあず良くなりました。

機種

;; (なんかNAVERまとめ みたいなエントリになってしまった。。。)

CUDA C プロフェッショナル プログラミング (impress top gear)

CUDA C プロフェッショナル プログラミング (impress top gear)

CUDA高速GPUプログラミング入門

CUDA高速GPUプログラミング入門

Raspberry Pi3とGrove PiとGrove 温湿度センサで温湿度REST APIを作ってみた。

はじめに

I2C通信用コネクタ同士をケーブルで接続するだけで使えちゃうGroveセンサシリーズの温湿度センサとRaspberry Piを連携してREST APIを作ってみました。Raspberry PiにHTTPでRESTのリクエストするとセンシングしている温湿度を返してくれる仕組みです。

REST APIにすることで一般のPCやサーバからも扱いやすくなり、最近バズってるいわゆるIoTになりますね。

用意したもの

今回、用意したものは以下の通りです。

Raspberry Pi 3 MODEL B

Raspberry Pi 3 MODEL B

GROVE - デジタル温度・湿度センサ

GROVE - デジタル温度・湿度センサ

GrovePi+

GrovePi+

加えて、Raspberry Pi3の電源(USB micro type B, 5V3A出力)とmicro SDカード(16GB class10)です。 また、温湿度センサにはケーブルが属していたので別途購入する必要はありませんでした。

やってみた

前提

Raspberry PiにはRaspbianをインストールし、SSHでログインできる状態であるものとします。

GrovePi+の取り付け

接続するだけです。端のピンに合わせれば良いようです。

こんな感じ。 f:id:hiroyky:20161207003425j:plainf:id:hiroyky:20161207003432j:plain

Raspberry Piのセットアップ

www.dexterindustries.comに沿って進めていきます.

インストール

こちらのリポジトリからクローンしてきます。 github.com

($ sudo apt-get install git)
$ git clone git@github.com:DexterInd/GrovePi.git
$ cd GrovePi/Script
$ sudo sh ./install.sh

しばらく待ってると完了し、再起動します。 f:id:hiroyky:20161207203121p:plain

起動後、不要なパッケージができているかもしれないので、autoremoveしておきます。

$ sudo apt-get autoremove

GrovePi+ 接続確認

次のコマンドを打って04と表示されればOKです。

 $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- 04 -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

温湿度センサの接続

温度センサを接続します.コネクタがきちんとしているので,向きを間違えることはないです.今回はD8ポートに接続します. f:id:hiroyky:20161211214329j:plainf:id:hiroyky:20161211214333j:plain

Pythonから温湿度取得

PythonからGroveのデバイスを操作するために次のライブラリが提供されています. www.dexterindustries.com

ページの最初にある動画をみると雰囲気がつかみやすいと思います.


Write Your First GrovePi Program in Python

つまり,grovepiの提供するAPIを使用していますね.

import grovepi

github.com

ただgrovepiライブラリがインストールされていなかったためインストールします. (grovepi自体は先ほどクローンしたGrovePiのGrovePi/Software/Python/にあります.)

$ cd GrovePi/Software/Python
$ sudo python setup.py install

以下のようにして気温と湿度を取得します.今回はD8ポートにセンサを接続しているのでdht関数の引数に8を与えています.

Python 2.7.9 (default, Sep 17 2016, 20:26:04)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import grovepi
>>> grovepi.dht(8, 0)
[24.0, 37.0]

OK! (小数点一桁目まで表示されてますが測定精度はどうなんってたっけ??とりあえず今回はスルー)

Webサーバ化

温湿度が取れることは分かったので,Webサーバ化してREST APIとして取得できるようにします. 今回は,WebフレームワークにFlaskを使用します.

インストール

$ sudo pip install Flask

実装

まあ,こんな感じですね./V1/enviromentにエントリポイントを設けて,温度と湿度をJSONでレスポンスします.

import flask
import grovepi

app = flask.Flask('sensor_api')

@app.route('/V1/enviroment')
def enviroment_handler():
    (temperature, humidity) = grovepi.dht(8, 0)
    return flask.jsonify(temperature=temperature, humidity=humidity)

if __name__ == '__main__':
    app.run()`

github.com

サーバを起動してみましょう.

$ export FLASK_APP=app.py # pythonスクリプトを指定
$ flask run --host=0.0.0.0

で,curlなりWebブラウザなどからアクセス

curl http://localhost:5000/V1/enviroment
{
  "humidity": 44.0,
  "temperature": 23.0
}

OK!

まとめ

  • Raspberry PiとGrovePiを接続して,ソフトウェアのセットアップしました.
  • Grove温湿度センサを接続して値を取得しました.
  • Flaskを使ってWeb APIを作成しました.

参考文献など