自習室

こもります

Visual c++ の同一ソリューション内ライブラリ参照と、ライブラリプロジェクトへの静的リンクライブラリのリンクの仕方。

概要

Visual Studio はエディターとして超優秀だけど、ビルド時の「追加の依存ファイル(静的リンクライブラリなど)」の指定や、インクルードパスの指定をする「プロジェクトプロパティ」がとてもわかりにくいし、それをわかりやすくしようとした結果生まれた .props ファイルによるプロジェクトプロパティのファイル定義がまたややこしいってんで、なにかちょっと不慣れなことをしようとしたり、古いOSSを新しいVSで使おうとすると痛い目に遭いがち。

今回はそれげな話の一つとして、実務で遭遇した以下の2件についてメモを残しておきます。

  1. スタティックリンクライブラリを作るプロジェクト(PrjS1)と、それを使って動く .exe を作るプロジェクト(PrjE)、の2プロジェクトを持つVisual Studio の一つのソリューション(Sln1)において、PrjEがPrjS1.libを使うケース
  2. 単独のスタティックリンクライブラリを作るプロジェクト(Sln2/PrjS2) を用意し、PrjS1.libが PrjS2.lib を使うケース

図にまとめるとこんな感じ

f:id:AMANE:20190606222454p:plain

それそれどのように "参照" やリンクを行うのか、についてまとめておこうと思います。

前提知識

私も記憶喪失になりがちなので、大事な前提知識として、DLLやLIBってなんやねん?を詳細に説明して下さっている記事へのリンクを貼らせていただきます。

kamino.hatenablog.com

(1) ソリューション内の参照

ソリューション内のプロジェクト参照とは何か。

こちらに記載があります。

プロジェクト内の参照の管理 - Visual Studio | Microsoft Docs

Visual Studio は、プロジェクトへのパスが指定されると、アセンブリを見つけることができます。

とありますが、.lib ファイルをビルドするプロジェクトを参照した場合は、結果の .lib ファイルを見つけることが出来る、と言う意味ですね。

ソリューション内の別のプロジェクトへの参照の仕方

  • プロジェクトを右クリック → 追加 → 参照 → ソリューション内の .lib ファイルを作るプロジェクト名にチェック
    • 参照右クリック→参照の追加、でも同じ

f:id:AMANE:20190606224220p:plain

似たような項目で、プロジェクトの依存関係というのがあります。

  • ソリューションを右クリック → プロパティ → 共通プロパティ → プロジェクトの依存関係

f:id:AMANE:20190606224330p:plain

こちらは、同一ソリューション内のプロジェクト間のビルド順を定義するための設定です。試しに PrjEPrjS1 に依存するよう指定すると、ビルド順こそ最適化されますが、 PrjS1 中のコードの header を持ってきて使おうとすると、 未解決の外部シンボルが参照されました のようなエラーが出ます。これは無論、 PrjS1.lib がリンクできていないからです。

方法: プロジェクトの依存関係を作成および削除する - Visual Studio | Microsoft Docs

逆に、先にPrjEからPrjS1を参照すると、自動的に PrjEPrjS1を依存することもプロパティに追記されます。

ソリューションの構成とプラットフォームの自動反映

このようにプロジェクト参照で引いている .lib は、構成(Debug/Release)やプラットフォーム(x86/x64) をソリューション全体で変えたとしても、利用する側の PrjE に対し、適切な構成・プラットフォームで作られた PrjS1.lib を渡してくれます。

それを確認するために、以下の手順で意地悪をしてみます。

  • 一旦ソリューションをクリーン
  • Debug/x86PrjS1 をプロジェクトのみビルド
  • Debug/x86PrjE をプロジェクトのみビルド → 正しくビルドされる( PrjS1.libデバッグビルド物を引けている)
  • Release/x86PrjE を再度プロジェクトのみビルド → ビルド完了出来ない(この時点ではPrjS1.lib** のリリースビルドが存在しないため)
  • Release/x86PrjS1.lib をプロジェクトのみビルド、Debug/x86 でビルドした PrjS1.lib を削除
  • Release/x86PrjE を再度プロジェクトのみビルド → 正しくビルドされる

その間、プロジェクトプロパティ類は一切編集しませんでしたがこのように、適切な .lib ファイルを引いてくれることがわかります。

ちなみに細かい話ですが、ソリューションエクスプローラ上で PrjE / 参照 / PrjS1 を選択してプロパティウィンドウを見ると、構成が Debug の時に Release\PrjS1.lib が完全パスとして指定されていたり、その逆だったりすることがあります。これはおそらく Visual Studio の表示上のバグで、実際、Visual Studio を再起動すると、正しい .lib ファイルのパスが記載されています。

