研究

2018年、Vの年

サークルメンバーで作ったVTuber配信ツール”KCSTuber”のHPをchoko君と作成しました.
https://kcs1959.github.io/kcstuber

記事を投稿しました.
https://kcs1959.jp/archives/4064/research/rust_image-crate_example

動画を投稿しました(VTuber活動).
https://youtu.be/oqtV6uOIpeU
https://youtu.be/dff4qQsI-XU

少し前に投稿したサークル製作のゲーム紹介
https://youtu.be/jHxQcV7AeV8
https://youtu.be/XK3xnx-_oQw


あらゆるすべてがアツ過ぎた2018年12月01日は過ぎ去りました.
あの滾るような1分1秒は過去のものとなり,人々の記憶からもいずれはなくなることでしょう.
我々は書物に残された記録から過去を復元しようとしますが,決して実感として体験することはできません.
一人の人間が経験し覚えていられることなど,所詮長い時間の中の一瞬に過ぎず,そういった意味では未来も過去も等しく未知といえます.

世界中の死んでいないみなさん,こんにちは.
Lait-au-Cafeです.
みなさんいかがお過ごしでしょうか.

今年はバーチャルユーチューバー(以下VTuber)が話題となった年かと思います.
世間一般の健常なみなさんがどう受け止めているかは想像もつきませんが,私にとってはかなりインパクトが大きいコンテンツでした.

理想的な作業環境
理想的な作業環境の図

アニメーションや漫画のキャラクターにも魂はありますが,アニメーションや漫画のキャラクターの魂は物語の文脈の中で描き出されるのに対し,VTuberというコンテンツではキャラクターが自発的に動き回るという特性のためか,VTuberには魂の存在を強く感じます.
アニメーションや漫画は作者→視聴者・読者という方向性が強く,基本的には作者の用意した文脈の中にキャラクターの魂を見出していく形になりますが,VTuberはそのインタラクティブ性質上,受け手側から堀り下げられる余地があり,魂の奥行きを実感することが出来るように思います.

そうした奥行きゆえか,一見して似たようなカテゴリに属するライバーたちでも,ライブを見ていると思わぬところで決定的な違いを感じ,驚かされることがしばしばあります.
そういった個別性・多様性がVTuberというコンテンツの魅力の一つだと考えます.

特に意味はないです.

←ふつかめ よっかめ→

Rustで画像入出力

みなさん,Rust使ってますか?
新元号がRustに決まったことは記憶に新しいですね.
まだ使っていないよ!という方もこれを機に始めてみてはいかがでしょうか.

さて今回はRustで画像処理を行うために画像入出力用のcrateであるimageを使ってみたいと思います.
https://docs.rs/image/0.20.1/image/

GolangではC++実装のOpenCVを直接呼出しました( GolangでもOpenCVしたい!!! ).
RustでもOpenCVのラッパであるcv-rsを使ってもよいのですが,どのみち処理はすべて自分で書くので入出力だけできればよく,今回はimageを選びました.(imshowが出来ないのはちょっと不便かも).

今回はサンプルとしてグレースケール化を実装します.
もちろん画像読み込み時に直接グレースケールとして解釈すればよいのですが,一連の処理フローを確認するという意味でいくつかの方法でべた書きで実装します.

コードは以下.

使い勝手はそれなりにいいですが,いくつかうーんと思う仕様もあります.
たとえばget_pixelは範囲外にアクセスした際にpanicしてしまいますが,RustなのだからOptionで返すなどしてほしいです.
またget_pixel時のオプションとして範囲外アクセス時の挙動(clamp, wrapなど,CUDAでいうところのアドレッシングモード)の指定ができるとよいですね.
他にもimage::Rgbで各要素に.r()/.g()/.b()でアクセスできるようにしたり,まだまだ開発途上感はありますが,PNGやJPEGを自前でエンコード/デコードしなくてよいのはそれだけで非常にありがたい事です.

FnMutを画像として読み込むことが出来るのも極めて興味深い点です.
テスト用の合成画像を作りたい場合や簡単なフィルタを適用したいときに便利かと思います.

最後に,image crateに限らず,もしライブラリのビルドに失敗する場合はrustup updateを適用してみてください.rustcのバージョン違いでビルドできていない可能性があります(一敗).

それではみなさん,Rustで楽しい画像処理ライフを.

Blenderで脚を作ります

