2014年4月28日月曜日

OpenStreetMapでバルーン表示してみた

OpenStreetMapってのは
誰でも自由に使用する事ができ、地図情報自体も編集できる地図です。

先日作成する勉強会に出てきたのですが実際地図も作ったりしてきました。
ちょっと地図使うサービスを作り始めていたので
せっかくなのでとOSMで地図表示してみる事にしました。

leafletとの出逢い
国内のブログを何個か見て「これは苦労するな」と感じました。 使用するJSにvar記述がなくて、変数等が どのように管理されているかわからなかったからです。 jQueryライクな記述ができるようなプラグインはないかな?と 感じていて「下手すると作るか?」と思った時に「leaflef」に出逢いました。
マップ作成方法
まずHTMLに表示部分を準備します(サイズ等はお任せします)
<div id="canvas"></div>
その後JSで
        var map = L.map('canvas').setView([38.0,140.0],6)
        L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
            attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);
setView()は中心の点です。次の引数がズーム率。 この場合日本がほぼ入る位の表示ですね。 2行目は著作権表示です。 これだけで表示されます!
バルーン(maker)を表示
私のサービスはTwitterで情報を拾う予定だったので 拾ってきた情報を表示させてみよう!って事になりました。 正直地図の表示だけだと、leafletは必要なかったんですけどね。
          var marker = L.marker([lat,lon]).addTo(map);
          marker.bindPopup(content);
これだけです><。 座標を渡してあげて、マップを指定。 それにcontent(表示するHTML)を設定してあげたら、 という感じで表示されます。 本当に簡単です。

2014年4月15日火曜日

Go言語開発をVimで

Shizuoka.goという勉強会を立ち上げて、
静岡にGo言語旋風を巻き起こそうと考えていたのですが、
立ち上げた割に開発自体はあんまりしてないなぁって事で最近目下勉強中です。
当初Twitterクライアントを作っていたのですが、
必殺のOAuth周りを触って覚えようって事で自作OAuthライブラリも作りました。
※この辺りは後日

ちなみに勉強会をやるに当たって、
みなさん多いだろうって事でIntelliJIDEAを導入して開発していたのですが、
『Vimでやりたい!』という嬉しい声が上がったので
私の設定をブログで起こす事になりました。

ちなみにIntelliJを採用したのは、viのプラグインがある為で、
『LiteIDE』にviキーバインドがあればそっちに行くと思います。
QtにFakeVimというのがあって、それをコンパイルから行えば可能かは現在調査中です。
※この辺りもうまく行けば後日

基本設定
GoのSDKについているものを読み込ませます。 環境変数GOROOTはSDKを配置したパスです。 .vimrcファイルに
set runtimepath+=$GOROOT/misc/vim
と追加します。 これによって『.go』ファイルがシンタックス表示されます。 コマンドモードで
:Import net/http
とすればnet/httpのimport文を自動補完してくれます。
:Fmt
とやると『go fmt xxx.go』をやってくれます。 これがSDK内にあるのは嬉しい! ただ私は
auto BufWritePre *.go Fmt
を追加して、ファイル保存時にfmtを勝手に行うようにしています。 これ実は各IDEに欲しくて、IntelliJのプラグインでやってくれる設定を PullRequestしようかと考えているところです。
入力補完
まず環境変数GOPATHを設定します。 これはGo言語のライブラリ等を取ってきた時に配置される場所です。 それでhttp://github.com/nsf/gocodeのコードを取ってきます。
go get github.com/nsf/gocode
取得できたら、
exe "set runtimepath+=".globpath($GOPATH, "src/github.com/nsf/gocode/vim")
を追加します。 これで、『.』の後にをタイプすれば、入力候補が出てきます。 色が気持ち悪いので
hi Pmenu ctermbg=7
hi PmenuSel ctermbg=4
hi PMenuSbar ctermbg=8
を設定しています。 私は『neocomplcache』を入れて『.』で自動的に入力候補を出してくれます。 NeoBundleを入れてやってます。
他の設定
私はプレゼンで使う事も多いので端末の文字が大きめなので Goだとタブがきついので
set tabstop=4
set shiftwidth=4
set softtabstop=4
を入れて少し幅を小さくしています。 プラグインのツリー表示を前に使ってたのですが、 最近パソコンがいかれたので、 折角なのでExモードでタブを使う用にしています。 『vim』ってだけ打つとExモードで立ち上がる設定
function! s:GetBufByte()
  let byte = line2byte(line('$') + 1)
  if byte == -1
    return 0
  else
    return byte - 1
  endif