f:id:AMANE:20190606224523p:plain

(2) ソリューション外のビルド物である .lib ファイルを、自分のライブラリ(.libを出力する)プロジェクトに追加する

他所のリリース物を自分のプロジェクトに取り込んで使う場合、こちらの方が良くあると思います。特に、 PrjE(.exeを作るプロジェクト)PrjS2(.lib) をリンクする場合は、 プロジェクトプロパティ / リンカー / 追加のライブラリディレクトリ入力 / 追加の依存ファイル(.libファイル) を指定する、という「VSおきまりの」手続になります。

qiita.com

今回やりたいのは、静的リンクライブラリを出力するプロジェクトに外部の静的リンクライブラリをリンクする、ことです。基本的には上記と同じなのですが、 .exe を出力するプロジェクトと .lib を出力するプロジェクトで、若干プロジェクトプロパティの構成が異なっています。 .h ファイルのインクルードに関しては、 .exe のプロジェクトの場合と同様に、 C/C++ / 全般 / 追加のインクルードディレクトリ に使いたい .h ファイルのあるディレクトリを指定します。

ライブラリファイルの指定は、ライブラリアン / 全般 タブ中の 追加の依存ファイル追加のライブラリディレクトリ を、それぞれ構成・プラットフォーム毎に正しく設定する必要があります。これにより、構成・プラットフォームを変えても、PrjS1 はそれ毎に正しい .lib ファイルをビルドしてくれ、最終的に PrjE は正しくビルドされることになります。

f:id:AMANE:20190606231518p:plain

所感

昔ながらの静的リンクライブラリのロードの仕方として、 #pragma comment(lib, "filename.lib") 式もありますが、その他の dll ファイルの置き場などすべてプロジェクトのプロパティから指定しているので、.lib` ファイルもプロジェクトプロパティの指定でやりきりたいなぁと私は考えています。コード中に書く方がマルチプラットフォーム開発では良いことも多いかも知れませんが、それはそのときに。

[VC++] リンクするライブラリファイルをソースコード内に記述する | あみだがみねのもろもろ備忘録

これまでかなり適当にやって、何となくビルドできればおっけーって感じで通ってきてた内容を一通り整理できて良かったです。

大学の研究室で、ラボ全体で使うつもりで有料のウェブサービスを契約するのがなかなか難しい

序文

とあるラボの立ち上げ期にがっつりかかわらせていただき、ちょっとしたシステムアドミンみたいなことをずーっとやっています。

とても特殊な経験をたくさんさせてもらったので、誰の役に立つかはわからないけど気が向いたときにメモとして残しておきたいと思っています。

  • G Suite for Education はラボ単位でも申し込めて、とてもとても良いよ、って話
  • GitHub Education もラボ単位で申し込めて、とてもても良いよ、って話
  • Slack はすげー学割が利くし、最近検索が少しずつ改善してきているので、有料プラン申し込もうぜって話(今日の話と少し関わりがある)
  • esa.io教育機関で申請すると無料で使えるぞ、って話
  • 大学が包括契約している様々なソフトを見逃すな!って話(c.f. UTokyo MATLAB Campus-Wide License)

などなど。けど、良い話より難しい話の方が、結論にたどり着くまでのいろいろが「知見」って感じなので、今回はその中から一つ、どうしてもWikiを無料にしたかった話をメモしておきたいと思います。

本文

何の話?→ ラボ Wiki の話。

研究室生活と研究活動に関するノウハウを集めるナレッジベースを作ろうと、1年半あまり試行錯誤をして、結局今は esa.io にたどり着いています。そこまでは…

と、主に3ステップを経て、いや、これじゃいかんと思っていわゆる普通に使える wiki 的な物を探して (とても色んな判断があって) esa.io を使っています。使い始めてから半年ほど経っていますが、学生さん他ちょいちょい記事を足したり修正してくれていて回っている気がします。

これは話の結論でもあるのですが、無料であること と、自サバ管理をしないこと が最優先事項でした。この記事はもしかすると esa の中の人にも読まれてしまうかも知れないので若干心苦しいのですが、無料であることがとてもとてもとても大事だったのです。それ以外の純粋な「研究室ナレッジベースのサービス選択」については、ここでは割愛します。本記事の趣旨は「大学でサブスクリプションの有料プラン契約厳しぃ…」ってことです。

自サバ管理をしないこと

理系の研究室には、1人や2人たとえその研究室がそっち系では無かったとしても、ウェブサービスの構築に詳しい人が居たりします。私は社会人博士としてラボに来ていて、業務で GitLab を立てたり、プロジェクト用に Wiki を運用したりしてたので少々出来るようになってしまっていたのですが、そのときの経験から、ラボでは自サバでのサービス運用はしたくないと決めていました。理由は以下の通り

  1. 大学研究室での活動の本分は研究である
  2. 大学研究室の構成員は会社とは比べものにならないくらい高速に入れ替わる
  3. 大学研究室のハードウェアは、本当に予想外の理由で止められたり壊れたりする