どうも。チョコです。

気づいたら12月になって、Advent Calenderに調子乗って2日投稿にしたのに何も書くネタが思い付かなくて焦っている(1日現在)。

これだと総代表様から怒られが発生するので、僕が一番素早く記事にできるものは何だろうと自分に問い詰めた結果、”Blenderでの KCSちゃん 幼女 名取 脚 の作り方”にたどり着きました。ちゃんと真面目で健全な内容なのでご安心ください。それでも総代表様から怒られが発生しうるが。

(Advent Calender2日目にこのようなしょうもない記事を出して誠に申し訳ございません。)

(私はプロモデラーではないので、これが脚のモデルのやり方だ!というのはあまり大きな声で言えないが、まあ参考までに。)

(タイトルを「Blenderでの脚の作り方」にしたかったんですけど、ちょっと調子に乗ってそうなので変えました。)


さて、脚はモデルの半分なので、綺麗に作るに越したことはありません。SDキャラだったら脚はcylinder作って若干下の部分を縮めば出来上がりですが、愛着が湧かないのでここではフルサイズキャラについて語ります。

Screenshot (461)
図1:SDのKCSちゃん

*愛着が湧かないというのは脚へのことであって、別にKCSちゃんへ愛着が湧かないなわけではありません。KCSちゃんには常に日頃愛着が湧いています。

ただし、綺麗な脚を作ろうと思うと、ボーンを物凄く入れて筋肉表現を表すことももちろんできますが、やりません。変態(褒め言葉)じゃないので。あくまでゲームで使うモデルで最小限のボーン数で再現できる範囲で作ります。そのほうが参考になるかと。多分。

Siri

脚が始まるところです。アニメーションをつけるキャラクタには、動かすことを意識して作らなければなりません。特に、脚が曲がったときにSiriの部分に体積が足りないとこうなります。

Screenshot (465) Screenshot (466)

図3:Siriが消える

したがって、この状態でも大丈夫よう、Siriの下部分に頂点が近づけますね。これが横から見ると特徴的な形状になりますね。ウェイトをつけるときは上のほうの頂点に影響少なめにすると、いい感じに曲がります。

Screenshot (462) Screenshot (463) Screenshot (464)

図3:頂点を足した

ただし、脚は裏には曲がりませんので、頂点が変になっても気にする必要がありません。脚が後ろに曲げたいときは腰のボーンを曲げましょう。

太もも

一番大事なところですね(個人的な意見)。太ももを前から見たときに、内側は3次曲線で外側は2次曲線、という認識を持っていますね。形状をもっと強調したいときは内側の下部分にU型のトポロジーを入れます。

a

図4:太もも

また、気づくかもしれませんが、内側の膨らみは筋肉なので、内側の方が食い込みます。つまり、ニーハイやベルトを装着するとこうなります。

asdfffdsa

図5:ニーハイの履き方

(ニーハイに食い込みは作らないですって?物理法則違反です。作りましょう。)

膝もまた変形するところですね。察すると思いますが、内側より外側の方に頂点を多く入れますね。膝の形も強調したいので、前から見るとU形になっている感じで作ります。そうすると小牛も自然に後ろに生えます。

膝の裏は、曲がるときに食い込みが発生する問題を解決するために、曲がるところに変形する頂点を置きません。また、靱帯は上から下に生えて逆Vを作っていますので、ボーンのウェイト平衡点を上部分にずらします。すると比較的に綺麗な曲がり方ができますね。あと、足も同じく前に曲がることはないので、膝のウェイトは太ももボーンが支配的ですね。

Screenshot (467) Screenshot (468) wfer

図6:膝

(うん?何か飛ばした?幻覚です。)

足は下から見ると特徴的な形状を持ちますね。正直いうと大体靴か靴下を履かせていますので生足経験が少ない(パワーワード)が、基本的にはこの不均一感を出すのは大事ですね。特に、内側が上に上がる部分はちゃんとあげましょう。

また、足首から足指にかけては、”へ”の形がしていて、決して平らや球状ではありません。足小指に行くと位置は後ろにずれますので、上から見ると斜めの配置になっていますね。

そして、足の裏は一本の太い靱帯と両側の凸の間は若干食い込みます。これを意識して作ると特に制服の靴を履くと若干隙間ができていいです()。

egrs
図7:足