endfunction
autocmd VimEnter * nested if @% == '' && s:GetBufByte() == 0 | Ex | endif
まぁこんな感じです。 本当にGo使う為だけに設定した.vimrcはこちら
package main

import "fmt"

func main() {
    fmt.Println("%d",5)
}

2012年4月28日土曜日

Windows8 でのデータ保存

さてGoogle+に対してのOAuthアクセスは作成しましたが、
アクセストークン等のアプリケーションデータを保存しよーと思ったら、
WindowsPhone的なIsolatedStorageFileStreamがない事に気付きました。

そう言われるとデータベースとか、スマートフォン系の特殊なアクセスばっかりで
「設定ファイル」何かを作ってデータを永続化してないなぁと思ったので
設定ファイルを作ってみる事にした。

シリアライズ可能なオブジェクト
シリアライズ可能なクラスを作成します。
        private static Settings _settings;
        public static Settings Settings {
            get { return _settings; }
        }
私はAppにstaticなメンバを作成しておきました。 この中にアクセストークンのデータを残すようにしておきます。 ※アクセストークン(string)、リフレッシュトークン(string)、有効期限(現在時刻から足し込んだDateTime)
データの保存
データの保存は以下の通りで行いました。
        async static private Task<StorageFile> GetSettingFile()
        {
            var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
            StorageFile file = await folder.CreateFileAsync("settings.xml",CreationCollisionOption.OpenIfExists);
            return file;
        }

        async static public void SaveSetting()
        {
            var file = await GetSettingFile();
            var serial = new XmlSerializer(typeof(Settings));
            using ( var tWriter = new StringWriter() ) {
                serial.Serialize(tWriter, _settings);
                using (var stream = await file.OpenAsync(FileAccessMode.ReadWriteUnsafe))
                {
                    var outputStream = stream.GetOutputStreamAt(0);
                    using (var writer = new DataWriter(outputStream))
                    {
                        writer.WriteString(tWriter.ToString());
                        await writer.StoreAsync();
                    }
                }
            }
        }
「Windows.ApplicationModel.Package.Current.InstalledLocation」で インストールしたディレクトリを取得する事ができます。 他にドキュメント内とかでも保存できるのですが、 他の場所に保存する場合は、パーミッション(Package.appxmanifest)が必要になるようです。 ※また機会あったら書きます。 CreateFileAsync()を使用していますが、 同期的に取得するのでawaitで呼び出しています。 第2引数の「CreationCollisionOption.OpenIfExists」は存在しない場合作成してくれます。 ※これを付けないとsettings(2).xmlとかをモリモリ作ります。 あとはSettingsオブジェクトをシリアライズして保存しています。
データの取得
        async private void LoadSetting() 
        {
            var file = await GetSettingFile();
            using (var stream = await file.OpenAsync(FileAccessMode.Read))
            {
                var inputStream = stream.GetInputStreamAt(0);
                using (var reader = new DataReader(inputStream))
                {
                    uint size = (uint)stream.Size;
                    await reader.LoadAsync(size);っd
                    string data = reader.ReadString(size);
                    if (!data.Equals(""))
                    {
                        using (var textReader = new StringReader(data))
                        {
                            XmlSerializer serial = new XmlSerializer(typeof(Settings));
                            _settings = (Settings)serial.Deserialize(textReader);
                        }
                    }
                    else
                    {
                        _settings = new Settings();
                    }
                }
            }
        }