もちろん一番の理由は1ですが、2と3については、ラボに来てからそれはもう思い知らされました。(この記事では割愛)

また、3だけはハードウェア的な意味でもオンプレミスを意味していますが、たとえAWSとかさくらのVPSでやろうとも、1, 2に関しては変わりません。

無料であること

私個人としては、良いもの、クリエイティビティや品質、生活の質の向上をもたらすサービスには対価を支払っていきたいと考えています。(大事)

しかし、大学の経費精算の融通の利かなさ と、人の出入の不確定さ がここに立ちはだかります。

年度末にクレカは使えない件

ウェブサービスの多くはクレジットカード払いだと思います。それ自体は問題無いです。大学は「見積・納品・請求」書3点セットで公費による「後払い」が基本なのですが、(昔はどうだったかわかりませんが少なくとも私の居る研究室では)教授名義のコーポレートカードで研究にかかわる買い物をさせていただくことがあります。

問題は、年度末にクレジットカードを使うことを極端に嫌がられると言うか、実質禁止であることです。公費のほとんどは年度毎にしめますので、「使ったのが3月だけどカードの引き落としは4月」みたいなのは許されないわけです。ウェブサービスが、利用状況で変動して毎月引き落としをするスタイルだと、これが問題になります。3月だけ支払えなくなってしまうのです。

人の出入が不確定な件

これはもしかすると弊研究室固有の問題かも知れませんが、インターンやアルバイト・客員の研究員を多数・随時受け入れているため、 いつからいつまで、ラボのメンバーが何人居る というのが確定せず常に変動します。 たとえば、いまわれわれのラボの Slack には 60余名のメンバーがいますが(強制参加のチャネルのメンバー数)、正式に所属のある人は、先生達 + いつもいる研究員+ 事務系職員 + 学生 で 40名程度のはずです。 Slack は非アクティブなユーザには課金されませんが、すべてのサービスでそうと言う訳ではありません。

大学の経費生産的には、極力「これにいくらかかる」というのが前もってわかっているのが望ましいです。

さてここで、Slackは変動するのに有料でやってるんでしょ?という件が出てきます。これについては答えは明確で、Slackくらい利用者側(我々)が価値を明確にわかっているならゴリ押す です。Slackはそれに加えて、学割が強力に効いて、案外めちゃくちゃ高くはならないことと、年払いに対応していること、が敷居を下げてくれています。

我々は、どのWikiが良いのか探索し、ラボとの相性が良いのかも模索しつつ、ソフトランディングさせていく必要がありました。ラボに合わないと感じたら途中で切り替える可能性もあったので、突然大きな支払をするのも気が引けました。

有料だと、んー、この人はもう不要かな?などと管理者が気を利かせてメンバー管理する必要が出てきます。管理者がラボの構成員全体に確実に目が行き届いているとは限りませんので、これは結構な負担となります。無料だと、とりあえず使いそうな人全員ユーザとしてぶち込んでおく、が可能なので、この辺の負担が減ります(その代わりセキュリティ面のリスクはあがるのですが…)

無料だとすべてが解決する(残念なことに!)

というわけで、非常に心苦しいのですが、無料であることは(その導入の手続や、支払の手間なども総合的に見ると)極めて重要ということになります。

Qiita Team と Notion は傍から見ていてとても良さそうでしたが、無料で導入できるサービスと比較すると、二の足を踏んでしまいました。

ここまで書いておいて改めて強調しますが、良いものにはお金を払うべきだし、大学の研究室もそれが完全に不可能なわけではありません。(大学の会計に詳しい人は、上の記事を見て2,3個抜け道を思いついたかと思います)しかし、導入と維持の楽さ を考えて、無料の物をえらんで「しまって」います。そしてこの選択の最大の問題は、 esa.io さんも別に慈善事業として通常無料でやっているわけでは無く、アカデミックプランとして特別に割引をしてくださっている状態なので、経営には少なからず影響を与えているでしょうし、いつまでこの状態が続くかも我々はわかりません。

結文

冒頭にも書きましたが、 esa.io は充分に使いやすいナレッジベースで、ラボでしっかり使われています。大変助かっています。大学の会計の融通のきかなさは「なんとも言えないけどつれぇ」って感じです。メモは以上です。

SkyWay JS SDK の TypeScript 型定義ファイルを DefinitelyTyped にコミットした話

この記事は SkyWay Advent Calendar 2018 の13日目の記事です。

経緯