(こうして記事にまとめると,足へのこだわりが足りないことに気づきますね.足だけに.反省.)

結論

以上のポイントを全部合わせると、綺麗な脚を作ることができるんでしょうね。まあ、場合によって強調する量を調整したり、違ったトポロジーで作ったりしますので、決して鉄則ではありませんね。3Dモデリングですから。

このしょうもない記事をここまで読んでいただいてありがとうございます。最後に、参考までに僕が2年前に作ったえりりを見てください。脚にはそれなりに力を入れました。

*これはAdventCalender 2日目の記事です*

←1日目 3日目→

超絶簡単unityライフハック -スクリプト生成編-

こんにちは。mo-takusanです。

さて、いよいよ始まりましたKCSアドベントカレンダーです。
第一日目ということで今回はあまり難解なことは書かずに、知っていると便利な機能を紹介していこうと思います。

unityのC#テンプレートがやばい

皆さんも毎日のようにunityを使っていると思いますが、この見出しみたいなこと感じたことありませんか?

2018-11-30_12h33_16

この図のようにunityのテンプレートはC#のコーディング規則に沿っておらずきれにするためにはいちいち書き換えなければなりません。このコメントアウトもいりませんし、ちゃんと名前空間も設定したいところです。さらにMonoBehaviourに限らずScriptableObjectなどもテンプレートとして用意されていれば便利そうです。

したがって今回の記事では、

2018-11-23_23h20_17

2018-11-24_18h59_40

こんな感じで「Create」->「MonoBehaviour」でMonoBehaviourが作成され、いい感じのテンプレートで自動的に初期化してくれることを目標にします。ScriptableObjectも作成したいですが、冗長になるだけなので今回は割愛します。

今回の記事はこの記事を参考にしました。

オリジナルのテンプレートを作る

それではまずはオリジナルのテンプレートを作成することから始めましょう。
参考記事によるとwindowsでは

"unityのアプリケーション位置"\Unity\Editor\Data\Resources\ScriptTemplates\

に存在するテキストを編集することで簡単にテンプレートを作成できるようです。
今回は以下のように編集してみました。なおファイル名は81-MonoBehaviour-NewBehaviourScript.cs.txtとしました。これで「Create」->「MonoBehaviour」から「NewBehaviourScript.cs」を作成することができるようになります。