後は同様に読み込むだけですね。
保存したデータ
<?xml version="1.0" encoding="utf-16"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <AccessToken>
    <accessToken>ya29.AHES6ZQ0-H7cotFnLdVAcZ5CVz280mBarXy4tUn7-_Bs-AE</accessToken>
    <refreshToken>1/x8SHm9FaHD299fdZSNNyme2m3X2Up_Ki90pcUc2Y2Iw</refreshToken>
    <expiresIn>2012-04-28T21:48:28.55985+09:00</expiresIn>
  </AccessToken>
</Settings>
こんな感じで残ってました。 ※AccessTokenクラスで保存しています。 開発中は、 「C:\Users\(ユーザ名)\Documents\Visual Studio 11\Projects\GooglePlus\GooglePlus\bin\Debug\AppX」 って位置にありました。 リリースすると 「C:\Users\(ユーザ名)\AppxLayouts\(アプリのID)」 のようです。
感想
保存方法がかなりWindowsPhoneと違いました。 現状開発中で気になっているのは Suspendingのイベントに対して、保存処理を行ったんですけど、 開発中だからか、動作せずにStaticで他の画面から呼び出している状態になってしまいました。
何か間違ってるのかな?

await async知らなかったので呼び出した時のエラー原因がわからなかったです。

2012年4月26日木曜日

Windows8でのWebViewのスクレイピング

Windows8用アプリの開発がVisualStudio11(ベータ版)により可能になりました。
iPhoneが世間を席巻している状況下でも
現在のWindowsユーザがMacへの移行をはじめるのかは怪しいところで、
※開発者の移動はかなりありましたが。
Windows8への移行により、かなりのユーザさんが、
困惑するんじゃないかなぁ~と心配しているところではあります。
※特にまだXPを使っている人

私はWindowsPhone(というよりMetro)のかわいさに負けて、
少し開発しましたが、Microsoftの底力というか、
言語設計からなる雰囲気に少しMicrosoftに惚れ直したところでして、
メインマシンをWindows8端末にして、Metro開発でもやってみよーかなーと思っていまして。

以前、WindowsPhoneで少し開発したGoogle+アプリを
Windows8に移植しようと思い立ったわけです。

で最初のOAuth部分でWebViewに関して仕様が違ったみたいなので
以前書いた「認可コードのスクレイピング(WindowsPhone)」との違いを書きたいと思います。

2点の違い
試したのは「LoadCompletedイベント」の時なので 他のイベントの時にどうかはわかりません。 以前のコードの仕様としては 「認可後のページを開いたら、HTMLを取得して認可コードを取得」 っていう感じなのですが、 ・WebViewのUriが更新されない ・WebViewのSaveToString()がサポートされてない の2点が違います。
アクセスしているUriの取得
これはすごく単純でイベント自体のUriを取得すれば良いので
private void loginView_LoadCompleted(object sender, NavigationEventArgs e)
{
            string uri = e.Uri.AbsoluteUri;
}
とする事で回避できます。 回避というか、これが当たり前かな?
HTMLの取得方法
これはトリッキーというか正式にやっていいものなのか。。。 別の方法がある気がするのですが、私的にはHTMLがどうしても欲しかったので JavaScriptを呼び出すInvokeScript()を使ってHTMLを抜き出してみました。 eval()関数でouterHTMLを取得してみました。
string html = loginView.InvokeScript("eval", new string[] { "document.documentElement.outerHTML;" });
これでHTMLが抜き出せます。
最終的には
単純に2つのプロパティ(メソッド)での取得データを切り替えただけでOKでした。
        private void loginView_LoadCompleted(object sender, NavigationEventArgs e)
        {
            string uri = e.Uri.AbsoluteUri;
            if (uri.IndexOf("/o/oauth2/approval") != -1)
            {
                string html = loginView.InvokeScript("eval", new string[] { "document.documentElement.outerHTML;" });

                int tagStart = html.IndexOf("<textarea");
                int tagStartEnd = html.IndexOf(">", tagStart);

                int tagEnd = html.IndexOf("</textarea>");

                string pin = html.Substring(tagStartEnd + 1, tagEnd - tagStartEnd - 1);
                pinText.Text = pin;
            }  
        }