f:id:AMANE:20181213093355p:plain

研究室で作っているシステムで WebRTC/SkyWay を活用させて頂いたのですが、気が向いてすべて TypeScript にしてしまいました。

開発開始当初は、その当時の DefinitelyTyped にあがっていた Toshiya Nakakura さんの型定義を使っていたのですが、あちこち細かい部分がメンテされていなかったので、トランスパイル時に結構エラーがはかれていました。SkyWay JS SDK に対する単純な型定義不足に加えて、 TypeScript 3.0 前後から WebRTC とその周辺に関する DOM の定義が徐々に TypeScript の標準に組み込まれて、 /// <reference types="webrtc" /> としている部分が多重定義になっているものもあったりしたのが原因です。

そこで、思い切って自分で型定義ファイルをつくってみて、Pull Request を出して、無事(*)マージしてもらえました

コード

リポジトリはこちらです

DefinitelyTyped/types/skyway at master · DefinitelyTyped/DefinitelyTyped · GitHub

これまでも何度か型定義ファイルをコミットしてきた(余談)のですが、勉強不足でよくわかってないことも多い気がします。が、以下の方針で書いてみました。

html 中で CDN から最新の skyway-latest.js をロードして利用する。

モジュール周りは最新の仕様に私がついて行けてなかったり、さまざまなパタンに対応しなければならないので考えないことにしました。ある意味最も「べた」な手法として皆が納得して(諦めて)くれるだろう手法にとりあえず対応しておく、という考え方です。異論は大いに認めますので、どなたか改善策を教えて下さい。。。

@types/webrtc を捨てる = TypeScript 3.0 利用とする

以前の型定義には /// <reference types="webrtc" /> と言う記述がありました。これは、かつての typescript には、 RTCほげほげ のような WebRTC 関連の型定義がほとんど無かったため、それを補うための型定義群を読み込む物でした。(参照→ DefinitelyTyped/types/webrtc at master · DefinitelyTyped/DefinitelyTyped · GitHub)これらは今(ちゃんと確かめてないけどたぶん TypeScript 3.0 あたり)では ts 公式の lib.dom.d.ts にきれいに定義されていて不要になりました。

もちろん、 SkyWay のドキュメントを読むと、あちこちに RTCほげほげ が出てきます。これが、 TypeScript の lib.dom.d.ts の記述と矛盾が無い様に書いていきます。

不安点

自分の不勉強が原因ですが、今回 namespace 等を利用していません。型定義を書いたのがちょっと前のことなので詳細な理由を失念してしまったのですが、はじめそうしようと思っていたのに上手く出来なくてやめました。

これのせいで、万が一他のライブラリとクラス名等かぶってしまうと大問題です。これについては どなたか助けてください(懇願)

手続

ここからは、本家にマージされるまでの手続について書いていきます。

制作

  • ドキュメントコードをみながら、自分の手元で skyway/index.d.ts を書く
  • 自分のプロジェクトで使い続けて、問題が無いことを日々確認し続ける (トテモダイジ)
  • DefinitelyTyped が最新になるよう pull してきて、上記 skyway/index.d.ts を突っ込む。
  • 自分の変更内容を既存の test.ts にも反映させる。
  • 僭越ながら、skyway/index.d.ts のヘッダ部に、自分に関する記名をさせて頂く

テスト

  • DefinitelyTyped プロジェクト内で npm i して、テスト等に必要なパッケージを取得
  • npm test (このコマンドを叩きなさい、というのは DefinitelyTyped リポジトリの READMEには書いて無くて、後で出てくる PR のコメントにはしろ、と書いてある)
  • あらためて npm test
  • npm run lint skyway して、テストコードをチェック。(これは、DefinitelyTyped の README 中に説明がある)
    • ERROR: 31:44 no-angle-bracket-type-assertion Type assertion using the '<>' syntax is forbidden. Use the 'as' syntax instead. と言うのが出るので修正。

Pull Request

  • コードを自分のリモートにプッシュ。
  • Pull Request してみる
  • コメント欄がテンプレートで自動生成されるので,チェックボックスを埋めたりしてPR→参照
  • Travis CI が走ったりして問題ことを確認(基本は、上でやったテストと同じことをしている模様)
  • メンテナーのチェック待ち状態になる

Merged

  • typescript-bot くんが @nakakura さんに review 依頼をする
  • 1週間音沙汰が無いので、 maintainer におはちが回ってくる。
  • そこから二日でマージされる

という感じ。自分でもずっと使っているコードなので嬉しかったです。

最後に

冒頭に紹介したものと、もう一つ別のプロジェクトでもこの型定義ファイルは使っているので、そこそこ大丈夫だとは思うのですが、特に名前衝突問題とか、改善点があったら是非 DefinitelyTyped に突っ込んでみてください。あるいは私に直接指摘して下さっても結構です。みなさんにとってより使いやすい物となりますように!

