Theta UVC Blenderもどきを作ってみた

Theta UVC Blender **もどき** を作ってみたので、そのメモです。

#Theta UVC Blender ?
Theta UVC Blender は、RICOH THETA SからUSB出力されるライブストリーミング映像（Dual Fisheye)をつなぎ合わせEquirectangularにして、仮想カメラ映像として使うことができるRICOH さんのPCアプリです。
RICOH さんの本家はこちらです。
>
[Theta UVC Blender](https://theta360.com/ja/info/news/2016-03-24-1/)

#目的
RICOH 公式で提供されてるものがあるのだから、自分で作るなんて無駄では？と思うかも知れませんが、シェーダーの勉強の題材が欲しかったのです(キッパリ)。

#必要な機能
1. 仮想カメラ
2. リアルタイムステッチング

#仮想カメラ
これは、今回の目的でないのと、自分の今の知識ではお手上げなので、[ycapture（わいきゃぷちゃ）](http://yzwlab.net/ycapture/)を使わせていただくことしました。感謝。
VisualStudioでのycaptureのbuild方法は下記を参考にしました。

>
- [ycapture で OpenCV での処理結果を Windows のビデオソースとしてブラウザに流し込む](http://izmiz.hateblo.jp/entry/2015/01/12/220133)
- [ゴニョゴニョに必要な『BaseClasses』ファイル群の入手方法](http://toroalt.blogspot.com/2016/02/baseclasses.html)

#リアルタイムステッチング
ここでいうステッチングは、DualFishEye画像をつなぎ合わせてEquirectangularにすることです。
リアルタイム"でない"計算方法はすでにOpenCVで作ってあったので、それをGLSL(OpenGL Shading Language) で書き直すことにしました。

OpenGL, GLSLは下記を参考にさせていただきました。

>
- [GLSL で画像処理 (1) 画像を取り込む](http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20140725)
- [GLFW による OpenGL 入門(pdf)](http://marina.sys.wakayama-u.ac.jp/~tokoi/GLFW.pdf)
- [OpenGL(GLSL)入門2 - はじめての三角形1](http://suzuichibolgpg.blog.fc2.com/blog-entry-109.html)
- [[連載]やってみれば超簡単！ WebGL と GLSL で始める、はじめてのシェーダコーディング（１）](http://qiita.com/doxas/items/f3f8bf868f12851ea143)

ステッチングのための情報は下記を参考にさせていただきました。
射影方式が_立体射影_なのがポイント。

>
- [RICHO THETA(製品紹介)](https://theta360.com/ja/promotion/?rp=b)
- [射影方式(有限会社フィット)](http://fit-movingeye.jp/products/optics/consumer_optics/fi_series/fi_02.html)

VisualStudio2013でビルドし、Windows8.1で動作確認した実行ファイルを下記に置きました。
[theta_uvc_blender_mdk](https://drive.google.com/folderview?id=0ByZmH_t2s7PuRkFxV3JUMDk3bmM&usp=sharing)

取り扱いが少し面倒くさいですが興味があればお試しください。全部のソースは置いてないですが、座標変換処理を行うフラグメントシェーダ（DFE2EquiShader.fragmentshader）は置きました。
また、理由がよくわからないのですが、仮想カメラとして、theta_uv_blender_mdkを選択してもうまく見れない時があります。
（自分の環境では、FaceRigから開くと問題ないけど,VLCはたまに失敗して表示が更新されなくなります。)

フラグメントシェーダで実施しているステッチングの調整パラメータとしては下記6つあります。

```lang:DFE2EquiShader.shader
float DFE_ALPHA_BLENDING_R_RATIO = 0.95; // 1.0=no alpha blending
float CAM_R = 283.0;	// radius[pixel] @ 1280x720 DualFisyEye USB Streming input
float CAM1_CPX = 319.0;	// x-coordinates of the center of 1st Fisyeye circle [pixel] 
float CAM1_CPY = 319.0; // y-coordinates of the center of 1st Fisyeye circle [pixel] 
float CAM2_CPX = 959.0; // x-coordinates of the center of 2nd Fisyeye circle [pixel] 
float CAM2_CPY = 319.0; // y-coordinates of the center of 2nd Fisyeye circle [pixel] 
```

FisyEyeの中心からCAM_R * DFE_ALPHA_BLENDING_R_RATIO以上の離れた座標を参照する場合は、2つのFisyeye画像をalpha blendingする処理を入れています。
~~座標変換は、基本的には下記の記事と同じと思います。~~

_(2016/05/03追記：↓の記事は「立体射影」ではなく「等距離射影」のように見えるので自分の実装とは違いそう。射影方式の違いのリンク([射影方式のちがい - 屋根の上](http://tin0.blog.fc2.com/blog-entry-49.html))も追加。)_

>
[THETAのDualfisheye動画をThree.jsで表示してみた](http://qiita.com/mechamogera/items/b6eb59912748bbbd7e5d)


# 結果
- ステッチ性能はやっぱり本家に負けている感じ
- 表示遅延は(自分の環境では)少し短いようだ

# 最後に
処理速度に関しては、OpenCVでCPU処理で実装したときは、1fps以下しか出てなかったけど、今回はGLSLを書いてGPU処理にすることで15fpsが問題なく出せている。
OpenGLやシェーダ(GLSL)は、今まで手を付けてこなかったけど、少なくとも平面(画像)から平面(画像)への変換をGLSLでやるのはそんなに難しくない。ただ、今回GLSLのデバッグは全くやらなかった(OpenCVソースからGLSLへの書き換えのみなので、バグはtypoぐらい)ので、その辺のやりやすさはまだ不明。
でも、画像処理でゴリゴリ座標変換をやるときには、シェーダを書いてGPUの処理能力パワーを使わない手はないなぁと思う。(この分野の人には当たり前なのだろうが）