という感じのコードになり
って感じで認可コードも自動設定可能になりました。 あくまで見た感じだと WindowsPhoneの時とUIの部分で色々大変な事がありそうですけどね。

2012年4月8日日曜日

もうこわくない。SELinux

第7回静岡ITPro勉強会インフラ部にて
SELinux界のスーパースターSELinuxで著名な石川さん(@ishikawa84g)をお呼びして
「SELinuxハンズオン」を行いました。

部長も興奮しています。

開催日の3/31はかなりの暴風雨に見まわれ、
予定していた部長のお迎えがなくなり、私が迎えに行くことに。

SELinuxは運用まで行くとまぁ色々あるんだろうなぁと思い
泣きながらDisabledにしていました。※石川さんごめんなさい。


ハンズオンがあったお陰で、すっかりSELinuxが怖くなくなりました。
というよりSELinuxを悪者だと誤解していたようです。
資料と重複しますが、おさらいも兼ねて、ブログに残しておきます。

Fedora16を準備して行ったのですが
手元のCentOSでは結構コマンドが違いましたのでご注意を。

SELinuxの歴史
Linux Kernel2.6.x から標準オプションになっていて 実際このころから「ヒャホー2.6インスコしたぜぇー」ってやって Apache起動したら「ん?なんぞ?動かん」って感じになった記憶がある。 海外の映画とかドラマで地味に出てくる「NSA」が開発して、 最近ではSEAndroidというものも現れてきている。これもNSAらしい。 政府、金融機関等の調達基準をLinuxで満たす為に生まれたらしい。
リファレンスモニタ
システムコールが発生して、システムリソースに到達するまでに セキュリティポリシーを読み込んでそれを判定をしている仕組み。 わかりやすいので資料から拝借。
こんな感じ。 私が想像していたよりKernelに近い部分にいるんだなぁ~と感じた。
DACとMAC
DACはUNIX系OSで用いられる制御方式。 読み書き実行をオーナー、グループ、その他で制御する方式ですね。 これはご存知のように結局rootだと何でもできるわけです。 それに対してMACはセキュリティポリシーの適用してブロックする方式で、 rootだろうがポリシーに合っているかだけで判断する。 SELinuxはDACでアクセス権限を評価したのち、MACのアクセス権限を評価しています。 SELinuxを適用している場合、 rootを乗っ取られたとしてもポリシー以外の事はできなくなる。 これが一番大きいですね。 実際CVEにも載っているようなコードを 実行さえできればかなりあっさりしたコードで乗っ取りが可能なわけなので システムを乗っ取りに対して最小限の被害に抑える事ができるようになる。
SELinuxの状態
SELinuxの状態は
  • Enforcing
  • Permissive
  • Disabled
があり、Enforcingは有効、 Permissiveはログだけを出力するモード(これ重要)、Disabledは無効にします。
$ getenforce
で現在のSELinuxの状態を知る事ができます。 「/etc/selinux/config」ファイルにある 「SELINUX=」の箇所を編集して、起動時の状態を設定できます。 より詳細な情報が知りたければ「sestatus」
$ sestatus

SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
Current mode:                   permissive
Mode from config file:          enforcing
Policy version:                 26
Policy from config file:        targeted
でファイルの状態や現在の状態を知る事ができます。 状態を変更する場合は
$ setenforce 0    → Permissiveに設定
$ setenforce 1    → Enforcingに設定
で切り替えが可能になります。 Disabledにするにはファイルの編集でしかできません。
コマンドにZオプション!
これは全く知らなかった! 様々なコマンドにZオプションをつけるとそれぞれのリソースが どのようなタイプかがわかります。
$ ls -Z

-rw-r--r--. root root system_u:object_r:selinux_config_t:s0 config
・・・

$ ps -Z