余談

TypeScript に関して過去に書いた記事のご紹介

izmiz.hateblo.jp

github.com

RealSense SDK 2.0 を自力でビルドして、C++やUnityで使う@Windows

基本的な導入に関してやったことのメモ。

これを試したかったので、最新の development ブランチをビルドすることを目的とします。 unagi (@UnagiHuman) | Twitter さんありがとうございます!

筆者利用環境

Quick Start

まずは自分でビルドせず、動作確認をします。Windows Distribution に従う。

  • GitHub Releases から最新の Intel.RealSense.SDK.exe をインストール。
    • これでカメラ精度を見る Depth.Quality.Tool.exe も、 可視化ツール Intel.RealSense.Viewer.exe もインストールされます
    • ちなみに、Releases にある Source code (zip) は、リリース時点のコードをまとめた物。
  • Intel.RealSense.SDK.exe のインストールでドライバもインストールされるので、D415/D435 を刺してWindowsが認識することを確認。
  • Intel.RealSense.Viewer.exe を起動
    • 2D でカラー・デプス・IRなどが表示されることを確認
    • 3D でポイントクラウドが表示できることを確認
  • サンプルのプロジェクトをビルドしてみる

C++ で開発する

コードを clone してきたら development ブランチに切り替えておく。

自分で realsense2.dll をビルドする

今回の記事には二つ dll が登場します。

  • realsense2.dll は、D415/D435 などを使う為のダイナミックリンクライブラリ。 c++ でも C# でも使います
  • LibrealsenseWrapper.dll というのもあとから出てきます。こちらは Unity で使えるようにする為の、Managed のプラグイン。 Unity および C# (Windowsプログラミングとか) でRealSenseを使う時に使います。

ここは realsense2.dll の話。ここに書いてあるのをやる→ Windows 8.1 & Windows 10 Installation

注:BUILD_CV_EXAMPLE などのビルドオプションが正直よくわかってないのですが、問題なく使えています

  • CMake は利用環境向けのソリューションファイルを生成するだけなので、その後各環境でビルドして始めて dll なりが作成されます
  • Example や Tools も一緒にビルドするので、その中で realsense-viewer.exe 等が動く事を確認する。

改めてC++のサンプルを読んで、ビルドしてみる

(例)上記でビルドした結果のディレクトリの中の examples ディレクトリ中の ReleaseExamples.sln を起動して、 rs-pointcloud プロジェクト等を単体でビルド、実行。など。

Unity で開発する

ここに書いてあることをやります→ Unity Wrapper for RealSense SDK 2.0

ビルド済のUnity用のdllを使う場合は、初めにインストールした C:\Program Files (x86)\Intel RealSense SDK 2.0\bin\x64 あたりから取ってくる

LibrealsenseWrapper.dll をビルドする

今回はここが目的。 Unity ラッパー用の dll を最新の development ブランチのコードでビルドする。

VS2017でビルドする場合、.NET Core 2.x SDK のインストールを事前にお忘れ無く

VS2017 でビルドすると .net2.0 向け、 .Net3.5 向け、.Net 4.6.1 向け~みたいな感じで複数の .dll が作成されますが、現状のUnityは 3.5 だった気がするので net35 のフォルダから dll を持ってきて使います。

Unity のサンプルを動かす

以上

これで C++ と Unity で開発できることが確認出来ました。おしまい。

Kinect v2 のデプス画像をそのままの解像度で点群としてUnityで表示する

f:id:AMANE:20171227222450p:plain

できあがった物がこちら

youtu.be

はじめに

遙か遠い昔(3年前) Kinect v2 のデプス情報を Unity で描画するシリーズをやっていました

前者では、単純に画像を表示しただけでした。後者ではポイントクラウド風の描画にチャレンジしていますが、当時はUnityの仕様的に、またマシンスペック的に点数を間引かざるを得ませんでした。

2017.3 の新機能「32 bit Mesh index buffers」

この新機能の登場で、65536点以上のメッシュを一つのオブジェクトとして利用出来るようになりました。

先に keijiro さんがこの機能を利用して、PCLのデータをインポートするエディタ拡張を作られていて、これが出来るなら Kinect V2 のデプスデータも似たような感じで扱えるのではと思い、今回のネタに着手しました。

github.com

大まかな方策

以下の作戦で行けると踏んで作業を始めました。結果、おおよそ行けました。

  • Kinect v2のデプスデータは画像の形式で得られる。各ピクセル輝度値(= 奥行き)に基づいてVertex Shader で点を動かす
  • Kinect v2 は 512x424 = 217088 点のデータなので、32bit のインデックスに収まる。これを一つのメッシュとして描画する

