http://www.fmaj7b5.info/wiki/api.php?action=feedcontributions&user=%E7%99%BD%E9%A3%AF&feedformat=atom
メモ帳@fmaj7b5.info - 利用者の投稿記録 [ja]
2024-03-29T11:42:05Z
利用者の投稿記録
MediaWiki 1.21.11
http://www.fmaj7b5.info/wiki/index.php?title=%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%97%E6%B3%95%E3%81%A8%E3%81%8B
最小二乗法とか
2013-09-16T15:26:38Z
<p>白飯: /* その1 */ カッコの抜けを修正</p>
<hr />
<div>== 目的 ==<br />
[[ファイル:Line_fitting.svg|300px|right|座標系]]<br />
二つの値<math>\{x_i, y_i\}</math>を沢山観測していると、どうやらこの値は直線にのるらしいことが分かった。<br />
でも観測した値には誤差が含まれているので、どう頑張っても全部の点を通る直線を引くのは無理っぽい。<br />
そこで、全ての観測した点に対してなるべく近くを通るような直線を求めるのを目指す。<br />
<br />
直線を求めるっていっても、紙に線を書くんじゃなくて、後で計算しやすいように直線の方程式の係数を求めるのがここのテーマ。<br />
<br style="clear: both" /><br />
<br />
== 最小二乗法 ==<br />
またの名を最小'''自'''乗法。<br />
読んで字のごとく誤差の2乗を最小にする係数を求める方法。<br />
<br />
=== その1 ===<br />
以下の式を考える。<br />
<br />
<math><br />
y = a x + b<br />
</math><br />
<br />
すると直線上の点は上式を満たすから、ある観測点の誤差<math>\Delta y_i</math>は次となる。<br />
<br />
<math><br />
\Delta y_i = y_i - (a x_i + b)<br />
</math><br />
<br />
この値は正負両方の値をとるので2乗し、全ての観測点に対する和が最小になるように係数<math>a, b</math>を定める。<br />
<br />
<math><br />
\sum_i \Delta y_i^2 \longrightarrow \min<br />
</math><br />
<br />
<math>a, b</math>でそれぞれ偏微分して0とおき、連立させて解くと以下を得る。<br />
<br />
<math><br />
\begin{array}{l}<br />
a = \frac{\sum_i (x_i - \bar{x})(y_i - \bar{y})}{\sum_i (x_i - \bar{x})^2} \\<br />
b = \bar{y} - \frac{\sum_i (x_y - \bar{x})(y_i - \bar{y})}{\sum_i (x_i - \bar{x})^2} \bar{x}<br />
\end{array}<br />
</math><br />
<br />
但し、<math>\bar{x}, \bar{y}</math>はそれぞれxとyの平均とする。<br />
これを変形すると、<br />
<br />
<math><br />
y - \bar{y} = \frac{\sum_i (x_i - \bar{x})(y_i - \bar{y})}{\sum_i (x_i - \bar{x})^2} (x - \bar{x})<br />
</math><br />
<br />
となるため、平均を通り、傾きが共分散/xの分散になる直線であることが分かる。<br />
<br />
ちなみに行列を使って表すと、誤差ベクトル<br />
<br />
<math><br />
\Delta \boldsymbol{y} = \boldsymbol{y} - X^\text{T}<br />
\begin{bmatrix}<br />
a \\ b<br />
\end{bmatrix}<br />
</math><br><br />
<math><br />
X^\text{T} =<br />
\begin{bmatrix}<br />
x_1 & 1 \\<br />
x_2 & 1 \\<br />
\vdots & \vdots<br />
\end{bmatrix}<br />
, \quad<br />
\boldsymbol{y} =<br />
\begin{bmatrix}<br />
y_1 \\ y_2 \\ \vdots<br />
\end{bmatrix}<br />
</math><br />
<br />
として、2乗誤差<math>\Delta \boldsymbol{y}^\text{T} \Delta \boldsymbol{y}</math>を係数ベクトルで微分すると次式を得る。<br />
<br />
<math><br />
X(\boldsymbol{y} - X^\text{T})<br />
\begin{bmatrix}<br />
a \\ b<br />
\end{bmatrix}<br />
= \boldsymbol{0}<br />
</math><br />
<br />
よって、<br />
<br />
<math><br />
\begin{bmatrix}<br />
a \\ b<br />
\end{bmatrix}<br />
=<br />
\left( X X^\text{T} \right)^{-1} X \boldsymbol{y}<br />
</math><br />
<br />
である。<br />
<br />
=== その2 ===<br />
さて、勘のいい人は気付いていると思うけど、一つ目の方法は'''y軸の誤差しかみていない'''ことが分かる。<br />
ご丁寧にも<math>\Delta \boldsymbol{y}</math>とか書いてたしねぇ。<br />
<br />
普通に観測したデータはx軸にもy軸にも誤差が含まれるのが当然ってことで、次の式を考える。<br />
<br />
<math><br />
c_1 x + c_2 y + c_3 = 0<br />
</math><br><br />
<br />
この式だと<math>x=\text{const}</math>というy軸に平行な直線も表せるのと、両辺に0でない定数を掛けても同じ直線を表す(=定数倍の不定性がある)のに注意。<br />
<br />
で、二乗の和を最小にする、と。<br />
<br />
<math><br />
\sum_i (c_1 x_i + c_2 y_i + c_3)^2 \longrightarrow \min<br />
</math><br />
<br />
定数倍の不定性を除去するために<math>c_1^2 + c_2^2 = 1</math>とすると、<br />
<br />
<math><br />
c_3 = - (c_1 \bar{x} + c_2 \bar{y})<br />
</math><br />
<br />
より、<br />
<br />
<math><br />
\left(\sum_i<br />
\begin{bmatrix}<br />
(x_i - \bar{x})^2 & (x_i - \bar{x})(y_i - \bar{y}) \\<br />
(x_i - \bar{x})(y_i - \bar{y}) & (y_i - \bar{y})^2<br />
\end{bmatrix}<br />
- \lambda I \right)<br />
\begin{bmatrix}<br />
c_1 \\ c_2<br />
\end{bmatrix}<br />
= \boldsymbol{0}<br />
</math><br />
<br />
が得られ、求めるものは分散・共分散行列の最小固有値に対応する固有ベクトルだと分かる。<br />
<br />
その1と同様に行列っぽく書くと、<br />
<br />
<math><br />
\boldsymbol{c}^\text{T} XX^\text{T} \boldsymbol{c} \longrightarrow \min<br />
</math><br><br />
<math><br />
X^\text{T} =<br />
\begin{bmatrix}<br />
x_1 & y_1 & 1 \\<br />
x_2 & y_2 & 1 \\<br />
\vdots & \vdots & \vdots<br />
\end{bmatrix}<br />
, \quad<br />
\boldsymbol{c} = [c1,\, c2,\, c3]^\text{T}<br />
</math><br />
<br />
で、<math>\boldsymbol{c}</math>は<math>XX^\text{T}</math>の最小固有値に対応する固有ベクトルになる。<br />
但し、正規化の方法が若干違う(<math>|\boldsymbol{c}| = 1</math>)ので注意。<br />
<br />
=== 直線以外への当てはめ ===<br />
当てはめに使っている方程式をぼーっと眺めてみると、<br />
<pre><br />
(係数1)(データ1) + (係数2)(データ2) + ・・・ = 0<br />
</pre><br />
という形をしている事が見えてくる。<br />
<br />
つまり、<math>(x_i^2,\, x_i,\, y_i, 1)</math>とすれば<math>y = ax^2 + bx + c</math>に当てはめたりできる。<br />
<br />
一般の2次曲線は<br />
<br />
<math><br />
\begin{bmatrix}<br />
x & y & 1<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
c_1 & c_2 & c_4 \\<br />
c_2 & c_3 & c_5 \\<br />
c_4 & c_5 & c_6<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
x \\ y \\ 1<br />
\end{bmatrix} = 0<br />
</math><br><br />
<math><br />
\Longleftrightarrow<br />
c_1 x^2 + 2 c_2 xy + c_3 y^2 + 2 c_4 x + 2 c_5 y + c_6 = 0<br />
</math><br><br />
<br />
で表されるので<br />
<br />
<math><br />
\begin{bmatrix}<br />
x^2 & 2xy & y^2 & 2x & 2y & 1<br />
\end{bmatrix}<br />
\boldsymbol{c} = 0<br />
</math><br />
<br />
とすれば、楕円とか双曲線とか放物線とかに当てはめられる。<br />
但し縮退する(2次の係数が0になるとか)場合があるので、計算できたからといっても安心できない。<br />
それに誤差ののり方がそれぞれの要素で変わってくるため、当てはめの結果が最適であるという保証が無い点にも注意が必要。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%81%9F%E3%81%8F%E3%81%95%E3%82%93%E3%81%AE4x4%E8%A1%8C%E5%88%97%E3%81%AE%E7%A9%8D
たくさんの4x4行列の積
2013-07-03T18:02:36Z
<p>白飯: 初版を執筆</p>
<hr />
<div>GPUは多量のデータを処理するのに向いているらしいので、よくある行列の積を計算するやつを[http://www.fmaj7b5.info/git?p=cuda.git;a=tree;f=mult_matrices;h=87c6b3bbe8d3a8f70511510c2f54d55ddee0a589;hb=474dc4b3cd13aea3ccaae408eac81bbb616c056b 作ってみた]。<br />
<br />
ただし大きな行列が速く計算できるというのは割とよく見るので、3次元画像処理でよく出てくる4x4行列の積をたくさん(100万個×10回)計算してみた。<br />
<br />
matrix_mult.cu:<br />
<syntaxhighlight><br />
__global__ void<br />
mult_matrix(float* C, const float* A, const float* B)<br />
{<br />
const size_t d(threadIdx.z);<br />
const size_t offset(16*(blockIdx.x*DIV + d));<br />
const size_t index(threadIdx.x + blockDim.y*threadIdx.y);<br />
<br />
__shared__ float _A[DIV][4*4], _B[DIV][4*4];<br />
<br />
_A[d][index] = A[offset + index];<br />
_B[d][index] = B[offset + index];<br />
<br />
float c_elem = 0.0;<br />
for (int k = 0; k < 4; ++k) {<br />
c_elem += _A[d][threadIdx.x + 4*k] * _B[d][k + 4*threadIdx.y];<br />
}<br />
<br />
C[offset + index] = c_elem;<br />
}<br />
</syntaxhighlight><br />
<br />
カーネルはあまり考えてない。各スレッドが答えの行列の1要素をそれぞれ計算する。<br />
もう少し並列計算っぽい雰囲気を出したい気もするけど、掛けて足す(積和演算)を1命令(fma)で処理できるらしいので、それに期待してみた。<br />
<br />
うちのボード(GeForce GTX 670)だと理論値は約<nowiki>2460 [GFLOPS]</nowiki>[http://www.gpureview.com/GeForce-GTX-670-card-669.html らしい]。どきどき。<br />
<br />
== 実行結果 ==<br />
<pre><br />
1 CPU: 546.0 [ms] (2.2 GFLOPS)<br />
<br />
kernel: 1.69661 [ms] (69.2208 GFLOPS)<br />
kernel: 1.6952 [ms] (69.2783 GFLOPS)<br />
kernel: 1.69296 [ms] (69.3699 GFLOPS)<br />
kernel: 1.69197 [ms] (69.4106 GFLOPS)<br />
kernel: 1.69302 [ms] (69.3673 GFLOPS)<br />
kernel: 1.69402 [ms] (69.3267 GFLOPS)<br />
kernel: 1.692 [ms] (69.4093 GFLOPS)<br />
kernel: 1.69446 [ms] (69.3084 GFLOPS)<br />
kernel: 1.6921 [ms] (69.4053 GFLOPS)<br />
kernel: 1.69213 [ms] (69.404 GFLOPS)<br />
GPU: 328.0 [ms] (3.6 GFLOPS)<br />
</pre><br />
<br />
CPU(Core i7 3770)の方はVisual C++ 2010 expressが自動並列化とかしてくれないらしくSIMD命令を使ってない上、OpenMPもライブラリが無くて使えなかったので参考程度。本気を出せば、この32倍くらいは出るかも。<br />
<br />
で、GPUの方だけど。・・・えー、100万個の行列積は<nowiki>1.7 [ms]</nowiki>弱で計算できてるけど、それ以外の所に18倍くらいの時間がかかってる。。<br />
っていうか、カーネルだけの演算性能も理論値の2.8%強しか出てない。<br />
<br />
原因はおそらく、コレ。<br />
<pre><br />
> bandwidthTest.exe --memory=pinned<br />
<pre><br />
[bandwidthTest.exe] starting...<br />
<br />
bandwidthTest.exe Starting...<br />
<br />
Running on...<br />
<br />
Device 0: GeForce GTX 670<br />
Quick Mode<br />
<br />
Host to Device Bandwidth, 1 Device(s), Pinned memory<br />
Transfer Size (Bytes) Bandwidth(MB/s)<br />
33554432 11822.3<br />
<br />
Device to Host Bandwidth, 1 Device(s), Pinned memory<br />
Transfer Size (Bytes) Bandwidth(MB/s)<br />
33554432 12040.7<br />
<br />
Device to Device Bandwidth, 1 Device(s)<br />
Transfer Size (Bytes) Bandwidth(MB/s)<br />
33554432 150019.5<br />
<br />
[bandwidthTest.exe] test results...<br />
PASSED<br />
</pre><br />
<br />
Host <-> Device間はとりあえずおいといて、Device間の方をみると大体<nowiki>150 [GB/s]</nowiki>の転送速度らしい。4x4行列の積を1回計算するには2個の行列を読み込んで1個の行列を書き込む必要があるので、各要素が単精度実数とすると<nowiki>192 [byte]</nowiki>のデータ転送が発生する。つまり、毎秒約7800万組のデータが転送できる計算になる。4x4行列の積の計算量(和と積の数)は112なので、この転送速度だと毎秒87.5Gの浮動小数点演算が発生する。あ、上で見た数字70Gに近いね。ひょっとしたらメモリの読み書きが同時にできるかもしれないけど、まあ、それはそれ。<br />
<br />
単純計算だと、<br />
<br />
<math><br />
\frac{150 \ \text{GB}}{3 n^2 \cdot 4 \ \text{B}} \cdot n^2 (2n - 1) = 12.5 \cdot (2n - 1) \geq 2460 \ [\text{GFLOPS}]<br />
</math><br />
<br />
となる最小の整数<math>n</math>は99なので、少なくともうちの環境では行列の大きさがもっと大きくならないと計算量に対してデータの転送量が追いつかないということになる。従って、GPUはそのほとんどの時間を手持ちぶさたで過ごしていたようだ。<br />
<br />
さらに言えばHost <-> Device間はもう一桁遅いので、大量の小さな行列を毎回Device側に転送して計算するような処理は効率が悪くなる。ただし、Host側とのデータ転送が無い場合は小さな(多量の)問題をGPUで処理してもCPUで解くのと遜色ない性能が出るとも考えられる。<br />
<br />
というわけで、入力に対して途中計算が爆発的に増える組み合わせ計算のような処理向きかな、と思った。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%A1%E3%83%A2%E5%B8%B3@fmaj7b5.info
メモ帳@fmaj7b5.info
2013-07-03T16:11:32Z
<p>白飯: /* CUDA */ 行列積の項目を追加</p>
<hr />
<div>== はじめに ==<br />
<br />
ここに書かれている内容は個人的なメモであり、正当性は保証しません。<br />
<br />
#コピペして怒られても知らないよ。<br />
<br />
== 基本 ==<br />
* [[線形代数]]<br />
* [[点の移動・回転]]<br />
* [[射影変換(2次元)]]<br />
* [[最小二乗法とか]]<br />
* [[非線形最適化]]<br />
<br />
== コンピュータ・ビジョン ==<br />
* [[カメラモデル]]<br />
* [[3次元復元]]<br />
<br />
[http://szeliski.org/Book いいもの]見つけた。<br />
<br />
== CUDA ==<br />
* [[準備]]<br />
* [[Hello CUDA!]]<br />
* [[画像の入出力 (非CUDA)]]<br />
* [[二値化]]<br />
* [[二値化 (deviceメモリ周りを扱いやすく)]]<br />
* [[たくさんの4x4行列の積]]<br />
<br />
== そのほか ==<br />
* [[おすすめプログラミング環境]]<br />
* [[lapackeインストールメモ]]<br />
* [[月の満ち欠けと月食]]<br />
* [[Windows7のUsersを(クリーンインストール時に)移動する法]]</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E7%B7%9A%E5%BD%A2%E4%BB%A3%E6%95%B0
線形代数
2013-06-18T12:58:45Z
<p>白飯: /* ベクトルの計算 */ typoを修正</p>
<hr />
<div>= スカラー =<br />
座標系に依らない数、らしい。向きや方向を持たない。たぶん一つの値で表現できると思う。このサイトでは小文字で表す事にする。<br />
<br />
例:<br />
<math><br />
a, \ x, \ \alpha, \dots<br />
</math><br />
みたいな。<br />
<br />
= ベクトル =<br />
これも座標系に依らないらしい。依存するのは座標軸を決めて、それを基準にして測った値の方。これをベクトルの成分という。このサイトではベクトルを太文字で表す。<br />
<br />
要するに、座標系に依らない(2次元の)ベクトル<math>\boldsymbol{v}</math>があって、座標軸の組(原点は同じ)<math>(\boldsymbol{e}_1,\,\boldsymbol{e}_2)</math>と<math>(\boldsymbol{e}_1^\prime,\,\boldsymbol{e}_2^\prime)</math>、それぞれの成分が<math>(x_1,\,x_2)</math>、<math>(x_1^\prime,\,x_2^\prime)</math>とすると、次の式が成り立つ。<br />
<br />
<math><br />
\boldsymbol{v} = \boldsymbol{e} x_1 + \boldsymbol{e}_2 x_2<br />
= \boldsymbol{e}_1^\prime x_1^\prime + \boldsymbol{e}_2^\prime x_2^\prime<br />
</math><br />
<br />
ただし、ベクトルの成分をベクトルと言っちゃう事もあるので注意すべし。普通は座標系を直交座標系(デカルト座標系)など1種類に決める事が多いので、問題は生じにくい。行間を読め、ということか。<br />
<br />
ちなみに、単に任意の<math>n</math>個の値を並べてもベクトルにならない場合があるので、うかつな事は言わないこと。詳しい定義はググるべし。まあ、直線!とか平面!とかそんな感じだったと思う。「線形」っていうくらいだし。<br />
<br />
== ベクトルの計算 ==<br />
<math>d</math>次元ベクトル<math>\boldsymbol{x},\,\boldsymbol{y} \in \mathbb{R}^d</math>があるとする。それぞれの<math>i</math>番目の成分を<math>x_i,\,y_i</math>で表す。<br />
<br />
=== 和と差 ===<br />
普通に成分同士を足したり引いたりすれば良い。<br />
<br />
<math><br />
\boldsymbol{x} \pm \boldsymbol{y} = \{x_i \pm y_i\}_i<br />
</math><br />
<br />
あ、<math>\{\cdot\}_i</math>は数列の<math>i</math>番目要素という意味にした。<br />
<br />
=== 内積 ===<br />
ベクトルには掛け算が2種類もある。内積はそのうちの一つで、計算結果がスカラーになる。成分ごとに掛け算して足す。<br />
<br />
<math><br />
\boldsymbol{x} \cdot \boldsymbol{y} = \sum_i x_i y_i<br />
</math><br />
<br />
=== 外積 ===<br />
内積は同じ基底の成分同士で計算したのに対し、外積は異なる成分同士で計算する。2次元の時は1個(スカラー)、3次元の時は3次元ベクトルになる。4次元以上の時は・・・知らん。<br />
<br />
;2次元<br />
<math><br />
\boldsymbol{x} \times \boldsymbol{y} = x_1 y_2 - x_2 y_1<br />
</math><br />
<br />
;3次元<br />
<math><br />
\boldsymbol{x} \times \boldsymbol{y} = (x_2 y_3 - x_3 y_2,\ x_3 y_1 - x_1 y_3,\ x_2 y_3 - x_3 y_2)<br />
</math><br />
<br />
=== 大きさ ===<br />
それ自身との内積のルート。<br />
<br />
<math><br />
|\boldsymbol{x}| = \sqrt{\boldsymbol{x} \cdot \boldsymbol{x}} = \sqrt{\sum_i x_i x_i}<br />
</math><br />
<br />
特に大きさが1のものを'''単位ベクトル'''と呼んだりする。<br />
<br />
= 行列 =<br />
スカラー<math>x</math> (添え字なし)、ベクトル<math>\{x_i\}</math> (添え字1個)とくると、添え字2個が出てくるのが自然の摂理。ベクトルが1次元配列で表現できるのに対して、行列は行・列で表せる表になる。何か言い回しが難しい。とにかく、行列は大文字で表すとする。<br />
<br />
例:<br><br />
<math><br />
A = \{a_{ij}\} =<br />
\begin{bmatrix}<br />
a_{11} & \cdots & a_{1n} \\<br />
\vdots & \ddots & \vdots \\<br />
a_{m1} & \cdots & a_{mn}<br />
\end{bmatrix}<br />
</math><br />
<br />
ベクトルも行または列の長さが1の行列として扱う事ができ、縦に長いやつを縦ベクトル(列ベクトル)、横に長いやつを横ベクトル(行ベクトル)と呼ぶ。このサイトではデフォルトを縦ベクトルとして<math>\boldsymbol{x}</math>で表し、横ベクトルを<math>\boldsymbol{x}^\text{T}</math>で表す、確率が高い。<br />
ベクトルも行列の1種であることから分かるように、行列も単に数字を並べれば良いわけでは無い。たぶん、行列<math>A</math>、スカラー<math>a,\,b</math>、ベクトル<math>\boldsymbol{x},\,\boldsymbol{y}</math>に対して(少なくとも)こんな式が成り立たなくてはいけないはず。<br />
<br />
<math><br />
A (a \boldsymbol{x} + b \boldsymbol{y}) = a A \boldsymbol{x} + b A \boldsymbol{y}<br />
</math><br />
<br />
詳しくはググる事。<br />
<br />
== 名前が付いてる行列 ==<br />
;正方行列<br />
行数と列数が同じ行列。正方形。<br />
<br />
;零行列<br />
成分が全て0。ブラックホール。<br />
<br />
;対角行列<br />
対角成分(<math>i=j</math>な所)にしか値が無い行列.あとは0。<br />
<br />
<math><br />
D =<br />
\begin{bmatrix}<br />
d_1 & & 0 \\<br />
& \ddots & \\<br />
0 & & d_n<br />
\end{bmatrix}<br />
= \text{diag}(d_1,\, \dots, d_n)<br />
</math><br />
<br />
対角成分が全て1のものを特に'''単位行列'''と呼ぶ。<br />
<br />
;上三角行列<br />
対角成分より上側にしか値が無い行列。あとは0。<br />
<br />
<math><br />
U =<br />
\begin{bmatrix}<br />
* & \cdots & * \\<br />
& \ddots & \vdots \\<br />
0 & & *<br />
\end{bmatrix}<br />
</math><br />
<br />
;下三角行列<br />
対角成分より下側にしか値が無い行列。上三角行列の逆。<br />
<br />
<math><br />
L =<br />
\begin{bmatrix}<br />
* & & 0 \\<br />
\vdots & \ddots & \\<br />
{}* & \cdots & *<br />
\end{bmatrix}<br />
</math><br />
<br />
;対称行列<br />
<math>a_{ij} = a_{ji}</math>な行列。<br />
<br />
;歪対称行列<br />
<math>a_{ij} = -a_{ji}</math>な行列。<br />
<br />
== 行列の計算 ==<br />
行列<math>A = \{a_{ij}\}</math>、<math>B = \{b_{ij}\}</math>があるとする。<br />
<br />
=== 和と差 ===<br />
ベクトルと同じように、成分ごとに足したり引いたりすれば良い。当然、行数・列数が同じでないとダメ。<br />
<br />
<math><br />
A \pm B = \{a_{ij} \pm b_{ij}\}_{ij}<br />
</math><br />
<br />
=== 積 ===<br />
計算結果の<math>(i,\,j)</math>成分は、左側の行列の第<math>i</math>行目の横ベクトルと右側の行列の第<math>j</math>列目の縦ベクトルとの内積になる。なので、左側の行列の幅と右側の行列の高さが同じでないといけない。<br />
<br />
<math><br />
A B = \left\{\sum_k a_{ik} b_{kj} \right\}_{ij}<br />
</math><br />
<br />
=== 転置 ===<br />
<math>(i,\,j)</math>成分と<math>(j,\,i)</math>成分を入れ替える。ここでは行列<math>A</math>の転置を<math>A^\text{T}</math>で表す。人によっては<math>{}^\text{T}A</math>とか<math>A^\prime</math>とかを用いる事もある。<br />
<br />
<math><br />
A^\text{T} = \{a_{ji}\}_{ij}<br />
</math><br />
<br />
=== 逆行列 ===<br />
行列<math>A</math>の逆行列は<math>A^{-1}</math>と書いてAのインバースと読む。Aと掛けると単位行列になってしまう不思議な行列。<br />
<br />
<math><br />
A A^{-1} = A^{-1} A = I<br />
</math><br />
<br />
ただし<math>I</math>は単位行列を表す。<br />
<br />
<math><br />
A^{-1} = \left\{<br />
\frac{1}{(n-1)!} \varepsilon^{i k_1 \dots k_{n-1}} \varepsilon_{j l_1 \dots l_{n-1}}<br />
a_{k_1}^{l_1} \dots a_{k_{n-1}}^{l_{n-1}}<br />
\right\}_{ij} / |A|<br />
</math><br />
<br />
=== 行列式 ===<br />
計算方法は教えてもらえるが、何に使うのかピンと来ない値。こゆうちの積とか。行列<math>A</math>の行列式は<math>|A|</math>とか<math>\text{det}(A)</math>とか書く。<br />
<br />
;2x2行列<br />
<math><br />
\begin{vmatrix}<br />
a_{11} & a_{12} \\<br />
a_{21} & a_{22}<br />
\end{vmatrix}<br />
= a_{11} a_{22} - a_{12} a_{21}<br />
</math><br />
<br />
外積みたいだな。<br />
<br />
;3x3行列<br />
<math><br />
\begin{vmatrix}<br />
a_{11} & a_{12} & a_{13} \\<br />
a_{21} & a_{22} & a_{23} \\<br />
a_{31} & a_{32} & a_{33}<br />
\end{vmatrix}<br />
= a_{11}<br />
\begin{vmatrix}<br />
a_{22} & a_{23} \\<br />
a_{32} & a_{33}<br />
\end{vmatrix}<br />
- a_{21}<br />
\begin{vmatrix}<br />
a_{12} & a_{13} \\<br />
a_{32} & a_{33}<br />
\end{vmatrix}<br />
+ a_{31}<br />
\begin{vmatrix}<br />
a_{12} & a_{13} \\<br />
a_{22} & a_{23}<br />
\end{vmatrix}<br />
</math><br />
<br />
;それ以上<br />
3x3の場合と同様にバラしていけば計算できるはず。というか、手計算でやる事はまず無いと思う。<br />
<br />
=== トレース ===<br />
対角成分の和。行列<math>A</math>のトレースは<math>\text{tr}(A)</math>と書いたり。こゆうちの和みたいな。<br />
<br />
<math><br />
\text{tr}(A) = \sum_i a_{ii}<br />
</math><br />
<br />
== 固有値 ==<br />
<math><br />
A \boldsymbol{v} = \boldsymbol{v} \lambda<br />
</math><br />
<br />
を満たすベクトル<math>\boldsymbol{v}</math>を固有ベクトル、<math>\lambda</math>を固有値という。この<math>\boldsymbol{v},\,\lambda</math>の組み合わせは複数出てくるので、固有値<math>\lambda</math>に対応する固有ベクトル<math>\boldsymbol{v}</math>のように表現することが多い。大抵、最大か最小固有値に対応するものが重要っぽい。<br />
<br />
固有値と固有ベクトルの算出は、次の特性方程式を解くことによる。<br><br />
<math><br />
|A \boldsymbol{v} - \lambda I| = 0<br />
</math><br />
<br />
但しn次正方行列に対しては一般にn次方程式が出てくるため、4次以上になると計算が難しい。従って計算機であれば繰り返し計算などの近似解法を用いる方が簡単。というか、そういうライブラリがあるので利用するのが賢い。<br />
<br />
== 行列の大きさ ==<br />
[http://ja.wikipedia.org/wiki/%E8%A1%8C%E5%88%97%E3%83%8E%E3%83%AB%E3%83%A0 Wikipedia]によると、色々あるらしい。<br />
<br />
;フロベニウスノルム<br />
<math><br />
||A||_\text{F} = \sqrt{\sum_i \sum_j (a_{ij})^2} = \sqrt{\text{tr}(A^\text{T} A)}<br />
</math><br />
<br />
これは固有値の和になるようだ。<br />
<br />
== 行列の分解 ==<br />
;LU分解<br />
行列を下三角行列(<math>L</math>)と上三角行列(U)の積に分解する方法。ガウスの消去法をやると上三角行列が出てくるが、それに至るまでの操作を行列で表現すると<math>L</math>の部分も分かる。<br />
<br />
LU分解を利用すると、連立線形方程式が簡単に(後進代入)解ける。<br><br />
<math><br />
A \boldsymbol{x} = (L U) \boldsymbol{x} = \boldsymbol{b}<br />
</math><br><br />
<math><br />
\Rightarrow \,<br />
\left\{<br />
\begin{array}{l}<br />
\boldsymbol{y} = L^{-1} \boldsymbol{b} \\<br />
U \boldsymbol{x} = \boldsymbol{y}<br />
\end{array}<br />
\right.<br />
</math><br />
<br />
;QR分解<br />
行列を直交行列(<math>Q</math>)と上三角行列(<math>R</math>)の積に分解する方法。上三角行列をRで表す宗派があるようで、LU分解もLR分解という別名がある。ランク落ちしてても使えるらしい。<br />
<br />
<math><br />
A = Q R<br />
</math><br />
<br />
;固有値分解<br />
正方行列を固有ベクトルを横に並べた行列<math>V</math>と対応する固有値を対角成分に持つ行列<math>D</math>の積に分解する方法。<br />
<br />
<math><br />
\begin{array}{l}<br />
A \left[ \boldsymbol{v}_1,\, \dots, \boldsymbol{v}_d \right] <br />
= \left[ \boldsymbol{v}_1,\, \dots, \boldsymbol{v}_d \right] \text{diag}\left( \lambda_1, \dots, \lambda_d \right)<br />
= V D \\<br />
A = V D V^{\text{T}}<br />
\end{array}<br />
</math><br />
<br />
;特異値分解(SVD)<br />
行列を直交行列<math>U,\,V</math>と対角行列<math>D</math>の積に分解する方法。正方行列以外にも使えるため便利。<br />
<br />
<math><br />
A = U D V^\text{T}<br />
</math><br />
<br />
ちなみに上式から明らかなように、<br><br />
<math><br />
\begin{array}{l}<br />
A A^\text{T} = U D^2 U^\text{T} \\<br />
A^\text{T} A = V D^2 V^\text{T} \\<br />
\end{array}<br />
</math><br><br />
であるため、データ<math>A=\left[ \boldsymbol{x}_1, \dots, \boldsymbol{x}_n \right]</math>の内積<math>A^\text{T} A</math>が分かれば、その固有ベクトルを使って共分散行列<math>A A^\text{T}</math>の固有ベクトルを次のように求める事ができる。<br><br />
<math><br />
U = A V D^{-1}<br />
</math><br><br />
何に使えるかは知らない。<br />
<br />
= その先? =<br />
添え字の数が0、1、2と増えてきたからには、[http://ja.wikipedia.org/wiki/%E5%A4%9A%E9%87%8D%E7%B7%9A%E5%9E%8B%E4%BB%A3%E6%95%B0 その先]があるに違いない。けど、興味ない(笑)。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E4%BA%8C%E5%80%A4%E5%8C%96_(device%E3%83%A1%E3%83%A2%E3%83%AA%E5%91%A8%E3%82%8A%E3%82%92%E6%89%B1%E3%81%84%E3%82%84%E3%81%99%E3%81%8F)
二値化 (deviceメモリ周りを扱いやすく)
2012-11-11T14:42:08Z
<p>白飯: 初版を執筆</p>
<hr />
<div>deviceメモリの確保・解放をやるクラスを[http://www.fmaj7b5.info/git?p=cuda.git;a=tree;h=869779783d0c06c34e02062ac7d23dac316e73a9;hb=869779783d0c06c34e02062ac7d23dac316e73a9 作った]。<br />
カーネルに渡す時はdeviceメモリのポインタと縦・横のバイト数なんかのデータを保持する構造体を渡すようにしてみた。<br />
<br />
<br />
binarize.cu:<br />
<syntaxhighlight><br />
#include "binarize.cuh"<br />
#include "DeviceMemory.cuh"<br />
<br />
using namespace FM7b5;<br />
<br />
void<br />
FM7b5::binarize_gpu(ImageGray& out, const ImageGray& in, const uint8_t thres)<br />
{<br />
if (in.width() != out.width() || in.height() != out.height()) {<br />
throw std::runtime_error("sizes of input and output images are diferent.");<br />
}<br />
<br />
const size_t width(in.width()), height(in.height()), bpp(in.bpp());<br />
<br />
const size_t threads_per_dim(32);<br />
dim3 threads_per_block(threads_per_dim, threads_per_dim);<br />
dim3 blocks_per_grid((width + threads_per_block.x - 1)/ threads_per_block.x,<br />
(height + threads_per_block.y - 1)/ threads_per_block.y);<br />
<br />
// allocate input/output memories<br />
memory::LinearPitch<uint8_t> d_in(width, height), d_out(width, height);<br />
<br />
// copy an input image to device memory<br />
d_in.copy_from(in.data(), bpp * width, height, in.stride());<br />
<br />
// launch kernel<br />
binarize<<<blocks_per_grid, threads_per_block>>>(d_out.ref(), d_in.ref(), width, height, thres);<br />
<br />
// copy the result back to host memory<br />
d_out.copy_to(out.data(), bpp * width, height, out.stride());<br />
}<br />
</syntaxhighlight><br />
<br />
<br />
ひょんな事からカーネルはヘッダファイルに移動。<br />
<br />
binarize.cuh:<br />
<syntaxhighlight><br />
#include "binarize.h"<br />
#include "MDView.cuh"<br />
<br />
namespace FM7b5<br />
{<br />
template <class T, class U><br />
__global__ void<br />
binarize(const MDView<T, 2> out, const MDView<U, 2> in, const size_t width, const size_t height, const uint8_t thres)<br />
{<br />
const size_t w(blockDim.x * blockIdx.x + threadIdx.x);<br />
const size_t h(blockDim.y * blockIdx.y + threadIdx.y);<br />
<br />
if (w >= width || h >= height) {<br />
return;<br />
}<br />
<br />
out[h][w] = (in[h][w] < thres) ? 0 : 255;<br />
}<br />
}<br />
</syntaxhighlight><br />
<br />
<br />
これでメモリ周りが多少すっきりしたかなぁ。2次元データを(x, y)で扱うか[y][x]で扱うか迷ってたり。<br />
<br />
あとtemplateを使ったせいかReleaseでコンパイルしないと非効率なPTXが生成されてしまうみたい。デバッグ用の情報を出力する(-G)オプション<br />
のせいで途中の一時的なクラスたちが残るのかも。<br />
<br />
それにしても1命令でも削ったり効率のいいものに置き換えたりしたいdeviceコードで、こんな悠長なことしててもいいんだろうか。<br />
実は*.cu/*.gpuから生成される*.ptxを眺めると、ちょっとした型の違いとかで出てくるものに違いが生じるのが分かる。<br />
特にビットサイズまわりが怪しくて16bit->32bit->16bitみたいな無意味な変換を平気で挟んでくるので、CUDAのコンパイラをあまり信用していなかったり。<br />
<br />
最終的にはPTXからもう一段階コンパイルが行われてGPUに渡されるのでそのときに最適化されていれば問題ないと思うけど、ぱっと見で無駄なコードがあると精神衛生上よくない。<br />
幸い計算結果が合わないとか致命的なヤツにはまだ遭遇していないので、コンパイラの性能向上に期待して待つことにする。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E4%BA%8C%E5%80%A4%E5%8C%96
二値化
2012-11-11T14:07:26Z
<p>白飯: 初版を執筆</p>
<hr />
<div>記念すべき画像処理第1回。<br />
<br />
白か黒かグレーな画像が入力で、適当な閾値より大きいか小さいかで白・黒を決める。<br />
この程度の処理ならCPUでやった方が間違いなく速いけど、画像の受け渡しと計算ができれば後は何とでもなるはず ([http://www.fmaj7b5.info/git?p=cuda.git;a=tree;h=7b77a912a4a1202f677ae9dbff672758e2b945e4;hb=7b77a912a4a1202f677ae9dbff672758e2b945e4 ソース])。<br />
<br />
<br />
binarize.cu:<br />
<syntaxhighlight><br />
#include "binarize.cuh"<br />
<br />
using namespace FM7b5;<br />
<br />
void<br />
FM7b5::binarize_gpu(ImageGray& out, const ImageGray& in, const uint8_t thres)<br />
{<br />
<br />
if (in.width() != out.width() || in.height() != out.height()) {<br />
throw std::runtime_error("sizes of input and output images are diferent.");<br />
}<br />
<br />
const size_t width(in.width()), height(in.height()), bpp(in.bpp());<br />
<br />
uint8_t* d_in(nullptr);<br />
uint8_t* d_out(nullptr);<br />
size_t in_pitch(0), out_pitch(0);<br />
<br />
const size_t threads_per_dim(32);<br />
dim3 threads_per_block(threads_per_dim, threads_per_dim);<br />
dim3 blocks_per_grid((width + threads_per_block.x - 1)/ threads_per_block.x,<br />
(height + threads_per_block.y - 1)/ threads_per_block.y);<br />
<br />
cudaError_t status;<br />
<br />
// allocate input/output memories<br />
status = cudaMallocPitch(&d_in, &in_pitch, width * bpp, height);<br />
if (status != cudaSuccess) {<br />
goto on_error_in;<br />
}<br />
<br />
status = cudaMallocPitch(&d_out, &out_pitch, width * bpp, height);<br />
if (status != cudaSuccess) {<br />
goto on_error_out;<br />
}<br />
<br />
// copy an input image to device memory<br />
status = cudaMemcpy2D(d_in, in_pitch, in.data(), in.stride(), bpp * width, height, cudaMemcpyHostToDevice);<br />
if (status != cudaSuccess) {<br />
goto on_error_proc;<br />
}<br />
<br />
// launch kernel<br />
binarize<<<blocks_per_grid, threads_per_block>>>(d_out, out_pitch, d_in, in_pitch, width, height, thres);<br />
<br />
// copy the result back to host memory<br />
status = cudaMemcpy2D(out.data(), out.stride(), d_out, out_pitch, bpp * width, height, cudaMemcpyDeviceToHost);<br />
if (status != cudaSuccess) {<br />
goto on_error_proc;<br />
}<br />
<br />
// free device memories<br />
cudaFree(d_out);<br />
cudaFree(d_in);<br />
<br />
return;<br />
<br />
// error handling<br />
on_error_proc:<br />
cudaFree(d_out);<br />
on_error_out:<br />
cudaFree(d_in);<br />
on_error_in:<br />
throw std::runtime_error(cudaGetErrorString(status));<br />
}<br />
<br />
__global__<br />
void<br />
FM7b5::binarize(uint8_t* out, const size_t out_pitch, const uint8_t* in, const size_t in_pitch, const size_t width, const size_t height, const uint8_t thres)<br />
{<br />
const size_t w(blockDim.x * blockIdx.x + threadIdx.x);<br />
const size_t h(blockDim.y * blockIdx.y + threadIdx.y);<br />
<br />
if (w >= width || h >= height) {<br />
return;<br />
}<br />
<br />
out[out_pitch * h + w] = (in[in_pitch * h + w] < thres) ? 0 : 255;<br />
}<br />
</syntaxhighlight><br />
<br />
Device側の入力、出力のメモリを用意して、hostにある入力画像データをdeviceにコピーして、カーネル走らせて、終わったらdeviceからhostにコピーして、最後に後始末。<br />
手順としては決まりきってるけど、なんか面倒。非同期転送とかストリームとか使うようになったら、もっと面倒そう。。<br />
<br />
あ、例外処理をgotoでやるのは個人的な趣味なので気にしないように。<br />
<br />
カーネルbinarize()はやってることが少ない割に引数がごちゃごちゃしている印象。構造体にまとめないとなぁ。<br />
カーネルの引数は値渡しなので、std::vectorみたいなのを作って渡せるようにすると必ずコピーが発生するし、結構気を遣う。<br />
このあたりは次節で。。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E7%94%BB%E5%83%8F%E3%81%AE%E5%85%A5%E5%87%BA%E5%8A%9B_(%E9%9D%9ECUDA)
画像の入出力 (非CUDA)
2012-11-11T13:34:21Z
<p>白飯: 初版を執筆</p>
<hr />
<div>いきなり脇道。<br />
<br />
CUDAは画像処理が速くなるというウワサなので、画像の読み書きがいるなぁ、と。<br />
適当なライブラリを拾ってきてもいいけど、インストールやらリンクやら説明するのが面倒なので最低限のものを作成。<br />
<br />
作るのは手書きもできる画像フォーマット[http://netpbm.sourceforge.net/ Netpbm]より、グレイスケール画像のpgmの入出力を[http://www.fmaj7b5.info/git?p=cuda.git;a=tree;h=8b2c111ed060599c6bc93b48732d36f8aa48eb2b;hb=8b2c111ed060599c6bc93b48732d36f8aa48eb2b 書いてみた]。<br />
以下は使用例。<br />
<br />
<br />
imageio_test.cpp:<br />
<br />
<syntaxhighlight><br />
#include "Image.h"<br />
#include "ImageIO.h"<br />
<br />
using namespace FM7b5;<br />
<br />
int _tmain(int argc, _TCHAR* argv[])<br />
{<br />
ImageGray image;<br />
<br />
try {<br />
image = loadPGM("..\\img\\sine.pgm");<br />
}<br />
catch (std::exception& e)<br />
{<br />
std::cout << e.what() << std::endl;<br />
return -1;<br />
}<br />
<br />
/* invert pixel values */<br />
ImageGray out(image.width(), image.height());<br />
for (size_t h = 0; h < image.height(); ++h) {<br />
for (size_t w = 0; w < image.width(); ++w) {<br />
out(w, h) = 255 - image(w, h);<br />
}<br />
}<br />
<br />
savePNM(out, "result.pgm");<br />
<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
loadなんちゃらで画像が読めてsaveなんちゃらで保存。(x, y)の形式で画素にアクセスできる。それだけ。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%A1%E3%83%A2%E5%B8%B3@fmaj7b5.info
メモ帳@fmaj7b5.info
2012-11-11T09:01:54Z
<p>白飯: /* CUDA */ 節を追加</p>
<hr />
<div>== はじめに ==<br />
<br />
ここに書かれている内容は個人的なメモであり、正当性は保証しません。<br />
<br />
#コピペして怒られても知らないよ。<br />
<br />
== 基本 ==<br />
* [[線形代数]]<br />
* [[点の移動・回転]]<br />
* [[射影変換(2次元)]]<br />
* [[最小二乗法とか]]<br />
* [[非線形最適化]]<br />
<br />
== コンピュータ・ビジョン ==<br />
* [[カメラモデル]]<br />
* [[3次元復元]]<br />
<br />
[http://szeliski.org/Book いいもの]見つけた。<br />
<br />
== CUDA ==<br />
* [[準備]]<br />
* [[Hello CUDA!]]<br />
* [[画像の入出力 (非CUDA)]]<br />
* [[二値化]]<br />
* [[二値化 (deviceメモリ周りを扱いやすく)]]<br />
<br />
== そのほか ==<br />
* [[おすすめプログラミング環境]]<br />
* [[lapackeインストールメモ]]<br />
* [[月の満ち欠けと月食]]<br />
* [[Windows7のUsersを(クリーンインストール時に)移動する法]]</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E6%BA%96%E5%82%99
準備
2012-11-11T08:53:00Z
<p>白飯: 基本いろいろを追記、細かな修正</p>
<hr />
<div>2周くらい遅れのCUDAをやってみる。<br />
ここではWindowsを使うけど、CUDAはOSに依存しないので他でも大丈夫・・・だと思う。<br />
<br />
== プログラム環境を整える ==<br />
<br />
以下の二つをインストール:<br />
* Visual C++ 2010 Express<br />
* CUDA Toolkit<br />
<br />
ググれば出てくるはずだけど、CUDAのダウンロード先で迷ったことがあるのでヒントを少々。CUDA周りのツール類は「NVIDIA > CUDA Zone > Download」と辿っていけば見つかる。とりあえずCUDA ZoneでDownloadとかToolkitとか書いてあるところをポチポチしよう。<br />
<br />
細かいところは先人のwebページを参考にしてね。<br />
<br />
== コンパイル方法 ==<br />
<br />
最低限必要なのは以下の三つ:<br />
* *.cuをCUDAコンパイラ(nvcc)でコンパイルするように指定する<br />
* CUDAコンパイラに対象にするGPUのアーキテクチャ(-arch)とcompute capability(-code)を指定する<br />
* cudart.lib (linuxだとlibcudart)をくっつけるよう指定する<br />
<br />
=== Visual C++ ===<br />
<br />
GUIは説明が面倒だ・・。<br />
# プロジェクトを追加<br />
#* コンソールアプリケーションとか、適当に<br />
# ビルドのカスタマイズ > CUDA x.x にチェック<br />
# *.cuソースを追加<br />
# プロパティ > CUDA C/C++ > Device > Code Generation で対象GPUを指定<br />
# リンカー > 入力 > 追加の依存ファイル にcudart.libを追加<br />
<br />
まあ、詳しくはググるべし。<br />
<br />
=== CMake ===<br />
<br />
Linuxな人はこっちが簡単かも。WindowsでVC++な人でも使えるので、便利な人には便利。<br />
<br />
<pre>CMakeLists.txt:<br />
<br />
cmake_minimum_required (VERSION 2.8)<br />
project (HelloProject)<br />
<br />
find_package (CUDA REQUIRED)<br />
<br />
cuda_add_executable (hello hello.cu)<br />
</pre><br />
cmake_minimum_requiredのバージョンは手持ちの環境に合わせただけなので、もっと低くても大丈夫かも知れない。<br />
<br />
=== GNU Make ===<br />
<br />
いや、漢(おとこ)ならmakeだ。という人の場合。CMakeもLinuxなら最終的にmakeするじゃん、というツッコミは無しで。<br />
<br />
<pre>cuda.mk:<br />
<br />
# Set a default location of CUDA<br />
CUDA_HOME ?= /usr/local/cuda<br />
<br />
# Set an architecture<br />
ifeq ($(ARCH),)<br />
ARCH = $(shell uname -i | tr '[:upper:]' '[:lower:]')<br />
endif<br />
<br />
# Binary path to CUDA<br />
CUDA_BINDIR = $(CUDA_HOME)/bin<br />
<br />
# Set library dir and nvcc command<br />
ifeq ($(ARCH),x86_64)<br />
CUDA_LIBDIR = $(CUDA_HOME)/lib64<br />
NVCC = $(CUDA_BINDIR)/nvcc<br />
else<br />
CUDA_LIBDIR = $(CUDA_HOME)/lib<br />
NVCC = $(CUDA_BINDIR)/nvcc -m32<br />
endif<br />
<br />
# Set default parameters for CUDA libraries<br />
CUDA_LDFLAGS ?= -L$(CUDA_LIBDIR)<br />
CUDA_LDLIBS ?= -lcudart<br />
<br />
<br />
# Build rules<br />
%.o: %.cu<br />
$(NVCC) $(NVCC_FLAGS) -c -o $@ $^<br />
<br />
%.ptx: %.cu<br />
$(NVCC) $(NVCC_FLAGS) --ptx -o $@ $^<br />
%.ptx: %.gpu<br />
$(NVCC) $(NVCC_FLAGS) --ptx -o $@ $^<br />
<br />
%.gpu: %.cu<br />
$(NVCC) $(NVCC_FLAGS) --gpu -o $@ $^<br />
<br />
</pre><br />
<br />
<pre>Makefile:<br />
<br />
include ./cuda.mk<br />
<br />
LDFLAGS = $(CUDA_LDFLAGS)<br />
LDLIBS = $(CUDA_LDLIBS)<br />
<br />
NVCC_FLAGS = --generate-code arch=compute_30,code=\'compute_30,sm_30\'<br />
<br />
TARGETS = hello<br />
<br />
all: $(TARGETS)<br />
<br />
clean:<br />
$(RM) $(TARGETS) *.o *~<br />
<br />
hello: hello.o<br />
</pre><br />
<br />
適当なので、あくまでも参考までに。大体、Makefileを手書きする変態さんならもっといいのを既に書いてるでしょう・・。<br />
あ、よく見たら[http://docs.nvidia.com/cuda/index.html Gettingなんちゃらガイド]にMakefileのサンプルが。<br />
<br />
== 基本いろいろ ==<br />
<br />
=== Host/device ===<br />
<br />
CPUとGPUが別々になっているPCだと、CPU+メインメモリ・GPU+VRAM(?)という構成でそれぞれ管理されている。前者をhost、後者をdeviceという。<br />
イメージとしては、hostでプログラムが動いていて面倒な計算をするときにdeviceへ丸投げする感じ。なので、device側はGPUに限らず面倒な計算を引き受けてくれるものなら何でもいい。<br />
<br />
基本的にはお互いのメモリに直接アクセスできないので、<br />
# Hostのメモリ内容をdevice側にコピー<br />
# Deviceで計算<br />
# Deviceメモリにある計算結果をhost側にコピー<br />
という処理になる。このコピーの処理がオーバーヘッドになるので、いくら並列計算向きな処理でも軽い処理ならhost側で普通に計算した方が速いってこともある。反対にdevice側で計算していて途中の処理が並列計算に不向きでも、host側に渡して計算してdevice側に書き戻すよりそのまま計算した方が速いことも。<br />
<br />
最近のGPUだと計算と次に使うメモリのコピーを同時にできるらしいからスループットを上げられるし、CPUとGPUがメモリを共有しているヤツとかコピーが不要だったりするけど、まあ、host <-> device間の転送はなるべく避ける方がいい。<br />
<br />
=== *.cuの運命 ===<br />
<br />
CUDAはhost用とdevice用のコードがまぜこぜになっているので、中で何が起きているのか分かりづらい。なので、何となくこんな感じかな~っと。<br />
<br />
;__host__または指定無し<br />
:<nowiki>*.cu</nowiki> -> CUDAっぽいキーワード削除・__global__関数の呼び出しコード追加 (*.c/*.cpp) -> 普通のC/C++コンパイラ(gccとかcl.exeとか)に渡す<br />
;__global__または__device__<br />
:<nowiki>*.cu</nowiki> -> GPU側コードの抽出 (*.gpu) -> 仮想アーキテクチャ用コード生成 (*.ptx) (-> 実アーキテクチャ用コード生成 (*.cubin) )<br />
<br />
たぶん、__host__とか__device__のキーワードを見て生成するソースに含めるかどうか決めているんだと。なので、__host__ __device__という風に両方ついているものはhost側、device側両方のソースに入れられる。ちなみにどちら側でコンパイルされているのかは__CUDA_ARCH__のあるなし、仮想アーキテクチャの種類はその値で判断できるようだ。<br />
<br />
=== 仮想/実アーキテクチャ ===<br />
<br />
nvccのコンパイルオプションで迷ったのが、--gpu-architecture (-arch)と--gpu-code (-code)。どっちも引数は同じようなのがとれるし、ヘルプも長ったらしいし。で、たぶんこういう事かなというのが分かった気がしたので書いておく。<br />
<br />
まず、2種類の引数について。<br />
;compute_*<br />
:仮想アーキテクチャ。どういう命令セットを使うかを表す。数字が大きい方がより多くの便利な命令が使えるけど、古い環境では対応できなくなる。<br />
;sm_*<br />
:実アーキテクチャ。バイナリコードを生成する対象を表す。当然、仮想アーキテクチャのバージョン以上でないとダメ。<br />
<br />
それからオプションの意味について。<br />
;--gpu-architecture (-arch)<br />
:<nowiki>*.cu</nowiki>が対象にしている仮想アーキテクチャを指定する。なので、基本的にはcompute_*の中から選択する。<br />
;--gpu-code (-code)<br />
:仮想アーキテクチャのコード(*.ptx)から生成して出力に加えるアーキテクチャを指定する。PTXを含めたいときはcompute_*、特定のGPU向けバイナリを含めたいときはsm_*を選択する。<br />
<br />
出力にPTXを含めておくと、自分向けのバイナリが見つからなかった時にその場でコンパイル (JIT)してくれるらしい。<br />
オプションを2個も指定するのが面倒な人向けに--gpu-architecture (-arch)にsm_*を指定すると、対応する仮想アーキテクチャと出力対象を指定したことにしてくれる便利機能がある。<br />
<br />
<pre><br />
# 同じ<br />
--gpu-architecture=compute_13 --gpu-code=sm_13<br />
-arch=compute_13 -code=sm_13<br />
<br />
# 未来のGPUでも動作するよう、出力にPTXコードを含める<br />
-arch=compute_13 -code=compute_13,sm_13<br />
-arch=sm_13<br />
<br />
# バイナリをたくさん入れる<br />
-arch=compute_13 -code=compute_13,sm_13,sm_21,sm_30<br />
<br />
# 複数のアーキテクチャに対応する<br />
--generate-code arch=compute_10,code=compute_10 --generate-code arch=compute_13,code=\'compute_13,sm_13\' ...<br />
</pre><br />
<br />
最後の例みたいに、複数の仮想アーキテクチャに対応するには--generate-code (-gencode)で指定する。ソース側は前述のとおり__CUDA_ARCH__でどのアーキテクチャ向けか判定する。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=Hello_CUDA!
Hello CUDA!
2012-11-08T15:58:41Z
<p>白飯: 初版を執筆</p>
<hr />
<div>新しい言語を始めるときの作法。まずはHello world的な[http://www.fmaj7b5.info/git?p=cuda.git;a=tree;h=1861b63569b53cfe44a6b07c5a03e03d55e12f82;hb=1861b63569b53cfe44a6b07c5a03e03d55e12f82 何か]を書いてみた。<br />
これには間違いなくコンパイルできるソースを使ってプログラム環境が正しく設定されているか確認する意味がある、とか無いとか。<br />
<br />
== Hello CUDA! ==<br />
<br />
hello.cu:<br />
<br />
<syntaxhighlight><br />
using namespace FM7b5;<br />
<br />
__device__ const char str[] = "Hello CUDA!";<br />
<br />
__global__ void hello(char* buf)<br />
{<br />
const size_t idx = threadIdx.x;<br />
<br />
if (idx < sizeof(str)) {<br />
buf[idx] = str[idx];<br />
}<br />
}<br />
<br />
void FM7b5::hello_gpu(char* buf)<br />
{<br />
const size_t num_str(sizeof(str));<br />
char* d_buf(nullptr);<br />
<br />
cudaMalloc(&d_buf, sizeof(char) * num_str);<br />
<br />
hello<<<1, num_str>>>(d_buf);<br />
<br />
cudaMemcpy(buf, d_buf, sizeof(char) * num_str, cudaMemcpyDeviceToHost);<br />
<br />
cudaFree(d_buf);<br />
}<br />
</syntaxhighlight><br />
<br />
hello_gpu()が普通の(C++の)関数で、hello()がGPUで実行される関数。まあ、ほぼC/C++と同じ。見慣れない__global__とか__device__とかはその関数や変数がCPU (host)用かGPU (device)用かなんかをコンパイラに教えるためのもの。<br />
<br />
{| border="1"<br />
| __global__ || hostから呼べるdeviceの関数。戻り値無し。<br />
|-<br />
| __device__ || device上で実行される関数の中からのみ呼べる関数。<br />
|-<br />
| __host__ || host上で実行される関数。CPU用の普通の関数。<br />
|-<br />
| (何も無し) || おそらく__host__と同じ。<br />
|}<br />
<br />
で、<<<...>>>が同時に走らせるスレッド数を指示するやつ。詳しくはマニュアルを・・。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%A1%E3%83%A2%E5%B8%B3@fmaj7b5.info
メモ帳@fmaj7b5.info
2012-11-08T15:18:22Z
<p>白飯: /* CUDA */ Hello world節を追加</p>
<hr />
<div>== はじめに ==<br />
<br />
ここに書かれている内容は個人的なメモであり、正当性は保証しません。<br />
<br />
#コピペして怒られても知らないよ。<br />
<br />
== 基本 ==<br />
* [[線形代数]]<br />
* [[点の移動・回転]]<br />
* [[射影変換(2次元)]]<br />
* [[最小二乗法とか]]<br />
* [[非線形最適化]]<br />
<br />
== コンピュータ・ビジョン ==<br />
* [[カメラモデル]]<br />
* [[3次元復元]]<br />
<br />
[http://szeliski.org/Book いいもの]見つけた。<br />
<br />
== CUDA ==<br />
* [[準備]]<br />
* [[Hello CUDA!]]<br />
<br />
== そのほか ==<br />
* [[おすすめプログラミング環境]]<br />
* [[lapackeインストールメモ]]<br />
* [[月の満ち欠けと月食]]<br />
* [[Windows7のUsersを(クリーンインストール時に)移動する法]]</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E6%BA%96%E5%82%99
準備
2012-11-07T15:54:50Z
<p>白飯: CUDA準備編の初版を執筆</p>
<hr />
<div>2周くらい遅れのCUDAをやってみる。<br />
ここではWindowsを使うけど、CUDAはOSに依存しないので他でも大丈夫・・・だと思う。<br />
<br />
== プログラム環境を整える ==<br />
<br />
以下の二つをインストール:<br />
* Visual C++ 2010 Express<br />
* CUDA Toolkit<br />
<br />
ググれば出てくるはずだけど、CUDAのダウンロード先で迷ったことがあるのでヒントを少々。CUDA周りのツール類は「NVIDIA > CUDA Zone > Download」と辿っていけば見つかる。とりあえずCUDA ZoneでDownloadとかToolkitとか書いてあるところをポチポチしよう。<br />
<br />
細かいところは先人のwebページを参考にしてね。<br />
<br />
== コンパイル方法 ==<br />
<br />
最低限必要なのは以下の三つ:<br />
* *.cuをCUDAコンパイラ(nvcc)でコンパイルするように指定する<br />
* CUDAコンパイラに対象にするGPUのアーキテクチャ(-arch)とcompute capability(-code)を指定する<br />
* cudart.lib (linuxだとlibcudart)をくっつけるよう指定する<br />
<br />
=== Visual C++ ===<br />
<br />
GUIは説明が面倒だ・・。<br />
# プロジェクトを追加<br />
#* コンソールアプリケーションとか、適当に<br />
# ビルドのカスタマイズ > CUDA x.x にチェック<br />
# *.cuソースを追加<br />
# プロパティ > CUDA C/C++ > Device > Code Generation で対象GPUを指定<br />
# リンカー > 入力 > 追加の依存ファイル にcudart.libを追加<br />
<br />
まあ、詳しくはググるべし。<br />
<br />
=== CMake ===<br />
<br />
Linuxな人はこっちが簡単かも。WindowsでVC++な人でも使えるので、便利な人には便利。<br />
<br />
<pre>CMakeLists.txt:<br />
<br />
cmake_minimum_required (VERSION 2.8)<br />
project (HelloProject)<br />
<br />
find_package (CUDA REQUIRED)<br />
<br />
cuda_add_executable (hello hello.cu)<br />
</pre><br />
cmake_minimum_requiredのバージョンは手持ちの環境に合わせただけなので、もっと低くても大丈夫かも知れない。<br />
<br />
=== GNU Make ===<br />
<br />
いや、漢(おとこ)ならmakeだ。という人の場合。CMakeもLinuxなら最終的にmakeするじゃん、というツッコミは無しで。<br />
<br />
<pre>cuda.mk:<br />
<br />
# Set a default location of CUDA<br />
CUDA_HOME ?= /usr/local/cuda<br />
<br />
# Set an architecture<br />
ifeq ($(ARCH),)<br />
ARCH = $(shell uname -i | tr '[:upper:]' '[:lower:]')<br />
endif<br />
<br />
# Binary path to CUDA<br />
CUDA_BINDIR = $(CUDA_HOME)/bin<br />
<br />
# Set library dir and nvcc command<br />
ifeq ($(ARCH),x86_64)<br />
CUDA_LIBDIR = $(CUDA_HOME)/lib64<br />
NVCC = $(CUDA_BINDIR)/nvcc<br />
else<br />
CUDA_LIBDIR = $(CUDA_HOME)/lib<br />
NVCC = $(CUDA_BINDIR)/nvcc -m32<br />
endif<br />
<br />
# Set default parameters for CUDA libraries<br />
CUDA_LDFLAGS ?= -L$(CUDA_LIBDIR)<br />
CUDA_LDLIBS ?= -lcudart<br />
<br />
<br />
# Build rules<br />
%.o: %.cu<br />
$(NVCC) $(NVCC_FLAGS) -c -o $@ $^<br />
<br />
%.ptx: %.cu<br />
$(NVCC) $(NVCC_FLAGS) --ptx -o $@ $^<br />
%.ptx: %.gpu<br />
$(NVCC) $(NVCC_FLAGS) --ptx -o $@ $^<br />
<br />
%.gpu: %.cu<br />
$(NVCC) $(NVCC_FLAGS) --gpu -o $@ $^<br />
<br />
</pre><br />
<br />
<pre>Makefile:<br />
<br />
include ./cuda.mk<br />
<br />
LDFLAGS = $(CUDA_LDFLAGS)<br />
LDLIBS = $(CUDA_LDLIBS)<br />
<br />
NVCC_FLAGS = --generate-code=arch=compute_30,code=sm_30<br />
<br />
TARGETS = hello<br />
<br />
all: $(TARGETS)<br />
<br />
clean:<br />
$(RM) $(TARGETS) *.o *~<br />
<br />
hello: hello.o<br />
</pre><br />
<br />
適当なので、あくまでも参考までに。大体、Makefileを手書きする変態さんならもっといいのを既に書いてるでしょう・・。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%A1%E3%83%A2%E5%B8%B3@fmaj7b5.info
メモ帳@fmaj7b5.info
2012-11-07T14:41:36Z
<p>白飯: CUDAの項目を追加</p>
<hr />
<div>== はじめに ==<br />
<br />
ここに書かれている内容は個人的なメモであり、正当性は保証しません。<br />
<br />
#コピペして怒られても知らないよ。<br />
<br />
== 基本 ==<br />
* [[線形代数]]<br />
* [[点の移動・回転]]<br />
* [[射影変換(2次元)]]<br />
* [[最小二乗法とか]]<br />
* [[非線形最適化]]<br />
<br />
== コンピュータ・ビジョン ==<br />
* [[カメラモデル]]<br />
* [[3次元復元]]<br />
<br />
[http://szeliski.org/Book いいもの]見つけた。<br />
<br />
== CUDA ==<br />
* [[準備]]<br />
<br />
== そのほか ==<br />
* [[おすすめプログラミング環境]]<br />
* [[lapackeインストールメモ]]<br />
* [[月の満ち欠けと月食]]<br />
* [[Windows7のUsersを(クリーンインストール時に)移動する法]]</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=Windows7%E3%81%AEUsers%E3%82%92(%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%99%82%E3%81%AB)%E7%A7%BB%E5%8B%95%E3%81%99%E3%82%8B%E6%B3%95
Windows7のUsersを(クリーンインストール時に)移動する法
2012-07-29T13:42:24Z
<p>白飯: 初版</p>
<hr />
<div>Windows7のSSD+HDDなマシンを新築したので記念に。<br />
[https://www.google.co.jp/search?hl=ja&q=windows7+move+users+inurl%3Amicrosoft.com ググれば出てくる]けど、まあ、メモってことで。<br />
<br />
== xmlファイルを手に入れる ==<br />
[http://answers.microsoft.com/en-us/windows/forum/windows_7-files/win7-how-do-i-move-user-folder-to-a-different/565f16a5-e5ed-43c9-8422-4f56aebb296e このあたり]からxmlなテキストをコピペしてUSBメモリなどに大事にとっておく。<br />
うちの環境だとProgramDataを移動させるとWindows updateがうまくいかないっぽいので、<nowiki><ProgramData>...</nowiki>の行は消した。<br />
<br />
== 途中まで普通にインストールする ==<br />
通常通りインストールを進めてユーザ名やらコンピュータ名を入れるところまで行ったら、何もせずにCtrl+Shift+F3を押す。<br />
するとリブートしてauditモードなるものに入る。<br />
<br />
== ディスク周りを設定する ==<br />
Auditモードに入ると、最初にデカデカとOOBEモードやら再起動やら聞いてくる変なダイアログがでるけど、キャンセルして退場願う。<br />
その後は普通に使えるので、データ用領域のフォーマットやらドライブ文字の設定やらを行う。<br />
<br />
== 虎の子のxmlファイルの出番 ==<br />
コマンドプロンプトを開いてsysprepディレクトリに移動、コマンドの実行をやる。<br />
<pre>cd C:\Windows\System32\sysprep<br />
sysprep.exe /audit /reboot /unattend:U:\folders.xml</pre><br />
これはxmlの名前がfolders.xmlでUSBメモリがUドライブになった場合。大抵そうじゃないので、自分の環境に合わせること。<br />
で、このコマンドを実行すると勝手にリブートしてauditモードに戻ってくる。<br />
<br />
== 元のインストールを再開する ==<br />
今度はデカデカと出ているダイアログでOOBE・再起動を選択してOKする。そうしたらまたリブート・・・。<br />
ただ今回はauditモードではなくてインストールっぽい画面になるはず。あとは淡々とインストールの続きをやる。<br />
<br />
== 仕上げ ==<br />
この段階でUsersが他の場所にあるWindows7になっているはず。でも、このままだとC:\Usersがあると決めつけている<del>クソ仕様の</del>プログラムが動かないことがある。そこで,「跡地」にジャンクションなりシンボリックリンクなりを置いておく。<br />
管理者権限でコマンドプロンプトを開いて以下を入力。<br />
<pre>cd C:\<br />
mklink /J Users D:\Users</pre><br />
<br />
これで大丈夫、・・・今のところ。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%A1%E3%83%A2%E5%B8%B3@fmaj7b5.info
メモ帳@fmaj7b5.info
2012-07-29T12:50:20Z
<p>白飯: /* そのほか */ Windows7の項目を追加</p>
<hr />
<div>== はじめに ==<br />
<br />
ここに書かれている内容は個人的なメモであり、正当性は保証しません。<br />
<br />
#コピペして怒られても知らないよ。<br />
<br />
== 基本 ==<br />
* [[線形代数]]<br />
* [[点の移動・回転]]<br />
* [[射影変換(2次元)]]<br />
* [[最小二乗法とか]]<br />
* [[非線形最適化]]<br />
<br />
== コンピュータ・ビジョン ==<br />
* [[カメラモデル]]<br />
* [[3次元復元]]<br />
<br />
[http://szeliski.org/Book いいもの]見つけた。<br />
<br />
== そのほか ==<br />
* [[おすすめプログラミング環境]]<br />
* [[lapackeインストールメモ]]<br />
* [[月の満ち欠けと月食]]<br />
* [[Windows7のUsersを(クリーンインストール時に)移動する法]]</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E6%9C%88%E3%81%AE%E6%BA%80%E3%81%A1%E6%AC%A0%E3%81%91%E3%81%A8%E6%9C%88%E9%A3%9F
月の満ち欠けと月食
2012-06-04T12:49:43Z
<p>白飯: 月の陰影について執筆</p>
<hr />
<div>だいたい同じようなモノ・・・と思っていたら違ったのでメモ。<br />
<br />
;月の満ち欠け<br />
: 太陽光の当たり方で変化する。暗いところは陰(attached shadow)。<br />
;月食<br />
: 太陽光を地球が遮る事によって生じる。暗いところは地球の影(cast shadow)。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%A1%E3%83%A2%E5%B8%B3@fmaj7b5.info
メモ帳@fmaj7b5.info
2012-06-04T12:43:24Z
<p>白飯: /* そのほか */ 項目を追加</p>
<hr />
<div>== はじめに ==<br />
<br />
ここに書かれている内容は個人的なメモであり、正当性は保証しません。<br />
<br />
#コピペして怒られても知らないよ。<br />
<br />
== 基本 ==<br />
* [[線形代数]]<br />
* [[点の移動・回転]]<br />
* [[射影変換(2次元)]]<br />
* [[最小二乗法とか]]<br />
* [[非線形最適化]]<br />
<br />
== コンピュータ・ビジョン ==<br />
* [[カメラモデル]]<br />
* [[3次元復元]]<br />
<br />
[http://szeliski.org/Book いいもの]見つけた。<br />
<br />
== そのほか ==<br />
* [[おすすめプログラミング環境]]<br />
* [[lapackeインストールメモ]]<br />
* [[月の満ち欠けと月食]]</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E7%B7%9A%E5%BD%A2%E4%BB%A3%E6%95%B0
線形代数
2012-02-25T07:10:47Z
<p>白飯: /* 逆行列 */ 係数を修正</p>
<hr />
<div>= スカラー =<br />
座標系に依らない数、らしい。向きや方向を持たない。たぶん一つの値で表現できると思う。このサイトでは小文字で表す事にする。<br />
<br />
例:<br />
<math><br />
a, \ x, \ \alpha, \dots<br />
</math><br />
みたいな。<br />
<br />
= ベクトル =<br />
これも座標系に依らないらしい。依存するのは座標軸を決めて、それを基準にして測った値の方。これをベクトルの成分という。このサイトではベクトルを太文字で表す。<br />
<br />
要するに、座標系に依らない(2次元の)ベクトル<math>\boldsymbol{v}</math>があって、座標軸の組(原点は同じ)<math>(\boldsymbol{e}_1,\,\boldsymbol{e}_2)</math>と<math>(\boldsymbol{e}_1^\prime,\,\boldsymbol{e}_2^\prime)</math>、それぞれの成分が<math>(x_1,\,x_2)</math>、<math>(x_1^\prime,\,x_2^\prime)</math>とすると、次の式が成り立つ。<br />
<br />
<math><br />
\boldsymbol{v} = \boldsymbol{e} x_1 + \boldsymbol{e}_2 x_2<br />
= \boldsymbol{e}_1^\prime x_1^\prime + \boldsymbol{e}_2^\prime x_2^\prime<br />
</math><br />
<br />
ただし、ベクトルの成分をベクトルと言っちゃう事もあるので注意すべし。普通は座標系を直交座標系(デカルト座標系)など1種類に決める事が多いので、問題は生じにくい。行間を読め、ということか。<br />
<br />
ちなみに、単に任意の<math>n</math>個の値を並べてもベクトルにならない場合があるので、うかつな事は言わないこと。詳しい定義はググるべし。まあ、直線!とか平面!とかそんな感じだったと思う。「線形」っていうくらいだし。<br />
<br />
== ベクトルの計算 ==<br />
<math>d</math>次元ベクトル<math>\boldsymbol{x},\,\boldsymbol{y} \in \mathbb{R}^d</math>があるとする。それぞれの<math>i</math>番目の成分を<math>x_i,\,y_y</math>で表す。<br />
<br />
=== 和と差 ===<br />
普通に成分同士を足したり引いたりすれば良い。<br />
<br />
<math><br />
\boldsymbol{x} \pm \boldsymbol{y} = \{x_i \pm y_i\}_i<br />
</math><br />
<br />
あ、<math>\{\cdot\}_i</math>は数列の<math>i</math>番目要素という意味にした。<br />
<br />
=== 内積 ===<br />
ベクトルには掛け算が2種類もある。内積はそのうちの一つで、計算結果がスカラーになる。成分ごとに掛け算して足す。<br />
<br />
<math><br />
\boldsymbol{x} \cdot \boldsymbol{y} = \sum_i x_i y_i<br />
</math><br />
<br />
=== 外積 ===<br />
内積は同じ基底の成分同士で計算したのに対し、外積は異なる成分同士で計算する。2次元の時は1個(スカラー)、3次元の時は3次元ベクトルになる。4次元以上の時は・・・知らん。<br />
<br />
;2次元<br />
<math><br />
\boldsymbol{x} \times \boldsymbol{y} = x_1 y_2 - x_2 y_1<br />
</math><br />
<br />
;3次元<br />
<math><br />
\boldsymbol{x} \times \boldsymbol{y} = (x_2 y_3 - x_3 y_2,\ x_3 y_1 - x_1 y_3,\ x_2 y_3 - x_3 y_2)<br />
</math><br />
<br />
=== 大きさ ===<br />
それ自身との内積のルート。<br />
<br />
<math><br />
|\boldsymbol{x}| = \sqrt{\boldsymbol{x} \cdot \boldsymbol{x}} = \sqrt{\sum_i x_i x_i}<br />
</math><br />
<br />
特に大きさが1のものを'''単位ベクトル'''と呼んだりする。<br />
<br />
= 行列 =<br />
スカラー<math>x</math> (添え字なし)、ベクトル<math>\{x_i\}</math> (添え字1個)とくると、添え字2個が出てくるのが自然の摂理。ベクトルが1次元配列で表現できるのに対して、行列は行・列で表せる表になる。何か言い回しが難しい。とにかく、行列は大文字で表すとする。<br />
<br />
例:<br><br />
<math><br />
A = \{a_{ij}\} =<br />
\begin{bmatrix}<br />
a_{11} & \cdots & a_{1n} \\<br />
\vdots & \ddots & \vdots \\<br />
a_{m1} & \cdots & a_{mn}<br />
\end{bmatrix}<br />
</math><br />
<br />
ベクトルも行または列の長さが1の行列として扱う事ができ、縦に長いやつを縦ベクトル(列ベクトル)、横に長いやつを横ベクトル(行ベクトル)と呼ぶ。このサイトではデフォルトを縦ベクトルとして<math>\boldsymbol{x}</math>で表し、横ベクトルを<math>\boldsymbol{x}^\text{T}</math>で表す、確率が高い。<br />
ベクトルも行列の1種であることから分かるように、行列も単に数字を並べれば良いわけでは無い。たぶん、行列<math>A</math>、スカラー<math>a,\,b</math>、ベクトル<math>\boldsymbol{x},\,\boldsymbol{y}</math>に対して(少なくとも)こんな式が成り立たなくてはいけないはず。<br />
<br />
<math><br />
A (a \boldsymbol{x} + b \boldsymbol{y}) = a A \boldsymbol{x} + b A \boldsymbol{y}<br />
</math><br />
<br />
詳しくはググる事。<br />
<br />
== 名前が付いてる行列 ==<br />
;正方行列<br />
行数と列数が同じ行列。正方形。<br />
<br />
;零行列<br />
成分が全て0。ブラックホール。<br />
<br />
;対角行列<br />
対角成分(<math>i=j</math>な所)にしか値が無い行列.あとは0。<br />
<br />
<math><br />
D =<br />
\begin{bmatrix}<br />
d_1 & & 0 \\<br />
& \ddots & \\<br />
0 & & d_n<br />
\end{bmatrix}<br />
= \text{diag}(d_1,\, \dots, d_n)<br />
</math><br />
<br />
対角成分が全て1のものを特に'''単位行列'''と呼ぶ。<br />
<br />
;上三角行列<br />
対角成分より上側にしか値が無い行列。あとは0。<br />
<br />
<math><br />
U =<br />
\begin{bmatrix}<br />
* & \cdots & * \\<br />
& \ddots & \vdots \\<br />
0 & & *<br />
\end{bmatrix}<br />
</math><br />
<br />
;下三角行列<br />
対角成分より下側にしか値が無い行列。上三角行列の逆。<br />
<br />
<math><br />
L =<br />
\begin{bmatrix}<br />
* & & 0 \\<br />
\vdots & \ddots & \\<br />
{}* & \cdots & *<br />
\end{bmatrix}<br />
</math><br />
<br />
;対称行列<br />
<math>a_{ij} = a_{ji}</math>な行列。<br />
<br />
;歪対称行列<br />
<math>a_{ij} = -a_{ji}</math>な行列。<br />
<br />
== 行列の計算 ==<br />
行列<math>A = \{a_{ij}\}</math>、<math>B = \{b_{ij}\}</math>があるとする。<br />
<br />
=== 和と差 ===<br />
ベクトルと同じように、成分ごとに足したり引いたりすれば良い。当然、行数・列数が同じでないとダメ。<br />
<br />
<math><br />
A \pm B = \{a_{ij} \pm b_{ij}\}_{ij}<br />
</math><br />
<br />
=== 積 ===<br />
計算結果の<math>(i,\,j)</math>成分は、左側の行列の第<math>i</math>行目の横ベクトルと右側の行列の第<math>j</math>列目の縦ベクトルとの内積になる。なので、左側の行列の幅と右側の行列の高さが同じでないといけない。<br />
<br />
<math><br />
A B = \left\{\sum_k a_{ik} b_{kj} \right\}_{ij}<br />
</math><br />
<br />
=== 転置 ===<br />
<math>(i,\,j)</math>成分と<math>(j,\,i)</math>成分を入れ替える。ここでは行列<math>A</math>の転置を<math>A^\text{T}</math>で表す。人によっては<math>{}^\text{T}A</math>とか<math>A^\prime</math>とかを用いる事もある。<br />
<br />
<math><br />
A^\text{T} = \{a_{ji}\}_{ij}<br />
</math><br />
<br />
=== 逆行列 ===<br />
行列<math>A</math>の逆行列は<math>A^{-1}</math>と書いてAのインバースと読む。Aと掛けると単位行列になってしまう不思議な行列。<br />
<br />
<math><br />
A A^{-1} = A^{-1} A = I<br />
</math><br />
<br />
ただし<math>I</math>は単位行列を表す。<br />
<br />
<math><br />
A^{-1} = \left\{<br />
\frac{1}{(n-1)!} \varepsilon^{i k_1 \dots k_{n-1}} \varepsilon_{j l_1 \dots l_{n-1}}<br />
a_{k_1}^{l_1} \dots a_{k_{n-1}}^{l_{n-1}}<br />
\right\}_{ij} / |A|<br />
</math><br />
<br />
=== 行列式 ===<br />
計算方法は教えてもらえるが、何に使うのかピンと来ない値。こゆうちの積とか。行列<math>A</math>の行列式は<math>|A|</math>とか<math>\text{det}(A)</math>とか書く。<br />
<br />
;2x2行列<br />
<math><br />
\begin{vmatrix}<br />
a_{11} & a_{12} \\<br />
a_{21} & a_{22}<br />
\end{vmatrix}<br />
= a_{11} a_{22} - a_{12} a_{21}<br />
</math><br />
<br />
外積みたいだな。<br />
<br />
;3x3行列<br />
<math><br />
\begin{vmatrix}<br />
a_{11} & a_{12} & a_{13} \\<br />
a_{21} & a_{22} & a_{23} \\<br />
a_{31} & a_{32} & a_{33}<br />
\end{vmatrix}<br />
= a_{11}<br />
\begin{vmatrix}<br />
a_{22} & a_{23} \\<br />
a_{32} & a_{33}<br />
\end{vmatrix}<br />
- a_{21}<br />
\begin{vmatrix}<br />
a_{12} & a_{13} \\<br />
a_{32} & a_{33}<br />
\end{vmatrix}<br />
+ a_{31}<br />
\begin{vmatrix}<br />
a_{12} & a_{13} \\<br />
a_{22} & a_{23}<br />
\end{vmatrix}<br />
</math><br />
<br />
;それ以上<br />
3x3の場合と同様にバラしていけば計算できるはず。というか、手計算でやる事はまず無いと思う。<br />
<br />
=== トレース ===<br />
対角成分の和。行列<math>A</math>のトレースは<math>\text{tr}(A)</math>と書いたり。こゆうちの和みたいな。<br />
<br />
<math><br />
\text{tr}(A) = \sum_i a_{ii}<br />
</math><br />
<br />
== 固有値 ==<br />
<math><br />
A \boldsymbol{v} = \boldsymbol{v} \lambda<br />
</math><br />
<br />
を満たすベクトル<math>\boldsymbol{v}</math>を固有ベクトル、<math>\lambda</math>を固有値という。この<math>\boldsymbol{v},\,\lambda</math>の組み合わせは複数出てくるので、固有値<math>\lambda</math>に対応する固有ベクトル<math>\boldsymbol{v}</math>のように表現することが多い。大抵、最大か最小固有値に対応するものが重要っぽい。<br />
<br />
固有値と固有ベクトルの算出は、次の特性方程式を解くことによる。<br><br />
<math><br />
|A \boldsymbol{v} - \lambda I| = 0<br />
</math><br />
<br />
但しn次正方行列に対しては一般にn次方程式が出てくるため、4次以上になると計算が難しい。従って計算機であれば繰り返し計算などの近似解法を用いる方が簡単。というか、そういうライブラリがあるので利用するのが賢い。<br />
<br />
== 行列の大きさ ==<br />
[http://ja.wikipedia.org/wiki/%E8%A1%8C%E5%88%97%E3%83%8E%E3%83%AB%E3%83%A0 Wikipedia]によると、色々あるらしい。<br />
<br />
;フロベニウスノルム<br />
<math><br />
||A||_\text{F} = \sqrt{\sum_i \sum_j (a_{ij})^2} = \sqrt{\text{tr}(A^\text{T} A)}<br />
</math><br />
<br />
これは固有値の和になるようだ。<br />
<br />
== 行列の分解 ==<br />
;LU分解<br />
行列を下三角行列(<math>L</math>)と上三角行列(U)の積に分解する方法。ガウスの消去法をやると上三角行列が出てくるが、それに至るまでの操作を行列で表現すると<math>L</math>の部分も分かる。<br />
<br />
LU分解を利用すると、連立線形方程式が簡単に(後進代入)解ける。<br><br />
<math><br />
A \boldsymbol{x} = (L U) \boldsymbol{x} = \boldsymbol{b}<br />
</math><br><br />
<math><br />
\Rightarrow \,<br />
\left\{<br />
\begin{array}{l}<br />
\boldsymbol{y} = L^{-1} \boldsymbol{b} \\<br />
U \boldsymbol{x} = \boldsymbol{y}<br />
\end{array}<br />
\right.<br />
</math><br />
<br />
;QR分解<br />
行列を直交行列(<math>Q</math>)と上三角行列(<math>R</math>)の積に分解する方法。上三角行列をRで表す宗派があるようで、LU分解もLR分解という別名がある。ランク落ちしてても使えるらしい。<br />
<br />
<math><br />
A = Q R<br />
</math><br />
<br />
;固有値分解<br />
正方行列を固有ベクトルを横に並べた行列<math>V</math>と対応する固有値を対角成分に持つ行列<math>D</math>の積に分解する方法。<br />
<br />
<math><br />
\begin{array}{l}<br />
A \left[ \boldsymbol{v}_1,\, \dots, \boldsymbol{v}_d \right] <br />
= \left[ \boldsymbol{v}_1,\, \dots, \boldsymbol{v}_d \right] \text{diag}\left( \lambda_1, \dots, \lambda_d \right)<br />
= V D \\<br />
A = V D V^{\text{T}}<br />
\end{array}<br />
</math><br />
<br />
;特異値分解(SVD)<br />
行列を直交行列<math>U,\,V</math>と対角行列<math>D</math>の積に分解する方法。正方行列以外にも使えるため便利。<br />
<br />
<math><br />
A = U D V^\text{T}<br />
</math><br />
<br />
ちなみに上式から明らかなように、<br><br />
<math><br />
\begin{array}{l}<br />
A A^\text{T} = U D^2 U^\text{T} \\<br />
A^\text{T} A = V D^2 V^\text{T} \\<br />
\end{array}<br />
</math><br><br />
であるため、データ<math>A=\left[ \boldsymbol{x}_1, \dots, \boldsymbol{x}_n \right]</math>の内積<math>A^\text{T} A</math>が分かれば、その固有ベクトルを使って共分散行列<math>A A^\text{T}</math>の固有ベクトルを次のように求める事ができる。<br><br />
<math><br />
U = A V D^{-1}<br />
</math><br><br />
何に使えるかは知らない。<br />
<br />
= その先? =<br />
添え字の数が0、1、2と増えてきたからには、[http://ja.wikipedia.org/wiki/%E5%A4%9A%E9%87%8D%E7%B7%9A%E5%9E%8B%E4%BB%A3%E6%95%B0 その先]があるに違いない。けど、興味ない(笑)。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E7%B7%9A%E5%BD%A2%E4%BB%A3%E6%95%B0
線形代数
2012-02-17T13:48:28Z
<p>白飯: /* 逆行列 */ 計算方法を追記</p>
<hr />
<div>= スカラー =<br />
座標系に依らない数、らしい。向きや方向を持たない。たぶん一つの値で表現できると思う。このサイトでは小文字で表す事にする。<br />
<br />
例:<br />
<math><br />
a, \ x, \ \alpha, \dots<br />
</math><br />
みたいな。<br />
<br />
= ベクトル =<br />
これも座標系に依らないらしい。依存するのは座標軸を決めて、それを基準にして測った値の方。これをベクトルの成分という。このサイトではベクトルを太文字で表す。<br />
<br />
要するに、座標系に依らない(2次元の)ベクトル<math>\boldsymbol{v}</math>があって、座標軸の組(原点は同じ)<math>(\boldsymbol{e}_1,\,\boldsymbol{e}_2)</math>と<math>(\boldsymbol{e}_1^\prime,\,\boldsymbol{e}_2^\prime)</math>、それぞれの成分が<math>(x_1,\,x_2)</math>、<math>(x_1^\prime,\,x_2^\prime)</math>とすると、次の式が成り立つ。<br />
<br />
<math><br />
\boldsymbol{v} = \boldsymbol{e} x_1 + \boldsymbol{e}_2 x_2<br />
= \boldsymbol{e}_1^\prime x_1^\prime + \boldsymbol{e}_2^\prime x_2^\prime<br />
</math><br />
<br />
ただし、ベクトルの成分をベクトルと言っちゃう事もあるので注意すべし。普通は座標系を直交座標系(デカルト座標系)など1種類に決める事が多いので、問題は生じにくい。行間を読め、ということか。<br />
<br />
ちなみに、単に任意の<math>n</math>個の値を並べてもベクトルにならない場合があるので、うかつな事は言わないこと。詳しい定義はググるべし。まあ、直線!とか平面!とかそんな感じだったと思う。「線形」っていうくらいだし。<br />
<br />
== ベクトルの計算 ==<br />
<math>d</math>次元ベクトル<math>\boldsymbol{x},\,\boldsymbol{y} \in \mathbb{R}^d</math>があるとする。それぞれの<math>i</math>番目の成分を<math>x_i,\,y_y</math>で表す。<br />
<br />
=== 和と差 ===<br />
普通に成分同士を足したり引いたりすれば良い。<br />
<br />
<math><br />
\boldsymbol{x} \pm \boldsymbol{y} = \{x_i \pm y_i\}_i<br />
</math><br />
<br />
あ、<math>\{\cdot\}_i</math>は数列の<math>i</math>番目要素という意味にした。<br />
<br />
=== 内積 ===<br />
ベクトルには掛け算が2種類もある。内積はそのうちの一つで、計算結果がスカラーになる。成分ごとに掛け算して足す。<br />
<br />
<math><br />
\boldsymbol{x} \cdot \boldsymbol{y} = \sum_i x_i y_i<br />
</math><br />
<br />
=== 外積 ===<br />
内積は同じ基底の成分同士で計算したのに対し、外積は異なる成分同士で計算する。2次元の時は1個(スカラー)、3次元の時は3次元ベクトルになる。4次元以上の時は・・・知らん。<br />
<br />
;2次元<br />
<math><br />
\boldsymbol{x} \times \boldsymbol{y} = x_1 y_2 - x_2 y_1<br />
</math><br />
<br />
;3次元<br />
<math><br />
\boldsymbol{x} \times \boldsymbol{y} = (x_2 y_3 - x_3 y_2,\ x_3 y_1 - x_1 y_3,\ x_2 y_3 - x_3 y_2)<br />
</math><br />
<br />
=== 大きさ ===<br />
それ自身との内積のルート。<br />
<br />
<math><br />
|\boldsymbol{x}| = \sqrt{\boldsymbol{x} \cdot \boldsymbol{x}} = \sqrt{\sum_i x_i x_i}<br />
</math><br />
<br />
特に大きさが1のものを'''単位ベクトル'''と呼んだりする。<br />
<br />
= 行列 =<br />
スカラー<math>x</math> (添え字なし)、ベクトル<math>\{x_i\}</math> (添え字1個)とくると、添え字2個が出てくるのが自然の摂理。ベクトルが1次元配列で表現できるのに対して、行列は行・列で表せる表になる。何か言い回しが難しい。とにかく、行列は大文字で表すとする。<br />
<br />
例:<br><br />
<math><br />
A = \{a_{ij}\} =<br />
\begin{bmatrix}<br />
a_{11} & \cdots & a_{1n} \\<br />
\vdots & \ddots & \vdots \\<br />
a_{m1} & \cdots & a_{mn}<br />
\end{bmatrix}<br />
</math><br />
<br />
ベクトルも行または列の長さが1の行列として扱う事ができ、縦に長いやつを縦ベクトル(列ベクトル)、横に長いやつを横ベクトル(行ベクトル)と呼ぶ。このサイトではデフォルトを縦ベクトルとして<math>\boldsymbol{x}</math>で表し、横ベクトルを<math>\boldsymbol{x}^\text{T}</math>で表す、確率が高い。<br />
ベクトルも行列の1種であることから分かるように、行列も単に数字を並べれば良いわけでは無い。たぶん、行列<math>A</math>、スカラー<math>a,\,b</math>、ベクトル<math>\boldsymbol{x},\,\boldsymbol{y}</math>に対して(少なくとも)こんな式が成り立たなくてはいけないはず。<br />
<br />
<math><br />
A (a \boldsymbol{x} + b \boldsymbol{y}) = a A \boldsymbol{x} + b A \boldsymbol{y}<br />
</math><br />
<br />
詳しくはググる事。<br />
<br />
== 名前が付いてる行列 ==<br />
;正方行列<br />
行数と列数が同じ行列。正方形。<br />
<br />
;零行列<br />
成分が全て0。ブラックホール。<br />
<br />
;対角行列<br />
対角成分(<math>i=j</math>な所)にしか値が無い行列.あとは0。<br />
<br />
<math><br />
D =<br />
\begin{bmatrix}<br />
d_1 & & 0 \\<br />
& \ddots & \\<br />
0 & & d_n<br />
\end{bmatrix}<br />
= \text{diag}(d_1,\, \dots, d_n)<br />
</math><br />
<br />
対角成分が全て1のものを特に'''単位行列'''と呼ぶ。<br />
<br />
;上三角行列<br />
対角成分より上側にしか値が無い行列。あとは0。<br />
<br />
<math><br />
U =<br />
\begin{bmatrix}<br />
* & \cdots & * \\<br />
& \ddots & \vdots \\<br />
0 & & *<br />
\end{bmatrix}<br />
</math><br />
<br />
;下三角行列<br />
対角成分より下側にしか値が無い行列。上三角行列の逆。<br />
<br />
<math><br />
L =<br />
\begin{bmatrix}<br />
* & & 0 \\<br />
\vdots & \ddots & \\<br />
{}* & \cdots & *<br />
\end{bmatrix}<br />
</math><br />
<br />
;対称行列<br />
<math>a_{ij} = a_{ji}</math>な行列。<br />
<br />
;歪対称行列<br />
<math>a_{ij} = -a_{ji}</math>な行列。<br />
<br />
== 行列の計算 ==<br />
行列<math>A = \{a_{ij}\}</math>、<math>B = \{b_{ij}\}</math>があるとする。<br />
<br />
=== 和と差 ===<br />
ベクトルと同じように、成分ごとに足したり引いたりすれば良い。当然、行数・列数が同じでないとダメ。<br />
<br />
<math><br />
A \pm B = \{a_{ij} \pm b_{ij}\}_{ij}<br />
</math><br />
<br />
=== 積 ===<br />
計算結果の<math>(i,\,j)</math>成分は、左側の行列の第<math>i</math>行目の横ベクトルと右側の行列の第<math>j</math>列目の縦ベクトルとの内積になる。なので、左側の行列の幅と右側の行列の高さが同じでないといけない。<br />
<br />
<math><br />
A B = \left\{\sum_k a_{ik} b_{kj} \right\}_{ij}<br />
</math><br />
<br />
=== 転置 ===<br />
<math>(i,\,j)</math>成分と<math>(j,\,i)</math>成分を入れ替える。ここでは行列<math>A</math>の転置を<math>A^\text{T}</math>で表す。人によっては<math>{}^\text{T}A</math>とか<math>A^\prime</math>とかを用いる事もある。<br />
<br />
<math><br />
A^\text{T} = \{a_{ji}\}_{ij}<br />
</math><br />
<br />
=== 逆行列 ===<br />
行列<math>A</math>の逆行列は<math>A^{-1}</math>と書いてAのインバースと読む。Aと掛けると単位行列になってしまう不思議な行列。<br />
<br />
<math><br />
A A^{-1} = A^{-1} A = I<br />
</math><br />
<br />
ただし<math>I</math>は単位行列を表す。<br />
<br />
<math><br />
A^{-1} = \left\{<br />
\frac{1}{2} \varepsilon^{i k_1 \dots k_{n-1}} \varepsilon_{j l_1 \dots l_{n-1}}<br />
a_{k_1}^{l_1} \dots a_{k_{n-1}}^{l_{n-1}}<br />
\right\}_{ij} / |A|<br />
</math><br />
<br />
=== 行列式 ===<br />
計算方法は教えてもらえるが、何に使うのかピンと来ない値。こゆうちの積とか。行列<math>A</math>の行列式は<math>|A|</math>とか<math>\text{det}(A)</math>とか書く。<br />
<br />
;2x2行列<br />
<math><br />
\begin{vmatrix}<br />
a_{11} & a_{12} \\<br />
a_{21} & a_{22}<br />
\end{vmatrix}<br />
= a_{11} a_{22} - a_{12} a_{21}<br />
</math><br />
<br />
外積みたいだな。<br />
<br />
;3x3行列<br />
<math><br />
\begin{vmatrix}<br />
a_{11} & a_{12} & a_{13} \\<br />
a_{21} & a_{22} & a_{23} \\<br />
a_{31} & a_{32} & a_{33}<br />
\end{vmatrix}<br />
= a_{11}<br />
\begin{vmatrix}<br />
a_{22} & a_{23} \\<br />
a_{32} & a_{33}<br />
\end{vmatrix}<br />
- a_{21}<br />
\begin{vmatrix}<br />
a_{12} & a_{13} \\<br />
a_{32} & a_{33}<br />
\end{vmatrix}<br />
+ a_{31}<br />
\begin{vmatrix}<br />
a_{12} & a_{13} \\<br />
a_{22} & a_{23}<br />
\end{vmatrix}<br />
</math><br />
<br />
;それ以上<br />
3x3の場合と同様にバラしていけば計算できるはず。というか、手計算でやる事はまず無いと思う。<br />
<br />
=== トレース ===<br />
対角成分の和。行列<math>A</math>のトレースは<math>\text{tr}(A)</math>と書いたり。こゆうちの和みたいな。<br />
<br />
<math><br />
\text{tr}(A) = \sum_i a_{ii}<br />
</math><br />
<br />
== 固有値 ==<br />
<math><br />
A \boldsymbol{v} = \boldsymbol{v} \lambda<br />
</math><br />
<br />
を満たすベクトル<math>\boldsymbol{v}</math>を固有ベクトル、<math>\lambda</math>を固有値という。この<math>\boldsymbol{v},\,\lambda</math>の組み合わせは複数出てくるので、固有値<math>\lambda</math>に対応する固有ベクトル<math>\boldsymbol{v}</math>のように表現することが多い。大抵、最大か最小固有値に対応するものが重要っぽい。<br />
<br />
固有値と固有ベクトルの算出は、次の特性方程式を解くことによる。<br><br />
<math><br />
|A \boldsymbol{v} - \lambda I| = 0<br />
</math><br />
<br />
但しn次正方行列に対しては一般にn次方程式が出てくるため、4次以上になると計算が難しい。従って計算機であれば繰り返し計算などの近似解法を用いる方が簡単。というか、そういうライブラリがあるので利用するのが賢い。<br />
<br />
== 行列の大きさ ==<br />
[http://ja.wikipedia.org/wiki/%E8%A1%8C%E5%88%97%E3%83%8E%E3%83%AB%E3%83%A0 Wikipedia]によると、色々あるらしい。<br />
<br />
;フロベニウスノルム<br />
<math><br />
||A||_\text{F} = \sqrt{\sum_i \sum_j (a_{ij})^2} = \sqrt{\text{tr}(A^\text{T} A)}<br />
</math><br />
<br />
これは固有値の和になるようだ。<br />
<br />
== 行列の分解 ==<br />
;LU分解<br />
行列を下三角行列(<math>L</math>)と上三角行列(U)の積に分解する方法。ガウスの消去法をやると上三角行列が出てくるが、それに至るまでの操作を行列で表現すると<math>L</math>の部分も分かる。<br />
<br />
LU分解を利用すると、連立線形方程式が簡単に(後進代入)解ける。<br><br />
<math><br />
A \boldsymbol{x} = (L U) \boldsymbol{x} = \boldsymbol{b}<br />
</math><br><br />
<math><br />
\Rightarrow \,<br />
\left\{<br />
\begin{array}{l}<br />
\boldsymbol{y} = L^{-1} \boldsymbol{b} \\<br />
U \boldsymbol{x} = \boldsymbol{y}<br />
\end{array}<br />
\right.<br />
</math><br />
<br />
;QR分解<br />
行列を直交行列(<math>Q</math>)と上三角行列(<math>R</math>)の積に分解する方法。上三角行列をRで表す宗派があるようで、LU分解もLR分解という別名がある。ランク落ちしてても使えるらしい。<br />
<br />
<math><br />
A = Q R<br />
</math><br />
<br />
;固有値分解<br />
正方行列を固有ベクトルを横に並べた行列<math>V</math>と対応する固有値を対角成分に持つ行列<math>D</math>の積に分解する方法。<br />
<br />
<math><br />
\begin{array}{l}<br />
A \left[ \boldsymbol{v}_1,\, \dots, \boldsymbol{v}_d \right] <br />
= \left[ \boldsymbol{v}_1,\, \dots, \boldsymbol{v}_d \right] \text{diag}\left( \lambda_1, \dots, \lambda_d \right)<br />
= V D \\<br />
A = V D V^{\text{T}}<br />
\end{array}<br />
</math><br />
<br />
;特異値分解(SVD)<br />
行列を直交行列<math>U,\,V</math>と対角行列<math>D</math>の積に分解する方法。正方行列以外にも使えるため便利。<br />
<br />
<math><br />
A = U D V^\text{T}<br />
</math><br />
<br />
ちなみに上式から明らかなように、<br><br />
<math><br />
\begin{array}{l}<br />
A A^\text{T} = U D^2 U^\text{T} \\<br />
A^\text{T} A = V D^2 V^\text{T} \\<br />
\end{array}<br />
</math><br><br />
であるため、データ<math>A=\left[ \boldsymbol{x}_1, \dots, \boldsymbol{x}_n \right]</math>の内積<math>A^\text{T} A</math>が分かれば、その固有ベクトルを使って共分散行列<math>A A^\text{T}</math>の固有ベクトルを次のように求める事ができる。<br><br />
<math><br />
U = A V D^{-1}<br />
</math><br><br />
何に使えるかは知らない。<br />
<br />
= その先? =<br />
添え字の数が0、1、2と増えてきたからには、[http://ja.wikipedia.org/wiki/%E5%A4%9A%E9%87%8D%E7%B7%9A%E5%9E%8B%E4%BB%A3%E6%95%B0 その先]があるに違いない。けど、興味ない(笑)。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%82%AB%E3%83%A1%E3%83%A9%E3%81%AE%E8%BC%83%E6%AD%A3
カメラの較正
2011-10-23T11:36:05Z
<p>白飯: スペースなどの調整</p>
<hr />
<div>== 平面を使った較正 ==<br />
[[ファイル:Plane_calib.svg|300px|right|平面を使った較正]]<br />
まあ、定番の手法。撮影した物体の3次元的な形状を計算するだけなら何もやらなくていいけど、対象物の大きさも知るためには既知の大きさの物体(参照物体)を撮影してみてそれが画像上で何画素になるか調べる必要がある。<br />
<br />
例えば一辺20 [cm]位の立方体を用意して各頂点が画像上のどこになるか観測すると、3次元空間でそれぞれの方向に20 [cm]動いたら画像上でどう変化するかがわかるので簡単に3次元座標と画像座標の関係を求められる。<br />
これが直感的で分かりやすいけど、その立方体を用意するのが大変。<br />
この立方体が長さと角度の基準になるから、精密に作らないと復元結果が歪んじゃう。<br />
もちろん画像上の点と3次元座標の関係が得られればいいので、立方体じゃ無くても、球とかタワシとかフィギュアとか3次元物体であれば何でも使えるけど、いずれにせよ精密に作るのは難しい。それに丁寧に扱わないといけないし。<br />
<br />
そこで平面ですよ。奥さん。<br />
<br />
真っ直ぐな板とプリンタさえあれば、誰でも簡単に作れちゃうスグレモノ。<br />
壊れたってすぐ作り直せるし、ぞんざいに扱っても大丈夫。投げようが踏もうが噛み付こうが問題なし。<br />
まあ、本当に精度良く較正しようとするなら、それなりに注意深く作る必要があるけどね。。<br />
<br />
<!-- <br style="clear: both" /> --><br />
== 座標系の設定 ==<br />
<br />
座標系は自由に決められるから何でもいいけど、後々混乱の元になるのできちんと決めておくべし。<br />
ここでは、<br />
* 右手座標系<br />
* 回転は右ネジ<br />
* 世界座標系=カメラ座標系 (世界座標系の原点にカメラ)<br />
* 画像座標系は右向きにx軸、下向きにy軸<br />
* カメラ座標系と画像座標系のy軸は平行・同じ向き<br />
* 平面の法線(z軸)は平面の表側が正<br />
とする (図参照)。<br />
<br />
== ホモグラフィ ==<br />
i 回目の観測画像における j 番目の画像座標<math>\tilde{\boldsymbol{m}}_{ij}</math>と参照平面上の座標<math>\tilde{\boldsymbol{p}}_{ij}</math>はホモグラフィ<math>H_i</math>を通して次の関係があるとする。<br />
<br />
<math><br />
\tilde{\boldsymbol{m}}_{ij}<br />
\propto<br />
H_i \tilde{\boldsymbol{p}}_{ij}<br />
</math><br />
<br />
<math>H_i</math>は参照平面に設定した座標と観測した画像座標の組が4点以上あれば[[射影変換(2次元)|計算]]できる。<br />
あと、この時参照平面に設定した単位系が世界座標系の単位系になる。つまり、平面上の座標を適当な原点から[mm]で表現すれば世界座標系も[mm]になる。<br />
<br />
== カメラパラメータとの関係 ==<br />
世界座標系からカメラ座標系への変換行列を<math>T_C</math>、モデル座標系から世界座標系への変換行列を<math>T_M</math>とすると、モデル座標系の3次元点<math>\tilde{\boldsymbol{X}}</math>は以下のように投影される。<br />
<br />
<math><br />
\tilde{\boldsymbol{m}} \propto (K | \boldsymbol{0}) T_C T_M \tilde{\boldsymbol{X}}<br />
</math><br /><br />
ただし、<br /><br />
<math><br />
K =<br />
\begin{bmatrix}<br />
\alpha & \gamma & u \\<br />
0 & \beta & v \\<br />
0 & 0 & 1<br />
\end{bmatrix},\quad<br />
T_C =<br />
\begin{bmatrix}<br />
I & \boldsymbol{0} \\<br />
0 & 1<br />
\end{bmatrix},\quad<br />
T_M =<br />
\begin{bmatrix}<br />
\boldsymbol{r}_1 & \boldsymbol{r}_2 & \boldsymbol{r}_3 & \boldsymbol{t} \\<br />
0 & 0 & 0 & 1<br />
\end{bmatrix}<br />
</math><br />
<br />
で、平面の x 軸 (= <math>\boldsymbol{r}_{1i}</math>)、 y 軸 (= <math>\boldsymbol{r}_{2i}</math>)と世界座標系における平面の原点位置 (= <math>\boldsymbol{t}_i</math>)から、平面上の点<math>\tilde{\boldsymbol{p}}_{ij}</math>は次のようになる。<br />
<br />
<math><br />
\begin{align}<br />
\tilde{\boldsymbol{m}_{ij}} &\propto K<br />
\begin{bmatrix}<br />
\boldsymbol{r}_{1i} & \boldsymbol{r}_{2i} & \boldsymbol{t}_i<br />
\end{bmatrix} \tilde{\boldsymbol{p}}_{ij} \\<br />
&\propto H_i \tilde{\boldsymbol{p}}_{ij}<br />
\end{align}<br />
</math><br />
<br />
そんな訳で、カメラパラメータとホモグラフィは以下の関係が成り立つ。<br />
<br />
<math><br />
s_i<br />
\begin{bmatrix}<br />
\boldsymbol{r}_{1i} & \boldsymbol{r}_{2i} & \boldsymbol{t}_i<br />
\end{bmatrix}<br />
= K^{-1} H_i<br />
</math><br /><br />
ただし、<math>s_i</math>は定数倍の係数。<br />
<br />
左辺の<math>\boldsymbol{r}_{1i}, \boldsymbol{r}_{2i}</math>は大きさ1で互いに直交するので、<math>H_i = [\boldsymbol{h}_{1i} \ \boldsymbol{h}_{2i} \ \boldsymbol{h}_{3i}]</math>とすると次を得る。<br />
<br />
<math><br />
\begin{cases}<br />
\boldsymbol{h}_{1i}^\text{T} \omega \boldsymbol{h}_{1i}<br />
= \boldsymbol{h}_{2i}^\text{T} \omega \boldsymbol{h}_{2i}<br />
= s_i^2 \\<br />
\boldsymbol{h}_{1i}^\text{T} \omega \boldsymbol{h}_{2i} = 0<br />
\end{cases}<br />
</math><br /><br />
ここで、<br /><br />
<math><br />
\omega = \left(K^{-1} \right)^\text{T} K^{-1}<br />
</math><br /><br />
とした。<br />
<br />
未知数は対称行列<math>\omega</math>の成分 (6自由度)のみで、1回の観測で得られる拘束式が2本なので、異なる位置・姿勢の参照平面を3回以上観測すれば解く事ができる。<br />
これによって<math>\omega</math>が求まれば<math>K</math>が計算できる。<br />
<br />
== <math>\omega</math>の算出 ==<br />
<br />
<math><br />
\begin{cases}<br />
\boldsymbol{h}_{1i}^\text{T} \omega \boldsymbol{h}_{1i}<br />
- \boldsymbol{h}_{2i}^\text{T} \omega \boldsymbol{h}_{2i}<br />
= 0 \\<br />
\boldsymbol{h}_{1i}^\text{T} \omega \boldsymbol{h}_{2i} = 0<br />
\end{cases}<br />
</math><br />
<br />
を<math>\omega</math>について整理する。<br />
<math>\boldsymbol{h}_{1i} = [h_{11i} \ h_{21i} \ h_{31i}]</math><br />
とすると、<br />
<br />
<math><br />
\begin{bmatrix}<br />
h_{11i}^2 - h_{12i}^2 & 2(h_{11i} h_{21i} - h_{12i} h_{22i}) & h_{21i}^2 - h_{22i}^2 & 2(h_{11i} h_{31i} - h_{12i} h_{32i}) & 2(h_{21i} h_{31i} - h_{22i} h_{32i}) & h_{31i}^2 - h_{32i}^2 \\<br />
h_{11i} h_{12i} & h_{11i} h_{22i} + h_{21i} h_{12i} & h_{21i} h_{22i} & h_{11i} h_{32i} + h_{31i} h_{12i} & h_{21i} h_{32i} + h_{31i} h_{22i} & h_{31i} h_{32i}<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
\omega_{11} \\ \omega_{12} \\ \omega_{22} \\ \omega_{13} \\ \omega_{23} \\ \omega_{33}<br />
\end{bmatrix}<br />
= 0<br />
</math><br />
<br />
となるから、各観測に対して求めたホモグラフィ<math>H_i</math>をこの式に従って並べて0固有値に対応する固有ベクトルを求めれば<math>\omega</math>になっているはず。<br />
あ、この式は<math>\omega_{ij} = \omega_{ji}</math>として並び替えただけなので、確かめてから使ってね。<br />
<br />
== 外部パラメータの算出 ==<br />
<math>\omega</math>から<math>K</math>が計算できるので、<br />
<br />
<math><br />
\begin{bmatrix}<br />
\boldsymbol{r}_{1i} & \boldsymbol{r}_{2i} & \boldsymbol{t}_i<br />
\end{bmatrix}<br />
= \frac{1}{s_i} K^{-1} H_i<br />
</math><br />
<br />
から外部パラメータ(<math> \boldsymbol{r}_{1i}, \boldsymbol{r}_{2i}, \boldsymbol{r}_{3i} = \boldsymbol{r}_{1i} {\times} \boldsymbol{r}_{2i}, \boldsymbol{t}_i</math>)が求まる。<br />
ここで<math>s_i</math>は<math>|\boldsymbol{r}_{1i}| = |\boldsymbol{r}_{2i}| = 1</math>を利用して定める。<br />
これは、各観測における平面の位置・姿勢を表す。<br />
<br />
== 仕上げ ==<br />
<br />
これで大体のパラメータが求まったけど、なんだかんだで誤差が大きかったりする。なので、求めたパラメータを初期値にして例えば再投影誤差を最小化したりする;<br />
<br />
<math><br />
e^2 = \sum_{ij} || \text{proj}(\boldsymbol{p}_{ij} | K, R_i, \boldsymbol{t}_i) - \boldsymbol{m}_{ij} ||^2 \rightarrow \min<br />
</math><br /><br />
ただし<math>\text{proj}(\boldsymbol{p}_{ij} | K, R_i, \boldsymbol{t}_i)</math>は平面上の点<math>\boldsymbol{p}_{ij}</math>を推定したパラメータで画像平面へ投影した座標とする。<br />
<br />
歪みパラメータも推定するなら(適当な初期値を設定して)この式に入れとけばいいはず。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%82%AB%E3%83%A1%E3%83%A9%E3%81%AE%E8%BC%83%E6%AD%A3
カメラの較正
2011-10-23T11:26:56Z
<p>白飯: 初版</p>
<hr />
<div>== 平面を使った較正 ==<br />
[[ファイル:Plane_calib.svg|300px|right|平面を使った較正]]<br />
まあ、定番の手法。撮影した物体の3次元的な形状を計算するだけなら何もやらなくていいけど、対象物の大きさも知るためには既知の大きさの物体(参照物体)を撮影してみてそれが画像上で何画素になるか調べる必要がある。<br />
<br />
例えば一辺20 [cm]位の立方体を用意して各頂点が画像上のどこになるか観測すると、3次元空間でそれぞれの方向に20 [cm]動いたら画像上でどう変化するかがわかるので簡単に3次元座標と画像座標の関係を求められる。<br />
これが直感的で分かりやすいけど、その立方体を用意するのが大変。<br />
この立方体が長さと角度の基準になるから、精密に作らないと復元結果が歪んじゃう。<br />
もちろん画像上の点と3次元座標の関係が得られればいいので、立方体じゃ無くても、球とかタワシとかフィギュアとか3次元物体であれば何でも使えるけど、いずれにせよ精密に作るのは難しい。それに丁寧に扱わないといけないし。<br />
<br />
そこで平面ですよ。奥さん。<br />
<br />
真っ直ぐな板とプリンタさえあれば、誰でも簡単に作れちゃうスグレモノ。<br />
壊れたってすぐ作り直せるし、ぞんざいに扱っても大丈夫。投げようが踏もうが噛み付こうが問題なし。<br />
まあ、本当に精度良く較正しようとするなら、それなりに注意深く作る必要があるけどね。。<br />
<br />
<!-- <br style="clear: both" /> --><br />
== 座標系の設定 ==<br />
<br />
座標系は自由に決められるから何でもいいけど、後々混乱の元になるのできちんと決めておくべし。<br />
ここでは、<br />
* 右手座標系<br />
* 回転は右ネジ<br />
* 世界座標系=カメラ座標系 (世界座標系の原点にカメラ)<br />
* 画像座標系は右向きにx軸、下向きにy軸<br />
* カメラ座標系と画像座標系のy軸は平行・同じ向き<br />
* 平面の法線(z軸)は平面の表側が正<br />
とする (図参照のこと)。<br />
<br />
== ホモグラフィ ==<br />
i回目の観測画像におけるj番目の画像座標<math>\tilde{\boldsymbol{m}}_{ij}</math>と参照平面上の座標<math>\tilde{\boldsymbol{p}}_{ij}</math>はホモグラフィ<math>H_i</math>を通して次の関係があるとする。<br />
<br />
<math><br />
\tilde{\boldsymbol{m}}_{ij}<br />
\propto<br />
H_i \tilde{\boldsymbol{p}}_{ij}<br />
</math><br />
<br />
<math>H_i</math>は参照平面に設定した座標と観測した画像座標の組が4点以上あれば[[射影変換(2次元)|計算]]できる。<br />
あと、この時参照平面に設定した単位系が世界座標系の単位系になる。つまり、平面上の座標を適当な原点から[mm]で表現すれば世界座標系も[mm]になる。<br />
<br />
== カメラパラメータとの関係 ==<br />
世界座標系からカメラ座標系への変換行列を<math>T_C</math>、モデル座標系から世界座標系への変換行列を<math>T_M</math>とすると、モデル座標系の3次元点<math>\tilde{\boldsymbol{X}}</math>は以下のように投影される。<br />
<br />
<math><br />
\tilde{\boldsymbol{m}} \propto (K | \boldsymbol{0}) T_C T_M \tilde{\boldsymbol{X}}<br />
</math><br /><br />
ただし、<br /><br />
<math><br />
K =<br />
\begin{bmatrix}<br />
\alpha & \gamma & u \\<br />
0 & \beta & v \\<br />
0 & 0 & 1<br />
\end{bmatrix},\quad<br />
T_C =<br />
\begin{bmatrix}<br />
I & \boldsymbol{0} \\<br />
0 & 1<br />
\end{bmatrix},\quad<br />
T_M =<br />
\begin{bmatrix}<br />
\boldsymbol{r}_1 & \boldsymbol{r}_2 & \boldsymbol{r}_3 & \boldsymbol{t} \\<br />
0 & 0 & 0 & 1<br />
\end{bmatrix}<br />
</math><br />
<br />
で、平面のx軸(= <math>\boldsymbol{r}_{1i}</math>)、y軸(= <math>\boldsymbol{r}_{2i}</math>)と世界座標系における平面の原点位置(= <math>\boldsymbol{t}_i</math>)から、平面上の点<math>\tilde{\boldsymbol{p}}_{ij}</math>は次のようになる。<br />
<br />
<math><br />
\begin{align}<br />
\tilde{\boldsymbol{m}_{ij}} &\propto K<br />
\begin{bmatrix}<br />
\boldsymbol{r}_{1i} & \boldsymbol{r}_{2i} & \boldsymbol{t}_i<br />
\end{bmatrix} \tilde{\boldsymbol{p}}_{ij} \\<br />
&\propto H_i \tilde{\boldsymbol{p}}_{ij}<br />
\end{align}<br />
</math><br />
<br />
そんな訳で、カメラパラメータとホモグラフィは以下の関係が成り立つ。<br />
<br />
<math><br />
s_i<br />
\begin{bmatrix}<br />
\boldsymbol{r}_{1i} & \boldsymbol{r}_{2i} & \boldsymbol{t}_i<br />
\end{bmatrix}<br />
= K^{-1} H_i<br />
</math><br /><br />
ただし、<math>s_i</math>は定数倍の係数。<br />
<br />
左辺の<math>\boldsymbol{r}_{1i}, \boldsymbol{r}_{2i}</math>は大きさ1で互いに直交するので、<math>H_i = [\boldsymbol{h}_{1i} \ \boldsymbol{h}_{2i} \ \boldsymbol{h}_{3i}]</math>とすると次を得る。<br />
<br />
<math><br />
\begin{cases}<br />
\boldsymbol{h}_{1i}^\text{T} \omega \boldsymbol{h}_{1i}<br />
= \boldsymbol{h}_{2i}^\text{T} \omega \boldsymbol{h}_{2i}<br />
= s_i^2 \\<br />
\boldsymbol{h}_{1i}^\text{T} \omega \boldsymbol{h}_{2i} = 0<br />
\end{cases}<br />
</math><br /><br />
ここで、<br /><br />
<math><br />
\omega = \left(K^{-1} \right)^\text{T} K^{-1}<br />
</math><br /><br />
とした。<br />
<br />
未知数は対称行列<math>\omega</math>の成分(6自由度)のみで、1回の観測で得られる拘束式が2本なので、異なる位置・姿勢の参照平面を3回以上観測すれば解く事ができる。<br />
これによって<math>\omega</math>が求まれば<math>K</math>が計算できる。<br />
<br />
== <math>\omega</math>の算出 ==<br />
<br />
<math><br />
\begin{cases}<br />
\boldsymbol{h}_{1i}^\text{T} \omega \boldsymbol{h}_{1i}<br />
- \boldsymbol{h}_{2i}^\text{T} \omega \boldsymbol{h}_{2i}<br />
= 0 \\<br />
\boldsymbol{h}_{1i}^\text{T} \omega \boldsymbol{h}_{2i} = 0<br />
\end{cases}<br />
</math><br />
<br />
を<math>\omega</math>について整理する。<br />
<math>\boldsymbol{h}_{1i} = [h_{11i} \ h_{21i} \ h_{31i}]</math><br />
とすると、<br />
<br />
<math><br />
\begin{bmatrix}<br />
h_{11i}^2 - h_{12i}^2 & 2(h_{11i} h_{21i} - h_{12i} h_{22i}) & h_{21i}^2 - h_{22i}^2 & 2(h_{11i} h_{31i} - h_{12i} h_{32i}) & 2(h_{21i} h_{31i} - h_{22i} h_{32i}) & h_{31i}^2 - h_{32i}^2 \\<br />
h_{11i} h_{12i} & h_{11i} h_{22i} + h_{21i} h_{12i} & h_{21i} h_{22i} & h_{11i} h_{32i} + h_{31i} h_{12i} & h_{21i} h_{32i} + h_{31i} h_{22i} & h_{31i} h_{32i}<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
\omega_{11} \\ \omega_{12} \\ \omega_{22} \\ \omega_{13} \\ \omega_{23} \\ \omega_{33}<br />
\end{bmatrix}<br />
= 0<br />
</math><br />
<br />
となるから、各観測に対して求めたホモグラフィ<math>H_i</math>をこの式に従って並べて0固有値に対応する固有ベクトルを求めれば<math>\omega</math>になっているはず。<br />
あ、この式は<math>\omega_{ij} = \omega_{ji}</math>として並び替えただけなので、確かめてから使ってね。<br />
<br />
== 外部パラメータの算出 ==<br />
<math>\omega</math>から<math>K</math>が計算できるので、<br />
<br />
<math><br />
\begin{bmatrix}<br />
\boldsymbol{r}_{1i} & \boldsymbol{r}_{2i} & \boldsymbol{t}_i<br />
\end{bmatrix}<br />
= \frac{1}{s_i} K^{-1} H_i<br />
</math><br />
<br />
から外部パラメータ(<math> \boldsymbol{r}_{1i}, \boldsymbol{r}_{2i}, \boldsymbol{r}_{3i} = \boldsymbol{r}_{1i} {\times} \boldsymbol{r}_{2i}, \boldsymbol{t}_i</math>)が求まる。<br />
ここで<math>s_i</math>は<math>|\boldsymbol{r}_{1i}| = |\boldsymbol{r}_{2i}| = 1</math>を利用して定める。<br />
これは、各観測における平面の位置・姿勢を表す。<br />
<br />
== 仕上げ ==<br />
<br />
これで大体のパラメータが求まったけど、なんだかんだで誤差が大きかったりする。なので、求めたパラメータを初期値にして例えば再投影誤差を最小化したりする;<br />
<br />
<math><br />
e^2 = \sum_{ij} || \text{proj}(\boldsymbol{p}_{ij} | K, R_i, \boldsymbol{t}_i) - \boldsymbol{m}_{ij} ||^2<br />
</math><br /><br />
ただし<math>\text{proj}(\boldsymbol{p}_{ij} | K, R_i, \boldsymbol{t}_i)</math>は平面上の点<math>\boldsymbol{p}_{ij}</math>を推定したパラメータで画像平面へ投影した座標とする。<br />
<br />
歪みパラメータも推定するなら(適当な初期値を設定して)この式に入れとけばいいはず。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Plane_calib.svg
ファイル:Plane calib.svg
2011-10-23T07:21:17Z
<p>白飯: 「ファイル:Plane calib.svg」の新しい版をアップロードしました: ドキュメントのサイズを修正</p>
<hr />
<div>平面を使ったキャリブレーション初版</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Plane_calib.svg
ファイル:Plane calib.svg
2011-10-23T07:17:16Z
<p>白飯: 平面を使ったキャリブレーション初版</p>
<hr />
<div>平面を使ったキャリブレーション初版</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E9%9D%9E%E7%B7%9A%E5%BD%A2%E6%9C%80%E9%81%A9%E5%8C%96
非線形最適化
2011-07-18T04:56:07Z
<p>白飯: /* ニュートン(・ラフソン?)法 */ ガウス・ニュートン法っぽい</p>
<hr />
<div>ここでは変数<math>\boldsymbol{x}</math>の関数<math>e(\boldsymbol{x})</math>が与えられた時に、その値を最小(または最大)にする<math>\boldsymbol{x}^*</math>を求める事を考える。<br />
<br />
で、<math>e</math>が<math>\boldsymbol{x}</math>の2次式以上になると'''非線形'''という。たぶん。<br />
<br />
最適化問題にはよく<math>x_1 > 0</math>とか条件が付いてくるけど、難しいので考えない。<br />
そういう問題を解く必要に迫られた人は「'''(非)線形計画問題'''」でググるといいかも。<br />
<math>|\boldsymbol{x}| = 1</math>的な等式の条件ならラグランジュの未定定数だか何だかで、どうにかなるらしい。<br />
<br />
要するに、関数が一つ与えられて、「この関数の最小値(とその時の<math>\boldsymbol{x}^*</math>)はいくつでしょう?」という問題しか扱いませんよ、という話。<br />
<br />
== 基本方針 ==<br />
2次や3次の方程式には解の公式ってのがあるけどその他はよく分からないので、「いま分かっている値よりいいものを探す」というのを繰り返す、繰り返し計算になる。<br />
当然、極値を求めているだけなので他にもっといい点がある可能性が存在する。<br />
なので、できるだけ極値が少ない(≒次数が低い)関数を使って、できるだけよい初期値から始めたり複数の初期値を使ったりする必要がある。<br />
処理の流れをざっくり書くと、こんな感じ。<br />
<br />
# 初期値を決める(大抵、線形解法・近似解法の解)<br />
# 評価する点を決める<br />
# 評価して良ければ値を更新<br />
# 改善の見込みが無くなれば終了。そうで無ければ2に戻る<br />
<br />
次に評価する点をいかに効率よく探すかがポイント。<br />
<br />
== 関数の値しか分からない場合 ==<br />
まずは<math>\boldsymbol{x}</math>が与えられた時に<math>e(\boldsymbol{x})</math>の値しか計算できない場合。<br />
つまり、次にどっちへいけば値が良くなるか分からない状態。<br />
<br />
=== シンプレックス法 ===<br />
たぶん、定番の方法。<br />
線形計画問題に出てくるシンプレックス法とは無関係。<br />
<br />
=== Differential Evolution ===<br />
GAっぽい感じ。こんなのもあるのかぁ。<br />
ただ、いつ終わればいいかが分からない。。<br />
<br />
* 関数が不連続でもよい<br />
* 取り敢えず試せる<br />
* 関数の評価を沢山やる必要がある<br />
<br />
== 1回微分が分かる場合 ==<br />
=== 最急降下法 ===<br />
<math>e(\boldsymbol{x})</math>の微分<math>\nabla e(\boldsymbol{x})</math>は値が大きくなる方向なので、極小値を求めているなら反対方向に動かせば良い。<br />
<br />
<math><br />
\begin{array}{l}<br />
\boldsymbol{x}^{(i+1)} = \boldsymbol{x}^{(i)} + \boldsymbol{r} \\<br />
r = - \lambda \nabla e(\boldsymbol{x}^{(i)})<br />
\end{array}<br />
</math><br />
<br />
<math>\lambda (> 0)</math>は適当に決める。<math>-\nabla e</math>に沿って動かしてみて値が小さくなる(大きくなる)ところを選べばいいかも。<br />
<br />
* どっちに進めばいいか分かるので安心<br />
* 極値なら<math>\lambda e(\boldsymbol{x}) = 0</math><br />
* あまり大きく動かせない<br />
<br />
== 2回微分まで分かる場合 ==<br />
<br />
=== (ガウス・)ニュートン法 ===<br />
<math>\boldsymbol{x}</math>の近くでは、更新量<math>\boldsymbol{r}</math>に対して以下の近似が成り立つ。<br />
<br />
<math><br />
\begin{array}{l}<br />
\nabla e(\boldsymbol{x} + \boldsymbol{r}) = \nabla e(\boldsymbol{x}) + H \boldsymbol{r} \\<br />
H = (h_{ij}) = \frac{\partial^2}{\partial x_i \partial x_j} e<br />
\end{array}<br />
</math><br />
<br />
で、極値だと<math>\nabla e(\boldsymbol{x} + \boldsymbol{r}) = 0</math>になるので、<br />
<br />
<math><br />
\boldsymbol{r} = - H^{-1} \nabla e(\boldsymbol{x})<br />
</math><br />
<br />
となる。<br />
<br />
* 関数が二次関数でうまく近似できる時は、メチャ速<br />
* 運が悪いと解が振動する<br />
<br />
=== レベンバーグ・マーカート法 ===<br />
よく忘れるので英語表記も書いておく。Le'''v'''en'''b'''erg-Mar'''q'''uard'''t'''・・・で合ってると思う。<br />
元ネタを読んだ事が無いので、雰囲気だけ。<br />
<br />
最急降下法とニュートン法を足して2で割ったような感じ:<br />
<br />
<math><br />
\left(H - \lambda I \right) \boldsymbol{r} = -\nabla e(\boldsymbol{x})<br />
</math><br />
<br />
更新がうまくいった時は<math>\lambda</math>を小さくしてニュートン法に近づけて、ダメな場合は大きくして最急降下法に近づける。<br />
非線形最適化といえば、まずコレをやっておけば問題ない・・・気がする。<br />
<br />
で、よく使う最小二乗法を考える。<br />
<br />
<math><br />
e(\boldsymbol{x}) = \frac{1}{2} \boldsymbol{f}(\boldsymbol{x})^\text{T} \boldsymbol{f}(\boldsymbol{x})<br />
</math><br />
<br />
このとき、<br />
<br />
<math><br />
\begin{array}{l}<br />
\nabla e = (\nabla \boldsymbol{f})^\text{T} \boldsymbol{f} \\<br />
H = (\nabla \boldsymbol{f})^\text{T} (\nabla \boldsymbol{f}) + \left( \frac{\partial^2}{\partial^2 \boldsymbol{x}} \boldsymbol{f} \right) \boldsymbol{f}<br />
\approx (\nabla \boldsymbol{f})^\text{T} (\nabla \boldsymbol{f})<br />
\end{array}<br />
</math><br />
<br />
更新量:<br />
<br />
<math><br />
\left( (\nabla \boldsymbol{f})^\text{T} (\nabla \boldsymbol{f}) - \lambda I \right) \boldsymbol{r}<br />
= - (\nabla \boldsymbol{f})^\text{T} \boldsymbol{f}<br />
</math><br />
<br />
なので、<math>\boldsymbol{f}(\boldsymbol{x}), \nabla \boldsymbol{f}(\boldsymbol{x})</math>が分かるだけで計算できる。<br />
<br />
また、実際の値の変化と予測を比べる事で2次近似の正否が見積もれる:<br />
<br />
<math><br />
s(\boldsymbol{x}, \boldsymbol{r}) = \frac{e(\boldsymbol{x} + \boldsymbol{r}) - e(\boldsymbol{x})}{(\nabla e(\boldsymbol{x}))^\text{T} \boldsymbol{r}}<br />
</math><br />
<br />
* 定番の方法<br />
* たまにランク落ちする事があるので、うまくやる<br />
* <math>\lambda</math>のいい決め方があるらしい<br />
* ちゃんと考えてプログラムしないとグチャグチャに</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%82%AB%E3%83%A1%E3%83%A9%E3%83%A2%E3%83%87%E3%83%AB
カメラモデル
2011-06-25T05:39:44Z
<p>白飯: 記号文字の変更</p>
<hr />
<div>== ピンホールカメラ ==<br />
迷ったら、コレ。<br />
<br />
画像座標を<math>\boldsymbol{m}</math>、3次元座標を<math>\boldsymbol{X}</math>とすると、<br><br />
<math><br />
s<br />
\begin{bmatrix}<br />
u \\ v \\ 1<br />
\end{bmatrix}<br />
= s \tilde{\boldsymbol{m}}<br />
= P \tilde{\boldsymbol{X}}<br />
</math><br><br />
で表される。ただし、<math>\tilde{\boldsymbol{m}}, \tilde{\boldsymbol{X}}</math>はそれぞれ画像座標と3次元座標の[[同次座標]]、<math>P</math>は3x4行列、<math>s</math>は0でない任意の定数とする。<br />
<br />
=== 射影行列<math>P</math>の例 ===<br />
<math>P</math>は3x3内部パラメータ行列<math>K</math>、3x4外部パラメータ行列<math>[R | \boldsymbol{t}]</math>に(だいたい)分ける事ができる。<br><br />
<math><br />
P = K [R | \boldsymbol{t}]<br />
</math><br><br />
なお<math>K</math>は上三角行列、<math>R, \boldsymbol{t}</math>はそれぞれカメラから見た世界座標系の軸の向き(回転行列)、原点の位置を表す。<br />
で、<math>P</math>には定数倍の不定性があるので、<math>P</math>と任意の0でない定数<math>a</math>を掛けた<math>aP</math>は同じ変換を表す。<br />
<br />
==== 普通のカメラ ====<br />
<math><br />
P =<br />
\begin{bmatrix}<br />
\alpha & \gamma & u_0 \\<br />
0 & \beta & v_0 \\<br />
0 & 0 & 1<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
R & \boldsymbol{t}<br />
\end{bmatrix}<br />
</math><br><br />
自由度は内部パラメータ5、外部パラメータ6(回転3、並進3)の計11。<br />
内部パラメータの(3, 3)成分を1しばりにすれば、定数倍の不定性が除去できる。<br />
<br />
==== 一般カメラ ====<br />
要はパラメータが物理的なものと関係なくてもよいというだけ。12個の成分を持ったただの行列。<br><br />
<math><br />
P =<br />
\begin{bmatrix}<br />
p_{11} & \dots & p_{14} \\<br />
\vdots & \ddots & \vdots \\<br />
p_{31} & \dots & p_{34}<br />
\end{bmatrix}<br />
</math><br><br />
ただし、定数倍の不定性があるので独立な成分は11個だけ。<br />
<br />
=== レンズ歪み ===<br />
カメラで直線を撮影しても、真っ直ぐに写らない事がある。<br />
原因があるらしいけど(霊的な何か?)、よく分からない。<br />
それはともかく、こういう歪みがあると画像処理が面倒になるので、頑張って補正する。<br />
<br />
<math>\boldsymbol{x} \in \mathcal{R}^3</math>を正規化カメラ画像座標とする。つまり3次元点<math>\tilde{\boldsymbol{X}}</math>に外部パラメータを作用させて、第3成分で割ったものとする;<br />
<br />
<math><br />
\begin{array}{l}<br />
\boldsymbol{\xi} =<br />
\begin{bmatrix}<br />
R & \boldsymbol{t}<br />
\end{bmatrix}<br />
\tilde{\boldsymbol{X}}<br />
\\<br />
\boldsymbol{x} =<br />
\begin{bmatrix}<br />
\xi_1 / \xi_3 \\ \xi_2 / \xi_3 \\ 1<br />
\end{bmatrix}<br />
\end{array}<br />
</math><br />
<br />
==== 放射方向の歪み ====<br />
<math><br />
\begin{array}{l}<br />
r^2 = x_1^2 + x_2^2 \\<br />
\begin{cases}<br />
\breve{x}_1 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \cdots) x_1 \\<br />
\breve{x}_2 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \cdots) x_2<br />
\end{cases}<br />
\end{array}<br />
</math><br />
<br />
レンズが回転対称なので歪みはどうせ偶関数だろう、という式。<math>{\kappa_i}</math>が係数で、[[カメラの較正|キャリブレーション]]する時に頑張って求める。<br />
普通のレンズなら<math>\kappa_2</math>までで十分だと思うけど、不安ならもう少し増やすべし。<br />
ただ、増やすだけ求めるべき係数も増えるのでキャリブレーション時の計算が不安定になるし、すぐに桁あふれするしで良い事は無い。<br />
<br />
==== OpenCV 2.1まで ====<br />
<math><br />
\begin{cases}<br />
\breve{x}_1 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6) x_1 + 2 p_1 x_1 x_2 + p_2 (r^2 + 2 x_1^2) \\<br />
\breve{x}_2 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6) x_2 + p_1 (r^2 + 2 x_2^2) + 2 p_2 x_1 x_2<br />
\end{cases}<br />
</math><br />
<br />
<math>p_1, p_2</math>は接線方向の歪み係数らしい。元ネタが分からないので、そんなもんかなーって感じ。<br />
<br />
==== OpenCV 2.2以降 ====<br />
<math><br />
\begin{cases}<br />
\breve{x}_1 = \frac{1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6}{1 + \kappa_4 r^2 + \kappa_5 r^4 + \kappa_6 r^6} x_1 + 2 p_1 x_1 x_2 + p_2 (r^2 + 2 x_1^2) \\<br />
\breve{x}_2 = \frac{1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6}{1 + \kappa_4 r^2 + \kappa_5 r^4 + \kappa_6 r^6} x_2 + p_1 (r^2 + 2 x_2^2) + 2 p_2 x_1 x_2<br />
\end{cases}<br />
</math><br />
<br />
放射方向の歪みに分母が付いた。複雑化の一途だなあ。<br />
<br />
この辺りの事情に疎いので、有名どころのOpenCVをパクってみた。<br />
<br />
=== 歪みの補正 ===<br />
画像を歪ませるのは上式の通りに計算すればできるけど、逆は非線形方程式を解かないといけないので少し面倒。<br />
とはいえ、座標値が少し変化するぐらいなので、歪んだ座標値を初期値にして適当に[[非線形最適化|数値計算]]すれば大丈夫。<br />
<br />
繰り返し計算はそれなりに遅くなるし、(カメラを触らなければ)計算される値はいつも同じなので、1回だけ計算して歪んだ座標から補正後の座標を引ける表を作っておくのが便利。<br />
<br />
==== 普通のカメラ画像を補正する手順 ====<br />
1. 歪んだ正規化座標を求める<br><br />
: <math>\breve{\boldsymbol{x}} = K^{-1} \tilde{\boldsymbol{m}}</math><br />
<br />
2. 計算するか、表を引いて補正後の座標を求める<br><br />
: <math>\boldsymbol{x} = \boldsymbol{x}(\breve{\boldsymbol{x}})</math><br />
<br />
3. 画像面に投影する<br><br />
: <math>\tilde{\boldsymbol{m}}^\prime = K^\prime \boldsymbol{x}</math><br />
<br />
ここのポイントは最後の画像を作る時に'''任意の内部パラメータが使える'''ということ。<br />
普通は<math>K^\prime = K</math>とすればいいけど、隙間ができても歪み補正後の画像全体を納めたい場合とか、周りを切り落として隙間の無い矩形領域を取り出したい場合とかに、ちょちょっと計算して新しい<math>K^\prime</math>を使う。<br />
<br />
蛇足だけど、歪み補正後の座標が画像の外に出る可能性があるから、メモリの変な所を触らないように注意してね。<br />
<br />
== ライトフィールドカメラ ==<br />
カメラは、視点に向かって飛んでくる光を方向毎に記録する装置であると考えられる。<br />
・・・って、別に光線が視点を通らなくても良くね?ということで、「3次元空間に存在する光線を記録する装置」を考える。<br />
<br />
普通の画像は、2次元の座標<math>(x, y)</math>と色<math>\lambda</math>を指定すると観測値を返す関数<math>f(x, y, \lambda)</math>と見なせるので、普通じゃない画像は3次元の直線<math>\boldsymbol{l}</math>と色<math>\lambda</math>を指定すると観測値を返す関数<math>f(\boldsymbol{l}, \lambda)</math>だろうと想像がつく。<br />
<br />
3次元の光線は観測点(3自由度)と飛んでくる方向(2自由度)で表現できるから、普通の画像の3次元(= 2+1)に対して6次元(= 3+2+1)になるっぽい。<br />
あ、あと時刻も入れとけば動画にできるなー。<br />
<br />
ただ、次元を増やすと指数的にデータが大きくなっちゃうので(例えば1000x1000の画像を1000x1000x1000にしたら1000倍)、使えそうな範囲で仮定を付ける事にする。<br />
窓から見える景色とか。<br />
<br />
窓から見える景色は、窓ガラスを通る光線の集合として表される。もちろん、窓を開けてる場合は窓枠の内側を通る光線になるけど。<br />
ここで重要なのは窓ガラスがあるかないかじゃなくて、そこを通る光だけを考えればいいって事。これらの光線は窓ガラスの外側の面と内側の面を通るから、通った2点の座標で1条の光線が表現できる。つまり、<br />
<br />
<math><br />
f(x_O, y_O, x_I, y_I, \lambda)<br />
</math><br />
<br />
によって、(ある時点の)窓の外の景色が完全に記録できるはず。別に窓の外から内でも同じだけど、ここでは外を見る事にする。変な想像はしないように。<br />
<br />
さて、データの表現方法が決まったところで、撮影の仕方を考える。<br />
これは単純に、窓の内側に[[カメラの較正|キャリブレーション]]済みのカメラ(互いの位置関係が完全に分かっている状態)を沢山並べれば良い。<br />
後は各カメラで観測した画像の全ての画素に対して、光線が通過した窓ガラス内外の2点を計算して値を格納すればライトフィールドのできあがり。<br />
<br />
適当な視点から見た窓の景色をレンダリングするには、視点の前にスクリーンを置いてスクリーン上の各点が窓ガラスのどこを通るか計算して、記録されている値を並べればできる。<br />
まあ、実際にはそのものズバリな光線が記録されている事は中々無いから近くの値を取ってきて補間する必要があるけど、それはまた別の話。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%82%AB%E3%83%A1%E3%83%A9%E3%83%A2%E3%83%87%E3%83%AB
カメラモデル
2011-06-25T05:33:20Z
<p>白飯: 記号の統一</p>
<hr />
<div>== ピンホールカメラ ==<br />
迷ったら、コレ。<br />
<br />
画像座標を<math>\boldsymbol{m}</math>、3次元座標を<math>\boldsymbol{X}</math>とすると、<br><br />
<math><br />
s \tilde{\boldsymbol{m}} = P \tilde{\boldsymbol{X}}<br />
</math><br><br />
で表される。ただし、<math>\tilde{\boldsymbol{m}}, \tilde{\boldsymbol{X}}</math>はそれぞれ画像座標と3次元座標の[[同次座標]]、<math>P</math>は3x4行列、<math>s</math>は0でない任意の定数とする。<br />
<br />
=== 射影行列<math>P</math>の例 ===<br />
<math>P</math>は3x3内部パラメータ行列<math>K</math>、3x4外部パラメータ行列<math>[R | \boldsymbol{t}]</math>に(だいたい)分ける事ができる。<br><br />
<math><br />
P = K [R | \boldsymbol{t}]<br />
</math><br><br />
なお<math>K</math>は上三角行列、<math>R, \boldsymbol{t}</math>はそれぞれカメラから見た世界座標系の軸の向き(回転行列)、原点の位置を表す。<br />
で、<math>P</math>には定数倍の不定性があるので、<math>P</math>と任意の0でない定数<math>a</math>を掛けた<math>aP</math>は同じ変換を表す。<br />
<br />
==== 普通のカメラ ====<br />
<math><br />
P =<br />
\begin{bmatrix}<br />
\alpha & \gamma & u_0 \\<br />
0 & \beta & v_0 \\<br />
0 & 0 & 1<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
R & \boldsymbol{t}<br />
\end{bmatrix}<br />
</math><br><br />
自由度は内部パラメータ5、外部パラメータ6(回転3、並進3)の計11。<br />
内部パラメータの(3, 3)成分を1しばりにすれば、定数倍の不定性が除去できる。<br />
<br />
==== 一般カメラ ====<br />
要はパラメータが物理的なものと関係なくてもよいというだけ。12個の成分を持ったただの行列。<br><br />
<math><br />
P =<br />
\begin{bmatrix}<br />
p_{11} & \dots & p_{14} \\<br />
\vdots & \ddots & \vdots \\<br />
p_{31} & \dots & p_{34}<br />
\end{bmatrix}<br />
</math><br><br />
ただし、定数倍の不定性があるので独立な成分は11個だけ。<br />
<br />
=== レンズ歪み ===<br />
カメラで直線を撮影しても、真っ直ぐに写らない事がある。<br />
原因があるらしいけど(霊的な何か?)、よく分からない。<br />
それはともかく、こういう歪みがあると画像処理が面倒になるので、頑張って補正する。<br />
<br />
<math>\boldsymbol{x} \in \mathcal{R}^3</math>を正規化カメラ画像座標とする。つまり3次元点<math>\tilde{\boldsymbol{X}}</math>に外部パラメータを作用させて、第3成分で割ったものとする;<br />
<br />
<math><br />
\begin{array}{l}<br />
\boldsymbol{v} =<br />
\begin{bmatrix}<br />
R & \boldsymbol{t}<br />
\end{bmatrix}<br />
\tilde{\boldsymbol{X}}<br />
\\<br />
\boldsymbol{x} =<br />
\begin{bmatrix}<br />
v_1 / v_3 \\ v_2 / v_3 \\ 1<br />
\end{bmatrix}<br />
\end{array}<br />
</math><br />
<br />
==== 放射方向の歪み ====<br />
<math><br />
\begin{array}{l}<br />
r^2 = x_1^2 + x_2^2 \\<br />
\begin{cases}<br />
\breve{x}_1 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \cdots) x_1 \\<br />
\breve{x}_2 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \cdots) x_2<br />
\end{cases}<br />
\end{array}<br />
</math><br />
<br />
レンズが回転対称なので歪みはどうせ偶関数だろう、という式。<math>{\kappa_i}</math>が係数で、[[カメラの較正|キャリブレーション]]する時に頑張って求める。<br />
普通のレンズなら<math>\kappa_2</math>までで十分だと思うけど、不安ならもう少し増やすべし。<br />
ただ、増やすだけ求めるべき係数も増えるのでキャリブレーション時の計算が不安定になるし、すぐに桁あふれするしで良い事は無い。<br />
<br />
==== OpenCV 2.1まで ====<br />
<math><br />
\begin{cases}<br />
\breve{x}_1 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6) x_1 + 2 p_1 x_1 x_2 + p_2 (r^2 + 2 x_1^2) \\<br />
\breve{x}_2 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6) x_2 + p_1 (r^2 + 2 x_2^2) + 2 p_2 x_1 x_2<br />
\end{cases}<br />
</math><br />
<br />
<math>p_1, p_2</math>は接線方向の歪み係数らしい。元ネタが分からないので、そんなもんかなーって感じ。<br />
<br />
==== OpenCV 2.2以降 ====<br />
<math><br />
\begin{cases}<br />
\breve{x}_1 = \frac{1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6}{1 + \kappa_4 r^2 + \kappa_5 r^4 + \kappa_6 r^6} x_1 + 2 p_1 x_1 x_2 + p_2 (r^2 + 2 x_1^2) \\<br />
\breve{x}_2 = \frac{1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6}{1 + \kappa_4 r^2 + \kappa_5 r^4 + \kappa_6 r^6} x_2 + p_1 (r^2 + 2 x_2^2) + 2 p_2 x_1 x_2<br />
\end{cases}<br />
</math><br />
<br />
放射方向の歪みに分母が付いた。複雑化の一途だなあ。<br />
<br />
この辺りの事情に疎いので、有名どころのOpenCVをパクってみた。<br />
<br />
=== 歪みの補正 ===<br />
画像を歪ませるのは上式の通りに計算すればできるけど、逆は非線形方程式を解かないといけないので少し面倒。<br />
とはいえ、座標値が少し変化するぐらいなので、歪んだ座標値を初期値にして適当に[[非線形最適化|数値計算]]すれば大丈夫。<br />
<br />
繰り返し計算はそれなりに遅くなるし、(カメラを触らなければ)計算される値はいつも同じなので、1回だけ計算して歪んだ座標から補正後の座標を引ける表を作っておくのが便利。<br />
<br />
==== 普通のカメラ画像を補正する手順 ====<br />
1. 歪んだ正規化座標を求める<br><br />
: <math>\breve{\boldsymbol{x}} = K^{-1} \tilde{\boldsymbol{m}}</math><br />
<br />
2. 計算するか、表を引いて補正後の座標を求める<br><br />
: <math>\boldsymbol{x} = \boldsymbol{x}(\breve{\boldsymbol{x}})</math><br />
<br />
3. 画像面に投影する<br><br />
: <math>\tilde{\boldsymbol{m}}^\prime = K^\prime \boldsymbol{x}</math><br />
<br />
ここのポイントは最後の画像を作る時に'''任意の内部パラメータが使える'''ということ。<br />
普通は<math>K^\prime = K</math>とすればいいけど、隙間ができても歪み補正後の画像全体を納めたい場合とか、周りを切り落として隙間の無い矩形領域を取り出したい場合とかに、ちょちょっと計算して新しい<math>K^\prime</math>を使う。<br />
<br />
蛇足だけど、歪み補正後の座標が画像の外に出る可能性があるから、メモリの変な所を触らないように注意してね。<br />
<br />
== ライトフィールドカメラ ==<br />
カメラは、視点に向かって飛んでくる光を方向毎に記録する装置であると考えられる。<br />
・・・って、別に光線が視点を通らなくても良くね?ということで、「3次元空間に存在する光線を記録する装置」を考える。<br />
<br />
普通の画像は、2次元の座標<math>(x, y)</math>と色<math>\lambda</math>を指定すると観測値を返す関数<math>f(x, y, \lambda)</math>と見なせるので、普通じゃない画像は3次元の直線<math>\boldsymbol{l}</math>と色<math>\lambda</math>を指定すると観測値を返す関数<math>f(\boldsymbol{l}, \lambda)</math>だろうと想像がつく。<br />
<br />
3次元の光線は観測点(3自由度)と飛んでくる方向(2自由度)で表現できるから、普通の画像の3次元(= 2+1)に対して6次元(= 3+2+1)になるっぽい。<br />
あ、あと時刻も入れとけば動画にできるなー。<br />
<br />
ただ、次元を増やすと指数的にデータが大きくなっちゃうので(例えば1000x1000の画像を1000x1000x1000にしたら1000倍)、使えそうな範囲で仮定を付ける事にする。<br />
窓から見える景色とか。<br />
<br />
窓から見える景色は、窓ガラスを通る光線の集合として表される。もちろん、窓を開けてる場合は窓枠の内側を通る光線になるけど。<br />
ここで重要なのは窓ガラスがあるかないかじゃなくて、そこを通る光だけを考えればいいって事。これらの光線は窓ガラスの外側の面と内側の面を通るから、通った2点の座標で1条の光線が表現できる。つまり、<br />
<br />
<math><br />
f(x_O, y_O, x_I, y_I, \lambda)<br />
</math><br />
<br />
によって、(ある時点の)窓の外の景色が完全に記録できるはず。別に窓の外から内でも同じだけど、ここでは外を見る事にする。変な想像はしないように。<br />
<br />
さて、データの表現方法が決まったところで、撮影の仕方を考える。<br />
これは単純に、窓の内側に[[カメラの較正|キャリブレーション]]済みのカメラ(互いの位置関係が完全に分かっている状態)を沢山並べれば良い。<br />
後は各カメラで観測した画像の全ての画素に対して、光線が通過した窓ガラス内外の2点を計算して値を格納すればライトフィールドのできあがり。<br />
<br />
適当な視点から見た窓の景色をレンダリングするには、視点の前にスクリーンを置いてスクリーン上の各点が窓ガラスのどこを通るか計算して、記録されている値を並べればできる。<br />
まあ、実際にはそのものズバリな光線が記録されている事は中々無いから近くの値を取ってきて補間する必要があるけど、それはまた別の話。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%82%AB%E3%83%A1%E3%83%A9%E3%83%A2%E3%83%87%E3%83%AB
カメラモデル
2011-06-25T05:24:47Z
<p>白飯: レンズの幾何歪みなどを追記</p>
<hr />
<div>== ピンホールカメラ ==<br />
迷ったら、コレ。<br />
<br />
画像座標を<math>\boldsymbol{m}</math>、3次元座標を<math>\boldsymbol{X}</math>とすると、<br><br />
<math><br />
s \tilde{\boldsymbol{m}} = P \tilde{\boldsymbol{X}}<br />
</math><br><br />
で表される。ただし、<math>\tilde{\boldsymbol{m}}, \tilde{\boldsymbol{X}}</math>はそれぞれ画像座標と3次元座標の[[同次座標]]、<math>P</math>は3x4行列、<math>s</math>は0でない任意の定数とする。<br />
<br />
=== 射影行列<math>P</math>の例 ===<br />
<math>P</math>は3x3内部パラメータ行列<math>K</math>、3x4外部パラメータ行列<math>[R | \boldsymbol{t}]</math>に(だいたい)分ける事ができる。<br><br />
<math><br />
P = K [R | \boldsymbol{t}]<br />
</math><br><br />
なお<math>K</math>は上三角行列、<math>R, \boldsymbol{t}</math>はそれぞれカメラから見た世界座標系の軸の向き(回転行列)、原点の位置を表す。<br />
で、<math>P</math>には定数倍の不定性があるので、<math>P</math>と任意の0でない定数<math>a</math>を掛けた<math>aP</math>は同じ変換を表す。<br />
<br />
==== 普通のカメラ ====<br />
<math><br />
P =<br />
\begin{bmatrix}<br />
\alpha & \gamma & u_0 \\<br />
0 & \beta & v_0 \\<br />
0 & 0 & 1<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
R & \boldsymbol{t}<br />
\end{bmatrix}<br />
</math><br><br />
自由度は内部パラメータ5、外部パラメータ6(回転3、並進3)の計11。<br />
内部パラメータの(3, 3)成分を1しばりにすれば、定数倍の不定性が除去できる。<br />
<br />
==== 一般カメラ ====<br />
要はパラメータが物理的なものと関係なくてもよいというだけ。12個の成分を持ったただの行列。<br><br />
<math><br />
P =<br />
\begin{bmatrix}<br />
p_{11} & \dots & p_{14} \\<br />
\vdots & \ddots & \vdots \\<br />
p_{31} & \dots & p_{34}<br />
\end{bmatrix}<br />
</math><br><br />
ただし、定数倍の不定性があるので独立な成分は11個だけ。<br />
<br />
=== レンズ歪み ===<br />
カメラで直線を撮影しても、真っ直ぐに写らない事がある。<br />
原因があるらしいけど(霊的な何か?)、よく分からない。<br />
それはともかく、こういう歪みがあると画像処理が面倒になるので、頑張って補正する。<br />
<br />
<math>\boldsymbol{\xi} \in \mathcal{R}^3</math>を正規化カメラ画像座標とする。つまり3次元点<math>\tilde{\boldsymbol{X}}</math>に外部パラメータを作用させて、第3成分で割ったものとする;<br />
<br />
<math><br />
\begin{array}{l}<br />
\boldsymbol{v} =<br />
\begin{bmatrix}<br />
R & \boldsymbol{t}<br />
\end{bmatrix}<br />
\tilde{\boldsymbol{X}}<br />
\\<br />
\boldsymbol{\xi} =<br />
\begin{bmatrix}<br />
v_1 / v_3 \\ v_2 / v_3 \\ 1<br />
\end{bmatrix}<br />
\end{array}<br />
</math><br />
<br />
==== 放射方向の歪み ====<br />
<math><br />
\begin{array}{l}<br />
r^2 = \xi_1^2 + \xi_2^2 \\<br />
\begin{cases}<br />
\breve{\xi}_1 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \cdots) \xi_1 \\<br />
\breve{\xi}_2 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \cdots) \xi_2<br />
\end{cases}<br />
\end{array}<br />
</math><br />
<br />
レンズが回転対称なので歪みはどうせ偶関数だろう、という式。<math>{\kappa_i}</math>が係数で、[[カメラの較正|キャリブレーション]]する時に頑張って求める。<br />
普通のレンズなら<math>\kappa_2</math>までで十分だと思うけど、不安ならもう少し増やすべし。<br />
ただ、増やすだけ求めるべき係数も増えるのでキャリブレーション時の計算が不安定になるし、すぐに桁あふれするしで良い事は無い。<br />
<br />
==== OpenCV 2.1まで ====<br />
<math><br />
\begin{cases}<br />
\breve{\xi}_1 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6) \xi_1 + 2 p_1 \xi_1 \xi_2 + p_2 (r^2 + 2 \xi_1^2) \\<br />
\breve{\xi}_2 = (1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6) \xi_2 + p_1 (r^2 + 2 \xi_2^2) + 2 p_2 \xi_1 \xi_2<br />
\end{cases}<br />
</math><br />
<br />
<math>p_1, p_2</math>は接線方向の歪み係数らしい。元ネタが分からないので、そんなもんかなーって感じ。<br />
<br />
==== OpenCV 2.2以降 ====<br />
<math><br />
\begin{cases}<br />
\breve{\xi}_1 = \frac{1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6}{1 + \kappa_4 r^2 + \kappa_5 r^4 + \kappa_6 r^6} \xi_1 + 2 p_1 \xi_1 \xi_2 + p_2 (r^2 + 2 \xi_1^2) \\<br />
\breve{\xi}_2 = \frac{1 + \kappa_1 r^2 + \kappa_2 r^4 + \kappa_3 r^6}{1 + \kappa_4 r^2 + \kappa_5 r^4 + \kappa_6 r^6} \xi_2 + p_1 (r^2 + 2 \xi_2^2) + 2 p_2 \xi_1 \xi_2<br />
\end{cases}<br />
</math><br />
<br />
放射方向の歪みに分母が付いた。複雑化の一途だなあ。<br />
<br />
この辺りの事情に疎いので、有名どころのOpenCVをパクってみた。<br />
<br />
=== 歪みの補正 ===<br />
画像を歪ませるのは上式の通りに計算すればできるけど、逆は非線形方程式を解かないといけないので少し面倒。<br />
とはいえ、座標値が少し変化するぐらいなので、歪んだ座標値を初期値にして適当に[[非線形最適化|数値計算]]すれば大丈夫。<br />
<br />
繰り返し計算はそれなりに遅くなるし、(カメラを触らなければ)計算される値はいつも同じなので、1回だけ計算して歪んだ座標から補正後の座標を引ける表を作っておくのが便利。<br />
<br />
==== 普通のカメラ画像を補正する手順 ====<br />
1. 歪んだ正規化座標を求める<br><br />
: <math>\breve{\boldsymbol{\xi}} = A^{-1} [x\, y\, 1]^\text{T}</math><br />
<br />
2. 計算するか、表を引いて補正後の座標を求める<br><br />
: <math>\boldsymbol{\xi} = \boldsymbol{\xi}(\breve{\boldsymbol{\xi}})</math><br />
<br />
3. 画像面に投影する<br><br />
: <math>\tilde{\boldsymbol{x}}^\prime = A^\prime \boldsymbol{\xi}</math><br />
<br />
ここのポイントは最後の画像を作る時に'''任意の内部パラメータが使える'''ということ。<br />
普通は<math>A^\prime = A</math>とすればいいけど、隙間ができても歪み補正後の画像全体を納めたい場合とか、周りを切り落として隙間の無い矩形領域を取り出したい場合とかに、ちょちょっと計算して新しい<math>A^\prime</math>を使う。<br />
<br />
蛇足だけど、歪み補正後の座標が画像の外に出る可能性があるから、メモリの変な所を触らないように注意してね。<br />
<br />
== ライトフィールドカメラ ==<br />
カメラは、視点に向かって飛んでくる光を方向毎に記録する装置であると考えられる。<br />
・・・って、別に光線が視点を通らなくても良くね?ということで、「3次元空間に存在する光線を記録する装置」を考える。<br />
<br />
普通の画像は、2次元の座標<math>(x, y)</math>と色<math>\lambda</math>を指定すると観測値を返す関数<math>f(x, y, \lambda)</math>と見なせるので、普通じゃない画像は3次元の直線<math>\boldsymbol{l}</math>と色<math>\lambda</math>を指定すると観測値を返す関数<math>f(\boldsymbol{l}, \lambda)</math>だろうと想像がつく。<br />
<br />
3次元の光線は観測点(3自由度)と飛んでくる方向(2自由度)で表現できるから、普通の画像の3次元(= 2+1)に対して6次元(= 3+2+1)になるっぽい。<br />
あ、あと時刻も入れとけば動画にできるなー。<br />
<br />
ただ、次元を増やすと指数的にデータが大きくなっちゃうので(例えば1000x1000の画像を1000x1000x1000にしたら1000倍)、使えそうな範囲で仮定を付ける事にする。<br />
窓から見える景色とか。<br />
<br />
窓から見える景色は、窓ガラスを通る光線の集合として表される。もちろん、窓を開けてる場合は窓枠の内側を通る光線になるけど。<br />
ここで重要なのは窓ガラスがあるかないかじゃなくて、そこを通る光だけを考えればいいって事。これらの光線は窓ガラスの外側の面と内側の面を通るから、通った2点の座標で1条の光線が表現できる。つまり、<br />
<br />
<math><br />
f(x_O, y_O, x_I, y_I, \lambda)<br />
</math><br />
<br />
によって、(ある時点の)窓の外の景色が完全に記録できるはず。別に窓の外から内でも同じだけど、ここでは外を見る事にする。変な想像はしないように。<br />
<br />
さて、データの表現方法が決まったところで、撮影の仕方を考える。<br />
これは単純に、窓の内側に[[カメラの較正|キャリブレーション]]済みのカメラ(互いの位置関係が完全に分かっている状態)を沢山並べれば良い。<br />
後は各カメラで観測した画像の全ての画素に対して、光線が通過した窓ガラス内外の2点を計算して値を格納すればライトフィールドのできあがり。<br />
<br />
適当な視点から見た窓の景色をレンダリングするには、視点の前にスクリーンを置いてスクリーン上の各点が窓ガラスのどこを通るか計算して、記録されている値を並べればできる。<br />
まあ、実際にはそのものズバリな光線が記録されている事は中々無いから近くの値を取ってきて補間する必要があるけど、それはまた別の話。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=Lapacke%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%83%A1%E3%83%A2
Lapackeインストールメモ
2011-05-31T12:00:06Z
<p>白飯: /* 注意点など */ l -> r</p>
<hr />
<div>== lapackeとは ==<br />
fortranで書かれている線形代数の(上位)ライブラリ[http://www.netlib.org/lapack/ LAPACK]を、C/C++から使うためのラッパーライブラリ。<br />
<br />
下位のBLASはCBLASっていうC/C++用のインタフェースが定義されてたから良かったんだけど、LAPACKには無くて今まではこの部分を自分で作ってた。<br />
ただ、これだと他の人にソースあげたりする時に面倒な事になってたので、誰か統一したインタフェースを作ってくれないかなぁと前々から思ってた。<br />
と言う訳で、lapackeは本当に嬉しい。Intelさんありがとーっ!<br />
<br />
・・・なんだけど、これ、lapack-3.3から本体にくっついてくるらしいので、それまでは自分でインストールする必要がある。<br />
このページはそんなインストールに関するメモ。<br />
<br />
== インストール環境 ==<br />
* Ubuntu-10.10 (vmware上で動作)<br />
* apt-get(かsynaptic)でliblapack-dev、libblas-devインストール済<br>(lapackは3.2.1だった)<br />
<br />
今回は特に使わないけど、/usr/includeの下あたりにcblas.hがインストールされている事を確認。<br />
<br />
== lapackeのコンパイル ==<br />
1. [http://www.netlib.org/lapack/lapacke.tgz lapacke]を取ってくる。<br><br />
2. 適当に展開する。<br><br />
3. make.incを編集する。(今回はmake.gnuをmake.incにコピーして少し編集した)<br><br />
<syntaxhighlight lang="make"><br />
make.inc:<br />
<br />
(snip)<br />
LAPACKE = liblapacke.a<br />
LIBS = -llapack -lblas -lm<br />
(snip)<br />
</syntaxhighlight><br />
4. makeする。<br />
5. トップディレクトリ(アーカイブを解凍してできたディレクトリ)にliblapack.aがあれば成功。<br />
<br />
* コンパイルに失敗した時<br />
エラーメッセージを読んで、問題のあるところを直す。<br />
<br />
== 使ってみる ==<br />
適当に作ったサンプルプログラム:<br />
<syntaxhighlight lang="c"><br />
#include <stdio.h><br />
#include <stdlib.h><br />
<br />
/* #include <cblas.h> このプログラムでは要らない */<br />
#include "lapacke.h"<br />
<br />
/* 行列の中身を表示する */<br />
static void print_mat(const int m, const int n, const double *a, const int lda)<br />
{<br />
int i, j;<br />
<br />
for (i = 0; i < m; ++i) {<br />
for (j = 0; j < n; ++j) {<br />
printf("% 10.3g ", a[j*lda + i]);<br />
}<br />
printf("\n");<br />
}<br />
printf("\n");<br />
}<br />
<br />
/* メイン関数 */<br />
int main()<br />
{<br />
const int n = 3;<br />
const int lda = n;<br />
<br />
double a[lda*n], ev[n];<br />
lapack_int info;<br />
int i, j;<br />
<br />
/* 行列に値を入れる */<br />
for (i = 0; i < n; ++i) {<br />
for (j = 0; j < n; ++j) {<br />
a[lda*i + j] = (i == j || i*j != 0) ? 1 : 0;<br />
}<br />
}<br />
<br />
/* ちゃんと入ったか確認 */<br />
printf("a:\n");<br />
print_mat(n, n, a, lda);<br />
<br />
/* lapacke経由でdsyev(対称行列の固有値・固有ベクトルを計算する関数)を呼び出す */<br />
info = LAPACKE_dsyev(LAPACK_COL_MAJOR, 'V', 'U', n, a, lda, ev);<br />
if (info != 0) {<br />
printf("dsyev failed. info = %d\n", info);<br />
return EXIT_FAILURE;<br />
}<br />
<br />
/* 固有ベクトル表示 */<br />
printf("eigen vectors:\n");<br />
print_mat(n, n, a, lda);<br />
<br />
/* 固有値表示 */<br />
printf("eigen values:\n");<br />
for (i = 0; i < n; ++i) {<br />
printf("% 10.3g ", ev[i]);<br />
}<br />
printf("\n");<br />
<br />
return EXIT_SUCCESS;<br />
}<br />
</syntaxhighlight><br />
<br />
コンパイルとリンク:<br />
<pre><br />
$ gcc -Ipath/to/lapacke/include -c -o hode.o hode.c<br />
$ gcc -Lpath/to/lapacke hoge.o -llapacke -llapack -lblas -lm -o hoge<br />
</pre><br />
<br />
実行結果:<br />
<pre><br />
$ ./hoge <br />
a:<br />
1 0 0 <br />
0 1 1 <br />
0 1 1 <br />
<br />
eigen vectors:<br />
0 1 0 <br />
-0.707 0 0.707 <br />
0.707 0 0.707 <br />
<br />
eigen values:<br />
0 1 2 <br />
</pre><br />
<br />
・・・動いたっぽい。<br />
<br />
== 注意点など ==<br />
LAPACKがfortranのライブラリなので、<br />
* column major(1列分進むと行(column)が1つ増える)にする方が良い<br />
* 要素を1から数えるので、ピボッティング結果を返す関数(getrfとか)の値はC/C++の感覚と1ずれる(はず)<br />
<br />
あと、使うライブラリによってはアライメント境界しばり(ex. 最初の要素が8の整数倍のアドレスで始まる)がある場合があるので、部分行列(行列の右下の3x3成分だけとか)をLAPACK/BLASの関数に入れるのは避けた方がよい。<br />
<br />
== 苦労した点 ==<br />
サンプルプログラムで分かりやすい結果になる行列を考えるところ。<br />
<br />
== これから ==<br />
行数・列数などをセットで覚えておく構造体、行列タイプに従って配列にアクセスする関数、メモリの割り当て/解放の面倒を見る関数があると便利、かも。<br />
<br />
[http://www.boost.org/ Boost]のuBLASでもいいけど、英語面倒だし。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E9%9D%9E%E7%B7%9A%E5%BD%A2%E6%9C%80%E9%81%A9%E5%8C%96
非線形最適化
2011-05-22T08:54:38Z
<p>白飯: カッコを追加</p>
<hr />
<div>ここでは変数<math>\boldsymbol{x}</math>の関数<math>e(\boldsymbol{x})</math>が与えられた時に、その値を最小(または最大)にする<math>\boldsymbol{x}^*</math>を求める事を考える。<br />
<br />
で、<math>e</math>が<math>\boldsymbol{x}</math>の2次式以上になると'''非線形'''という。たぶん。<br />
<br />
最適化問題にはよく<math>x_1 > 0</math>とか条件が付いてくるけど、難しいので考えない。<br />
そういう問題を解く必要に迫られた人は「'''(非)線形計画問題'''」でググるといいかも。<br />
<math>|\boldsymbol{x}| = 1</math>的な等式の条件ならラグランジュの未定定数だか何だかで、どうにかなるらしい。<br />
<br />
要するに、関数が一つ与えられて、「この関数の最小値(とその時の<math>\boldsymbol{x}^*</math>)はいくつでしょう?」という問題しか扱いませんよ、という話。<br />
<br />
== 基本方針 ==<br />
2次や3次の方程式には解の公式ってのがあるけどその他はよく分からないので、「いま分かっている値よりいいものを探す」というのを繰り返す、繰り返し計算になる。<br />
当然、極値を求めているだけなので他にもっといい点がある可能性が存在する。<br />
なので、できるだけ極値が少ない(≒次数が低い)関数を使って、できるだけよい初期値から始めたり複数の初期値を使ったりする必要がある。<br />
処理の流れをざっくり書くと、こんな感じ。<br />
<br />
# 初期値を決める(大抵、線形解法・近似解法の解)<br />
# 評価する点を決める<br />
# 評価して良ければ値を更新<br />
# 改善の見込みが無くなれば終了。そうで無ければ2に戻る<br />
<br />
次に評価する点をいかに効率よく探すかがポイント。<br />
<br />
== 関数の値しか分からない場合 ==<br />
まずは<math>\boldsymbol{x}</math>が与えられた時に<math>e(\boldsymbol{x})</math>の値しか計算できない場合。<br />
つまり、次にどっちへいけば値が良くなるか分からない状態。<br />
<br />
=== シンプレックス法 ===<br />
たぶん、定番の方法。<br />
線形計画問題に出てくるシンプレックス法とは無関係。<br />
<br />
=== Differential Evolution ===<br />
GAっぽい感じ。こんなのもあるのかぁ。<br />
ただ、いつ終わればいいかが分からない。。<br />
<br />
* 関数が不連続でもよい<br />
* 取り敢えず試せる<br />
* 関数の評価を沢山やる必要がある<br />
<br />
== 1回微分が分かる場合 ==<br />
=== 最急降下法 ===<br />
<math>e(\boldsymbol{x})</math>の微分<math>\nabla e(\boldsymbol{x})</math>は値が大きくなる方向なので、極小値を求めているなら反対方向に動かせば良い。<br />
<br />
<math><br />
\begin{array}{l}<br />
\boldsymbol{x}^{(i+1)} = \boldsymbol{x}^{(i)} + \boldsymbol{r} \\<br />
r = - \lambda \nabla e(\boldsymbol{x}^{(i)})<br />
\end{array}<br />
</math><br />
<br />
<math>\lambda (> 0)</math>は適当に決める。<math>-\nabla e</math>に沿って動かしてみて値が小さくなる(大きくなる)ところを選べばいいかも。<br />
<br />
* どっちに進めばいいか分かるので安心<br />
* 極値なら<math>\lambda e(\boldsymbol{x}) = 0</math><br />
* あまり大きく動かせない<br />
<br />
== 2回微分まで分かる場合 ==<br />
<br />
=== ニュートン(・ラフソン?)法 ===<br />
<math>\boldsymbol{x}</math>の近くでは、更新量<math>\boldsymbol{r}</math>に対して以下の近似が成り立つ。<br />
<br />
<math><br />
\begin{array}{l}<br />
\nabla e(\boldsymbol{x} + \boldsymbol{r}) = \nabla e(\boldsymbol{x}) + H \boldsymbol{r} \\<br />
H = (h_{ij}) = \frac{\partial^2}{\partial x_i \partial x_j} e<br />
\end{array}<br />
</math><br />
<br />
で、極値だと<math>\nabla e(\boldsymbol{x} + \boldsymbol{r}) = 0</math>になるので、<br />
<br />
<math><br />
\boldsymbol{r} = - H^{-1} \nabla e(\boldsymbol{x})<br />
</math><br />
<br />
となる。<br />
<br />
* 関数が二次関数でうまく近似できる時は、メチャ速<br />
* 運が悪いと解が振動する<br />
<br />
=== レベンバーグ・マーカート法 ===<br />
よく忘れるので英語表記も書いておく。Le'''v'''en'''b'''erg-Mar'''q'''uard'''t'''・・・で合ってると思う。<br />
元ネタを読んだ事が無いので、雰囲気だけ。<br />
<br />
最急降下法とニュートン法を足して2で割ったような感じ:<br />
<br />
<math><br />
\left(H - \lambda I \right) \boldsymbol{r} = -\nabla e(\boldsymbol{x})<br />
</math><br />
<br />
更新がうまくいった時は<math>\lambda</math>を小さくしてニュートン法に近づけて、ダメな場合は大きくして最急降下法に近づける。<br />
非線形最適化といえば、まずコレをやっておけば問題ない・・・気がする。<br />
<br />
で、よく使う最小二乗法を考える。<br />
<br />
<math><br />
e(\boldsymbol{x}) = \frac{1}{2} \boldsymbol{f}(\boldsymbol{x})^\text{T} \boldsymbol{f}(\boldsymbol{x})<br />
</math><br />
<br />
このとき、<br />
<br />
<math><br />
\begin{array}{l}<br />
\nabla e = (\nabla \boldsymbol{f})^\text{T} \boldsymbol{f} \\<br />
H = (\nabla \boldsymbol{f})^\text{T} (\nabla \boldsymbol{f}) + \left( \frac{\partial^2}{\partial^2 \boldsymbol{x}} \boldsymbol{f} \right) \boldsymbol{f}<br />
\approx (\nabla \boldsymbol{f})^\text{T} (\nabla \boldsymbol{f})<br />
\end{array}<br />
</math><br />
<br />
更新量:<br />
<br />
<math><br />
\left( (\nabla \boldsymbol{f})^\text{T} (\nabla \boldsymbol{f}) - \lambda I \right) \boldsymbol{r}<br />
= - (\nabla \boldsymbol{f})^\text{T} \boldsymbol{f}<br />
</math><br />
<br />
なので、<math>\boldsymbol{f}(\boldsymbol{x}), \nabla \boldsymbol{f}(\boldsymbol{x})</math>が分かるだけで計算できる。<br />
<br />
また、実際の値の変化と予測を比べる事で2次近似の正否が見積もれる:<br />
<br />
<math><br />
s(\boldsymbol{x}, \boldsymbol{r}) = \frac{e(\boldsymbol{x} + \boldsymbol{r}) - e(\boldsymbol{x})}{(\nabla e(\boldsymbol{x}))^\text{T} \boldsymbol{r}}<br />
</math><br />
<br />
* 定番の方法<br />
* たまにランク落ちする事があるので、うまくやる<br />
* <math>\lambda</math>のいい決め方があるらしい<br />
* ちゃんと考えてプログラムしないとグチャグチャに</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E9%9D%9E%E7%B7%9A%E5%BD%A2%E6%9C%80%E9%81%A9%E5%8C%96
非線形最適化
2011-05-22T08:52:22Z
<p>白飯: 初版</p>
<hr />
<div>ここでは変数<math>\boldsymbol{x}</math>の関数<math>e(\boldsymbol{x})</math>が与えられた時に、その値を最小(または最大)にする<math>\boldsymbol{x}^*</math>を求める事を考える。<br />
<br />
で、<math>e</math>が<math>\boldsymbol{x}</math>の2次式以上になると'''非線形'''という。たぶん。<br />
<br />
最適化問題にはよく<math>x_1 > 0</math>とか条件が付いてくるけど、難しいので考えない。<br />
そういう問題を解く必要に迫られた人は「'''(非)線形計画問題'''」でググるといいかも。<br />
<math>|\boldsymbol{x}| = 1</math>的な等式の条件ならラグランジュの未定定数だか何だかで、どうにかなるらしい。<br />
<br />
要するに、関数が一つ与えられて、「この関数の最小値(とその時の<math>\boldsymbol{x}^*</math>)はいくつでしょう?」という問題しか扱いませんよ、という話。<br />
<br />
== 基本方針 ==<br />
2次や3次の方程式には解の公式ってのがあるけどその他はよく分からないので、「いま分かっている値よりいいものを探す」というのを繰り返す、繰り返し計算になる。<br />
当然、極値を求めているだけなので他にもっといい点がある可能性が存在する。<br />
なので、できるだけ極値が少ない(≒次数が低い)関数を使って、できるだけよい初期値から始めたり複数の初期値を使ったりする必要がある。<br />
処理の流れをざっくり書くと、こんな感じ。<br />
<br />
# 初期値を決める(大抵、線形解法・近似解法の解)<br />
# 評価する点を決める<br />
# 評価して良ければ値を更新<br />
# 改善の見込みが無くなれば終了。そうで無ければ2に戻る<br />
<br />
次に評価する点をいかに効率よく探すかがポイント。<br />
<br />
== 関数の値しか分からない場合 ==<br />
まずは<math>\boldsymbol{x}</math>が与えられた時に<math>e(\boldsymbol{x})</math>の値しか計算できない場合。<br />
つまり、次にどっちへいけば値が良くなるか分からない状態。<br />
<br />
=== シンプレックス法 ===<br />
たぶん、定番の方法。<br />
線形計画問題に出てくるシンプレックス法とは無関係。<br />
<br />
=== Differential Evolution ===<br />
GAっぽい感じ。こんなのもあるのかぁ。<br />
ただ、いつ終わればいいかが分からない。。<br />
<br />
* 関数が不連続でもよい<br />
* 取り敢えず試せる<br />
* 関数の評価を沢山やる必要がある<br />
<br />
== 1回微分が分かる場合 ==<br />
=== 最急降下法 ===<br />
<math>e(\boldsymbol{x})</math>の微分<math>\nabla e(\boldsymbol{x})</math>は値が大きくなる方向なので、極小値を求めているなら反対方向に動かせば良い。<br />
<br />
<math><br />
\begin{array}{l}<br />
\boldsymbol{x}^{(i+1)} = \boldsymbol{x}^{(i)} + \boldsymbol{r} \\<br />
r = - \lambda \nabla e(\boldsymbol{x}^{(i)})<br />
\end{array}<br />
</math><br />
<br />
<math>\lambda (> 0)</math>は適当に決める。<math>-\nabla e</math>に沿って動かしてみて値が小さくなる(大きくなる)ところを選べばいいかも。<br />
<br />
* どっちに進めばいいか分かるので安心<br />
* 極値なら<math>\lambda e(\boldsymbol{x}) = 0</math><br />
* あまり大きく動かせない<br />
<br />
== 2回微分まで分かる場合 ==<br />
<br />
=== ニュートン(・ラフソン?)法 ===<br />
<math>\boldsymbol{x}</math>の近くでは、更新量<math>\boldsymbol{r}</math>に対して以下の近似が成り立つ。<br />
<br />
<math><br />
\begin{array}{l}<br />
\nabla e(\boldsymbol{x} + \boldsymbol{r}) = \nabla e(\boldsymbol{x}) + H \boldsymbol{r} \\<br />
H = (h_{ij}) = \frac{\partial^2}{\partial x_i \partial x_j} e<br />
\end{array}<br />
</math><br />
<br />
で、極値だと<math>\nabla e(\boldsymbol{x} + \boldsymbol{r}) = 0</math>になるので、<br />
<br />
<math><br />
\boldsymbol{r} = - H^{-1} \nabla e(\boldsymbol{x})<br />
</math><br />
<br />
となる。<br />
<br />
* 関数が二次関数でうまく近似できる時は、メチャ速<br />
* 運が悪いと解が振動する<br />
<br />
=== レベンバーグ・マーカート法 ===<br />
よく忘れるので英語表記も書いておく。Le'''v'''en'''b'''erg-Mar'''q'''uard'''t'''・・・で合ってると思う。<br />
元ネタを読んだ事が無いので、雰囲気だけ。<br />
<br />
最急降下法とニュートン法を足して2で割ったような感じ:<br />
<br />
<math><br />
\left(H - \lambda I \right) \boldsymbol{r} = -\nabla e(\boldsymbol{x})<br />
</math><br />
<br />
更新がうまくいった時は<math>\lambda</math>を小さくしてニュートン法に近づけて、ダメな場合は大きくして最急降下法に近づける。<br />
非線形最適化といえば、まずコレをやっておけば問題ない・・・気がする。<br />
<br />
で、よく使う最小二乗法を考える。<br />
<br />
<math><br />
e(\boldsymbol{x}) = \frac{1}{2} \boldsymbol{f}(\boldsymbol{x})^\text{T} \boldsymbol{f}(\boldsymbol{x})<br />
</math><br />
<br />
このとき、<br />
<br />
<math><br />
\begin{array}{l}<br />
\nabla e = (\nabla \boldsymbol{f})^\text{T} \boldsymbol{f} \\<br />
H = (\nabla \boldsymbol{f})^\text{T} (\nabla \boldsymbol{f}) + \left( \frac{\partial^2}{\partial^2 \boldsymbol{x}} \boldsymbol{f} \right) \boldsymbol{f}<br />
\approx (\nabla \boldsymbol{f})^\text{T} (\nabla \boldsymbol{f})<br />
\end{array}<br />
</math><br />
<br />
更新量:<br />
<br />
<math><br />
\left( (\nabla \boldsymbol{f})^\text{T} (\nabla \boldsymbol{f}) - \lambda I \right) \boldsymbol{r}<br />
= - \nabla \boldsymbol{f}^\text{T} \boldsymbol{f}<br />
</math><br />
<br />
なので、<math>\boldsymbol{f}(\boldsymbol{x}), \nabla \boldsymbol{f}(\boldsymbol{x})</math>が分かるだけで計算できる。<br />
<br />
また、実際の値の変化と予測を比べる事で2次近似の正否が見積もれる:<br />
<br />
<math><br />
s(\boldsymbol{x}, \boldsymbol{r}) = \frac{e(\boldsymbol{x} + \boldsymbol{r}) - e(\boldsymbol{x})}{(\nabla e(\boldsymbol{x}))^\text{T} \boldsymbol{r}}<br />
</math><br />
<br />
* 定番の方法<br />
* たまにランク落ちする事があるので、うまくやる<br />
* <math>\lambda</math>のいい決め方があるらしい<br />
* ちゃんと考えてプログラムしないとグチャグチャに</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%97%E6%B3%95%E3%81%A8%E3%81%8B
最小二乗法とか
2011-04-24T08:55:48Z
<p>白飯: 初稿</p>
<hr />
<div>== 目的 ==<br />
[[ファイル:Line_fitting.svg|300px|right|座標系]]<br />
二つの値<math>\{x_i, y_i\}</math>を沢山観測していると、どうやらこの値は直線にのるらしいことが分かった。<br />
でも観測した値には誤差が含まれているので、どう頑張っても全部の点を通る直線を引くのは無理っぽい。<br />
そこで、全ての観測した点に対してなるべく近くを通るような直線を求めるのを目指す。<br />
<br />
直線を求めるっていっても、紙に線を書くんじゃなくて、後で計算しやすいように直線の方程式の係数を求めるのがここのテーマ。<br />
<br style="clear: both" /><br />
<br />
== 最小二乗法 ==<br />
またの名を最小'''自'''乗法。<br />
読んで字のごとく誤差の2乗を最小にする係数を求める方法。<br />
<br />
=== その1 ===<br />
以下の式を考える。<br />
<br />
<math><br />
y = a x + b<br />
</math><br />
<br />
すると直線上の点は上式を満たすから、ある観測点の誤差<math>\Delta y_i</math>は次となる。<br />
<br />
<math><br />
\Delta y_i = y_i - (a x_i + b)<br />
</math><br />
<br />
この値は正負両方の値をとるので2乗し、全ての観測点に対する和が最小になるように係数<math>a, b</math>を定める。<br />
<br />
<math><br />
\sum_i \Delta y_i^2 \longrightarrow \min<br />
</math><br />
<br />
<math>a, b</math>でそれぞれ偏微分して0とおき、連立させて解くと以下を得る。<br />
<br />
<math><br />
\begin{array}{l}<br />
a = \frac{\sum_i (x_i - \bar{x})(y_i - \bar{y})}{\sum_i x_i - \bar{x})^2} \\<br />
b = \bar{y} - \frac{\sum_i (x_y - \bar{x})(y_i - \bar{y})}{\sum_i x_i - \bar{x})^2} \bar{x}<br />
\end{array}<br />
</math><br />
<br />
但し、<math>\bar{x}, \bar{y}</math>はそれぞれxとyの平均とする。<br />
これを変形すると、<br />
<br />
<math><br />
y - \bar{y} = \frac{\sum_i (x_i - \bar{x})(y_i - \bar{y})}{\sum_i x_i - \bar{x})^2} (x - \bar{x})<br />
</math><br />
<br />
となるため、平均を通り、傾きが共分散/xの分散になる直線であることが分かる。<br />
<br />
ちなみに行列を使って表すと、誤差ベクトル<br />
<br />
<math><br />
\Delta \boldsymbol{y} = \boldsymbol{y} - X^\text{T}<br />
\begin{bmatrix}<br />
a \\ b<br />
\end{bmatrix}<br />
</math><br><br />
<math><br />
X^\text{T} =<br />
\begin{bmatrix}<br />
x_1 & 1 \\<br />
x_2 & 1 \\<br />
\vdots & \vdots<br />
\end{bmatrix}<br />
, \quad<br />
\boldsymbol{y} =<br />
\begin{bmatrix}<br />
y_1 \\ y_2 \\ \vdots<br />
\end{bmatrix}<br />
</math><br />
<br />
として、2乗誤差<math>\Delta \boldsymbol{y}^\text{T} \Delta \boldsymbol{y}</math>を係数ベクトルで微分すると次式を得る。<br />
<br />
<math><br />
X(\boldsymbol{y} - X^\text{T})<br />
\begin{bmatrix}<br />
a \\ b<br />
\end{bmatrix}<br />
= \boldsymbol{0}<br />
</math><br />
<br />
よって、<br />
<br />
<math><br />
\begin{bmatrix}<br />
a \\ b<br />
\end{bmatrix}<br />
=<br />
\left( X X^\text{T} \right)^{-1} X \boldsymbol{y}<br />
</math><br />
<br />
である。<br />
<br />
=== その2 ===<br />
さて、勘のいい人は気付いていると思うけど、一つ目の方法は'''y軸の誤差しかみていない'''ことが分かる。<br />
ご丁寧にも<math>\Delta \boldsymbol{y}</math>とか書いてたしねぇ。<br />
<br />
普通に観測したデータはx軸にもy軸にも誤差が含まれるのが当然ってことで、次の式を考える。<br />
<br />
<math><br />
c_1 x + c_2 y + c_3 = 0<br />
</math><br><br />
<br />
この式だと<math>x=\text{const}</math>というy軸に平行な直線も表せるのと、両辺に0でない定数を掛けても同じ直線を表す(=定数倍の不定性がある)のに注意。<br />
<br />
で、二乗の和を最小にする、と。<br />
<br />
<math><br />
\sum_i (c_1 x_i + c_2 y_i + c_3)^2 \longrightarrow \min<br />
</math><br />
<br />
定数倍の不定性を除去するために<math>c_1^2 + c_2^2 = 1</math>とすると、<br />
<br />
<math><br />
c_3 = - (c_1 \bar{x} + c_2 \bar{y})<br />
</math><br />
<br />
より、<br />
<br />
<math><br />
\left(\sum_i<br />
\begin{bmatrix}<br />
(x_i - \bar{x})^2 & (x_i - \bar{x})(y_i - \bar{y}) \\<br />
(x_i - \bar{x})(y_i - \bar{y}) & (y_i - \bar{y})^2<br />
\end{bmatrix}<br />
- \lambda I \right)<br />
\begin{bmatrix}<br />
c_1 \\ c_2<br />
\end{bmatrix}<br />
= \boldsymbol{0}<br />
</math><br />
<br />
が得られ、求めるものは分散・共分散行列の最小固有値に対応する固有ベクトルだと分かる。<br />
<br />
その1と同様に行列っぽく書くと、<br />
<br />
<math><br />
\boldsymbol{c}^\text{T} XX^\text{T} \boldsymbol{c} \longrightarrow \min<br />
</math><br><br />
<math><br />
X^\text{T} =<br />
\begin{bmatrix}<br />
x_1 & y_1 & 1 \\<br />
x_2 & y_2 & 1 \\<br />
\vdots & \vdots & \vdots<br />
\end{bmatrix}<br />
, \quad<br />
\boldsymbol{c} = [c1,\, c2,\, c3]^\text{T}<br />
</math><br />
<br />
で、<math>\boldsymbol{c}</math>は<math>XX^\text{T}</math>の最小固有値に対応する固有ベクトルになる。<br />
但し、正規化の方法が若干違う(<math>|\boldsymbol{c}| = 1</math>)ので注意。<br />
<br />
=== 直線以外への当てはめ ===<br />
当てはめに使っている方程式をぼーっと眺めてみると、<br />
<pre><br />
(係数1)(データ1) + (係数2)(データ2) + ・・・ = 0<br />
</pre><br />
という形をしている事が見えてくる。<br />
<br />
つまり、<math>(x_i^2,\, x_i,\, y_i, 1)</math>とすれば<math>y = ax^2 + bx + c</math>に当てはめたりできる。<br />
<br />
一般の2次曲線は<br />
<br />
<math><br />
\begin{bmatrix}<br />
x & y & 1<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
c_1 & c_2 & c_4 \\<br />
c_2 & c_3 & c_5 \\<br />
c_4 & c_5 & c_6<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
x \\ y \\ 1<br />
\end{bmatrix} = 0<br />
</math><br><br />
<math><br />
\Longleftrightarrow<br />
c_1 x^2 + 2 c_2 xy + c_3 y^2 + 2 c_4 x + 2 c_5 y + c_6 = 0<br />
</math><br><br />
<br />
で表されるので<br />
<br />
<math><br />
\begin{bmatrix}<br />
x^2 & 2xy & y^2 & 2x & 2y & 1<br />
\end{bmatrix}<br />
\boldsymbol{c} = 0<br />
</math><br />
<br />
とすれば、楕円とか双曲線とか放物線とかに当てはめられる。<br />
但し縮退する(2次の係数が0になるとか)場合があるので、計算できたからといっても安心できない。<br />
それに誤差ののり方がそれぞれの要素で変わってくるため、当てはめの結果が最適であるという保証が無い点にも注意が必要。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Line_fitting.svg
ファイル:Line fitting.svg
2011-04-24T02:30:50Z
<p>白飯: 「ファイル:Line fitting.svg」の新しい版をアップロードしました: 余白を修正</p>
<hr />
<div>直線当てはめの模式図</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Line_fitting.svg
ファイル:Line fitting.svg
2011-04-24T02:25:40Z
<p>白飯: 直線当てはめの模式図</p>
<hr />
<div>直線当てはめの模式図</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E5%B0%84%E5%BD%B1%E5%A4%89%E6%8F%9B(2%E6%AC%A1%E5%85%83)
射影変換(2次元)
2011-04-01T13:17:38Z
<p>白飯: 初版</p>
<hr />
<div>== 定義 ==<br />
知らない。<br />
<br />
3次元空間にある、2枚の平面とそれらの平面上に無い固定された1点に関して、片方の点をその点と固定点を通る直線が他方の平面と交わる点に写すような変換を表す。<br />
要するに、影絵みたいなもの。この場合は固定点が光源、実物と影があるところがそれぞれの平面になる。<br />
<br />
== 点の変換 ==<br />
[[同次座標]]で表された2次元の点<math>\tilde{\boldsymbol{x}}</math>を他方の点<math>\tilde{\boldsymbol{x}}^\prime</math>に移す変換は次のように書ける。<br><br />
<math><br />
\begin{align}<br />
\tilde{\boldsymbol{x}}^\prime &\propto<br />
\begin{bmatrix}<br />
h_{11} & \cdots & h_{13} \\<br />
\vdots & \ddots & \vdots \\<br />
h_{31} & \cdots & h_{33}<br />
\end{bmatrix}<br />
\tilde{\boldsymbol{x}} \\<br />
&= H \tilde{\boldsymbol{x}}<br />
\end{align}<br />
</math><br />
<br />
反対の変換は、当然<br><br />
<math><br />
\tilde{\boldsymbol{x}} \propto H \tilde{\boldsymbol{x}}^\prime<br />
</math><br><br />
になる。<br />
<br />
射影変換は0で無い任意の定数をHに掛けても同じ変換を表すため、この行列の自由度は8となる。<br />
<br />
== 線の変換 ==<br />
直線の方程式:<br><br />
<math><br />
\boldsymbol{l}^\text{T} \tilde{\boldsymbol{x}} = 0<br />
</math><br><br />
より、<br><br />
<math><br />
\begin{array}{l}<br />
\boldsymbol{l}^\text{T} H^{-1} H \tilde{\boldsymbol{x}} = 0 \\<br />
\Rightarrow \ \left( H^{-\text{T}} \boldsymbol{l} \right)^\text{T} \tilde{\boldsymbol{x}}^\prime = 0<br />
\end{array}<br />
</math><br><br />
であるため、変換後の直線<math>\boldsymbol{l}^\prime</math>は次式で表される。<br><br />
<math><br />
\boldsymbol{l}^\prime \propto H^{-\text{T}} \boldsymbol{l}<br />
</math><br />
<br />
ただし、<math>H^{-\text{T}}</math>は逆行列の転置を表すとする。<br />
<br />
== 2次曲線の変換 ==<br />
同様に、<br><br />
<math><br />
\tilde{\boldsymbol{x}}^\text{T} C \tilde{\boldsymbol{x}} = 0<br />
</math><br><br />
より、<br><br />
<math><br />
\begin{array}{l}<br />
\left( H \tilde{\boldsymbol{x}} \right)^\text{T} H^{-\text{T}} C H^{-1} \left( H \tilde{\boldsymbol{x}} \right) = 0 \\<br />
\Rightarrow \ \tilde{\boldsymbol{x}}^{\prime \text{T}} \left( H^{-\text{T}} C H^{-1} \right) \tilde{\boldsymbol{x}}^\prime = 0<br />
\end{array}<br />
</math><br><br />
従って、次の変換式が得られる。<br />
<br />
<math><br />
C^\prime \propto H^{-\text{T}} C H^{-1}<br />
</math><br />
<br />
== Hを求める ==<br />
変換前後の点<math>\tilde{\boldsymbol{x}},\, \tilde{\boldsymbol{x}}^\prime</math>が分かっている時、次式を満たすHを求める事を考える。<br><br />
<math><br />
\tilde{\boldsymbol{x}}^\prime \propto H \tilde{\boldsymbol{x}} = 0<br />
\Leftrightarrow<br />
\tilde{\boldsymbol{x}}^\prime \times H \tilde{\boldsymbol{x}} = \boldsymbol{0}<br />
</math><br />
<br />
成分をバラして整理すると以下の線形方程式を得る。<br><br />
<math><br />
\begin{bmatrix}<br />
0 & -x^\prime_3 \tilde{\boldsymbol{x}}^\text{T} & x^\prime_2 \tilde{\boldsymbol{x}}^\text{T} \\<br />
x^\prime_3 \tilde{\boldsymbol{x}}^\text{T} & 0 & -x^\prime_1 \tilde{\boldsymbol{x}}^\text{T} \\<br />
-x^\prime_2 \tilde{\boldsymbol{x}}^\text{T} & x^\prime_1 \tilde{\boldsymbol{x}}^\text{T} & 0<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
h_{11} \\ h_{12} \\ h_{13} \\ h_{21} \\ \vdots \\ h_{33}<br />
\end{bmatrix}<br />
= \boldsymbol{0}<br />
</math><br><br />
これで式が3本得られるが線形独立なものは2本であり、どれか一つ嫌いな行を省いても良い。<br />
<br />
つまり1組の点に対して2本の式が得られるため、4組の対応点が得られれば「並べて行列にして、最小固有値に対応する固有ベクトルを求める」というアレで計算できる。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%A1%E3%83%A2%E5%B8%B3@fmaj7b5.info
メモ帳@fmaj7b5.info
2011-04-01T12:11:29Z
<p>白飯: 射影変換の項目を追加</p>
<hr />
<div>== はじめに ==<br />
<br />
ここに書かれている内容は個人的なメモであり、正当性は保証しません。<br />
<br />
#コピペして怒られても知らないよ。<br />
<br />
== 基本 ==<br />
* [[線形代数]]<br />
* [[点の移動・回転]]<br />
* [[射影変換(2次元)]]<br />
* [[最小二乗法とか]]<br />
* [[非線形最適化]]<br />
<br />
== コンピュータ・ビジョン ==<br />
* [[カメラモデル]]<br />
* [[3次元復元]]<br />
<br />
[http://szeliski.org/Book いいもの]見つけた。<br />
<br />
== そのほか ==<br />
* [[おすすめプログラミング環境]]<br />
* [[lapackeインストールメモ]]</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E5%90%8C%E6%AC%A1%E5%BA%A7%E6%A8%99
同次座標
2011-04-01T12:07:33Z
<p>白飯: 平行線の件を追加</p>
<hr />
<div>== 何がいいの? ==<br />
よく知っている幾何学だと、「平行でない2直線は1点で交わる」ことになっている。けど、平行な時だけ仲間はずれにするのは可哀想だということで、「平行な2直線も1点で交わる」ことにしちゃったものを表す時に使うのが同次座標(斉次座標)。<br />
だから、点や直線を考える時に余計な事を考えなくても良くなるという便利な物。<br />
<br />
イメージとしては、直線の式<br><br />
<math><br />
y = a x + b<br />
</math><br><br />
が<math>\boldsymbol{y}</math>軸に平行な直線を表せないのに対して、<br><br />
<math><br />
a x + b y + c = 0<br />
</math><br><br />
だと例外なく表せる、みたいな感じ。<br />
<br />
== 普通の座標と同次座標を対応させる ==<br />
2次元の場合、普通の座標は変数2個で表現するけど、同次座標はもう一つ余分に使う。<br><br />
<math><br />
(x, y) \mapsto (x, y, 1)<br />
</math><br><br />
ただし、三つの成分が全て0になることは無いとする。<br />
で、同次座標<math>(x_1, x_2, x_3)</math>から普通の座標への変換は以下のように'''決めておく'''。<br><br />
<math><br />
(x, y) = \left( \frac{x_1}{x_3}, \frac{x_2}{x_3} \right)<br />
</math><br />
<br />
同次座標は成分の比が重要なので普通の座標とどう対応づけても関係無いけど、途中で変えると面倒な事になりそう。<br />
なので、ここでは3番目の成分で他の成分を割った物が普通の座標になるとしておく。<br />
<br />
この決め事からも自明だけど、同次座標に0で無い定数を掛けても同じ点を表す。<br><br />
<math><br />
\begin{bmatrix}<br />
x_1 \\ x_2 \\ x_3<br />
\end{bmatrix}<br />
\propto<br />
s \begin{bmatrix}<br />
x_1 \\ x_2 \\ x_3<br />
\end{bmatrix}<br />
\mapsto<br />
\begin{bmatrix}<br />
s x_1 / s x_3 \\ s x_2 / s x_3<br />
\end{bmatrix}<br />
=<br />
\begin{bmatrix}<br />
x_1 / x_3 \\ x_2 / x_3<br />
\end{bmatrix}<br />
</math><br><br />
ただし、<math>\propto</math>は定数倍を除いて等しい事を表すとする。<br />
<br />
ざっくりいうと、原点を通る3次元空間の直線<math>s (x_1, x_2, x_3)</math>を考えて、<math>x_3 = 1</math>で表される平面と交わるところが対応する2次元の座標ということになる。<br />
つまり、折角3個の変数を使っているのに意味があるのは平面上だけで、あとはムダになる。<br />
このムダを埋めてくれるのが次の話。<br />
<br />
== 無限遠点 ==<br />
上記のように、3番目の成分で割った物が普通の座標に対応することにした。では、3番目の成分が0ならどうなるか。<br />
これは普通の座標には対応しない雰囲気がある。ここで、<br><br />
<math><br />
\frac{1}{x_3}<br />
\begin{bmatrix}<br />
x_1 \\ x_2<br />
\end{bmatrix}<br />
</math><br><br />
を考えて<math>x_3</math>を小さくしていくと、原点から<math>(x_1, x_2)</math>の方向にどんどん遠のいていく事が分かる。<br />
つまり、これは無限遠の1点を表してるはず。<br />
<br />
というわけで、<br />
* <math>x_3 \neq 0</math>:普通の座標<br />
* <math>x_3 = 0</math>:無限遠点<br />
を表すことが分かった。<br />
ただし、これはあくまでも普通の座標に変換したあとの話で、同次座標で表している間は気にしなくても良い。<br />
何も考えずに普通の点と無限遠の点を扱えるというのがここのポイント。<br />
<br />
== 直線 ==<br />
同次座標では、直線は次のように表す。<br><br />
<math><br />
l_1 x_1 + l_2 x_2 + l_3 x_3 = 0<br />
</math><br><br />
まあ、直線の方程式そのものだね。この式は右辺が0なので、任意の0で無い定数を掛けてもやっぱり同じ直線を表す。<br />
<br />
ベクトルっぽく書くと、こんな感じ。<br><br />
<math><br />
\begin{bmatrix}<br />
l_1 & l_2 & l_3<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
x_1 \\ x_2 \\ x_3<br />
\end{bmatrix}<br />
= \boldsymbol{l}^\text{T} \tilde{\boldsymbol{x}}<br />
= 0<br />
</math><br><br />
ただし、<math>\tilde{\boldsymbol{x}}</math>は<math>\boldsymbol{x}</math>の同次座標を表す。<br />
<br />
=== 2点を通る直線 ===<br />
<math>\tilde{\boldsymbol{x}}_1, \tilde{\boldsymbol{x}}_2</math>を通る直線は、次のように計算できる。<br><br />
<math><br />
\boldsymbol{l} = \tilde{\boldsymbol{x}}_1 \times \tilde{\boldsymbol{x}}_2<br />
</math><br><br />
まさかの外積。これが上式を満たす事は適当に確認できる。<br />
<br />
=== 2直線の交点 ===<br />
<math>\boldsymbol{l}_1, \boldsymbol{l}_2</math>の交点は、次のように計算できる。<br><br />
<math><br />
\tilde{\boldsymbol{x}} = \boldsymbol{l}_1 \times \boldsymbol{l}_2<br />
</math><br><br />
またしても外積。これがそれぞれの直線上にある事も簡単に確かめられる。<br />
<br />
* 2点 → 直線<br />
* 2直線 → 点<br />
ということで、点と直線が双対な関係にある。<br />
<br />
=== 平行線の交点 ===<br />
平行な線:<br><br />
<math><br />
\begin{array}{l}<br />
\boldsymbol{l}_1 = [ a \ b \ c_1 ]^\text{T} \\<br />
\boldsymbol{l}_2 = [ a \ b \ c_2 ]^\text{T}<br />
\end{array}<br />
</math><br><br />
を考える。<br />
<br />
交点<math>\tilde{\boldsymbol{x}}</math>は上でみたとおり、次のようになる。<br><br />
<math><br />
\begin{align}<br />
\tilde{\boldsymbol{x}} &\propto \boldsymbol{l}_1 \times \boldsymbol{l}_2 \\<br />
&=<br />
\begin{bmatrix}<br />
b c_2 - b c_1 \\<br />
c_1 a - c_2 a \\<br />
a b - a b<br />
\end{bmatrix}<br />
\propto<br />
\begin{bmatrix}<br />
-b \\ a \\ 0<br />
\end{bmatrix}<br />
\end{align}<br />
</math><br><br />
3番目の成分が0である事から、無限遠点である事が分かる。<br />
こんな風に平行な線も、そうでない線も同じように扱えるのが同次座標の魅力。<br />
<br />
ちなみにこの交点、なんと・・・と言うべきか、当然と言うべきか直線と平行な(直線が伸びる)方向を表している。<br />
<br />
== 2次曲線 ==<br />
普通の座標での2次曲線:<br><br />
<math><br />
a x^2 + 2 b xy + c y^2 + 2 d x + 2 e y + f = 0<br />
</math><br><br />
で<math>(x, y) \leftarrow (x_1/x_3, x_2/x_3)</math>とすると、以下を得る。<br><br />
<math><br />
a x_1^2 + 2 b x_1 x_2 + c x_2^2 + 2 d x_1 x_3 + 2 e x_2 x_3 + f x_3^2 = 0<br />
</math><br />
<br />
整理すると、<br><br />
<math><br />
\begin{bmatrix}<br />
x_1 & x_2 & x_3<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
a & b & d \\<br />
b & c & e \\<br />
d & e & f<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
x_1 \\ x_2 \\ x_3<br />
\end{bmatrix}<br />
=<br />
\tilde{\boldsymbol{x}}^\text{T} C \tilde{\boldsymbol{x}}<br />
</math><br><br />
となる。<br />
<br />
この式で表される曲線は、円錐をある平面で切断した時の断面に現れる曲線になるため、'''円錐曲線'''と言う事もあるらしい。<br />
<br />
=== 接線 ===<br />
点<math>\tilde{\boldsymbol{x}}</math>が2次曲線上(=<math>\tilde{\boldsymbol{x}}^\text{T} C \tilde{\boldsymbol{x}} = 0</math>)にある時、<br><br />
<math><br />
\boldsymbol{l} = C \tilde{\boldsymbol{x}}<br />
</math><br><br />
はその点を通る2次曲線の接線である。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E5%90%8C%E6%AC%A1%E5%BA%A7%E6%A8%99
同次座標
2011-03-27T14:55:00Z
<p>白飯: 初版</p>
<hr />
<div>== 何がいいの? ==<br />
よく知っている幾何学だと、「平行でない2直線は1点で交わる」ことになっている。けど、平行な時だけ仲間はずれにするのは可哀想だということで、「平行な2直線も1点で交わる」ことにしちゃったものを表す時に使うのが同次座標(斉次座標)。<br />
だから、点や直線を考える時に余計な事を考えなくても良くなるという便利な物。<br />
<br />
イメージとしては、直線の式<br><br />
<math><br />
y = a x + b<br />
</math><br><br />
が<math>\boldsymbol{y}</math>軸に平行な直線を表せないのに対して、<br><br />
<math><br />
a x + b y + c = 0<br />
</math><br><br />
だと例外なく表せる、みたいな感じ。<br />
<br />
== 普通の座標と同次座標を対応させる ==<br />
2次元の場合、普通の座標は変数2個で表現するけど、同次座標はもう一つ余分に使う。<br><br />
<math><br />
(x, y) \mapsto (x, y, 1)<br />
</math><br><br />
ただし、三つの成分が全て0になることは無いとする。<br />
で、同次座標<math>(x_1, x_2, x_3)</math>から普通の座標への変換は以下のように'''決めておく'''。<br><br />
<math><br />
(x, y) = \left( \frac{x_1}{x_3}, \frac{x_2}{x_3} \right)<br />
</math><br />
<br />
同次座標は成分の比が重要なので普通の座標とどう対応づけても関係無いけど、途中で変えると面倒な事になりそう。<br />
なので、ここでは3番目の成分で他の成分を割った物が普通の座標になるとしておく。<br />
<br />
この決め事からも自明だけど、同次座標に0で無い定数を掛けても同じ点を表す。<br><br />
<math><br />
\begin{bmatrix}<br />
x_1 \\ x_2 \\ x_3<br />
\end{bmatrix}<br />
\propto<br />
s \begin{bmatrix}<br />
x_1 \\ x_2 \\ x_3<br />
\end{bmatrix}<br />
\mapsto<br />
\begin{bmatrix}<br />
s x_1 / s x_3 \\ s x_2 / s x_3<br />
\end{bmatrix}<br />
=<br />
\begin{bmatrix}<br />
x_1 / x_3 \\ x_2 / x_3<br />
\end{bmatrix}<br />
</math><br><br />
ただし、<math>\propto</math>は定数倍を除いて等しい事を表すとする。<br />
<br />
ざっくりいうと、原点を通る3次元空間の直線<math>s (x_1, x_2, x_3)</math>を考えて、<math>x_3 = 1</math>で表される平面と交わるところが対応する2次元の座標ということになる。<br />
つまり、折角3個の変数を使っているのに意味があるのは平面上だけで、あとはムダになる。<br />
このムダを埋めてくれるのが次の話。<br />
<br />
== 無限遠点 ==<br />
上記のように、3番目の成分で割った物が普通の座標に対応することにした。では、3番目の成分が0ならどうなるか。<br />
これは普通の座標には対応しない雰囲気がある。ここで、<br><br />
<math><br />
\frac{1}{x_3}<br />
\begin{bmatrix}<br />
x_1 \\ x_2<br />
\end{bmatrix}<br />
</math><br><br />
を考えて<math>x_3</math>を小さくしていくと、原点から<math>(x_1, x_2)</math>の方向にどんどん遠のいていく事が分かる。<br />
つまり、これは無限遠の1点を表してるはず。<br />
<br />
というわけで、<br />
* <math>x_3 \neq 0</math>:普通の座標<br />
* <math>x_3 = 0</math>:無限遠点<br />
を表すことが分かった。<br />
ただし、これはあくまでも普通の座標に変換したあとの話で、同次座標で表している間は気にしなくても良い。<br />
何も考えずに普通の点と無限遠の点を扱えるというのがここのポイント。<br />
<br />
== 直線 ==<br />
同次座標では、直線は次のように表す。<br><br />
<math><br />
l_1 x_1 + l_2 x_2 + l_3 x_3 = 0<br />
</math><br><br />
まあ、直線の方程式そのものだね。この式は右辺が0なので、任意の0で無い定数を掛けてもやっぱり同じ直線を表す。<br />
<br />
ベクトルっぽく書くと、こんな感じ。<br><br />
<math><br />
\begin{bmatrix}<br />
l_1 & l_2 & l_3<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
x_1 \\ x_2 \\ x_3<br />
\end{bmatrix}<br />
= \boldsymbol{l}^\text{T} \tilde{\boldsymbol{x}}<br />
= 0<br />
</math><br><br />
ただし、<math>\tilde{\boldsymbol{x}}</math>は<math>\boldsymbol{x}</math>の同次座標を表す。<br />
<br />
=== 2点を通る直線 ===<br />
<math>\tilde{\boldsymbol{x}}_1, \tilde{\boldsymbol{x}}_2</math>を通る直線は、次のように計算できる。<br><br />
<math><br />
\boldsymbol{l} = \tilde{\boldsymbol{x}}_1 \times \tilde{\boldsymbol{x}}_2<br />
</math><br><br />
まさかの外積。これが上式を満たす事は適当に確認できる。<br />
<br />
=== 2直線の交点 ===<br />
<math>\boldsymbol{l}_1, \boldsymbol{l}_2</math>の交点は、次のように計算できる。<br><br />
<math><br />
\tilde{\boldsymbol{x}} = \boldsymbol{l}_1 \times \boldsymbol{l}_2<br />
</math><br><br />
またしても外積。これがそれぞれの直線上にある事も簡単に確かめられる。<br />
<br />
* 2点 → 直線<br />
* 2直線 → 点<br />
ということで、点と直線が双対な関係にある。<br />
<br />
== 2次曲線 ==<br />
普通の座標での2次曲線:<br><br />
<math><br />
a x^2 + 2 b xy + c y^2 + 2 d x + 2 e y + f = 0<br />
</math><br><br />
で<math>(x, y) \leftarrow (x_1/x_3, x_2/x_3)</math>とすると、以下を得る。<br><br />
<math><br />
a x_1^2 + 2 b x_1 x_2 + c x_2^2 + 2 d x_1 x_3 + 2 e x_2 x_3 + f x_3^2 = 0<br />
</math><br />
<br />
整理すると、<br><br />
<math><br />
\begin{bmatrix}<br />
x_1 & x_2 & x_3<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
a & b & d \\<br />
b & c & e \\<br />
d & e & f<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
x_1 \\ x_2 \\ x_3<br />
\end{bmatrix}<br />
=<br />
\tilde{\boldsymbol{x}}^\text{T} C \tilde{\boldsymbol{x}}<br />
</math><br><br />
となる。<br />
<br />
=== 接線 ===<br />
点<math>\tilde{\boldsymbol{x}}</math>が2次曲線上(=<math>\tilde{\boldsymbol{x}}^\text{T} C \tilde{\boldsymbol{x}} = 0</math>)にある時、<br><br />
<math><br />
\boldsymbol{l} = C \tilde{\boldsymbol{x}}<br />
</math><br><br />
はその点を通る2次曲線の接線である。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%82%AB%E3%83%A1%E3%83%A9%E3%83%A2%E3%83%87%E3%83%AB
カメラモデル
2011-03-27T13:14:19Z
<p>白飯: 同次座標のリンクを追加</p>
<hr />
<div>== ピンホールカメラ ==<br />
迷ったら、コレ。<br />
<br />
画像座標を<math>\boldsymbol{m}</math>、3次元座標を<math>\boldsymbol{X}</math>とすると、<br><br />
<math><br />
s \tilde{\boldsymbol{m}} = P \tilde{\boldsymbol{X}}<br />
</math><br><br />
で表される。ただし、<math>\tilde{\boldsymbol{m}}, \tilde{\boldsymbol{X}}</math>はそれぞれ画像座標と3次元座標の[[同次座標]]、<math>P</math>は3x4行列、<math>s</math>は0でない任意の定数とする。<br />
<br />
== 射影行列<math>P</math>の例 ==<br />
<math>P</math>は3x3内部パラメータ行列<math>K</math>、3x4外部パラメータ行列<math>[R | \boldsymbol{t}]</math>に(だいたい)分ける事ができる。<br><br />
<math><br />
P = K [R | \boldsymbol{t}]<br />
</math><br><br />
なお<math>K</math>は上三角行列、<math>R, \boldsymbol{t}</math>はそれぞれカメラから見た世界座標系の軸の向き(回転行列)、原点の位置を表す。<br />
で、<math>P</math>には定数倍の不定性があるので、<math>P</math>と任意の0でない定数<math>a</math>を掛けた<math>aP</math>は同じ変換を表す。<br />
<br />
=== 普通のカメラ ===<br />
<math><br />
P =<br />
\begin{bmatrix}<br />
\alpha & \gamma & u_0 \\<br />
0 & \beta & v_0 \\<br />
0 & 0 & 1<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
R & \boldsymbol{t}<br />
\end{bmatrix}<br />
</math><br><br />
自由度は内部パラメータ5、外部パラメータ6(回転3、並進3)の計11。<br />
内部パラメータの(3, 3)成分を1しばりにすれば、定数倍の不定性が除去できる。<br />
<br />
=== 一般カメラ ===<br />
要はパラメータが物理的なものと関係なくてもよいというだけ。12個の成分を持ったただの行列。<br><br />
<math><br />
P =<br />
\begin{bmatrix}<br />
p_{11} & \dots & p_{14} \\<br />
\vdots & \ddots & \vdots \\<br />
p_{31} & \dots & p_{34}<br />
\end{bmatrix}<br />
</math><br><br />
ただし、定数倍の不定性があるので独立な成分は11個だけ。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%82%AB%E3%83%A1%E3%83%A9%E3%83%A2%E3%83%87%E3%83%AB
カメラモデル
2011-02-20T08:01:44Z
<p>白飯: u, vに添え字追加</p>
<hr />
<div>== ピンホールカメラ ==<br />
迷ったら、コレ。<br />
<br />
画像座標を<math>\boldsymbol{m}</math>、3次元座標を<math>\boldsymbol{X}</math>とすると、<br><br />
<math><br />
s \tilde{\boldsymbol{m}} = P \tilde{\boldsymbol{X}}<br />
</math><br><br />
で表される。ただし、<math>\tilde{\boldsymbol{m}}, \tilde{\boldsymbol{X}}</math>はそれぞれ画像座標と3次元座標の同次座標、<math>P</math>は3x4行列、<math>s</math>は0でない任意の定数とする。<br />
<br />
== 射影行列<math>P</math>の例 ==<br />
<math>P</math>は3x3内部パラメータ行列<math>K</math>、3x4外部パラメータ行列<math>[R | \boldsymbol{t}]</math>に(だいたい)分ける事ができる。<br><br />
<math><br />
P = K [R | \boldsymbol{t}]<br />
</math><br><br />
なお<math>K</math>は上三角行列、<math>R, \boldsymbol{t}</math>はそれぞれカメラから見た世界座標系の軸の向き(回転行列)、原点の位置を表す。<br />
で、<math>P</math>には定数倍の不定性があるので、<math>P</math>と任意の0でない定数<math>a</math>を掛けた<math>aP</math>は同じ変換を表す。<br />
<br />
=== 普通のカメラ ===<br />
<math><br />
P =<br />
\begin{bmatrix}<br />
\alpha & \gamma & u_0 \\<br />
0 & \beta & v_0 \\<br />
0 & 0 & 1<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
R & \boldsymbol{t}<br />
\end{bmatrix}<br />
</math><br><br />
自由度は内部パラメータ5、外部パラメータ6(回転3、並進3)の計11。<br />
内部パラメータの(3, 3)成分を1しばりにすれば、定数倍の不定性が除去できる。<br />
<br />
=== 一般カメラ ===<br />
要はパラメータが物理的なものと関係なくてもよいというだけ。12個の成分を持ったただの行列。<br><br />
<math><br />
P =<br />
\begin{bmatrix}<br />
p_{11} & \dots & p_{14} \\<br />
\vdots & \ddots & \vdots \\<br />
p_{31} & \dots & p_{34}<br />
\end{bmatrix}<br />
</math><br><br />
ただし、定数倍の不定性があるので独立な成分は11個だけ。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=3%E6%AC%A1%E5%85%83%E5%BE%A9%E5%85%83
3次元復元
2011-02-20T08:00:57Z
<p>白飯: 初版</p>
<hr />
<div>== ステレオ法 ==<br />
別の場所から撮影した2枚(以上)の画像を使って3次元復元をする方法。<br />
人間が左右の目の見え方の違いを使うのと原理は同じはず。<br />
いわゆる三角測量ね。<br />
<br />
=== 素朴な方法 ===<br />
3次元点<math>X</math>が2台のカメラに以下のように撮影されたとする。<br><br />
<math><br />
\begin{align}<br />
s \tilde{\boldsymbol{m}} &= P \tilde{\boldsymbol{X}} \\<br />
s^\prime \tilde{\boldsymbol{m}}^\prime &= P^\prime \tilde{\boldsymbol{X}}<br />
\end{align}<br />
</math><br />
<br />
観測された画像座標<math>(u, v) = (\tfrac{m_1}{m_3}, \tfrac{m_2}{m_3})</math>とすると、<br><br />
<math><br />
\begin{array}{l}<br />
m_3 (u, v) = (m_1, m_2) \\<br />
\Rightarrow \left( p_{31} X_1 + p_{32} X_2 + p_{33} X_3 + p_{34} X_4 \right)<br />
\begin{bmatrix}<br />
u \\ v<br />
\end{bmatrix}<br />
=<br />
\begin{bmatrix}<br />
p_{11} X_1 + p_{12} X_2 + p_{13} X_3 + p_{14} X_4 \\<br />
p_{21} X_1 + p_{22} X_2 + p_{23} X_3 + p_{24} X_4<br />
\end{bmatrix}<br />
\end{array}<br />
</math><br><br />
<math>X_4 = 1</math>として<math>X</math>について整理すると、以下を得る。<br><br />
<math><br />
\begin{bmatrix}<br />
p_{11} - p_{31} u & p_{12} - p_{32} u & p_{13} - p_{33} u \\<br />
p_{21} - p_{31} v & p_{22} - p_{32} v & p_{23} - p_{33} v<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
X_1 \\ X_2 \\ X_3<br />
\end{bmatrix}<br />
= -<br />
\begin{bmatrix}<br />
p_{14} - p_{34} u \\ p_{24} - p_{34} v<br />
\end{bmatrix}<br />
</math><br><br />
このままだと式が足りないので、もう一つのカメラも同じようにする。<br><br />
<math><br />
\begin{bmatrix}<br />
p_{11} - p_{31} u & p_{12} - p_{32} u & p_{13} - p_{33} u \\<br />
p_{21} - p_{31} v & p_{22} - p_{32} v & p_{23} - p_{33} v \\<br />
p^\prime_{11} - p^\prime_{31} u^\prime & p^\prime_{12} - p^\prime_{32} u^\prime & p^\prime_{13} - p^\prime_{33} u^\prime \\<br />
p^\prime_{21} - p^\prime_{31} v^\prime & p^\prime_{22} - p^\prime_{32} v^\prime & p^\prime_{23} - p^\prime_{33} v^\prime<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
X_1 \\ X_2 \\ X_3<br />
\end{bmatrix}<br />
= -<br />
\begin{bmatrix}<br />
p_{14} - p_{34} u \\ p_{24} - p_{34} v \\<br />
p^\prime_{14} - p^\prime_{34} u^\prime \\ p^\prime_{24} - p^\prime_{34} v^\prime<br />
\end{bmatrix}<br />
</math><br><br />
これを解けばもとの3次元座標が得られる。ちなみに未知数が3個に対して式が4本あるので、2台目のカメラの画像座標は片方だけ分かればいい。けど、そういう場合ってあるのか?<br />
<br />
=== もっといい方法 ===<br />
観測には誤差がつきもの。なので、そこら辺を考慮した計算をすればもっと良くなるはず。<br />
<br />
== 射影復元 ==<br />
<math>M</math>台のカメラに<math>N</math>点の3次元点が観測されたとする。<br><br />
<math><br />
\begin{bmatrix}<br />
s_{11} \tilde{\boldsymbol{m}}_{11} & \dots & s_{1N} \tilde{\boldsymbol{m}}_{1N} \\<br />
\vdots & & \vdots \\<br />
s_{M1} \tilde{\boldsymbol{m}}_{M1} & \dots & s_{MN} \tilde{\boldsymbol{m}}_{MN}<br />
\end{bmatrix}<br />
=<br />
\begin{bmatrix}<br />
P_1 \\ \vdots \\ P_M<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
\tilde{\boldsymbol{X}}_1 & \dots & \tilde{\boldsymbol{X}}_N<br />
\end{bmatrix}<br />
</math><br><br />
そうすると、左辺の3MxN行列は3Mx4行列と4xN行列の積でできているから、ランクが4しか無い事が分かる。<br />
なので、観測した点の画像座標を並べた行列をうまく分解して3Mx4行列<math>A</math>と4xN行列<math>B</math>にすると、それぞれ射影行列と3次元点になりますよって話。<br />
<br />
ただし、任意の正則な4x4行列<math>H</math>を使って<br><br />
<math><br />
A B = A H^{-1} H B = \left( A H^{-1} \right) \left( H B \right) = A^\prime B^\prime<br />
</math><br><br />
とできるので、分解の仕方は一通りではない。<br />
世の中それほどうまくいかない。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%82%AB%E3%83%A1%E3%83%A9%E3%83%A2%E3%83%87%E3%83%AB
カメラモデル
2011-02-20T07:02:32Z
<p>白飯: 初版</p>
<hr />
<div>== ピンホールカメラ ==<br />
迷ったら、コレ。<br />
<br />
画像座標を<math>\boldsymbol{m}</math>、3次元座標を<math>\boldsymbol{X}</math>とすると、<br><br />
<math><br />
s \tilde{\boldsymbol{m}} = P \tilde{\boldsymbol{X}}<br />
</math><br><br />
で表される。ただし、<math>\tilde{\boldsymbol{m}}, \tilde{\boldsymbol{X}}</math>はそれぞれ画像座標と3次元座標の同次座標、<math>P</math>は3x4行列、<math>s</math>は0でない任意の定数とする。<br />
<br />
== 射影行列<math>P</math>の例 ==<br />
<math>P</math>は3x3内部パラメータ行列<math>K</math>、3x4外部パラメータ行列<math>[R | \boldsymbol{t}]</math>に(だいたい)分ける事ができる。<br><br />
<math><br />
P = K [R | \boldsymbol{t}]<br />
</math><br><br />
なお<math>K</math>は上三角行列、<math>R, \boldsymbol{t}</math>はそれぞれカメラから見た世界座標系の軸の向き(回転行列)、原点の位置を表す。<br />
で、<math>P</math>には定数倍の不定性があるので、<math>P</math>と任意の0でない定数<math>a</math>を掛けた<math>aP</math>は同じ変換を表す。<br />
<br />
=== 普通のカメラ ===<br />
<math><br />
P =<br />
\begin{bmatrix}<br />
\alpha & \gamma & u \\<br />
0 & \beta & v \\<br />
0 & 0 & 1<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
R & \boldsymbol{t}<br />
\end{bmatrix}<br />
</math><br><br />
自由度は内部パラメータ5、外部パラメータ6(回転3、並進3)の計11。<br />
内部パラメータの(3, 3)成分を1しばりにすれば、定数倍の不定性が除去できる。<br />
<br />
=== 一般カメラ ===<br />
要はパラメータが物理的なものと関係なくてもよいというだけ。12個の成分を持ったただの行列。<br><br />
<math><br />
P =<br />
\begin{bmatrix}<br />
p_{11} & \dots & p_{14} \\<br />
\vdots & \ddots & \vdots \\<br />
p_{31} & \dots & p_{34}<br />
\end{bmatrix}<br />
</math><br><br />
ただし、定数倍の不定性があるので独立な成分は11個だけ。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%A1%E3%83%A2%E5%B8%B3@fmaj7b5.info
メモ帳@fmaj7b5.info
2011-02-20T06:26:10Z
<p>白飯: 項目追加</p>
<hr />
<div>== はじめに ==<br />
<br />
ここに書かれている内容は個人的なメモであり、正当性は保証しません。<br />
<br />
#コピペして怒られても知らないよ。<br />
<br />
== 基本 ==<br />
* [[線形代数]]<br />
* [[点の移動・回転]]<br />
* [[最小二乗法とか]]<br />
* [[非線形最適化]]<br />
<br />
== コンピュータ・ビジョン ==<br />
* [[カメラモデル]]<br />
* [[3次元復元]]<br />
<br />
[http://szeliski.org/Book いいもの]見つけた。<br />
<br />
== そのほか ==<br />
* [[おすすめプログラミング環境]]<br />
* [[lapackeインストールメモ]]</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=Lapacke%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%83%A1%E3%83%A2
Lapackeインストールメモ
2011-02-16T15:38:05Z
<p>白飯: /* 使ってみる */</p>
<hr />
<div>== lapackeとは ==<br />
fortranで書かれている線形代数の(上位)ライブラリ[http://www.netlib.org/lapack/ LAPACK]を、C/C++から使うためのラッパーライブラリ。<br />
<br />
下位のBLASはCBLASっていうC/C++用のインタフェースが定義されてたから良かったんだけど、LAPACKには無くて今まではこの部分を自分で作ってた。<br />
ただ、これだと他の人にソースあげたりする時に面倒な事になってたので、誰か統一したインタフェースを作ってくれないかなぁと前々から思ってた。<br />
と言う訳で、lapackeは本当に嬉しい。Intelさんありがとーっ!<br />
<br />
・・・なんだけど、これ、lapack-3.3から本体にくっついてくるらしいので、それまでは自分でインストールする必要がある。<br />
このページはそんなインストールに関するメモ。<br />
<br />
== インストール環境 ==<br />
* Ubuntu-10.10 (vmware上で動作)<br />
* apt-get(かsynaptic)でliblapack-dev、libblas-devインストール済<br>(lapackは3.2.1だった)<br />
<br />
今回は特に使わないけど、/usr/includeの下あたりにcblas.hがインストールされている事を確認。<br />
<br />
== lapackeのコンパイル ==<br />
1. [http://www.netlib.org/lapack/lapacke.tgz lapacke]を取ってくる。<br><br />
2. 適当に展開する。<br><br />
3. make.incを編集する。(今回はmake.gnuをmake.incにコピーして少し編集した)<br><br />
<syntaxhighlight lang="make"><br />
make.inc:<br />
<br />
(snip)<br />
LAPACKE = liblapacke.a<br />
LIBS = -llapack -lblas -lm<br />
(snip)<br />
</syntaxhighlight><br />
4. makeする。<br />
5. トップディレクトリ(アーカイブを解凍してできたディレクトリ)にliblapack.aがあれば成功。<br />
<br />
* コンパイルに失敗した時<br />
エラーメッセージを読んで、問題のあるところを直す。<br />
<br />
== 使ってみる ==<br />
適当に作ったサンプルプログラム:<br />
<syntaxhighlight lang="c"><br />
#include <stdio.h><br />
#include <stdlib.h><br />
<br />
/* #include <cblas.h> このプログラムでは要らない */<br />
#include "lapacke.h"<br />
<br />
/* 行列の中身を表示する */<br />
static void print_mat(const int m, const int n, const double *a, const int lda)<br />
{<br />
int i, j;<br />
<br />
for (i = 0; i < m; ++i) {<br />
for (j = 0; j < n; ++j) {<br />
printf("% 10.3g ", a[j*lda + i]);<br />
}<br />
printf("\n");<br />
}<br />
printf("\n");<br />
}<br />
<br />
/* メイン関数 */<br />
int main()<br />
{<br />
const int n = 3;<br />
const int lda = n;<br />
<br />
double a[lda*n], ev[n];<br />
lapack_int info;<br />
int i, j;<br />
<br />
/* 行列に値を入れる */<br />
for (i = 0; i < n; ++i) {<br />
for (j = 0; j < n; ++j) {<br />
a[lda*i + j] = (i == j || i*j != 0) ? 1 : 0;<br />
}<br />
}<br />
<br />
/* ちゃんと入ったか確認 */<br />
printf("a:\n");<br />
print_mat(n, n, a, lda);<br />
<br />
/* lapacke経由でdsyev(対称行列の固有値・固有ベクトルを計算する関数)を呼び出す */<br />
info = LAPACKE_dsyev(LAPACK_COL_MAJOR, 'V', 'U', n, a, lda, ev);<br />
if (info != 0) {<br />
printf("dsyev failed. info = %d\n", info);<br />
return EXIT_FAILURE;<br />
}<br />
<br />
/* 固有ベクトル表示 */<br />
printf("eigen vectors:\n");<br />
print_mat(n, n, a, lda);<br />
<br />
/* 固有値表示 */<br />
printf("eigen values:\n");<br />
for (i = 0; i < n; ++i) {<br />
printf("% 10.3g ", ev[i]);<br />
}<br />
printf("\n");<br />
<br />
return EXIT_SUCCESS;<br />
}<br />
</syntaxhighlight><br />
<br />
コンパイルとリンク:<br />
<pre><br />
$ gcc -Ipath/to/lapacke/include -c -o hode.o hode.c<br />
$ gcc -Lpath/to/lapacke hoge.o -llapacke -llapack -lblas -lm -o hoge<br />
</pre><br />
<br />
実行結果:<br />
<pre><br />
$ ./hoge <br />
a:<br />
1 0 0 <br />
0 1 1 <br />
0 1 1 <br />
<br />
eigen vectors:<br />
0 1 0 <br />
-0.707 0 0.707 <br />
0.707 0 0.707 <br />
<br />
eigen values:<br />
0 1 2 <br />
</pre><br />
<br />
・・・動いたっぽい。<br />
<br />
== 注意点など ==<br />
LAPACKがfortlanのライブラリなので、<br />
* column major(1列分進むと行(column)が1つ増える)にする方が良い<br />
* 要素を1から数えるので、ピボッティング結果を返す関数(getrfとか)の値はC/C++の感覚と1ずれる(はず)<br />
<br />
あと、使うライブラリによってはアライメント境界しばり(ex. 最初の要素が8の整数倍のアドレスで始まる)がある場合があるので、部分行列(行列の右下の3x3成分だけとか)をLAPACK/BLASの関数に入れるのは避けた方がよい。<br />
<br />
== 苦労した点 ==<br />
サンプルプログラムで分かりやすい結果になる行列を考えるところ。<br />
<br />
== これから ==<br />
行数・列数などをセットで覚えておく構造体、行列タイプに従って配列にアクセスする関数、メモリの割り当て/解放の面倒を見る関数があると便利、かも。<br />
<br />
[http://www.boost.org/ Boost]のuBLASでもいいけど、英語面倒だし。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=Lapacke%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%83%A1%E3%83%A2
Lapackeインストールメモ
2011-02-16T15:07:37Z
<p>白飯: ハイライト表示に変更</p>
<hr />
<div>== lapackeとは ==<br />
fortranで書かれている線形代数の(上位)ライブラリ[http://www.netlib.org/lapack/ LAPACK]を、C/C++から使うためのラッパーライブラリ。<br />
<br />
下位のBLASはCBLASっていうC/C++用のインタフェースが定義されてたから良かったんだけど、LAPACKには無くて今まではこの部分を自分で作ってた。<br />
ただ、これだと他の人にソースあげたりする時に面倒な事になってたので、誰か統一したインタフェースを作ってくれないかなぁと前々から思ってた。<br />
と言う訳で、lapackeは本当に嬉しい。Intelさんありがとーっ!<br />
<br />
・・・なんだけど、これ、lapack-3.3から本体にくっついてくるらしいので、それまでは自分でインストールする必要がある。<br />
このページはそんなインストールに関するメモ。<br />
<br />
== インストール環境 ==<br />
* Ubuntu-10.10 (vmware上で動作)<br />
* apt-get(かsynaptic)でliblapack-dev、libblas-devインストール済<br>(lapackは3.2.1だった)<br />
<br />
今回は特に使わないけど、/usr/includeの下あたりにcblas.hがインストールされている事を確認。<br />
<br />
== lapackeのコンパイル ==<br />
1. [http://www.netlib.org/lapack/lapacke.tgz lapacke]を取ってくる。<br><br />
2. 適当に展開する。<br><br />
3. make.incを編集する。(今回はmake.gnuをmake.incにコピーして少し編集した)<br><br />
<syntaxhighlight lang="make"><br />
make.inc:<br />
<br />
(snip)<br />
LAPACKE = liblapacke.a<br />
LIBS = -llapack -lblas -lm<br />
(snip)<br />
</syntaxhighlight><br />
4. makeする。<br />
5. トップディレクトリ(アーカイブを解凍してできたディレクトリ)にliblapack.aがあれば成功。<br />
<br />
* コンパイルに失敗した時<br />
エラーメッセージを読んで、問題のあるところを直す。<br />
<br />
== 使ってみる ==<br />
適当に作ったサンプルプログラム:<br />
<syntaxhighlight><br />
#include <stdio.h><br />
#include <stdlib.h><br />
<br />
/* #include <cblas.h> このプログラムでは要らない */<br />
#include "lapacke.h"<br />
<br />
/* 行列の中身を表示する */<br />
static void print_mat(const int m, const int n, const double *a, const int lda)<br />
{<br />
int i, j;<br />
<br />
for (i = 0; i < m; ++i) {<br />
for (j = 0; j < n; ++j) {<br />
printf("% 10.3g ", a[j*lda + i]);<br />
}<br />
printf("\n");<br />
}<br />
printf("\n");<br />
}<br />
<br />
/* メイン関数 */<br />
int main()<br />
{<br />
const int n = 3;<br />
const int lda = n;<br />
<br />
double a[lda*n], ev[n];<br />
lapack_int info;<br />
int i, j;<br />
<br />
/* 行列に値を入れる */<br />
for (i = 0; i < n; ++i) {<br />
for (j = 0; j < n; ++j) {<br />
a[lda*i + j] = (i == j || i*j != 0) ? 1 : 0;<br />
}<br />
}<br />
<br />
/* ちゃんと入ったか確認 */<br />
printf("a:\n");<br />
print_mat(n, n, a, lda);<br />
<br />
/* lapacke経由でdsyev(対称行列の固有値・固有ベクトルを計算する関数)を呼び出す */<br />
info = LAPACKE_dsyev(LAPACK_COL_MAJOR, 'V', 'U', n, a, lda, ev);<br />
if (info != 0) {<br />
printf("dsyev failed. info = %d\n", info);<br />
return EXIT_FAILURE;<br />
}<br />
<br />
/* 固有ベクトル表示 */<br />
printf("eigen vectors:\n");<br />
print_mat(n, n, a, lda);<br />
<br />
/* 固有値表示 */<br />
printf("eigen values:\n");<br />
for (i = 0; i < n; ++i) {<br />
printf("% 10.3g ", ev[i]);<br />
}<br />
printf("\n");<br />
<br />
return EXIT_SUCCESS;<br />
}<br />
</syntaxhighlight><br />
<br />
コンパイルとリンク:<br />
<pre><br />
gcc -Iwhere/to/lapacke/include -c -o hode.o hode.c<br />
gcc -Lwhere/to/lapacke hoge.o -llapacke -llapack -lblas -lm -o hoge<br />
</pre><br />
<br />
実行結果:<br />
<pre><br />
$ ./hoge <br />
a:<br />
1 0 0 <br />
0 1 1 <br />
0 1 1 <br />
<br />
eigen vectors:<br />
0 1 0 <br />
-0.707 0 0.707 <br />
0.707 0 0.707 <br />
<br />
eigen values:<br />
0 1 2 <br />
</pre><br />
<br />
・・・動いたっぽい。<br />
<br />
== 注意点など ==<br />
LAPACKがfortlanのライブラリなので、<br />
* column major(1列分進むと行(column)が1つ増える)にする方が良い<br />
* 要素を1から数えるので、ピボッティング結果を返す関数(getrfとか)の値はC/C++の感覚と1ずれる(はず)<br />
<br />
あと、使うライブラリによってはアライメント境界しばり(ex. 最初の要素が8の整数倍のアドレスで始まる)がある場合があるので、部分行列(行列の右下の3x3成分だけとか)をLAPACK/BLASの関数に入れるのは避けた方がよい。<br />
<br />
== 苦労した点 ==<br />
サンプルプログラムで分かりやすい結果になる行列を考えるところ。<br />
<br />
== これから ==<br />
行数・列数などをセットで覚えておく構造体、行列タイプに従って配列にアクセスする関数、メモリの割り当て/解放の面倒を見る関数があると便利、かも。<br />
<br />
[http://www.boost.org/ Boost]のuBLASでもいいけど、英語面倒だし。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E5%88%A9%E7%94%A8%E8%80%85:%E7%99%BD%E9%A3%AF
利用者:白飯
2011-02-13T09:50:02Z
<p>白飯: 初稿</p>
<hr />
<div>== 略歴 ==<br />
* 20世紀 生まれる<br />
* 21世紀 働く<br />
<br />
== 使用言語 ==<br />
割と使える<br />
* 日本語<br />
* C言語<br />
<br />
それなり<br />
* C++ (これ、マスターした人居るのか?)<br />
<br />
勉強中<br />
* JavaScript (緩い感じがイイ)<br />
* python (ライブラリが豊富だし、流行りっていうか・・)<br />
* 英語 (何かと必要なんだよなぁ)<br />
<br />
見た・聞いた<br />
* common LISP, emacs LISP<br />
* Prolog<br />
* BASIC (VBじゃないやつ)<br />
* x86 asm<br />
<br />
キライ<br />
* Java (手のひらで踊らされている感じ)<br />
<br />
== ライブラリ ==<br />
よく使う<br />
* lapack/blas<br />
* OpenGL (といっても画像の表示用)<br />
* libdc1394 (これが無いと何も始まらない)<br />
<br />
使った事がある<br />
* OpenCV (ぱっと動かす分には楽なんだけど)<br />
* gtk+2 (あ、3.0が出てる)<br />
* imagemagick<br />
* qhull<br />
* fftw</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=Lapacke%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%83%A1%E3%83%A2
Lapackeインストールメモ
2011-02-13T09:28:54Z
<p>白飯: 初稿</p>
<hr />
<div>== lapackeとは ==<br />
fortranで書かれている線形代数の(上位)ライブラリ[http://www.netlib.org/lapack/ LAPACK]を、C/C++から使うためのラッパーライブラリ。<br />
<br />
下位のBLASはCBLASっていうC/C++用のインタフェースが定義されてたから良かったんだけど、LAPACKには無くて今まではこの部分を自分で作ってた。<br />
ただ、これだと他の人にソースあげたりする時に面倒な事になってたので、誰か統一したインタフェースを作ってくれないかなぁと前々から思ってた。<br />
と言う訳で、lapackeは本当に嬉しい。Intelさんありがとーっ!<br />
<br />
・・・なんだけど、これ、lapack-3.3から本体にくっついてくるらしいので、それまでは自分でインストールする必要がある。<br />
このページはそんなインストールに関するメモ。<br />
<br />
== インストール環境 ==<br />
* Ubuntu-10.10 (vmware上で動作)<br />
* apt-get(かsynaptic)でliblapack-dev、libblas-devインストール済<br>(lapackは3.2.1だった)<br />
<br />
今回は特に使わないけど、/usr/includeの下あたりにcblas.hがインストールされている事を確認。<br />
<br />
== lapackeのコンパイル ==<br />
1. [http://www.netlib.org/lapack/lapacke.tgz lapacke]を取ってくる。<br><br />
2. 適当に展開する。<br><br />
3. make.incを編集する。(今回はmake.gnuをmake.incにコピーして少し編集した)<br><br />
<pre><br />
make.inc:<br />
<br />
(snip)<br />
LAPACKE = liblapacke.a<br />
LIBS = -llapack -lblas -lm<br />
(snip)<br />
</pre><br />
4. makeする。<br />
5. トップディレクトリ(アーカイブを解凍してできたディレクトリ)にliblapack.aがあれば成功。<br />
<br />
* コンパイルに失敗した時<br />
エラーメッセージを読んで、問題のあるところを直す。<br />
<br />
== 使ってみる ==<br />
適当に作ったサンプルプログラム:<br />
<pre><br />
#include <stdio.h><br />
#include <stdlib.h><br />
<br />
/* #include <cblas.h> このプログラムでは要らない */<br />
#include "lapacke.h"<br />
<br />
/* 行列の中身を表示する */<br />
static void print_mat(const int m, const int n, const double *a, const int lda)<br />
{<br />
int i, j;<br />
<br />
for (i = 0; i < m; ++i) {<br />
for (j = 0; j < n; ++j) {<br />
printf("% 10.3g ", a[j*lda + i]);<br />
}<br />
printf("\n");<br />
}<br />
printf("\n");<br />
}<br />
<br />
/* メイン関数 */<br />
int main()<br />
{<br />
const int n = 3;<br />
const int lda = n;<br />
<br />
double a[lda*n], ev[n];<br />
lapack_int info;<br />
int i, j;<br />
<br />
/* 行列に値を入れる */<br />
for (i = 0; i < n; ++i) {<br />
for (j = 0; j < n; ++j) {<br />
a[lda*i + j] = (i == j || i*j != 0) ? 1 : 0;<br />
}<br />
}<br />
<br />
/* ちゃんと入ったか確認 */<br />
printf("a:\n");<br />
print_mat(n, n, a, lda);<br />
<br />
/* lapacke経由でdsyev(対称行列の固有値・固有ベクトルを計算する関数)を呼び出す */<br />
info = LAPACKE_dsyev(LAPACK_COL_MAJOR, 'V', 'U', n, a, lda, ev);<br />
if (info != 0) {<br />
printf("dsyev failed. info = %d\n", info);<br />
return EXIT_FAILURE;<br />
}<br />
<br />
/* 固有ベクトル表示 */<br />
printf("eigen vectors:\n");<br />
print_mat(n, n, a, lda);<br />
<br />
/* 固有値表示 */<br />
printf("eigen values:\n");<br />
for (i = 0; i < n; ++i) {<br />
printf("% 10.3g ", ev[i]);<br />
}<br />
printf("\n");<br />
<br />
return EXIT_SUCCESS;<br />
}<br />
</pre><br />
<br />
コンパイルとリンク:<br />
<pre><br />
gcc -Iwhere/to/lapacke/include -c -o hode.o hode.c<br />
gcc -Lwhere/to/lapacke hoge.o -llapacke -llapack -lblas -lm -o hoge<br />
</pre><br />
<br />
実行結果:<br />
<pre><br />
$ ./hoge <br />
a:<br />
1 0 0 <br />
0 1 1 <br />
0 1 1 <br />
<br />
eigen vectors:<br />
0 1 0 <br />
-0.707 0 0.707 <br />
0.707 0 0.707 <br />
<br />
eigen values:<br />
0 1 2 <br />
</pre><br />
<br />
・・・動いたっぽい。<br />
<br />
== 注意点など ==<br />
LAPACKがfortlanのライブラリなので、<br />
* column major(1列分進むと行(column)が1つ増える)にする方が良い<br />
* 要素を1から数えるので、ピボッティング結果を返す関数(getrfとか)の値はC/C++の感覚と1ずれる(はず)<br />
<br />
あと、使うライブラリによってはアライメント境界しばり(ex. 最初の要素が8の整数倍のアドレスで始まる)がある場合があるので、部分行列(行列の右下の3x3成分だけとか)をLAPACK/BLASの関数に入れるのは避けた方がよい。<br />
<br />
== 苦労した点 ==<br />
サンプルプログラムで分かりやすい結果になる行列を考えるところ。<br />
<br />
== これから ==<br />
行数・列数などをセットで覚えておく構造体、行列タイプに従って配列にアクセスする関数、メモリの割り当て/解放の面倒を見る関数があると便利、かも。<br />
<br />
[http://www.boost.org/ Boost]のuBLASでもいいけど、英語面倒だし。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%A1%E3%83%A2%E5%B8%B3@fmaj7b5.info
メモ帳@fmaj7b5.info
2011-02-13T07:50:49Z
<p>白飯: lapackeの項目を追加</p>
<hr />
<div>== はじめに ==<br />
<br />
ここに書かれている内容は個人的なメモであり、正当性は保証しません。<br />
<br />
#コピペして怒られても知らないよ。<br />
<br />
== 基本 ==<br />
* [[線形代数]]<br />
* [[点の移動・回転]]<br />
* [[最小二乗法とか]]<br />
* [[非線形最適化]]<br />
<br />
== コンピュータ・ビジョン ==<br />
(そのうち書くかも)<br />
<br />
[http://szeliski.org/Book いいもの]見つけた。<br />
<br />
== そのほか ==<br />
* [[おすすめプログラミング環境]]<br />
* [[lapackeインストールメモ]]</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%81%8A%E3%81%99%E3%81%99%E3%82%81%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E7%92%B0%E5%A2%83
おすすめプログラミング環境
2011-01-22T15:35:06Z
<p>白飯: 初版</p>
<hr />
<div>独断と偏見で勧める、プログラミングし易い環境。<br />
<br />
== OS ==<br />
1. (GNU/)Linux・BSD<br />
: 無料で始められるというのも大きいが、ソースコードをやりとりしている人達がメインのため、選択できるプログラミング言語の幅が広い。<br />
: 加えて自由に使えるライブラリを、どこかの奇特な(暇な?)プログラマ達が沢山公開しているのも魅力。<br />
: 主要なディストリビューションを選べば、何か問題があった場合でも対処し易い。ググり方くらいは身につけておこう。<br />
<br />
2. Mac OSX<br />
: ほとんど触った事がない。けど、中身はBSDなので、LinuxやBSD向けのプログラムが大体動くらしい。<br />
: 信者と初心者しかいないところが気にならなければ、それなりに使えるんじゃないかと思う。<br />
<br />
3. Windows<br />
: 昔はWindowsじゃなきゃヤダ!っていう時代もあった。が、いざプログラムを書こうと思うと大変・選択肢が少ないことに気付く。<br />
: Windowsユーザ相手に商売するんじゃなければ避けた方が良い。<br />
<br />
<br />
== プログラミング言語 ==<br />
1. C<br />
: 普及率でいけば、やっぱりコレ。(動)画像の入出力とか、カメラの制御とか、複雑な計算とかのライブラリは大抵C言語向けなので、一番ラクができる。<br />
: 是非はともかくダーティーなコーディングもできるので、行き当たりばったりの実験・趣味プログラミングに最適。<br />
<br />
2. C++<br />
: マルチパラダイム言語。昔ながらのスタイル、オブなんとか指向、ジェネリック、ジェネティック・・・などなど懐の深さが良い。<br />
: クラスは仕様が決まっているものの方が扱い易いため、自分用ライブラリを書く時にお勧め。<br />
<br />
3. [http://www.python.org/ Python]<br />
: スクリプト言語なんだけど、ライブラリが豊富で使いやすそう。文法にクセがあるので慣れが必要かも。<br />
<br />
(ランク外) Java<br />
: 手のひらで踊らされているというか、箱庭っぽいというか、拡張の無さが微妙。ユーザが結構いるっぽいんだけど。<br />
: まあ、個人的にオブジェクト指向がキライっていうのもあるかな。ケータイプログラマ目指すなら必須か。<br />
<br />
<br />
== ライブラリ ==<br />
=== 数値計算 ===<br />
; [http://www.netlib.org/blas/ BLAS]<br />
: 線形代数ライブラリのデファクトスタンダード。これに従っておいた方が後々苦労しない、かも。<br />
: このライブラリはベクトル・行列の四則演算くらいしかできない。QR分解や固有ベクトルを求めたりしたい時は次のLAPACKと一緒に使う。<br />
: フリーならリファレンス版の他に[http://math-atlas.sourceforge.net/ ATLAS]、[http://www.tacc.utexas.edu/resources/software/ GotoBLAS]あたりが有名。あるいはCPUに応じて[http://software.intel.com/en-us/articles/intel-mkl/ Intel MKL]、[http://developer.amd.com/cpu/Libraries/acml/downloads/pages/default.aspx ACML]とか。<br />
: C言語から利用する場合はCBLASから使うのが(多少)安全。大体BLASのライブラリに付いてくるのでcblas.hが無いか探すべし。<br />
<br />
; [http://www.netlib.org/lapack/ LAPACK] (読み:lay-pack, L-A-pack)<br />
: レイパック、エルエーパックなどと読むらしい。ラパックなんて呼ぶヤツはモグリだ。気をつけよう。<br />
: これを使えばLU分解、QR分解、線形連立方程式の計算、固有値・固有ベクトル、特異値分解などなど面倒な計算が簡単にできる。<br />
: もともとFORTRANのライブラリでCから呼ぶのが長年の悩みの種だったけど、version 3.3からC言語用インタフェースが含まれた。<br />
: Intelさん、太っ腹!<br />
<br />
; [http://www.fftw.org/ FFTW]<br />
: フーリエ変換のライブラリ。コンピュータ・ビジョンで必要という訳ではないけど、手法によっては使う事もあるので一応。<br />
<br />
=== 画像表示 ===<br />
; [http://www.opengl.org/ OpenGL]<br />
: 3次元グラフィックス向けのAPI。なんとかってのと違って、Linux、windows、macのどれでも対応しているのが利点。<br />
: ちょっと結果を確認したい程度なら[http://www.opengl.org/resources/libraries/glut/ GLUT](または[http://freeglut.sourceforge.net/ freeglut])が簡単。<br />
: Cg、CUDA、OpenCL、GPGPUあたりのキーワードに興味のある人にも。<br />
<br />
=== 画像入出力 ===<br />
; [http://www.imagemagick.org/ ImageMagick]<br />
: MagicじゃなくてMagic'''k'''。画像周りの色々なライブラリをまとめたようなソフトのライブラリ。<br />
: 画像の読込/保存だけに使うのはオーバースペック気味だけど、簡単に使えるのでお勧め。<br />
<br />
; [http://ffmpeg.org/ FFmpeg]<br />
: 動画が読み書きできるライブラリ。ImageMagickも利用しているらしい。動画を扱う人むけかな。<br />
<br />
=== カメラ ===<br />
; [http://damien.douxchamps.net/ieee1394/libdc1394/ libdc1394]<br />
: IIDCっていう規格のIEEE1394カメラを制御するライブラリ。[http://www.ptgrey.com/index.asp Point Grey]のカメラとか使う事があれば。<br />
: 1394カメラっていってもDVとかとは違うので注意。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E3%83%A1%E3%83%A2%E5%B8%B3@fmaj7b5.info
メモ帳@fmaj7b5.info
2011-01-22T13:55:17Z
<p>白飯: 項目の追加</p>
<hr />
<div>== はじめに ==<br />
<br />
ここに書かれている内容は個人的なメモであり、正当性は保証しません。<br />
<br />
#コピペして怒られても知らないよ。<br />
<br />
== 基本 ==<br />
* [[線形代数]]<br />
* [[点の移動・回転]]<br />
* [[最小二乗法とか]]<br />
* [[非線形最適化]]<br />
<br />
== コンピュータ・ビジョン ==<br />
(そのうち書くかも)<br />
<br />
[http://szeliski.org/Book いいもの]見つけた。<br />
<br />
== そのほか ==<br />
* [[おすすめプログラミング環境]]</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E7%82%B9%E3%81%AE%E7%A7%BB%E5%8B%95%E3%83%BB%E5%9B%9E%E8%BB%A2
点の移動・回転
2011-01-09T04:54:55Z
<p>白飯: /* 3次元の回転 */ 順番の修正</p>
<hr />
<div>= 座標系 =<br />
[[ファイル:Coordinate_system.svg|300px|right|座標系]]<br />
3次元空間を表す(直交)座標系は二種類あるらしい。右手系と左手系。えーと、親指の付け根に原点をとって、親指、人差し指、中指にそれぞれ<math>\boldsymbol{x}</math>、<math>\boldsymbol{y}</math>、<math>\boldsymbol{z}</math>軸を割り当てると<math>\boldsymbol{x} \rightarrow \boldsymbol{y} \rightarrow \boldsymbol{z} \rightarrow \boldsymbol{x} \rightarrow \cdots</math>が右回りになるのが右手系、左回りになるのが左手系。<br />
図から分かるように、この二つの座標系はどんなに回転させても一致しないので無駄な努力はしない事。右手系と左手系の変換は適当な軸一つまたは三軸全ての符号を反転させればよい。ただし、二つの座標系を一緒に扱うと混乱するだけなので、どちらか一方に統一した方が賢い。まぜるな危険。<br />
ここでは特に断らない限り右手系を用いる事にする。<br />
<br />
3次元の回転は「どれだけ回したか」の他に「どの軸周りに回したか」というのも必要。なので、回転軸と回転角を適当に定める必要がある。<br />
ここでは図に示すように、右手系は右ネジの向き、左手系は左ネジの向きに軸と回転方向をとる。こうすると2次元の回転方向と(z軸周りの)回転方向が同じになるため、何かと考えやすいはず。<br />
<br />
<br style="clear: both" /><br />
= 2次元の点の移動と回転 =<br />
[[ファイル:2d_trans.svg|300px|right|座標変換]]<br />
いわゆる座標変換の話。ここで注意するのは、どの座標系からどの座標系への変換なのか、計算して出てきた座標はどの座標系で表されているかという事。ここでは、固定されている座標系から見て原点が<math>(u, v)</math>にあって<math>\theta</math>回転している座標系で表現されている点<math>\boldsymbol{x}^\prime</math>を、もとの固定されている座標系で表すとどうなるか、というのをやる。言い換えると、「ある点<math>\boldsymbol{x}</math>が原点周りに<math>\theta</math>回転して、平行移動<math>(u, v)</math>しました。この点<math>\boldsymbol{x}^\prime</math>はどこでしょう」という問題。<br />
<br />
まあ、これは図を見たまんまで。<math>\boldsymbol{x}^\prime = \left( x^\prime_1, x^\prime_2 \right)</math>とすると、<math>x^\prime_1</math>成分はもとの座標系で<math>x^\prime_1 ( \cos \theta, \sin \theta )</math>、<math>x^\prime_2</math>は<math>x^\prime_2 ( -\sin \theta, \cos \theta )</math>になって<math>(u, v)</math>平行移動するので、次式になる。<br><br />
<math><br />
\boldsymbol{x} =<br />
\begin{bmatrix}<br />
\cos \theta & -\sin \theta \\<br />
\sin \theta & \cos \theta<br />
\end{bmatrix}<br />
\boldsymbol{x}^\prime<br />
+<br />
\begin{bmatrix}<br />
u \\<br />
v<br />
\end{bmatrix}<br />
</math><br />
<br />
このとき、<br><br />
<math><br />
R =<br />
\begin{bmatrix}<br />
\cos \theta & -\sin \theta \\<br />
\sin \theta & \cos \theta<br />
\end{bmatrix}<br />
</math><br><br />
を回転行列といったりする。この行列は正規直交行列(<math>R^\text{T} R = R R^\text{T} = I</math>)で<math>\text{det}(R) = 1</math>になる。<br />
<br />
<br style="clear: both" /><br />
== 複素数を使った回転の表現 ==<br />
2次元の原点周りの回転は、大きさ1の複素数<math>c + is</math>を使っても表現できる。<br><br />
<math><br />
\begin{align}<br />
\left (x^\prime_1 + ix^\prime_2 \right) (c + is) &= c x^\prime_1 - s x^\prime_2 + i (s x^\prime_1 + c x^\prime_2) \\<br />
&=<br />
\begin{bmatrix}<br />
c & -s<br />
\end{bmatrix}<br />
\boldsymbol{x}^\prime<br />
+ i<br />
\begin{bmatrix}<br />
s & c<br />
\end{bmatrix}<br />
\boldsymbol{x}^\prime<br />
\end{align}<br />
</math><br><br />
ただし<math>i</math>は虚数単位とする。<br />
<br />
で、何が言いたいかというと、「大きさが1」という条件は使いやすいので、覚えておくと良い事あるかもよってこと。<br />
<br />
<br />
= 3次元の座標変換 =<br />
2次元の場合と同じ。原点周りの回転<math>R</math>、平行移動<math>\boldsymbol{t}</math>とすると次式になる。<br><br />
<math><br />
\boldsymbol{x} = R \boldsymbol{x}^\prime + \boldsymbol{t}<br />
</math><br><br />
ただし、<math>R</math>は行列式の値が1の3x3正規直交行列である。<br />
<br />
ちなみに、<math>\text{det}(R) = -1</math>のものは擬回転と呼ぶ事があり、これは回転の他に右手系・左手系を一方から他方に変換する作用も持つ。<br />
つまり、右手系の話しかしていない時にこのような行列が出てきた場合は、何かがおかしいってこと。<br />
<br />
<br />
= 逆問題を解く =<br />
今までは、移動量や回転角が与えられた場合に点がどう動くかを見てきた。ここでは反対に移動前後の点の組<math>\left \{ (x_i, x^\prime_i) \right \}_i</math>が与えられた時に、平行移動量および回転角(・回転軸)を求めることを考える。<br />
<br />
要するに、次式を満たす<math>R, \boldsymbol{t}</math>を求める。<br><br />
<math><br />
\boldsymbol{x}_i = R \boldsymbol{x}^\prime_i + \boldsymbol{t}<br />
</math><br><br />
実際には誤差なんかがあって等式は成り立たなくなるだろうから、できるだけ等しくなるような値を求める事にする。<br />
<br />
== 重心に関する運動 ==<br />
とりあえず、<math>\boldsymbol{x}_i</math>の重心<math>\bar{\boldsymbol{x}}</math>を計算してみる。<br><br />
<math><br />
\begin{align}<br />
\bar{\boldsymbol{x}} &= \frac{1}{n} \sum_i^n \boldsymbol{x}_i \\<br />
&= \frac{1}{n} \sum_i^n R \boldsymbol{x}^\prime_i + \boldsymbol{t} \\<br />
&= R \bar{\boldsymbol{x}}^\prime + \boldsymbol{t}<br />
\end{align}<br />
</math><br />
<br />
重心を原点にとって<math>\left( \boldsymbol{y}_i, \boldsymbol{y}^\prime_i \right)</math>とすると、<br><br />
<math><br />
\begin{align}<br />
\boldsymbol{y}_i &= \boldsymbol{x}_i - \bar{\boldsymbol{x}} \\<br />
&= \left( R \boldsymbol{x}^\prime_i + \boldsymbol{t} \right) - \left( R \bar{\boldsymbol{x}}^\prime + \boldsymbol{t} \right) \\<br />
&= R \left( \boldsymbol{x}^\prime_i - \bar{\boldsymbol{x}}^\prime \right) \\<br />
&= R \boldsymbol{y}^\prime_i<br />
\end{align}<br />
</math><br><br />
となるから、これから回転<math>R</math>が求められそうだ。<br />
<br />
回転が分かれば平行移動成分は次式で計算できる。<br><br />
<math><br />
\boldsymbol{t} = \bar{\boldsymbol{x}} - R \bar{\boldsymbol{x}}^\prime<br />
</math><br><br />
特に<math>\bar{\boldsymbol{x}}^\prime = \boldsymbol{0}</math>なら回転と無関係に平行移動成分が分かる。<br />
<br />
というわけで、ここからは回転を求める事に精を出す。<br />
<br />
== 2次元の回転 ==<br />
誤差の定義は色々考えられるが、ここではベクトルの内積は同じベクトル同士を掛けた時が最大というのを使って次式を用いる。<br><br />
<math><br />
e = \sum_i \boldsymbol{y}_i^\text{T} R \boldsymbol{y}^\prime_i \longrightarrow \max<br />
</math><br><br />
これは長いベクトルの方が誤差に表れやすいため、そういうベクトルを重視する人向けの誤差である。<br />
<br />
<math><br />
R =<br />
\begin{bmatrix}<br />
c & -s \\<br />
s & c<br />
\end{bmatrix}<br />
</math><br><br />
とおいて<math>e</math>を未知数<math>c, s</math>に付いて書き直すと以下の式を得る。<br><br />
<math><br />
e = \sum_i<br />
\begin{bmatrix}<br />
y_{1i} y^\prime_{1i} + y_{2i} y^\prime_{2i} & y_{2i} y^\prime_{1i} - y_{1i} y^\prime_{2i}<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
c \\<br />
s<br />
\end{bmatrix}<br />
</math><br />
<br />
この式が<math>c^2 + s^2 = 1</math>の下で最大になるのは当然次の時。<br><br />
<math><br />
\begin{bmatrix}<br />
c \\ s<br />
\end{bmatrix}<br />
= \frac{\boldsymbol{u}}{|\boldsymbol{u}|}<br />
</math><br><br />
<math><br />
\boldsymbol{u} =<br />
\begin{bmatrix}<br />
\sum_i \boldsymbol{y}_i \cdot \boldsymbol{y}^\prime_i \\<br />
-\sum_i \boldsymbol{y}_i \times \boldsymbol{y}^\prime_i<br />
\end{bmatrix}<br />
</math><br />
<br />
<br />
== 3次元の回転 ==<br />
まあ、2次元の時と同じ。大きさが1の複素数<math>c + is</math>の代わりに、大きさが1のクォータニオン<math>\boldsymbol{q}</math>を使うだけ。<br />
<br />
クォータニオンの成分を<br><br />
<math><br />
\boldsymbol{q} = \left( s; \boldsymbol{v} \right)<br />
</math><br><br />
とすると、回転は次式で表される。<br><br />
<math><br />
\boldsymbol{y}_i = 2 \boldsymbol{v} \left( \boldsymbol{v} \cdot \boldsymbol{y}^\prime_i \right) + \left ( 2 s^2 - 1 \right) \boldsymbol{y}^\prime_i + 2 s \left( \boldsymbol{v} \times \boldsymbol{y}^\prime_i \right)<br />
</math><br />
<br />
で、内積が最大になるようにする、と。<br><br />
<math><br />
e = \sum_i \left( 2 \left( \boldsymbol{v} \cdot \boldsymbol{y}_i \right) \left( \boldsymbol{v} \cdot \boldsymbol{y}^\prime_i \right) + \left ( 2 s^2 - 1 \right) \left( \boldsymbol{y}_i \cdot \boldsymbol{y}^\prime_i \right) + 2 s \, \boldsymbol{y}_i {\cdot} \left( \boldsymbol{v} \times \boldsymbol{y}^\prime_i \right) \right) \longrightarrow \max<br />
</math><br />
<br />
<math>1 = s^2 + \boldsymbol{v}^\text{T} \boldsymbol{v}</math>に注意して適当に整理する。<br><br />
<math><br />
\begin{align}<br />
e &=<br />
\begin{bmatrix}<br />
s & v_1 & v_2 & v_3<br />
\end{bmatrix}<br />
\sum_i \left(<br />
\begin{bmatrix}<br />
0 & \boldsymbol{0}^\text{T} \\<br />
\boldsymbol{0} & \boldsymbol{y}_i {\boldsymbol{y}^\prime_i}^\text{T} + \left( \boldsymbol{y}_i {\boldsymbol{y}^\prime_i}^\text{T} \right)^\text{T}<br />
\end{bmatrix} +<br />
\left( \boldsymbol{y}_i \cdot \boldsymbol{y}^\prime \right)<br />
\begin{bmatrix}<br />
1 & \boldsymbol{0}^\text{T} \\<br />
\boldsymbol{0} & -I<br />
\end{bmatrix} +<br />
\begin{bmatrix}<br />
0 & \left( -\boldsymbol{y}_i \times \boldsymbol{y}^\prime_i \right)^\text{T} \\<br />
-\boldsymbol{y}_i \times \boldsymbol{y}^\prime_i & O<br />
\end{bmatrix}<br />
\right)<br />
\begin{bmatrix}<br />
s \\ v_1 \\ v_2 \\ v_3<br />
\end{bmatrix} \\<br />
&=<br />
\boldsymbol{q}^\text{T}<br />
\left( \sum_i<br />
\begin{bmatrix}<br />
y_{1i} y^\prime_{1i} + y_{2i} y^\prime_{2i} + y_{3i} y^\prime_{3i} & y_{3i} y^\prime_{2i} - y_{2i} y^\prime_{3i} & y_{1i} y^\prime_{3i} - y_{3i} y^\prime_{1i} & y_{2i} y^\prime_{1i} - y_{1i} y^\prime_{2i} \\<br />
y_{3i} y^\prime_{2i} - y_{2i} y^\prime_{3i} & y_{1i} y^\prime_{1i} - y_{2i} y^\prime_{2i} - y_{3i} y^\prime_{3i} & y_{1i} y^\prime_{2i} + y_{2i} y^\prime_{1i} & y_{1i} y^\prime_{3i} + y_{3i} y^\prime_{1i} \\<br />
y_{1i} y^\prime_{3i} - y_{3i} y^\prime_{1i} & y_{1i} y^\prime_{2i} + y_{2i} y^\prime_{1i} & -y_{1i} y^\prime_{1i} + y_{2i} y^\prime_{2i} - y_{3i} y^\prime_{3i} & y_{2i} y^\prime_{3i} + y_{3i} y^\prime_{2i} \\<br />
y_{2i} y^\prime_{1i} - y_{1i} y^\prime_{2i} & y_{1i} y^\prime_{3i} + y_{3i} y^\prime_{1i} & y_{3i} y^\prime_{2i} + y_{2i} y^\prime_{3i} & -y_{1i} y^\prime_{1i} - y_{2i} y^\prime_{2i} + y_{3i} y^\prime_{3i}<br />
\end{bmatrix}<br />
\right)<br />
\boldsymbol{q} \\<br />
&= \boldsymbol{q}^\text{T} M \boldsymbol{q}<br />
\end{align}<br />
</math><br />
<br />
ということで、<math>M</math>の最大固有値に対応する(単位)固有ベクトルを求めればよい。<math>|\boldsymbol{q}| = 1</math>っていうのがこの辺で役に立つね。ちなみに、固有ベクトルには符号の不定性があって<math>\pm \boldsymbol{q}</math>になるけど、どっちを使っても同じ回転になる。なので、好きな方を採用すべし。<br />
<br />
この計算は[http://scholar.google.co.jp/scholar?as_q=Closed-form+solution+of+absolute+orientation+using+unit+quaternions&num=10&btnG=Scholar+%E6%A4%9C%E7%B4%A2&as_epq=&as_oq=&as_eq=&as_occt=any&as_sauthors=Horn&as_publication=&as_ylo=&as_yhi=&hl=ja Hornの方法]と多分同じ。</div>
白飯
http://www.fmaj7b5.info/wiki/index.php?title=%E7%82%B9%E3%81%AE%E7%A7%BB%E5%8B%95%E3%83%BB%E5%9B%9E%E8%BB%A2
点の移動・回転
2011-01-09T04:44:53Z
<p>白飯: /* 3次元の回転 */ 計算方法を追記</p>
<hr />
<div>= 座標系 =<br />
[[ファイル:Coordinate_system.svg|300px|right|座標系]]<br />
3次元空間を表す(直交)座標系は二種類あるらしい。右手系と左手系。えーと、親指の付け根に原点をとって、親指、人差し指、中指にそれぞれ<math>\boldsymbol{x}</math>、<math>\boldsymbol{y}</math>、<math>\boldsymbol{z}</math>軸を割り当てると<math>\boldsymbol{x} \rightarrow \boldsymbol{y} \rightarrow \boldsymbol{z} \rightarrow \boldsymbol{x} \rightarrow \cdots</math>が右回りになるのが右手系、左回りになるのが左手系。<br />
図から分かるように、この二つの座標系はどんなに回転させても一致しないので無駄な努力はしない事。右手系と左手系の変換は適当な軸一つまたは三軸全ての符号を反転させればよい。ただし、二つの座標系を一緒に扱うと混乱するだけなので、どちらか一方に統一した方が賢い。まぜるな危険。<br />
ここでは特に断らない限り右手系を用いる事にする。<br />
<br />
3次元の回転は「どれだけ回したか」の他に「どの軸周りに回したか」というのも必要。なので、回転軸と回転角を適当に定める必要がある。<br />
ここでは図に示すように、右手系は右ネジの向き、左手系は左ネジの向きに軸と回転方向をとる。こうすると2次元の回転方向と(z軸周りの)回転方向が同じになるため、何かと考えやすいはず。<br />
<br />
<br style="clear: both" /><br />
= 2次元の点の移動と回転 =<br />
[[ファイル:2d_trans.svg|300px|right|座標変換]]<br />
いわゆる座標変換の話。ここで注意するのは、どの座標系からどの座標系への変換なのか、計算して出てきた座標はどの座標系で表されているかという事。ここでは、固定されている座標系から見て原点が<math>(u, v)</math>にあって<math>\theta</math>回転している座標系で表現されている点<math>\boldsymbol{x}^\prime</math>を、もとの固定されている座標系で表すとどうなるか、というのをやる。言い換えると、「ある点<math>\boldsymbol{x}</math>が原点周りに<math>\theta</math>回転して、平行移動<math>(u, v)</math>しました。この点<math>\boldsymbol{x}^\prime</math>はどこでしょう」という問題。<br />
<br />
まあ、これは図を見たまんまで。<math>\boldsymbol{x}^\prime = \left( x^\prime_1, x^\prime_2 \right)</math>とすると、<math>x^\prime_1</math>成分はもとの座標系で<math>x^\prime_1 ( \cos \theta, \sin \theta )</math>、<math>x^\prime_2</math>は<math>x^\prime_2 ( -\sin \theta, \cos \theta )</math>になって<math>(u, v)</math>平行移動するので、次式になる。<br><br />
<math><br />
\boldsymbol{x} =<br />
\begin{bmatrix}<br />
\cos \theta & -\sin \theta \\<br />
\sin \theta & \cos \theta<br />
\end{bmatrix}<br />
\boldsymbol{x}^\prime<br />
+<br />
\begin{bmatrix}<br />
u \\<br />
v<br />
\end{bmatrix}<br />
</math><br />
<br />
このとき、<br><br />
<math><br />
R =<br />
\begin{bmatrix}<br />
\cos \theta & -\sin \theta \\<br />
\sin \theta & \cos \theta<br />
\end{bmatrix}<br />
</math><br><br />
を回転行列といったりする。この行列は正規直交行列(<math>R^\text{T} R = R R^\text{T} = I</math>)で<math>\text{det}(R) = 1</math>になる。<br />
<br />
<br style="clear: both" /><br />
== 複素数を使った回転の表現 ==<br />
2次元の原点周りの回転は、大きさ1の複素数<math>c + is</math>を使っても表現できる。<br><br />
<math><br />
\begin{align}<br />
\left (x^\prime_1 + ix^\prime_2 \right) (c + is) &= c x^\prime_1 - s x^\prime_2 + i (s x^\prime_1 + c x^\prime_2) \\<br />
&=<br />
\begin{bmatrix}<br />
c & -s<br />
\end{bmatrix}<br />
\boldsymbol{x}^\prime<br />
+ i<br />
\begin{bmatrix}<br />
s & c<br />
\end{bmatrix}<br />
\boldsymbol{x}^\prime<br />
\end{align}<br />
</math><br><br />
ただし<math>i</math>は虚数単位とする。<br />
<br />
で、何が言いたいかというと、「大きさが1」という条件は使いやすいので、覚えておくと良い事あるかもよってこと。<br />
<br />
<br />
= 3次元の座標変換 =<br />
2次元の場合と同じ。原点周りの回転<math>R</math>、平行移動<math>\boldsymbol{t}</math>とすると次式になる。<br><br />
<math><br />
\boldsymbol{x} = R \boldsymbol{x}^\prime + \boldsymbol{t}<br />
</math><br><br />
ただし、<math>R</math>は行列式の値が1の3x3正規直交行列である。<br />
<br />
ちなみに、<math>\text{det}(R) = -1</math>のものは擬回転と呼ぶ事があり、これは回転の他に右手系・左手系を一方から他方に変換する作用も持つ。<br />
つまり、右手系の話しかしていない時にこのような行列が出てきた場合は、何かがおかしいってこと。<br />
<br />
<br />
= 逆問題を解く =<br />
今までは、移動量や回転角が与えられた場合に点がどう動くかを見てきた。ここでは反対に移動前後の点の組<math>\left \{ (x_i, x^\prime_i) \right \}_i</math>が与えられた時に、平行移動量および回転角(・回転軸)を求めることを考える。<br />
<br />
要するに、次式を満たす<math>R, \boldsymbol{t}</math>を求める。<br><br />
<math><br />
\boldsymbol{x}_i = R \boldsymbol{x}^\prime_i + \boldsymbol{t}<br />
</math><br><br />
実際には誤差なんかがあって等式は成り立たなくなるだろうから、できるだけ等しくなるような値を求める事にする。<br />
<br />
== 重心に関する運動 ==<br />
とりあえず、<math>\boldsymbol{x}_i</math>の重心<math>\bar{\boldsymbol{x}}</math>を計算してみる。<br><br />
<math><br />
\begin{align}<br />
\bar{\boldsymbol{x}} &= \frac{1}{n} \sum_i^n \boldsymbol{x}_i \\<br />
&= \frac{1}{n} \sum_i^n R \boldsymbol{x}^\prime_i + \boldsymbol{t} \\<br />
&= R \bar{\boldsymbol{x}}^\prime + \boldsymbol{t}<br />
\end{align}<br />
</math><br />
<br />
重心を原点にとって<math>\left( \boldsymbol{y}_i, \boldsymbol{y}^\prime_i \right)</math>とすると、<br><br />
<math><br />
\begin{align}<br />
\boldsymbol{y}_i &= \boldsymbol{x}_i - \bar{\boldsymbol{x}} \\<br />
&= \left( R \boldsymbol{x}^\prime_i + \boldsymbol{t} \right) - \left( R \bar{\boldsymbol{x}}^\prime + \boldsymbol{t} \right) \\<br />
&= R \left( \boldsymbol{x}^\prime_i - \bar{\boldsymbol{x}}^\prime \right) \\<br />
&= R \boldsymbol{y}^\prime_i<br />
\end{align}<br />
</math><br><br />
となるから、これから回転<math>R</math>が求められそうだ。<br />
<br />
回転が分かれば平行移動成分は次式で計算できる。<br><br />
<math><br />
\boldsymbol{t} = \bar{\boldsymbol{x}} - R \bar{\boldsymbol{x}}^\prime<br />
</math><br><br />
特に<math>\bar{\boldsymbol{x}}^\prime = \boldsymbol{0}</math>なら回転と無関係に平行移動成分が分かる。<br />
<br />
というわけで、ここからは回転を求める事に精を出す。<br />
<br />
== 2次元の回転 ==<br />
誤差の定義は色々考えられるが、ここではベクトルの内積は同じベクトル同士を掛けた時が最大というのを使って次式を用いる。<br><br />
<math><br />
e = \sum_i \boldsymbol{y}_i^\text{T} R \boldsymbol{y}^\prime_i \longrightarrow \max<br />
</math><br><br />
これは長いベクトルの方が誤差に表れやすいため、そういうベクトルを重視する人向けの誤差である。<br />
<br />
<math><br />
R =<br />
\begin{bmatrix}<br />
c & -s \\<br />
s & c<br />
\end{bmatrix}<br />
</math><br><br />
とおいて<math>e</math>を未知数<math>c, s</math>に付いて書き直すと以下の式を得る。<br><br />
<math><br />
e = \sum_i<br />
\begin{bmatrix}<br />
y_{1i} y^\prime_{1i} + y_{2i} y^\prime_{2i} & y_{2i} y^\prime_{1i} - y_{1i} y^\prime_{2i}<br />
\end{bmatrix}<br />
\begin{bmatrix}<br />
c \\<br />
s<br />
\end{bmatrix}<br />
</math><br />
<br />
この式が<math>c^2 + s^2 = 1</math>の下で最大になるのは当然次の時。<br><br />
<math><br />
\begin{bmatrix}<br />
c \\ s<br />
\end{bmatrix}<br />
= \frac{\boldsymbol{u}}{|\boldsymbol{u}|}<br />
</math><br><br />
<math><br />
\boldsymbol{u} =<br />
\begin{bmatrix}<br />
\sum_i \boldsymbol{y}_i \cdot \boldsymbol{y}^\prime_i \\<br />
-\sum_i \boldsymbol{y}_i \times \boldsymbol{y}^\prime_i<br />
\end{bmatrix}<br />
</math><br />
<br />
<br />
== 3次元の回転 ==<br />
まあ、2次元の時と同じ。大きさが1の複素数<math>c + is</math>の代わりに、大きさが1のクォータニオン<math>\boldsymbol{q}</math>を使うだけ。<br />
<br />
クォータニオンの成分を<br><br />
<math><br />
\boldsymbol{q} = \left( s; \boldsymbol{v} \right)<br />
</math><br><br />
とすると、回転は次式で表される。<br><br />
<math><br />
\boldsymbol{y}_i = 2 \boldsymbol{v} \left( \boldsymbol{v} \cdot \boldsymbol{y}^\prime_i \right) + \left ( 2 s^2 - 1 \right) \boldsymbol{y}^\prime_i + 2 s \left( \boldsymbol{v} \times \boldsymbol{y}^\prime_i \right)<br />
</math><br />
<br />
で、内積が最大になるようにする、と。<br><br />
<math><br />
e = \sum_i \left( 2 \left( \boldsymbol{v} \cdot \boldsymbol{y}_i \right) \left( \boldsymbol{v} \cdot \boldsymbol{y}^\prime_i \right) + \left ( 2 s^2 - 1 \right) \left( \boldsymbol{y}_i \cdot \boldsymbol{y}^\prime_i \right) + 2 s \, \boldsymbol{y}_i {\cdot} \left( \boldsymbol{v} \times \boldsymbol{y}^\prime_i \right) \right) \longrightarrow \max<br />
</math><br />
<br />
<math>1 = s^2 + \boldsymbol{v}^\text{T} \boldsymbol{v}</math>に注意して適当に整理する。<br><br />
<math><br />
\begin{align}<br />
e &=<br />
\begin{bmatrix}<br />
s & v_1 & v_2 & v_3<br />
\end{bmatrix}<br />
\sum_i \left(<br />
\begin{bmatrix}<br />
\boldsymbol{y}_i {\boldsymbol{y}^\prime_i}^\text{T} + \left( \boldsymbol{y}_i {\boldsymbol{y}^\prime_i}^\text{T} \right)^\text{T} & \boldsymbol{0} \\<br />
\boldsymbol{0} & 0<br />
\end{bmatrix} +<br />
\left( \boldsymbol{y}_i \cdot \boldsymbol{y}^\prime \right)<br />
\begin{bmatrix}<br />
-I & \boldsymbol{0} \\<br />
\boldsymbol{0}^\text{T} & 1<br />
\end{bmatrix} +<br />
\begin{bmatrix}<br />
O & -\boldsymbol{y}_i \times \boldsymbol{y}^\prime_i \\<br />
\left( -\boldsymbol{y}_i \times \boldsymbol{y}^\prime_i \right)^\text{T} & 0<br />
\end{bmatrix}<br />
\right)<br />
\begin{bmatrix}<br />
s \\ v_1 \\ v_2 \\ v_3<br />
\end{bmatrix} \\<br />
&=<br />
\boldsymbol{q}^\text{T}<br />
\left( \sum_i<br />
\begin{bmatrix}<br />
y_{1i} y^\prime_{1i} - y_{2i} y^\prime_{2i} - y_{3i} y^\prime_{3i} & y_{1i} y^\prime_{2i} + y_{2i} y^\prime_{1i} & y_{1i} y^\prime_{3i} + y_{3i} y^\prime_{1i} & y_{3i} y^\prime_{2i} - y_{2i} y^\prime_{3i} \\<br />
y_{1i} y^\prime_{2i} + y_{2i} y^\prime_{1i} & -y_{1i} y^\prime_{1i} + y_{2i} y^\prime_{2i} - y_{3i} y^\prime_{3i} & y_{2i} y^\prime_{3i} + y_{3i} y^\prime_{2i} & y_{1i} y^\prime_{3i} - y_{3i} y^\prime_{1i} \\<br />
y_{1i} y^\prime_{3i} + y_{3i} y^\prime_{1i} & y_{3i} y^\prime_{2i} + y_{2i} y^\prime_{3i} & -y_{1i} y^\prime_{1i} - y_{2i} y^\prime_{2i} + y_{3i} y^\prime_{3i} & y_{2i} y^\prime_{1i} - y_{1i} y^\prime_{2i} \\<br />
y_{3i} y^\prime_{2i} - y_{2i} y^\prime_{3i} & y_{1i} y^\prime_{3i} - y_{3i} y^\prime_{1i} & y_{2i} y^\prime_{1i} - y_{1i} y^\prime_{2i} & y_{1i} y^\prime_{1i} + y_{2i} y^\prime_{2i} + y_{3i} y^\prime_{3i}<br />
\end{bmatrix}<br />
\right)<br />
\boldsymbol{q} \\<br />
&= \boldsymbol{q}^\text{T} M \boldsymbol{q}<br />
\end{align}<br />
</math><br />
<br />
ということで、<math>M</math>の最大固有値に対応する(単位)固有ベクトルを求めればよい。<math>|\boldsymbol{q}| = 1</math>っていうのがこの辺で役に立つね。ちなみに、固有ベクトルには符号の不定性があって<math>\pm \boldsymbol{q}</math>になるけど、どっちを使っても同じ回転になる。なので、好きな方を採用すべし。<br />
<br />
この計算は[http://scholar.google.co.jp/scholar?as_q=Closed-form+solution+of+absolute+orientation+using+unit+quaternions&num=10&btnG=Scholar+%E6%A4%9C%E7%B4%A2&as_epq=&as_oq=&as_eq=&as_occt=any&as_sauthors=Horn&as_publication=&as_ylo=&as_yhi=&hl=ja Hornの方法]と多分同じ。</div>
白飯