system_u:system_r:httpd_t:s0    17718 ?        00:00:00 httpd
・・・
をー。 これでリソースがどのようなタイプになっているかがわかる。 ここについてくるタイプによってどのようにコントロールされるかが 決まってくるわけですね。
SELinuxが悪さをした(守ってくれた)場合
これが一番勉強になったというか、なるほどとそういう事かと思った。 「アプリが動かん!SELinuxをDisabled。動いたー」 という悪魔の囁きを無視し、行儀よくする方法を教わった。 ハンズオンではApacheを利用してファイルへのアクセスを試しました。 アプリが動かない!となって「SELinuxかな?」と感じたら まずsetenforceコマンドで「Permissive」状態に切り替えます。でアプリを動かしてみる。 で、アプリが動いたとなると 「SELinuxが止めてくれている。石川さんありがとう(´ω`)」 と東京の方向を向いてお辞儀をする。 でログを見ます。 ログは「/var/log/audit/audit.log」に出力されます。 ※auditが停止中は「/var/log/message」に出力されるそうです。 これがEnforcingの状態では実際にアクセスをストップさせ、 Permissiveモードの時はログだけを吐き出してくれる。 アクセスが停止される原因は「denied」と記述してあるので ログをgrepすると
$ /var/log/audit/audit.log | grep denied

type=AVC msg=audit(1333866553.014:218): avc:
denied  { open } for  pid=17721 comm="httpd" name="1.html" dev="dm-1" 
ino=2621442 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file
という感じのログが出てくる。 これの意味については資料にありますが 簡単に言うと「1.htmlは~httpd_t:s0からopenされたけど、~default_t:s0タイプだぜ」 と言ってくれてます。 Webで見ようとしたファイルがApacheからOpenできないと叫んでいます。 この場合は
$ restorecon -FRv パス
としてファイルコンテキストを設定しなおす事で Apacheからこのファイルを見れるようになるってわけです。
感想
SELinuxが原因かな?と感じたら
  • Permissiveで原因の切り分け
  • ログの確認
  • ポリシーに合わせる、再利用する、作成する(※)
  • 動作させる
って感じでやる。(※これのうちのどれかを行う この癖を身につける。これ大事。 SELinuxの特徴として、基本的には「ポリシー自体は一般的なもの」なので 例えばWebのディレクトリをデフォルトから移動したかったりした場合には ポリシーを再利用、ポリシーの作成を行なっていくわけです。(ハンズオンで行いました。 もっと他にも色々なコマンドを教わったのですが、 一応書いたところが基本的なところかなぁ~と思います。(詳しくは資料を見て 石川さんも言ってましたが、 これらは経験を重ねて行って慣れていくしかないようですね。 「開発はDisabled!」こう感じていたんですけど、 開発者はポリシー自体を意識するのはすごく大事で、 例えば、ポリシーにかなり逸脱したようなアプリを作ってしまうと SELinuxに長けてないような運用者では「Disabledで」と選択してしまいそうな気がします。 Windowsで開発して、Linuxにデプロイってやっていると こういう問題も知らないで開発しちゃいそうですね。 アプリの脆弱性を突かれてしまった場合の最後の砦なので SELinuxを大事にしてあげないとなぁ~と感じました。 意識して開発しなきゃと本当勉強になりました。石川さんありがとうございました。 これで明日からLet's Enforcing!

2012年3月17日土曜日

DropboxにRESTfulアクセス

少しDropboxにデータを送り込みたいとなって
エンジンをGoogleAppEngineで送り込む事になったので少し調べました。

SDKはiOS,Android(Java),Ruby,Pythonがあります。
今回はJavaを選択したので見ましたがHttp「s」URLConnectionを使用しており、
これがAppEngineでサポートしてないので実行環境でエラーが起こります。

ってことでRESTfulアクセスで行なってみました。

準備
まずここからアプリを登録してクライアントキーを作成します。
作成するとこんな感じ。
AccessTypeは後から変えられません。 「App folder」は専用のディレクトリのみアクセス可能なもの。 「Full Dropbox」はすべてのファイルアクセスです。 専用のディレクトリのみでOKだったんですけど、Fullにしました。
リクエストトークンの取得
https://api.dropbox.com/1/oauth/request_tokenにアクセスを行います。 で戻り値(line)は「oauth_token_secret=biv7oeyp3nvkb1g&oauth_token=ql0l5qpk8h10rjz」みたいな感じです。 buildOAuthHeader()はSDKのコードにもありますが、
    private static String buildOAuthHeader(boolean accessToken) {
        Map<String,String> map = new HashMap<String,String>();
        map.put("oauth_version","1.0");
        map.put("oauth_signature_method","PLAINTEXT");
        map.put("oauth_consumer_key",APP_KEY);

        String sig = null;
        if ( accessToken ) {
            map.put("oauth_token",TOKEN_KEY);
            sig = encode(APP_SECRET) + "&" + encode(TOKEN_SECRET);
        } else {
            sig = encode(APP_SECRET) + "&";
        }
        map.put("oauth_signature",sig);

        StringBuilder buf = new StringBuilder();
        buf.append("OAuth ");

        Iterator<Entry<String, String>> itr = map.entrySet().iterator();
        while ( itr.hasNext() ) {
            Entry<String, String> entry = itr.next();
            buf.append(entry.getKey() + "=" + entry.getValue() + ",");
        }
        String data = buf.toString();
        return data.substring(0,data.length()-1);
    }
といった感じでヘッダに設定してあげてます。
認可用のURL作成
lineにある「oauth_token」の値を使います。
        System.out.println("https://www.dropbox.com/1/oauth/authorize?oauth_token=" + argsMap.get("oauth_token"));
        System.in.read();
read()はブラウザからの入力待ちです。 ブラウザを立ち上げるとDropboxに認証してその後許可のアクションがあります。
このアクセスの際に「oauth_callback」を指定しておくとコールバックしてくれます。 操作後の挙動としては ・許可(あり):コールバックURLに戻る ・許可(なし):許可画面に遷移 ・拒否:Dropboxのhomeに遷移 って感じです。 ここで良くOAuthでの認可コードを取るようなコードを書いてましたが、 コールバックを指定したとしてもそれはDropboxにはありません。 ※ただしコールバックにはuidとoauth_tokenを付けてくれるのでユーザ特定は可能です。 ようは「認可」を行った時点でアクセストークンは取れる状態になるってわけです。
アクセストークンの取得
認可されていれば以下ができます。
        TOKEN_KEY    = argsMap.get("oauth_token");
        TOKEN_SECRET = argsMap.get("oauth_token_secret");
        url = new URL("https://api.dropbox.com/1/oauth/access_token");
        connection = (HttpURLConnection)url.openConnection();
        connection.setDoInput(true);
        connection.addRequestProperty("Authorization", buildOAuthHeader(true));
        connection.setRequestMethod("GET");

        inStream = connection.getInputStream();
        input = new BufferedReader(new InputStreamReader(inStream));

        line = input.readLine();
リクエストトークンで取ってきていた値を設定してbuildOAuthHeader()の値をtrueの方で動かします。 アクセストークンもリクエスト時と同じように 「oauth_token_secret=biv7oeyp3nvkb1g&oauth_token=ql0l5qpk8h10rjz」 という値で入ってきます。 これでアクセス可能になります。
ファイルのアップロード
使用するのはファイルのアップロードだけだったので PUTでbodyに設定してあげてアップします。
    public static void put(String name,byte[] data) throws Exception {

        URL url = new URL("https://api-content.dropbox.com/1/files_put/dropbox/" + name);
        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.addRequestProperty("Authorization", buildOAuthHeader(true));
        connection.setRequestMethod("PUT");

        OutputStream outputStream = connection.getOutputStream();
        outputStream.write(data);
        outputStream.close();

        InputStream inStream = connection.getInputStream();
        BufferedReader input = new BufferedReader(new InputStreamReader(inStream));
        String line = "";
        while ((line = input.readLine()) != null) {
            System.out.println(line);
        }
    }
ここでURLにある「dropbox」です。 「Full Dropbox」を選択した場合はこれでOKなのですが、 「App folder」の場合は「sandbox」を指定してあげます。 前者はフルなのでユーザのディレクトリ通りに設定していったりするのですが、 sandboxの場合、「apps/[アプリID]」が使用されて、そこのアクセスのみが可能です。 フルアクセスのViewerアプリを作らない限り、後者を選択した方が良いでしょうね。 ※ブログ書き始めてアプリを変更しました。
感想
DropboxのAPIって使うとは思ってなかったのでアクセスするとは思いませんでした。 まぁ本家が作っているクライアントが同期とかもかなり優秀なので あまりアクセスする人はいないのかもしれませんね。 ただファイルアクセスはかなり簡単にできたので、 クラウドから一時的に持ってくるツールは作りやすいかな?と感じました。 何点か気になるのは ・POSTアクセスのAPIがGETで取得できる ・PLAINTEXTを使用している ・一度認可されていると許可とかが出ない って感じですかね? ちなみに自分以外に公開するには 「My Apps」のところで「Additional users」をONにする必要があります。 製品バージョンと特に制限はないみたいに見えます。 ※開発中は5人だけっぽい感じには書いてありますけど。

2012年3月10日土曜日

Xtionで画像を作ってみる

XtionでDepthGeneratorで深度を取得できますが、
カメラ的に使うにはImageGeneratorを使用します。

今回はJavaで取得してみます。


設定値
実はここではまりました。。。
<Node type="Image" stopOnError="false" >
 <Configuration>
  <MapOutputMode xRes="640" yRes="480" FPS="30"/> 
 </Configuration>
</Node>
とConfigに記述しておきます。 画面を指定してない場合、320x240で取得できます。 なんでハマったかというと 描画する予定の画面(640x480)に対して、 320x240のデータを埋め込んでしまった為、うまく描画できないという 現象に引っかかったからです。 なので作成する画像が320x240の場合は指定しなくてもOKです。
画像データの作成
imageGen = ImageGenerator.create(context);
でインスタンスを取得。 描画処理等の永久ループの箇所に
  ImageMetaData imageMD = imageGen.getMetaData();
  ImageMap imageMap = imageMD.getData();
  ByteBuffer image = imageMap.createByteBuffer();
  while ( image.remaining() > 0 ) {
   int pos = image.position();
   byte pixel = image.get();
      imgbytes[pos] = pixel;
  }
という風にbyte[]を作成します。 imageMapにある値はwidth*height*3になります。 *3の値はRGBを表しています。 表示する際には深度の時と同じように
        DataBufferByte dataBuffer = new DataBufferByte(imgbytes, width*height*3);
        WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, width, height, width * 3, 3, new int[]{0, 1, 2}, null); 
        
        ColorModel colorModel = new ComponentColorModel(
          ColorSpace.getInstance(ColorSpace.CS_sRGB), 
          new int[]{8, 8, 8}, 
          false, 
          false, 
          ComponentColorModel.OPAQUE, 
          DataBuffer.TYPE_BYTE);

        bimg = new BufferedImage(colorModel, raster, false, null);
        g.drawImage(bimg, 0, 0, null);
となります。 (gは画面上にあるpaint用のGraphicsオブジェクトです) これでカメラのように画像を出力する事が可能です。
( ー`дー´)キリッ。。。もちろん動きますよ。
例えば
私のアプリはWebSocketと連携させているので この画像データをそのままWebSocketで送ってみようと思っています。 しかしそれはXtionとはあまり関係ないので 例えばですが1枚無人の背景データを用意してみて、 人間として認識された部分を背景データと重ねて塗りつぶす。 というプレデターごっこ(おそらくある程度マージンがあるので微妙に人の形が浮かび上がる)等が できるかなぁ。。。と思っています。 Depthとかの話とか書いてないから何か微妙な説明になるかなぁ、、、 もう少し、Generator周りとかがかっこよくなったら、記述したいと思います。