イメージは下の図のような感じです。大きな値ほど、遠くに頂点が移動します。

f:id:AMANE:20171226210541p:plain

実装 - C#の部分

作成したプロジェクトを公開しています。

github.com

ビルド・実行

ビルド・実行の仕方については、上記リポジトリの Readme.md に記載しています

512x424 頂点のメッシュを用意する

f:id:AMANE:20171226214001j:plain

初めBlender でそのようなデータを用意しようかと思ったのですが、柔軟で良いし勉強になると思いコードで作ることにしました。こちらの記事を参考にさせて頂きました。

上を参考に書いたコードはこちらになります

詳細はコードを読んでいただきたいのですが、ポイントだけ整理しておきます。

32 bit Mesh index buffers を有効にする

2017.3 から追加された 32 bit Mesh index buffers を有効にするために、以下の指定を行います。これで、頂点数が32bit… 4,294,967,296 まで拡張されます。

mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
テクスチャ座標を 0~1 ではなく、半ピクセル分シフトさせる。

これにより、すべての頂点とすべてのデプス画像の画素を1対1で対応づけます。

Vector2[] uv = new Vector2[w * h];
for (var y = 0; y < h; y++)
{
  for (var x = 0; x < w; x++)
  {
    uv[y * w + x] = new Vector2(((float)x + 0.5f) / (float)w, ((float)y + 0.5f) / (float)h);
  }
}
mesh.uv = uv;

ただしこの工夫、やらなくてもぱっと見差がありません。シフトをやらない方の関数も一応用意しました Mesh MeshGenerateNormal(int w, int h, float pitch)

頂点のみ描画する(ポイント指定)

こちらの記事を参考にさせて頂きました。 この記事のスクリプトを、メッシュを含む GameObject に貼り付けるだけです

Kinect v2 のデプス画像データを、16bit の画像として上記メッシュに貼る

f:id:AMANE:20171226214142p:plain

デプスのデータを取ってくる部分は、 Kinect for Windows SDK 2.0 Unity Pro Add-in のサンプルままですので説明は割愛します。できあがりのコードを見つつポイントだけ説明します。

ushort 配列としてデプス画像を取得する

デプス画像は ushort の値を1ピクセルとして、512x424 要素の配列で表現されます。ushort は 2byte = 16bit です。

// L29
_Data = new ushort[frameDesc.LengthInPixels];

// L53
frame.CopyFrameDataToArray(_Data);
Unity のテクスチャデータにする

Unity には GRAY 1ch 16bit 深度のテクスチャとして TextureFormat.R16 があるので、これを使います。

// L30
_RawData = new byte[frameDesc.LengthInPixels * 2]; // ushort 配列を byte 配列として扱う
_Texture = new Texture2D(frameDesc.Width, frameDesc.Height, TextureFormat.R16, false); // R16 の形式でテクスチャを用意する
// L58
Buffer.BlockCopy(_Data, 0, _RawData, 0, _Data.Length * 2); // ushort 配列をそのまままるっと byte 配列にはめ込む
// L62
_Texture.LoadRawTextureData(_RawData); // 毎フレーム、byte配列を元にテクスチャを更新する
_Texture.Apply();

実装 - Shaderの部分

これで、メッシュを動かすのに必要な情報は、シェーダ側に渡されました。ここからはシェーダでの記述になります。できあがりのコードは以下です。

このシェーダでは、メッシュの頂点数とテクスチャの画素数が一致していることを前提として、その画素数 = 頂点数分だけ vertex shader がまわり、各頂点を画素のデータに基づき奥に動かす、と言うことをしていきます。ここも重要な箇所だけ見ていきます。

奥行きデータを再構成する

_Displacement は奥行き感を調整するための係数です。用意したメッシュのサイズなどと照らし合わせて適宜調整して下さい。(実際はMaterialの inspector から調整出来ます)

// KinectDepthBasic.shader L72
float d = col.x * 4000 * _Displacement;

4000 という定数がありますが、これは今回他のテクスチャの使い方も試したときの名残ですのであまり気にしないでください

デプス画像座標系から、空間に展開する

CoordinateMapper.MapDepthFrameToCameraSpace が使えない件