参考ページに載っているようにテンプレートにはパラメータがいくつか用意されており、スクリプト名などを勝手に置き替えてくれるようです。しかし、5行目のコメントアウトから分かるようにプロジェクト名や作成したディレクトリ名もパラメータとして用意してあれば文句はなかったのですが、さすがにそこまで融通は利かないようです。(ちなみにディレクトリ名を名前空間として設定するってC#でもやるんですかね?)

そこで、次節ではこれらのパラメータも認識するように工夫していきます。

自作パラメータを置き換えさせる

ところが、先ほどのように単に一筋縄ではいきません。ちゃんとエディタ拡張をする必要があるようです。
unityではアセットを作成した際に対応したEndNameEditAction抽象クラスのActionメソッドが呼ばれるそうです。つまり、前節のようにしてテンプレートを作成すると、unity側が命名規則に従って自動的にデフォルトのActionメソッドを呼び出してくれると考えることができます。パラメータを自作する場合はこのActionメソッド内にパラメータを置き換えるコードを書くことになります。

それでは早速プロジェクトを作成します。
まず、プロジェクト内に「Editor」フォルダを作成し、ここに先ほど作成したテンプレートを入れてしまいます。この時、先ほどのコメントアウトは消しておきます。以降で作成するパラメータは他のプロジェクトでは機能しないのでこのようにして隔離しておく必要があります。

それではEditorファイル内にスクリプトを生成します。

23~27行目のようにしてパラメータに対応する文字列を入れ替えていきます。それ以降にも何やらごちゃごちゃと書いていますが、エディタ拡張職人になるわけではないので「このような手順を踏んでアセットが作成されるんだ」くらいの認識で良いと思います。

ところが、これだけではうまく動きません。先ほど述べたようにActionメソッドは対応したアセットに対してのみ実行されるため、作成したC#スクリプトに結び付けてやる必要があるのです。そこで、メニューアイテムも自作し、EndNameEditActionを紐づけます。メニューアイテムを自作するにはstaticメソッドにMenuItem属性を付与する必要があります。ただし、このようにしてメニューアイテムを自作する場合、既存のC#スクリプトと競合させてしまうと色々と面倒だったため、別のメニューとして作成します。また、この際の紐づけには次のメソッドを利用します。

このように事細かく指定する必要があるのは、自作したメニューアイテムからではデフォルトのActionメソッドだけでなく、アイコンを指定するメソッドや、アセット名を自動的に付けてくれる処理も呼ばれないためです。
完成したスクリプトは以下のようになります。

これで見事今回の目標は達成となりました。

全てのプロジェクトで使いたい

しかし、このままでは今回作成したプロジェクト内でしか利用することができません。そこで、以降に作成するプロジェクト全てに自動的に反映されるように工夫しましょう。と言っても非常に簡単です。windowsでは

"unityのアプリケーション位置"\Unity\Editor\Data\Resources\PackageManager\ProjectTemplates\

にデフォルトのプロジェクトが用意されているので、ここに作成したファイルを入れてやるだけです。
今回はcom.unity.template.3d\Assetsに入れてみます。

それでは試にプロジェクトを作成してみましょう。

2018-11-26_20h49_05

プロジェクトに先ほど作成したファイルが存在していることが確認できると思います。

おわりに

日々利用するアプリケーションだからこそ開発環境を自分好みにカスタマイズすることが大切ですね。unityのエディタ拡張は専用のメソッドが多く、わかりにくいように感じられますが、「全てを理解する必要はない」と割り切ってしまえばそう難しくもないと思います。実際エディタ拡張ばかりではいつになってもゲームは完成しませんしね。

それでは明日以降も部員たちによる個性的な記事が投稿されることを期待して本記事を締めさせて頂きます。

矢上祭とSwitch

チョコです.

矢上祭,お疲れさまでした.

今年もぴょんぴょん(+)人気だったな,,,,思った以上に「面白い」,「ハマる」って感想いただけてすごくうれしかったです.

 

さて,ぴょんぴょんですが,去年のゲームジャムで3日で作ったにして結構人気があったシューティングゲーム.今年用に二人プレイできるようにしたのと,かわいいかわいいKCSちゃんモードを入れました.

3

図1:ぴょんぴょん+の宣伝図(宣伝してない)

このゲームを毎年プロジェクターを使って大画面でやるのが恒例なのですが,KCSは Oculusを買う金はあるのに 同じコントローラーを二つも持っていません.ではここで,「試しにNintendo SwitchさまのJoyconを使ってはどう」という発想に至ったわけです.

さっそくSwitchを借りて,やってみよう!と思ったわけですが,ここでとても重要な2ポイントがあります.

1.PCにはBluetoothで通信できる

2.UnityはJoyconをコントローラーとして認識できる

まさに神.2秒で移植できるじゃん.(1日かかりました)

(1ですが,Joyconの上にある小さいボタンを長押ししながらpairingをしないと認識できません.)

とりあえず,ボタンを軸登録することですね.なお,キーボードででも同じ操作ができるように,以下のような入力ラッパーを書きました.

public static bool jbt(int i, int j) {
    return Input.GetKey("joystick " + i.ToString() + " button " + j.ToString());
}
public static bool jump(int i) {
    return Input.GetKey("space") || jbt(i, 4);
}

リスト1:InputManager.csの一部

ボタンのIDはこんな感じですね.

httrhgtrh

図2:JoyconのボタンID

Bluetoothの登録順でjoystick 1, joystick 2, …で決まりますね.つまり,複数台Switch持ってれば(ry

 

最後に,Input.GetButtonDown(“Jump”)を全部InputManager.jumpに変えるだけですね.

 

あ,そう.あとは一人プレイ用に書いたガバスクリプトを二人プレイにしたときに発生したバグの塊を解消する作業ですね...

 

来年も出したいな,,,(卒業しなくていいの?)

完.

ジオメトリシェーダでファーシェーダを実装した話

こんにちは、mo-takusanです。
今回は題名の通りファーシェーダを実装しました。

https://kcs1959.jp/archives/1928/research/unity%E3%81%A7fur-shader

こちらの先輩の記事を見て私も実装してみようと思い、今回取り組んでみました。
またググってみると、unityでファーシェーダを実装している記事は多くあるのですが、ジオメトリシェーダを利用しているものが見当たらなかったため、今回はこれを利用したものにすることを目標としました。

完成図は図のようになります。左側が今回実装したファーシェーダになります。左右どちらの球も同じテクスチャを貼っているのですが、違いは一目瞭然ですね。

2018-09-16_11h12_28

なお、実装においてはこちらの記事を大いに参考にしました。
https://qiita.com/edo_m18/items/75db04f117355adcadbb

また、実装において利用したテクスチャは次のサイトから使わせて頂きました。
http://photoshopvip.net/25382

ファーシェーダとは

ファーシェーダとは、その名の通り毛並みのふわふわ感を表現するシェーダを指します。
ファーシェーダは主にシェル法やフィン法などの実装方法がありますが、今回はシェル法を用いていきます。

シェル法

シェル法においては毛並みを一本一本描画していくわけではなく、層状に毛の断面を描画していくことで、全体として毛並みのように表現する手法です。
具体的には、次のように行います。

  1. 各頂点に対してそれぞれの法線方向に一定距離だけ移動した頂点を生成し、元のメッシュより一回り大きいメッシュを生成する。
  2. メインテクスチャとは別に毛束の断面のようなテクスチャを用意し、そのテクスチャのRGB値が一定を超えたテクセルに対してのみメインテクスチャの色を乗せ、それ以外の部分は描画を行わないようにする。
  3. メインテクスチャを貼る閾値を少し大きくしていきながら、1.及び2.の操作をくり返す。

1.の操作をジオメトリシェーダで、2.の操作をフラグメントシェーダによって実装していきます。

先述のようにシェル法では毛を一本一本描画するわけではないため、層の数が十分でない時は毛束に見えません。以下の図に示すように層が目立ってしまうことが分かると思います。

2018-09-16_11h19_14

実装

それでは実装を示していきます。
先述のように、1.をジオメトリシェーダで、2.をフラグメントシェーダで実装します。

まずは1.です。

今回ジオメトリシェーダでは三角メッシュを受け取って三角メッシュを出力します。それぞれのメッシュは依存関係がないため、PointStreamの入出力でもよさそうですが、仕様上PointStreamで出力してしまうと、面を貼ってくれなくなってしまうため、このように記述するしかありません。
ちなみに、maxvertexcountが81になっていますが、これより大きい3の倍数でVaridation Errorになってしまうためです(maxvertexcountのリファレンスを読んだのですがこれに関する記述は見当たりませんでした)。この値はフラグメントシェーダに値を渡す構造体の大きさにも依存していたので、渡せるデータサイズの上限値があるのでしょう。

残りは先ほど提示したページの実装とほとんど同じものです。
異なる部分はライティングのためのものになりますが、今回の内容と直接の関係はないので割愛します。触りだけ説明すると、ライトの方向を計算し、ライト情報を適切にフラグメントシェーダに渡しています。
詳しくはこちらの記事をご覧ください。
https://qiita.com/edo_m18/items/21d3b37596da3fd4b32b#%E9%96%A2%E9%80%A3%E3%83%AA%E3%83%B3%E3%82%AF

2.についてもライティング以外は同様の処理です。違いといえばノーマルマップによるライティングを行っている程度です。

さて3.についてですが、先ほどmaxvertexcountの値が81だと述べましたが、これでは層の数が最大でも27層までとなってしまい、元々表現したかったフサフサ感が損なわれてしまいます。これでは本末転倒なので、今回の実装でも参考ページと同様に同じパスを何度も記述して層の量増しを行いました。
といっても一回のパスで27層描画できるので3つだけ同じパスを用意しました。

ジオメトリシェーダによる実装のメリット

ジオメトリシェーダを利用して実装を行うと、プラットフォームによっては動かない可能性があるため、望ましくないことも多々あります。しかしながら、ジオメトリシェーダによってファーシェーダを実装した場合、次のようなメリットが考えられます。

  • 全く同じパスを数十個書く必要がないためスマート
  • バッチ数を抑えられる
  • 層の数を可変にすることができる

特に、層を可変にできることでお好みのフサフサ具合を表現でき、非常に便利でしょう。バッチ数を抑えることで
パフォーマンスは改善されるのでしょうか?ジオメトリシェーダがどれほど重いか計測が面倒だったので言及しないことにします。

まとめ

ジオメトリシェーダを利用することで、比較的スマートにファーシェーダを実装することができました。シェーダはすぐにビジュアルに表れるので実装していてとても楽しいですね!

UnityでのDeferred Celシェーディング

どうも,チョコです.院試おつ↓かれ↑ーい.

いつものUnityでのCelシェーディングをやっていました.しかし,シェーダを全部Cel式に変えるのはめんどい.そしてunlit shaderで照明を扱うのはどうも釈然としない.そこで,Deferredでなんとかならない?と思ったわけです.

UnityではDeferredの内部シェーダを変えられるようになっています.このように.

(Edit->Project Settings->Graphics)

Screenshot (13)

ここに自分のシェーダを突っ込めば,カメラの出力を制御できるというわけですね.

さて,これをやるにはまずbuiltin_shadersアセットをダウンロードしないといけない(ダウンロードページにあります).そして,Internal-DeferredShading.shaderを見つけます.

シェーダを中を見ると,おっと,この部分は画面に色を吐いているなと,読めばわかると思います.

half4 CalculateLight (unity_v2f_deferred i)
{
    ...
    half4 res = UNITY_BRDF_PBS (data.diffuseColor, data.specularColor, oneMinusReflectivity, data.smoothness, data.normalWorld, -eyeVec, light, ind);
    return res;
}

なるほど.つまり,ここの戻り値を変えればいいと.さて,Celシェーディングでやりたいことを確認しましょう.

deftoon

図の通り,まずは白の材質に対しての照明を得て(1),そしてCel風のカーブにし(2),最後にDiffuseを適用(3)すればいいです.上のコードを書き換えましょう.

uniform float _deferred_cutoff;
uniform float _deferred_shade;
uniform float _deferred_saturate;
half4 CalculateLight (unity_v2f_deferred i)
{
    ...
    half4 inten = UNITY_BRDF_PBS (half3(1,1,1), half3(0, 0, 0), 1, 0.5, data.normalWorld, -eyeVec, light, ind);
    float brg = clamp(ceil(inten.r - _deferred_cutoff), 0, 1);
    float shd = 1-((1-_deferred_shade) * (1-data.smoothness));
    return half4(saturate(data.diffuseColor * (brg * (1-shd) + shd), 1 + _deferred_saturate - (_deferred_saturate) * brg), 1);
}

(デフォルトシェーダの仕組みはどこにも書いていないので,関数の意味などはCGIncludesの中身を自分で読まないといけませんね)

はい.ここで,UNITY_BRDF_PBS は(1)なので,それを(2)にしたのがbrgとなって,最後にdata.diffuseColorを適用しました(3).

最後に,Inspectorでデフォルトのシェーダを変えよう.そしたら(エラーがなければ)オブジェクトのマテリアルを一切触っていないのにカメラの出力が変わります.

Screenshot (12)

これで1つのシェーダでアニメ風にできますね.スタイル変換捗りー.

(実はこの方法は,照明ごとに行われるので,光が重なるととても明るくなります.解決したらパーツ2の記事にします)

では.

ブルースカイ・カタストロフィ

ブルースカイ・カタストロフィ(つよそう)

スクリーンショット 2018-05-05 17.10.41

↑は参考にあった Blue sky orbit in the Gavrilov-A. Shilnikov model でパラメータも同じもの.

以下は, \(\varepsilon\) をそのまま固定しながら, \(\mu\) を \([0.1, 1.1]\) の範囲で動かしたもの(0.02 間隔で 80000 回のルンゲクッタ.最初の 1000 回は捨てている).

OpenCVでカメラキャリブレーション

OpenCVを使ったカメラキャリブレーションをググると,まずCでの実装例が出てきて,次に比較的新しいpythonでの実装例が出てくるが,C++での実装例がいまいちヒットしない.

加えて,カメラキャリブレーションは基本的にあらかじめ用意したデータについて行うが,私は普段Webカメラを利用するので,データの収集とキャリブレーションを同時にできるといいな,というのがあった.

ので,作った.

リポジトリはこっち
https://github.com/Lait-au-Cafe/calibration

本当はできるだけシンプルにしたかったのだが,冒頭で述べたようにプリセットのデータがなくてもできるようにしつつ,でもプリセットのデータがあってもできる,という仕様にした結果少し複雑になってしまったように思う.

当該リポジトリを利用する際にはビルドにOpenCV3.xとpkg-configが必要なのに注意.

P行列の自由度とパラメータ

物の本によると,カメラによる物体の投影を表現する “P行列” は以下のようにあらわされる.

PMat1

なるほどな.

別の本では以下のように記述されている.

PMat2

まあそういうこともあるだろう.

ところで,P行列の自由度(DOF, Degree of Freedom)は一般に11とされる.
P行列は全部まとめると3×4行列になるが,定数倍しても意味が変わらないのでここから1を引いて11,という説明がされているのを見かける.

さて,上の二つの式をもう一度見てみよう.
回転行列Rと並進ベクトルtは二つの式で共通で,自由度はともに3であるから,一番左の行列だけで自由度は3+3=6.
真ん中の行列は焦点距離fがあって自由度1.
左の行列は,上の式では変数がa, s, c_x, c_yの4つで自由度4.
下の式では変数がδ_x, δ_y, c_x, c_yの4つで自由度4.

上の式も下の式も全部合わせて4+1+6=11自由度!
世界は今日も平和だなぁ…





ほんまか???

上の式と下の式で異なる点は以下の二点である.

  • 上の式ではx軸方向のスキューsが考慮されている.
  • 上の式ではアスペクト比aで表しているものを,下の式ではピクセルの物理的なサイズδ_x, δ_yで表している.

これを見て私が抱いた疑問が以下の2点.

  • 上の式でもaではなく物理的なサイズδ_x, δ_yで表せば自由度が12になる??
  • さらにy軸方向のスキューも考慮すれば自由度が13になる???

式にするとこんな感じ.

PMat3

しかし自由度12はギリギリ許容できても,3×4行列の自由度が13になることは逆立ちしてもあり得ない.
ではどれが正しくてどれがどう間違っているのか.

順に確かめていこう.

アスペクト比aとピクセルの物理的なサイズδ_x, δ_yについて



結論
“焦点距離f”と”アスペクト比a”のペアで表すか,”ピクセルの(相対的な)物理的サイズδ_x, δ_y”で表すのが冗長の無い表現である.

先程のP行列の式で,下の方の式では焦点距離fとピクセルの物理的なサイズδ_x, δ_yを用いてP行列を表現していた.
これは解釈上はいいのだが,自由度の観点ではこのうちの一つは冗長なので誤解を生じやすい.
変数はf, δ_x, δ_yの三つあるが,実際には自由度は2しかない.

式的な解釈としては,先ほどの式を少し変形して以下の形にするとわかる

PMat4

ここの式を見ると,1/δ_x’=f/δ_x, 1/δ_y’=f/δ_yと置き直しても普遍性を失わないことがわかる.
よって,スキューを考えない場合P行列の自由度は10.
あるいはf’=f/δ_x, a=δ_x/δ_yと置きなおすと,焦点距離とアスペクト比で表現できる.

f, δ_x, δ_yの変数のうち一つが不要である理由を言葉で表現するとどうなるか.
ここでもう一度焦点距離fの意味について問い直すと,これはカメラ座標系のスケールを正規化するためのパラメータとして解釈できる.
焦点距離fによる正規化を行った後,再度ピクセルの物理的なサイズδ_x, δ_yを用いてスケーリングを行うのだが,別に一度正規化を挟まずとも直接,カメラ座標系のスケールに対するピクセルの物理的なサイズδ_x’, δ_y’によってスケーリングしてしまえばそれでよい.
わざわざ一度fでスケーリングを行う必要はない.
(もちろん,処理に物理的な解釈を与えるという観点では重要な操作である).

もしスキューを考慮しないP行列で自由度が11になると説明する人間が居たら「ほんまか??」をぶつけよう.

y軸方向のスキューについて

結論
y軸方向のスキューは回転R,並進t,焦点距離f,アスペクト比aを取り直すことで消えるので考慮しなくてもよい.仮にy軸方向のスキューを明示的にP行列の式に組み込んでも自由度は変わらず11となる.

証明は以下.

PMat5

かなり雑に言葉で説明すると,カメラを90度回転させればx軸方向のスキューがy軸方向のスキューになる,という感じ.

画像作り終えてからsinの符号が逆なのに気づいた….
眠い…,適宜読み替えて読んでください….

まとめ

P行列の自由度は,
焦点距離f + アスペクト比a + スキューs + 画像中心の座標c_x, c_y
+ カメラの回転R(3自由度) + カメラの並進t(3自由度)= 11自由度.

以上.