本来 Kinect v2 SDK には、 CoordinateMapper.MapDepthFrameToCameraSpace という関数があり、これを使うことでカメラの正確な諸元によって深度画像を3次元上の点に変換出来ます。(以前書いた記事ではそれを使いました

しかし今回は、1ch 16bit テクスチャとしてシェーダに送り込みたいので、事前に変換してしまうと、以下の様な問題が起きます

  • 1点につき xyz それぞれ float の値として表現されてしまう
    • これはデータをchar形式とかに圧縮させることで回避可能だが…
  • CoordinateMapper.MapDepthFrameToCameraSpace の結果はそもそも、 CameraSpacePoint という実質 Vector3 形式の配列で与えられるため、圧縮作業を行うためには要素数だけ for 文を回す必要がある。for文を回した時点で、高速化は見込めない。

というわけで、今回は C# …というかSDKの層での3次元展開は諦めます。

Vertex Shader で模擬して展開する

正確では無いかも知れませんが、「それっぽく見せたい」程度で良ければ、自力で変換してしまえば良いです。図を見ながら説明します。

f:id:AMANE:20171226223839p:plain

水平面でまず考えますが、垂直面も同じです。 Kinect V2 のデプス画像の水平視野角は 70deg なので、画像の左端のx座標を 256 (=デプス画像横幅512の半分) と置いたとき、デプス画像は D= 365.6 の高さの四角錐の底面に投影されている、と表現できます。

各画素について、Kinect v2 から得られるデータとして、画像上でのX軸の座標を x , 奥行きを d とすると、実際のX軸座標は x' = x * d / D で表すことが出来ます。

これをy座標についても行います。実際のシェーダの記述は以下の様になります

// KinectDepthBasic.shader L57
v.vertex.x = v.vertex.x * d / 3.656;
v.vertex.y = v.vertex.y * d / 3.656;
v.vertex.z = d;

(おまけ)奥行きに合わせてHueを回して着色する

f:id:AMANE:20171227212959p:plain

このままだとすべての点が単色で表示されます。空間中に点があるのでこれでもわかりますが、せっかくなのでより奥行きがわかりやすくしてみます。

hsv 表色系の hue の値を奥行きに連動させるようにします。 0~1 でhue が虹色のように一周し元の色に戻ってきます。

// L25 vertex shader で位置情報をもとに色を計算して、fragment shaderに渡せるよう構造体に color の情報を加える
struct v2f
{
  float2 uv : TEXCOORD0;
  float4 vertex : SV_POSITION;
  float4 col : COLOR;
};

// 中略

// L40 HSV から RGB に変換する関数
// 参照→ http://www.chilliant.com/rgb2hsv.html

float3 HUEtoRGB(in float  H)
{
  float R = abs(H * 6 - 3) - 1;
  float G = 2 - abs(H * 6 - 2);
  float B = 2 - abs(H * 6 - 4);
  return saturate(float3(R, G, B));
}

float3 HSVtoRGB(in float3 HSV)
{
  float3 RGB = HUEtoRGB(HSV.x);
  return ((RGB - 1) * HSV.y + 1) * HSV.z;
}

// 中略
// L86 
// 距離に応じた色をつけてあげる
float3 hsv = float3(1, 1, 1);
hsv.x = v.vertex.z % 1.0;
float3 rgb = HSVtoRGB(hsv);
o.col = float4(rgb.xyz, 1);

実装は以上です。

最後に

制約

今回の実装にはいくつか制約がありますので、ご注意下さい。

  • Kinect SDK の公式な Coordinate mapping をしていないので、点群の位置が不正確
  • Color 情報と点群のマッピングもしたいけど、それができる設計になっていない(自力でシェーダ内で実装できるか?)

感想

しかし、結果としては高速に大量の点を描くことが出来るようになったので満足です。メディアアート的なアプリにはもってこいなのではないでしょうか。

上記制約を解消したり、実装面でイケてない所とか見つけた人、これを使ってなにかおもしろいことをやった人がいたらぜひおしえてください〜

OpenCV 3.3.1 で Yolo v2 for object detection を動かしてみる (Windows)

わーい!

というわけで、とりあえずサンプルを動かしてみました。今回は速報版ってことでご容赦下さい。

環境構築

環境

インストール

  • Windows (vc14) 用にビルドされた物を落としてくる
  • .exe だけど自己解凍するだけなので、適当な場所に置く
    • 私の場合は D:\develop\opencv331 となるように置いた
  • OpenCVのいつもの儀式、パス通し。こちらの記事に習って行う

とりあえずサンプルを動かす

sources\samples\dnn\yolo_object_detection.cpp を動かす。

サンプルのためのプロジェクトを作成
  • Visual Studio 2015 > ファイル > 新規作成 > プロジェクト
  • テンプレート > Visual C++ > 「空のプロジェクト」 を選択
サンプルコードの取り込み
  • $(ProjectDir) 直下に、上記 sources\samples\dnn\yolo_object_detection.cpp を配置
  • Visual Studio の ソリューションエクスプローラー上で、ソースファイル フィルタに、上記ファイルを投げ込む

f:id:AMANE:20171028110435p:plainf:id:AMANE:20171028110439p:plain

インクルードとライブラリのリンク

Debug 時と Release 時で使う .lib ファイルが異なる事に注意。

ビルド
  • ビルド > ソリューションのビルド。 とりあえず Debug でやってみる。
コンフィグとモデルの用意
  • 今回はとりあえず動けば良いので、用途や適正は考えずに、yolo 公式から最もベタっぽいやつを取ってくる。
    • yolo.cfg
    • yolo.weights
  • Visual Studio 上でデバッグ実行する場合、作業ディレクトリは $(ProjectDir) となる。ので、そこに、yoloの cfg ファイルと weights ファイルを置く。
  • ついでに、テスト画像も用意。Yolo の学習にも使っている COCO から適当に。

f:id:AMANE:20171028112003p:plainf:id:AMANE:20171028112141p:plain

実行
  • cfg と model が読まれるようコマンド引数を設定。たとえば以下の様な感じ。
    • -cfg=yolo.cfg -model=yolo.weights -image=3320359009_059880900a_z.jpg
    • これは OpenCVCommandLineParser の書式です。
  • F5 デバッグ実行。

f:id:AMANE:20171028113621p:plain

結果

f:id:AMANE:20171028114308p:plain

見つけたオブジェクトに枠がついています。学習に使われたラベルはこちらです。

奥の車が見つかって無くて、ショルダーバッグだけで無く買い物袋も認識されていて、と言う結果です。良いのか悪いのかはわかりません。

それと、今のところ、提示されたクラスのIDと、その名称の紐付け方がよくわかっていません。他の結果も見た感じ、 Class:0 が人、 Class:2 が車っぽいって事はわかりました(笑)

f:id:AMANE:20171028114953p:plain

次にやりたいこと

  • Class の id から、クラス名を引く方法は?
  • サンプルを元に、動画ファイルやウェブカムを食わせる
  • Tiny YOLO はどうやら高速に動きそう、などモデルと設定の特性について学びたい

Raspberry Pi に VNC でアクセスする (2017/9版)

やりたいこと

  • Raspberry Pi に毎回 HDMI・キーボ・マウスをつなぐのが面倒なので、MacとかからGUIで触れるようにしたい。
  • ChromiumVisual Studio Code を、VNC アクセス中に起動したい。★ここがポイント

f:id:AMANE:20170903180625p:plain

結論

最近のラズパイ…というか raspbian OS には、 RealVNC のモジュールがプリインストールされているので、サーバのサービスを入れてあげれば即VNCサーバ化する。

Raspberry Pi | RealVNC

手順

ラズパイ側

RealVNC 公式に書いてあるとおり→ Docs | VNC Connect and Raspberry Pi | RealVNC

  • 最新の Raspbian Jessie にする sudo apt-get update && sudo apt-get upgrade
  • RealVNC のサーバをインストール。 sudo apt-get install realvnc-vnc-server
  • ラズパイの設定からVNCを利用可能に設定。これだけで、ラズパイ起動時のサービス自動起動設定までされます。

f:id:AMANE:20170903172256p:plain

  • ラズパイの ip を調べる。 ifconfig または、RealVNC Server をインストールしたらタスクバーに出てくる RealVNC のアイコンをクリックすると表示される。例えば 192.168.11.11

f:id:AMANE:20170903172612p:plain

クライアント側(今回はMac

というわけで簡単です。

事の顛末(チラ裏)

この件は誰でもまとめてそうだし、入門書の類に載ってそうなのでまとめるほどでは無い記事だと思ったのですが、自分がちょいとハマったのでまとめることにしました。以下その顛末メモ。なのである意味ここからが本編。

古い情報を鵜呑みにしない

特に最新のラズパイがどんなことになっているかをあまり調べないまま「TightVNC を入れて、Systemd に自動起動を登録しよう」という記事を読んでそのままやってみたところ、以下のような問題が起きました。

あげつらうのは感じ悪いけど、メモとしてはじめ参考にしていた記事を残しておく

Visual Studio Code が VNC 経由だと表示できない

VNC経由で Visual Studio Code (Code-oss) のインストールを済ませて、いざ起動しようと思ったら起動しない。インストール手順間違えたかな、とか思いながらラズパイに直接ログインして操作したら起動できる。あっれ~となって調べる。と、以下の記事を発見

www.hanselman.com

TIghtVNC サーバを使うと、 Visual Studio Code だけじゃなく、Chromium ブラウザ他、 Electron っぽい奴らが全滅するらしい。 セキュリティとグラフィックの掛け算でのなんか問題なのでしょう。

代替手段の調査

インストールしてしまった TightVNC を消す

RealVNC サーバの構成

この記事の先頭に戻る。