2012年12月21日金曜日

多変数二次関数の平方完成

二乗誤差の最小化問題

何らかの二乗誤差を最小化する問題を取り扱っていると、次のような二次関数(二次式?適当な言葉が見つからない)に出くわすことがよくある。\(u\)は\(n\)次元のベクトルで変数ベクトル\(x\)と定数ベクトル\(c\)を含んでおり、\(n\times n\)の対称行列\(A\)には定数しか含まれていない。このとき、誤差関数\(y(x)\)の最小値は何になるのか?また、その時の変数\(x\)は何か?これらを求めるにはどうしたらよいか?

\[ y(x) = u^T A u = \left[ \begin{array}{c|c} 変数 & 定数\\ \end{array} \right] \left[ \begin{array}{c|c} 2次式の係数 & 1次式の係数\\ \hline 1次式の係数 & 定数項\\ \end{array} \right] \left[ \begin{array}{c} 変数\\ \hline 定数\\ \end{array} \right] \] \[ = \left[ \begin{array}{cc} {\bf x}^T & {\bf c}^T \end{array} \right] \left[ \begin{array}{cc} {\bf a^{(2)}} & {\bf a^{(1)}}\\ {\bf a^{(1)}}^T & {\bf a^{(0)}} \end{array} \right] \left[ \begin{array}{c} {\bf x}\\ {\bf c} \end{array} \right] \]

二次関数の平方完成

やり方は高校の時に習った二次関数の平方完成とほとんど同じである。 これを多変数関数に当てはめてみる。 手順は、まず二次の項の係数だけ残して、平方完成できたとして式を立ててみる。 次に、その平方完成した式と下の式をそれぞれ展開して、見比べてみる。

まず、平方完成できたとすると以下のような式になるはずである。

\[ y(x)=(x+d)^T a^{(2)} (x+d) + e \] \[ = x^T a^{(2)} x + 2 d^T a^{(2)} x + d^T a^{(2)} d + e \]

一方、元の式を展開すると以下のようになる。

\[ y(x) = x^T a^{(2)} x + 2 c^T a^{(1)} x + c^T a^{(0)} c \]

2つの式をよく見比べると1次の項と定数項に対して次の等式が成立する。

\[ 2 d^T a^{(2)} = 2 c^T a^{(1)} \] \[ d^T a^{(2)} d + e = c^T a^{(0)} c \]

\(d\)および\(e\)について解く。

\[ \begin{array}{ccl} d & = & {a^{(2)}}^{-1} {a^{(1)}}^T c\\ e & = & c^T a^{(0)} c - d^T a^{(2)} d\\ & = & c^T a^{(0)} c - ({a^{(2)}}^{-1} {a^{(1)}}^T c)^T a^{(2)} ({a^{(2)}}^{-1} {a^{(1)}}^T c)\\ & = & c^T a^{(0)} c - c^T {a^{(1)}} {a^{(2)}}^{-1} {a^{(1)}}^T c \end{array} \]

最小化問題の解

平方完成された式を再度見てみると、\(x+d=0\)のとき\(y(x)\)が最小値\(e\)をとる。 ということは、誤差\(y(x)\)を最小化する\(x\)は\(x=-d\)のときでありこれが最適解である。 ただし、当然上に凸の二次関数では最小値はないので、条件がある。その条件は、上式を見るとわかるように\({a^{(2)}}^{-1}\)が存在することである。さらに、下に凸でなければならないので、\)a^{(2)}\)が正定値である必要もある。

2012年11月14日水曜日

ofxFaceTrackerの動かし方

ofxFaceTrackerはOpenFrameWorksのaddonで、安定して顔の3次元トラッキングができるプログラム。 試しにOpenFrameWorksからインストールして、動かしてみた。日本語でofxFaceTrackerのインストール方法など扱うサイトが見つからなかったので、メモを残しとく。

動作環境

  • Visual Studio 2010 SP1
  • Windows 7 Pro SP1
  • Nvidia Quadro 4000
  • NVIDIA CUDA Toolkit v4.2
  • OpenFrameWorks v0072 vs2010

コンパイル

  1. OpenFrameWorksをダウンロード
    http://www.openframeworks.cc/
  2. OpenFrameWorksをインストール。ここのページに従えば、特に問題なくサンプルコードが実行できる。
    http://www.myu.ac.jp/~xkozima/lab/mobile-iphone2011-wm.html
  3. oxfCVのダウンロード。
    https://github.com/kylemcdonald/ofxCv
  4. of_v0072_vs2010_release\addonsに展開したフォルダをコピーし、ofxCVというフォルダ名に変更。
  5. ofxFaceTrackerのダウンロード。
    https://github.com/kylemcdonald/ofxFaceTracker
  6. of_v0072_vs2010_release\addonsに展開したフォルダをコピーし、ofxFaceTrackerというフォルダ名に変更。
  7. AdvancedExample.slnを起動してビルド。
    of_v0072_vs2010_release\addons\ofxFaceTracker\example-advanced\AdvancedExample.sln

example-advancedの実行

  1. binフォルダにdataフォルダ作成
    of_v0072_vs2010_release\addons\ofxFaceTracker\example-advanced\bin\data\
  2. libs\FaceTracker\modelフォルダをexample-advanced\bin\data内にコピー
    of_v0072_vs2010_release\addons\ofxFaceTracker\libs\FaceTracker\model
  3. 実行

2012年11月13日火曜日

3次元計測による画像の平行化

平面を写した画像が手元にあり、そのカメラの姿勢(外部パラメータ)や画角など(内部パラメータ)が分かっている。さらに、その画像内の疎な(sparse)な点の3次元位置が分かっている時、その平面の画像を真正面から見た画像に変換することを考える。Stucture-from-motionやレーザレンジファインダ、MicrosoftのKinectなどで平面を含む対象を計測する場合に良くそのような状況に出くわす。Homography変換を使えばいいんだ、という程度の知識があることを前提に具体的方法をまとめる。

Homography変換による画像の平行化

このタスクは、平面に固定された座標$S$からカメラ座標$C$への剛体変換が回転行列$R_{S \rightarrow C}$と$t_{S \rightarrow C}$として分かっており、計測点の3次元位置が平面座標$S$で表現されていれば話は簡単である。まず通常の射影行列を考えてHomography変換行列を導出する。射影行列$P$は以下のように表す事ができる。

\[ P= \left[ \begin{array}{cccc} p_{11} & p_{12} & p_{13} & p_{14} \\ p_{21} & p_{22} & p_{23} & p_{24} \\ p_{31} & p_{32} & p_{33} & p_{34} \\ \end{array} \right] = K[R_{S \rightarrow C}|t_{S \rightarrow C}] \]

計測点の3次元位置をこの射影行列に掛ければ画像上のどの位置に投影されるかが計算できる。 今回、平面上の点を考えているので、平面座標$S$で表された3次元位置$(x_S, y_S, z_S)$の$z_S$は常に0だとすることができる。そうすると射影行列は下記のように$3 \times 3$の行列$H$に簡単化できてこれがHomography行列と呼ばれる。

\[ d \left[ \begin{array}{c} u\\ v\\ 1 \end{array} \right] = \left[ \begin{array}{cccc} p_{11} & p_{12} & p_{13} & p_{14} \\ p_{21} & p_{22} & p_{23} & p_{24} \\ p_{31} & p_{32} & p_{33} & p_{34} \\ \end{array} \right] \left[ \begin{array}{c} x_{S}\\ y_{S}\\ 0\\ 1 \end{array} \right] = \left[ \begin{array}{ccc} p_{11} & p_{12} & p_{14} \\ p_{21} & p_{22} & p_{24} \\ p_{31} & p_{32} & p_{34} \\ \end{array} \right] \left[ \begin{array}{c} x_{S}\\ y_{S}\\ 1 \end{array} \right] = H \left[ \begin{array}{c} x_{S}\\ y_{S}\\ 1 \end{array} \right] \]

3次元位置が世界座標系で表される場合

冒頭で示した状況もそうだけど、実際には、観測点やカメラの姿勢は平面に固定された座標で表されていることはなく、センサの位置を基準とした別の座標系で表されていることが一般的である。ここでは、その座標系を世界座標系$W$として、上のHomography行列$H$がどのようになるのか確認する。世界座標系で表現された平面座標$S$の基底ベクトル$e^{(x)}_W$、$e^{(y)}_W$、$e^{(z)}_W$と原点$O_W$は、それぞれ世界座標で表されたものが得られているものとする。また、カメラの姿勢は、世界座標$W$からカメラ座標$C$への変換$R_{W \rightarrow C}$、$t_{W \rightarrow C}$のみが得られており、平面座標$S$からカメラ座標$C$への剛体変換$R_{S \rightarrow C}$、$t_{S \rightarrow C}$は未知とする。

世界座標から平面座標への変換

観測点の3次元位置が世界座標で表されていても、 平面座標に変換することができれば後は上のHomography行列がそのまま使えることになる。 そこで下記のように平面座標$X_S$、$Y_S$、$Z_S$に変換する。

\[ \left[ \begin{array}{c} X_S\\ Y_S\\ Z_S \end{array} \right] = \left[ \begin{array}{c} {e^{(x)}_W}^T(X_W - O_W)\\ {e^{(y)}_W}^T(Y_W - O_W)\\ {e^{(z)}_W}^T(Z_W - O_W)\\ \end{array} \right] \]

同時座標表現をすれば、シンプルになる。

\[ \left[ \begin{array}{c|c} X_S\\ Y_S\\ Z_S\\ 1 \end{array} \right] = \left[ \begin{array}{cc} {e^{(x)}_W}^T & -{e^{(x)}_W}^T O_W\\ {e^{(y)}_W}^T & -{e^{(y)}_W}^T O_W\\ {e^{(z)}_W}^T & -{e^{(z)}_W}^T O_W\\ O & 1\\ \end{array} \right] \left[ \begin{array}{c} X_W\\ Y_W\\ Z_W\\ 1 \end{array} \right] = \left[ \begin{array}{cc} R_{W \rightarrow S} & -R_{W \rightarrow S} O_W\\ O & 1\\ \end{array} \right] \left[ \begin{array}{c} X_W\\ Y_W\\ Z_W\\ 1 \end{array} \right] \]

ただし、回転行列$R_{W->S}$は以下のように定義した。

\[ R_{W \rightarrow S} \equiv \left[ \begin{array}{c} {e^{(x)}_W}^T\\ {e^{(y)}_W}^T\\ {e^{(z)}_W}^T\\ \end{array} \right] \]

平面座標からカメラ座標への変換

これで世界座標$W$から平面座標$S$への変換が求まったので、この逆変換と世界座標$W$からカメラ座標$C$への変換$R_{W \rightarrow C}$、$t_{W \rightarrow C}$から、平面座標$S$からカメラ座標$C$への変換を求めHomography行列を算出する。

\[ \left[ \begin{array}{cc} R_{S \rightarrow C} & t_{S \rightarrow C}\\ O & 1\\ \end{array} \right] = \left[ \begin{array}{cc} R_{W \rightarrow C} & t_{W \rightarrow C}\\ O & 1\\ \end{array} \right] \left[ \begin{array}{cc} R_{W \rightarrow S} & -R_{W \rightarrow S} O_W\\ O & 1\\ \end{array} \right]^{-1} \] \[ = \left[ \begin{array}{cc} R_{W \rightarrow C} & t_{}\\ O & 1\\ \end{array} \right] \left[ \begin{array}{cccc} e^{(x)}_W & e^{(y)}_W & e^{(z)}_W & O_W\\ &O&&1 \end{array} \right] \]

求めるHomography行列$H$は、平面座標系$z$座標を0として以下のようになる。

\[ H= K \left[ \begin{array}{cc} R_{W \rightarrow C} & t_{}\\ O & 1\\ \end{array} \right] \left[ \begin{array}{cccc} e^{(x)}_W & e^{(y)}_W & O_W\\ 0 & 0 &1 \end{array} \right] \]

レンズの非線形歪みがある場合

レンズ歪は通常非線形変換であるから、レンズ歪を含む画像を扱う場合は、上記のように単純な変換にはならない。ここでは、$z=1$の面上で歪みが加わるレンズ歪みモデルの場合を扱う。

歪みのない座標$x_u$から歪みのある座標$x_v$へ変換する非線形変換関数を$f(x_u)$とすると、上記Homography変換は下記のような変換になる。

\[ x_d= K f\left( \left[ \begin{array}{cc} R_{W \rightarrow C} & t_{}\\ O & 1\\ \end{array} \right] \left[ \begin{array}{cccc} e^{(x)}_W & e^{(y)}_W & O_W\\ 0 & 0 &1 \end{array} \right] \left[ \begin{array}{c} X_S\\ Y_S\\ 1 \end{array} \right] \right) \]

ただし、オリジナルのTsaiのモデルでは、$z=f$の面上で歪が加わるため下記のようにはならない。 OpenCVに採用されているレンズ歪みモデルは、多項式モデルを含んでいるためTsaiのモデルの拡張とみなることができるが、$z=1$の面上で歪みが加わるモデルである。

2012年10月2日火曜日

プラハのボルダリングジム

プラハのボルダリングジムをまとめて紹介。 なぜプラハか?右の写真を見れば分かるようにチェコのクライミングレベルは高い。 ペンキ塗りですらクライミング技術をたしなんでいるのである。 過去にワールドカップも開かれたくらいだ。

Google Mapで検索して出てくるところは全て行き尽くした。 中々海外旅行に来てボルダリングジムに行く事もないだろうけれど、 自分と似たような境遇の人がどこかにいるはずだと思って紹介したい。

1.共通点・一般的な利用法など

実は、日本も含めて他のボルダリングジムにそれほど行ったことがないので、これが標準的なのかどうかは分からないが、利用法はだいたい共通していた。

飲み屋とセット

まず、どこのジムも飲み屋(バー)とセットになっていて、ボルダリングで汗をかいたあと、ビールを飲んで語り合うことができるようになっている。だから、店の入口はボルダリングジムらしくなく、飲み屋そのものなので見つけにくい。

一般的なルール

大抵、ホールドにテープが貼ってあり色で難しさ、番号でルートが示されている。二枚のテープがハの字に貼ってある箇所がスタート地点。ゴールも同様でそれ以外は一枚。ゴールにハの字が見当たらない場合は最上部を両手で持つことがゴールの姿勢。手足限定が基本で、足用の小さなやつは自由に使って良いようだ。手用と足用は見分けにくいので、その時は自分流で。足限定が難しすぎるなら足フリーにしてしまえばいいが誰もそうしていない。

ロッカー

これまで行ったジム全てで、受け付けて料金を払うとロッカー用のカギをもらえた。 このカギに書かれている番号と対応するロッカーを利用すればいい。 日本のスーパー銭湯と似たような方式。 銭湯といえば、そういえばかならずロッカーにシャワーがついていて、 皆裸を惜しげもなくさらけ出してシャワーを浴びに行く。 中欧東欧は裸を見せることに日本人よりも抵抗がないようだ。 髪の毛も洗えるボディーシャンプを持っていくのが常識みたい。

2.プラハのジム一覧

Lokal Blok S.r.o

地図HP

ここは飲み屋というよりレストランとしても人気があるようで、いつもボルダリングしない客でいっぱい。店に入ってもまさかボルダリングジムがあるとは思えない普通のレストランだけど、奥の地下への階段をおりると汗臭い匂いでジムがあることが分かる。1回利用でデポジットなしの90CZK(=360円)。靴も貸してくれるがいくらか忘れた。初級コースは20パターン。初級でも結構難しい。本当の初心者は、テープにかまわず登るかトラバースの方がいいかもしれない。

Lezecké Centrum

このジムは鉄道の車庫か何かを改装して立てられたらしく、かつての線路の上にある超巨大な建物の中にある。そもそもこの敷地への入口が見つけにくかった。北側から現地に向かわなければ入り口すら分からない。鉄道の倉庫の敷地に入るには大きな工場の門のように、ゲートをくぐらなければならない。

ここは正確にはボルダリングジムではない。ボルダリングは、ほんの一部しかなく、メインはリードクライミングである。あえて「リード」と書いたのは、トップロープでやるクライミングすらこの場所ではマイナーだから。初心者コースがトップロープ。一般人はリードクライミングでやる。だから、少なくとも二人で行かないと楽しめない。スタッフも昼間の空いている時なら確保できるといっていた。靴、確保器はただで貸してくれる。デポジット100CZKこみの200CZKで利用できる。 時間がなく1ルートしか登っていないが、トップロープのコースはボルダリングにくらべてはるかに簡単だった。持久力を鍛えたり高所での恐怖を慣らしたりするのにもってこい。

Boulder bar

店の名前が分かりやすいから比較的通りから見つけやすかった。 門をくぐれば、外から登っている人の様子が見える。 デポジット110CZKこみの200CZKで利用できる。 初回は妙なデータベースに登録しなければならないのと、管理者の英語力があまりないので登録に時間が掛かる。 コースはかなり多く、番号順に難しくなる訳ではないみたい。 初級コースの難しさは中程度。 マットが硬いから落ちるのが怖い。

Ultraant

ここも入り口を見つけるのに苦労した。 Google Street Viewの写真をみて看板も何もないドアを開け、階段を降りようとするとようやく店名が書かれた看板があった。つまり、そとからは全く分からない。 利用するには入会が必要で年会費20CZK(だったと思う)に加えて、1回分の利用料65CZKが必要。 改装したてでかなり綺麗。 シャワーも綺麗で暖かいお湯がふんだんに出る。 初級コースは比較的簡単で徐々に難しくなるので面白い。 電動で壁の傾きが代わる装置もある。

2012年9月24日月曜日

白黒画像のカラー化

白黒画像からそれらしいカラー画像を生成する技術Colorizationに関する論文を理解しようとすると、習ったはずの線形代数を復習するエッセンスがたっぷり含まれていることに気づいた。習ったはずだからと教科書に書かれている公式を駆使するのではなくて、なるべく原点(高校卒くらいの知識+α)にもどって考えてみる。

Colorizationとは

Colorizationの問題は,グレースケール画像$Y(r) (r \in \mathbb{R}^2)$とユーザにより複数$(i=1 \dots s)$の画素$r_i$の色が$U(r_i)=U_i$, $V(r_i)=V_i$で与えられたとき、以下に示すエネルギー関数が最小となる$U(r),V(r)$の全画素値を見つけること。

\[ J(U) = \sum_r \left|U(r) - \sum_{s \in N(r)}w_{sr}U(s)\right|^2 \] \[ w_{sr} = e^{-(Y(r) - Y(s))^2/2\sigma_r^2} \]

ただし、$U(r)$と$V(r)$は全く対称なので、$U(r)$のみ代表して記載している。 また、$N(r)$は画素$r$の近傍画素の集合、$\sigma_r$は画素$r$の近傍画素値$\{Y(r)|r \in N(r)\}$の分散である。

$w_{sr}$は、色画像の近傍画素どうしの性質を拘束する重みで、輝度画像の$Y(r)$と$Y(s)$が近い値、つまりのっぺりとしたら$U(r)$と$U(s)$の値ものっぺりでなければならなく、$Y(r)$と$Y(s)$に大きな差があれば、つまりエッジの両側であれば、$U(r)$と$U(s)$もエッジで良い。

このエネルギー$J$が$\sum(測定値-予測値)^2$の形をしていたらLevenberg-Marquardt法が使えるので簡単だけど、この場合は$\sum(予測値-予測値 \times 測定値)^2$のような形をしている。どうやって最適化するのが正攻法なのか。

エネルギー関数をベクトルと行列(二次形式)で表す

論文中にも書かれているが$J=x^TAx$の形にして考えてみる。まず、全画素数を$n$とし、色画像$U(r)$を$n$次元ベクトル$u=[U(r_1),U(r_2),\dots,U(r_n)]^T$で表現するとエネルギー関数は次のようにベクトルの内積で表し、求めるパラメータである色画像の画素値$U$の要素だけを分離する。

\[ J(U) = \left| \left[ \begin{array}{c} U(r_1) - (w_{11}U(r_1) + \cdots + w_{1?}U(r_?) + \cdots)\\ U(r_2) - (\cdots + w_{22}U(r_2) + w_{2?}U(r_?) + \cdots)\\ \vdots\\ U(r_n) - (\cdots + w_{n?}U(r_?) + \cdots + w_{nn}U(r_n))\\ \end{array} \right] \right|^2 \] \[ = { \left| \left[ \begin{array}{cccc} 1 - w_{11} & \cdots & - w_{1?} & \cdots\\ \cdots & 1 - w_{22} & - w_{2?} & \cdots\\ \vdots&& \ddots\\ \cdots & -w_{n?} & \cdots & 1 - w_{nn}\\ \end{array} \right] \left[ \begin{array}{c} U(r_1)\\ U(r_2)\\ \vdots\\ U(r_n) \end{array} \right] \right|^2 } \] \[ = \left[ \begin{array}{cccc} U(r_1) & U(r_2) & \cdots & U(r_n) \end{array} \right] (I-W)^T (I-W) \left[ \begin{array}{c} U(r_1)\\ U(r_2)\\ \vdots\\ U(r_n) \end{array} \right] = u^T A u \]

ただし、記号?は近傍画素に対応するインデクス。 ここで$I$は単位行列、$W$は以下に示す任意の2組の画素の間の重みを網羅的に表す行列である。

\[ W = \left[ \begin{array}{cccc} w_{11} & w_{12} & \cdots & w_{1n}\\ w_{21} & w_{22} & & w_{2n}\\ \vdots & & \ddots & \vdots\\ w_{n1} & w_{n2} & \cdots & w_{nn}\\ \end{array} \right] \]

しかも、画素$r_i$が$r_j$の近傍でない場合、すなわち$r_i \notin N(r_j)$のとき、$w_{ij} = 0$となる。 要するに行列$W$は至る所の要素が0の行列である。また、$(I-W)^T (I-W)$の形から行列$A$は実対称行列である。 いずれにせよ、これでエネルギー関数$J(U)$が二次形式の形を取ることが示された。

\[ J(U)=u^TAu \]

エネルギー関数の性質を考える

エネルギー関数が二次形式になっていることは分かった。二次形式は端的に言えば二次関数の多変数バージョン。ただ方向によって上向きに凸、下向きに凸、平坦な場合の組み合わせが存在するということ。行列$A$が正定値実対称行列であれば、この関数は下記のような形になって、図のように0を頂点にどの方向に向かっても上向きに反り上がる関数となる(図は$x^2+y^2+xy$)この関数は、全ての変数$U(r_i) = 0\ (i=1,\cdots,n)$のときのみ、最小値0をとる。

\[ a_{1}x_{1}^2 + a_{2}x_{1}^2 + \cdots + a_{n}x_{n}^2 \]

[Gnuplotコード表示/非表示]

エネルギー関数は確かに$x_i=0(i=1,\cdots,n)$で、もともと二乗和なので0以上である。ところがこの関数は、上のようなおわん型をしていない。おわん型で常に0以上なら$x_i=0(i=1,\cdots,n)$以外では0にならないはず。ところがこの関数に輝度一定($U(r_1) = U(r_2) = \cdots = U(r_n)$)の画像を入れると関数値は0になる。画素値$U(r)$と近傍画素値の重み付き平均$\sum_{s \in N(r)}w_{sr}U(s)$が等しくなってしまうから。

このことから少なくともエネルギー関数は、直線$U(r_1) = U(r_2) = \cdots = U(r_n)$の方向は一定値0をとる関数であることが分かる。これは一部の固有値が0をとることに等しい。この場合、下図のようにある方向には二次関数のように上向きに反り返り、ある方向には谷のようになだらかな形の関数になる。この図は、関数$x^2+y^2-2xy$のグラフで、$x$方向および$y$方向は上向きに反り上がっているが、$x=y$方向は0になっている。

[Gnuplotコード表示/非表示]

全ての画素の輝度値が0というのでも困るし、おわんの底が無いので最適な(エネルギー関数が最小となる)画素値が求まらないじゃないか。そこで次に、拘束条件としてユーザにより与えられた色$U(r_i)=U_i$をこの関数に導入することを考える。

拘束条件を加えたエネルギー関数

拘束条件は、ユーザにより与えられる複数$(i=1 \dots s)$の画素$r_i$の色$U(r_i)=U_i$, $V(r_i)=V_i$である。拘束条件が与えられれば、すぐに思いつくのはラグランジュの未定定数法だけど、この場合は非常に簡単な拘束条件なので、もとのエネルギー関数に代入する方法が良さそうだ。

\[ J(U) = u^T A u = \left[ \begin{array}{c} U(r_{s+1})\\ U(r_{s+2})\\ \vdots\\ U(r_n)\\ U_1\\ \vdots\\ U_s \end{array} \right]^T A \left[ \begin{array}{c} U(r_{s+1})\\ U(r_{s+2})\\ \vdots\\ U(r_n)\\ U_1\\ \vdots\\ U_s \end{array} \right] \]

このような形になると変数$u$の場所に定数が含まれてしまって、この式の最小値を求めるなど一見難しそうに見える。しかし、全部掛け合わせてパラしてしまうと、以下のような形になるはず。

\[ J = [U(r_i)^2の項] + [U(r_i)U(r_j)の項] + [U(r_i)の項] + [定数の項] \]

さらにこの式は、$U'(r_i) = f(U(r_1), \cdots, U(r_n))$などと変数変換すれば、以下のように二乗の項のみになる。

\[ J = [U'(r_i)^2の項] + [定数の項] \]

これは$y=x^2 + 2x + 2$という二次式が与えられても、$x' = x + 1$と変数変換(一次変換)することで、$y=(x+1)^2+1=x'^2+1$と書きなおすことができて、$x = -1$のときに最小値1をとることが分かることと同じ。これの多変数バージョンだと考えればいい。多変数の場合も同じように、全ての変数の二乗の項+定数となるように、適切に一次変換してくれる行列$L$を見つける問題となる。

\[ \left[ \begin{array}{c} x'_1\\ \vdots\\ x'_n \end{array} \right] = L \left[ \begin{array}{c} x_1\\\ \vdots\\ x_n \end{array} \right] \]

上で述べた、エネルギー関数を変数の二乗の項+定数に変換できる、という根拠は行列の対角化可能性である。実対称行列は常に対角化可能だから。それがどう関係あるかというと、 もし、エネルギー関数を(変数変換により)変形し、下記の式のような形に出来るなら、二乗の項+定数の形にできる。つまり行列Aを下記のように対角化できるなら変数変換だけで最適解$x_i = 0(i=1,\cdots,n-1)$と最小値$a_n c^2$が計算できることになる。

\[ \left[ \begin{array}{c} x_1\\ x_2\\ \vdots\\ x_{n-1}\\ c \end{array} \right]^T \left[ \begin{array}{cccc} a_1 & 0 & & 0 & 0\\ 0 & a_2 & & 0 & 0\\ & & \ddots& \vdots&\vdots\\ 0 & 0 & \cdots& a_{n-1} & 0\\ 0 & 0 & \cdots& 0 & a_n\\ \end{array} \right] \left[ \begin{array}{c} x_1\\ x_2\\ \vdots\\ x_{n-1}\\ c \end{array} \right] = \sum_{i=1}^{n-1} a_i x_i^2 + a_n c^2\ \ \ (a_i \geqq 0) \]

実際のエネルギー関数を見ると制約条件が複数($i=1,\cdots,s$)あるので定数部分が複数ある。 複数あったとしても、下記のように行列のこの定数部分を1つにまとめることができる。

\[ J(U) = u^T A u = \left[ \begin{array}{c|c} 変数 & 定数\\ \end{array} \right] \left[ \begin{array}{c|c} 2次式の係数 & 1次式の係数\\ \hline 1次式の係数 & 定数項\\ \end{array} \right] \left[ \begin{array}{c} 変数\\ \hline 定数\\ \end{array} \right] \] \[ = \left[ \begin{array}{cc} {\bf x}^T & {\bf c}^T \end{array} \right] \left[ \begin{array}{cc} {\bf a^{(2)}} & {\bf a^{(1)}}\\ {\bf a^{(1)}}^T & {\bf a^{(0)}} \end{array} \right] \left[ \begin{array}{c} {\bf x}\\ {\bf c} \end{array} \right] = \left[ \begin{array}{cc} {\bf x}^T & 1 \end{array} \right] \left[ \begin{array}{cc} {\bf a^{(2)}} & {\bf a}^{(1)}{\bf c}\\ {\bf c}^T{\bf a}^{(1)T} & {\bf c}^T {\bf a}^{(2)} {\bf c} \end{array} \right] \left[ \begin{array}{c} {\bf x}\\ 1 \end{array} \right] = u'^TA'u' \]

ただし、${\bf a^{(2)}}$は二次の係数、${\bf a}^{(1)}$は一次の係数、${\bf c}^T {\bf a}^{(2)} {\bf c}$は定数項である。これでようやく定数の要素が一箇所になった。

拘束条件付きの最適化

この関数を最小化する輝度$U(r_i)$を求めるには、既に述べたように$U(r_i)$を変数変換して、行列$A'$が対角化されるようにしなければならない。変数変換は一次変換でいいのでこの変換を$Lu'$とする。行列$A'$を対角化する行列$L$は、ひと通りではないけれど少なくとも、$L$の最終行、つまり$n-s+1$行目が対角成分以外全部0でないといけない。なぜなら$u'$の最後の要素は定数項に対応する1であり、$L$の最終行の要素に余計な成分が入っていると、線形変換により変数が混じってしまうことになる。これでは折角定数だけ下にもってきて1要素にまとめた意味がなくなってしまう。つまり、変換後も定数部分は定数のままでいるように保証されるような変換がよく、以下のような行列$L$で$u'$を$u''$に変換して対角化しなければならない。

\[ u'' = Lu' = \left[ \begin{array}{cccc} l_{1,1} & \cdots & l_{1,(n-s)} & l_{1,(n-s+1)}\\ \vdots & \ddots & \vdots & \vdots\\ l_{n,1} & \cdots & l_{(n-s),(n-s)} & l_{(n-s),(n-s+1)}\\ 0 & \cdots & 0 & l_{(n-s+1),(n-s+1)}\\ \end{array} \right] u' \]

この条件を満たす対角化方法が少なくとも1つあり、コレスキー分解である。ただし、行列$A$が正定値でないので行列$A'$も正定値でない可能性が高い。コレスキー分解は正定値限定なので、正定値の制約のない修正コレスキー分解を用いる。この分解では、$L$が上三角行列になるが、上の条件を満たし、以下のように元の変数を一次変換してくれる。

\[ J = u'^TA'u' = u'^T (L^T D L) u' = (Lu')^T D (Lu') \] \[ D= \left[ \begin{array}{ccc} d_1 & & \\ & \ddots & \\ & & d_{n-s+1} \end{array} \right] \]

$D$は対角行列で行列$A'$が対角化されたということである。

一旦、コレスキー分解ができ$u''=[u''_1, \cdots, u''_{(n-s)}, u''_{(n-s+1)}]^T$と求まれば、最適解は$u''_i = 0 (i=1, \cdots, n-s)$でその時のエネルギー関数の値は$d_{(n-s+1),(n-s+1)}u''^2_{(n-s+1)}$である。このままでは一次変換したままの変数なので以下の式により元の画素値に逆変換する。

\[ \tilde{u} = L^{-1}u'' \]

参考

2012年9月23日日曜日

内積から二次形式へ

以下のように二次形式を$x^Tx$から$y^TAy$へ変換するとき、行列の中身はどうなるのか?確認してみた。

\[ \left[ \begin{array}{cc} x & y \end{array} \right] \left[ \begin{array}{cc} a_{11} & a_{12}\\ a_{21} & a_{22}\\ \end{array} \right] \left[ \begin{array}{c} x\\ y \end{array} \right] = \left[ \begin{array}{cc} b_{11}x + b_{12}y & b_{21}x + b_{22}y \end{array} \right] \left[ \begin{array}{c} b_{11}x + b_{12}y\\ b_{21}x + b_{22}y \end{array} \right] \]

左辺と右辺をそれぞれ展開して共通項を見てみる。

\[ \begin{array}{rcl} 左辺&=& \left[ \begin{array}{c} x & y \end{array} \right] \left[ \begin{array}{c} a_{11}x + a_{12}y\\ a_{21}x + a_{22}y\\ \end{array} \right] \\ &=& a_{11}x^2 + a_{12}xy + a_{21}xy + a_{22}y^2 \\ &=& a_{11}x^2 + (a_{12} + a_{21})xy + a_{22}y^2 \\ 右辺&=& (b_{11}x + b_{12}y)^2 + (b_{21}x + b_{22}y)^2 \\ &=& (b_{11}^2 + b_{21}^2)x^2 + 2(b_{11}b_{12} + b_{21}b_{22})xy + (b_{12}^2 + b_{22}^2)y^2 \end{array} \]

$a_{12}=a_{21}$に注意して両辺の共通項を取り出すと以下のようになる。

\[ \left\{ \begin{array}{l} a_{11} = b_{11}^2 + b_{21}^2\\ a_{12} = a_{21} = b_{11}b_{12} + b_{21}b_{22}\\ a_{22} = b_{12}^2 + b_{22}^2 \end{array} \right. \]

2012年9月6日木曜日

OpenCVの最新ソース取得方法 (2012/09/06)

SubversionからGitへ

2012年09月06日現在、Googleでたとどりつける日本語ページには殆ど掲載されてないが、OpenCVのソースコードは2012年7月26日からSubversionではなく、基本的にGitで取得するように変わったらしい。 http://sourceforge.net/mailarchive/message.php?msg_id=29589413

Gitでのソース取得方法

ソースコードの取得方法はこちら。 http://code.opencv.org/projects/opencv/wiki/Working_with_OpenCV_git_repository

git clone git://code.opencv.org/opencv.git
公式ホームページはこれ。 http://opencv.org/

これらの情報は古いので注意

ネットでは以下のURLからソースコードが取得できると書かれているものが多いが既に古い情報で全てダメだった。http://code.opencv.org/に掲載されている情報ですら一部古くてリンク切れしているものもある。

以下のページには、既に古くなった情報が掲載されいているので注意。

2012年9月5日水曜日

3D-2D位置合わせ技術

3D-2D位置合わせ技術とは

ここでの「3D-2D位置合わせ技術」とは,モデルベーストラッキングやレンジデータへのテクスチャマッピングに限らず,画像と3次元空間の対応を得るという広い意味で,特に複数の画像を用いる技術に関してまとめた。具体的には以下のキーワードが該当するが、未完成であり、新しいものを発見次第追加していく。

  • Camera Calibration
  • Tracking
  • Structure-from-motion (SfM)
  • Multiple View Stereo (MVS)
  • Simultaneous Mapping and Localization (SLAM)

代表的な国際会議

  • International Journal of Computer Vision (ICCV)
  • Conference on Computer Vision and Pattern Recognition (CVPR)
  • International Conference on Pattern Recognition (ICPR)
  • International Symposium on Mixed and Augmented Reality (ISMAR)
  • British Machine Vision Conference (BMCV)
  • European Conference on Computer Vision (ECCV)
  • Asian Conference on Computer Vision (ACCV)

代表的な論文誌

  • International Journal of Computer Vision (IJCV)
  • IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI)

サーベイ論文・教科書

  • Vincent Lepetit and Pascal Fua. 2005. Monocular model-based 3D tracking of rigid objects. Found. Trends. Comput. Graph. Vis. 1, 1 (January 2005), 1-89
  • R. Hartley and A. Zisserman, Multiple View Geometry in Computer Vision. Cambridge University Press, 2000.
  • Feng Zhou, Henry Been-Lirn Duh, and Mark Billinghurst. 2008. Trends in augmented reality tracking, interaction and display: A review of ten years of ISMAR. In Proceedings of the 7th IEEE/ACM International Symposium on Mixed and Augmented Reality (ISMAR '08)
  • Josep Aulinas, Yvan Petillot, Joaquim Salvi, and Xavier Llado. 2008. The SLAM problem: a survey. In Proceedings of the 2008 conference on Artificial Intelligence Research and Development: Proceedings of the 11th International Conference of the Catalan Association for Artificial Intelligence, 363-371.

特徴点抽出・記述

ロバスト推定 for 対応点探索

  • PROSAC: Progressive Sample Consensus
  • IRLS: Iteratively Re-weighted Least Square
  • GOODSAC: Good Sample Consensus
  • SURSAC: SUfficient Random SAmple Coverage
  • RANSAC: RANdom SAmple Consensus
  • LMedS: Least Median of Squares
  • M-estimator

C/C++ライブラリなど

評価サイト・評価用データ

2012年8月31日金曜日

cos関数の線形最小二乗法

目的

データ$(x_i,y_i)$が複数($i=1 \cdots n$)与えられたとき、次のモデルのパラメータ$(a,b,c)$を線形最小二乗法により最適化する。

\[ y=a\cos(x + b) + c \]

方法

  1. 加法定理と$a' = a\cos b$、$b' = a\sin b$で変形し、3変数連立一次方程式の形にする。
  2. 平均自乗誤差を最小化する問題として線形最小二乗法の問題として解く。
  3. 求まった最適な($a',b',c$)から$a = \sqrt{a'^2 + b'^2}$、$b = \tan^{-1}(b'/a')$として元の変数を求める。

ソースコード

Jupyter NotebookのipynbファイルをGitHubで公開している.

STEP 1: 加法定理と変数変換でパラメータの線形和に

実際には、データ$(x,y)$には観測誤差が含まれて定式の等号は成り立たないので観測誤差をまとめて$\epsilon_i$と表すと、$i$番目のデータ$(x_i,y_i)$の関係は以下のように表現できる。

\[ \epsilon_i = a\cos(x_i + b) + c - y_i \]

ここでの最適化とはこの全てのデータに対して$\epsilon_i$が小さくなる$(a,b,c)$を探すことなので、その指標として平均自乗誤差$E = \sum_i^n \epsilon_i^2/n$を最小化する。加法定理で変形し、$a' = a\cos b$、$b' = a\sin b$と置くと以下のように変形できる。

\[ \begin{array}{ll} \epsilon_i &= a\cos(x_i + b) + c - y_i\\ &= a\{\cos(x_i)\cos(b) - \sin(x_i)\sin(b)\} + c - y_i\\ &= a'\cos(x_i) - b'\sin(x_i)\ + c - y_i \end{array} \]

STEP 2: 誤差関数を微分する

今、平均自乗誤差$E$を最小化しようとしているので、最適な値$(a',b',c)$では$E$が極値になることが必要条件である。

\[ \frac{\partial E}{\partial a'} = 0,\ \frac{\partial E}{\partial b'} = 0,\ \frac{\partial E}{\partial c} = 0 \]

以下、微分に関係ない定数$n$を左辺に移項して上式を実際に計算する。

\[ \begin{array}{ll} n\frac{\partial E}{\partial a'} &= \frac{\partial}{\partial a'}\sum_i^n \epsilon_i^2\\ &= \sum_i^n \frac{\partial}{\partial a'} \epsilon_i^2\ = \sum_i^n 2\epsilon_i \frac{\partial}{\partial a'}\epsilon_i\\ &= \sum_i^n 2\{a'\cos(x_i) - b'\sin(x_i)\ + c - y_i \}\cos(x_i)\\ &= 2\{a'\sum_i^n\cos^2(x_i) - b'\sum_i^n \sin(x_i)\cos(x_i) + c\sum_i^n \cos(x_i) - \sum_i^n y_i\cos(x_i) \} = 0\\ % n\frac{\partial E}{\partial b'} &= \sum_i^n - 2\{a'\cos(x_i) - b'\sin(x_i)\ + c - y_i \}\sin(x_i) = 0\\ &= -2\{a'\sum_i^n\cos(x_i)\sin(x_i) - b'\sum_i^n\sin^2(x_i) + c\sum_i^n \sin(x_i) - \sum_i^n y_i\sin(x_i) \} = 0\\ % n\frac{\partial E}{\partial c} &= \sum_i^n 2\{a'\cos(x_i) - b'\sin(x_i)\ + c - y_i \} = 0\\ &= 2\{a'\sum_i^n\cos(x_i) - b'\sum_i^n\sin(x_i) + cn - \sum_i^n y_i \} = 0\ \end{array} \]

上記3つの方程式は、パラメータ$(a',b',c)$のみに注目すれば3変数の連立一次方程式である。大学生の線形代数の知識を使って、以下のように行列の式にすれば簡単に解ける。

\[ \left[ \begin{array}{ccc} \sum_i^n\cos^2(x_i)& -\sum_i^n \sin(x_i)\cos(x_i)& \sum_i^n \cos(x_i)\\ \sum_i^n\cos(x_i)\sin(x_i)& -\sum_i^n\sin^2(x_i)& \sum_i^n \sin(x_i)\\ \sum_i^n\cos(x_i)& -\sum_i^n\sin(x_i)& n \end{array} \right] \left[ \begin{array}{c} a'\\ b'\\ c \end{array} \right] = \left[ \begin{array}{c} \sum_i^n y_i\cos(x_i)\\ \sum_i^n y_i\sin(x_i)\\ \sum_i^n y_i \end{array} \right] \]

STEP 3: 変数変換を戻して答えを求める

求まった最適な($a',b',c$)から$a = \sqrt{a'^2 + b'^2}$、$b = \tan^{-1}(b'/a')$として元の変数を求める。プログラム上では関数atan2を使うと$a'=0$の場合分けの必要がない。使い方は以下のとおり(ただし、a_=$a'$、b_=$b'$)。

b = atan2(a_, b_)

2012年8月22日水曜日

折古の浜

折古の浜の思い出

私は子供のころよく親に連れられて折古の浜に海水浴をしに行った。夏休みはおじいちゃんのいる因島で過ごすのが定番だった。海水浴が一番の遊びだった。折古の浜だ。折古の浜までは家から歩いて行かねばならない。車のとおる大通りをとおって、細い道を左折すると右手にお宮さん下の公園、左手に井戸の手押しポンプがある。正面の冷んやり涼しいトンネルを抜けると海が見え、子供たちのキャーキャー言う声が聞こえてくる。小さな浜だけど子供と大人で結構いっぱいになったものだ。

海水浴場の真ん中に水面からわずかに頭を出す赤い飛び込み台があり、ここから飛び込んだり、潜って飛び込み台をくぐり抜けたりして遊んだ。浜にはブランコが設置されており、満潮の時はブランコを思いっきり漕いでそのままジャンプすれば海に飛び込むこともできた。

海水浴からの帰りは、必ず同じ道をとぼとぼ歩いて、井戸で塩気を落とす。井戸水は冷たかった。体が小さく冷えやすかった自分はこの井戸水を背中から浴びるのはあまり好きではなかった。それでもポンプの棒を上下に動かして水を流すのは面白かった。ここで綺麗に洗っても、帰り道は濡れたビーチサンダルが道路の砂を跳ね上げ、足の甲とビーチサンダルの鼻緒の部分の隙間に入るからピリピリと痛かった。

子供時代の夏休み一番の思い出と言えばお父さんとお姉ちゃんと、この折古の浜で遊んだことだ。さんざん遊んで夏の暑さをしのぎ、家に帰ったら畳で昼寝するのだ。クーラーなんかなくてもなんとなく涼しい。

ゴミだらけになっていた折古の浜

大人になって折古の浜を訪れてビックリした。真夏だというのにブランコも飛び込み台もなくなっており、浜はゴミだらけになっていた。そういえば島で子供の姿を見ない。日立造船の撤退が原因か、少子化なのか?そもそもこの浜を利用する人が殆ど居なくなってしまったのだとか。とにかく島全体が廃れてしまった上に、なぜか交通量もそんなに多くないはずの道路がビックリするほど広くなってしまっていた。

田舎っぽい雰囲気が良かったのに・・・中途半端に便利にしようとしてもダメなんじゃないか。どうろを広くしたって経済が活性化する訳じゃない。田中角栄以来の通り一遍の政治が、因島の古き良きまでもダメにしてしまっているように感じた。「どうろを通せば国が豊かになる」、「公共事業をやれば経済が活性化する 」いうことが、いつまでも成り立つわけないことくらい、これまでの失敗例で分かっているはずなのに・・・・一時的な票欲しさに政治家が土建の幸せのみに最適化された行動をとってしまうのだろう。本当に大事な物を失ってしまった気がした。

問題は若者が島を出たがること

根本的な問題は島に、若者が居なくなっていることだと思う。少子化で相対的に少なくなるのは、仕方がない。国家レベル、もしくは世界レベルの課題だから。しかし、日本の若者が東京ばかりに行ってしまうことくらいは、食い止めれないものか。島に仕事なければ、出ていかざるをえない。その結果、優秀な人材は都会にでてしまい、優秀でない人のみが残る。その中から市長だの、町長だの選出しても良くなるはずがない。道を作れば経済がよくなる、ありもしない天守閣の水軍城を作れば観光客があつまるなどと騙されて、バカをやってしまうのだ。そして、大事な大事な因島の良さを失ってしまう。その結果、魅力はなくなり若者は出てゆき、悪循環は続いてゆくのだ。

似たような現象は高度成長期どこの田舎でも起こった。どれだけ多くの人の収入が倍増しても、自然と向き合う一次産業の収入は上がらない。その結果、田舎から都会へと人が流れたのだ。
そして、この成長がとまったとき、もぬけの殻になっていた。私は因島の折古の浜でそれを見たのだ。ショックだった。なぜ、そうなったのか考えた。瀬戸内海の海砂を大量に採掘しているから、浜の砂がなくなり人が減ったのだとか、護岸工事で水が汚くなってしまって人が減ったのだとか、色々聞いた。しかし、それは本質ではないだろう。必要とされていれば汚くなれば浄化しようと、砂が減らないようにしようと、人が動いただろう。そうではなく、そもそも、使いたいと思う人が居なくなってしまったのだ。色々考えても行き着く先は、島の問題ではなく、国家をどうすべきなのかとい根本的問題になる。

因島の問題ではなく、国家システムの問題だ

つまり、折古の浜がひと気のないゴミだらけの浜になってしまった根本的理由は、砂の流失やがけ崩れなどではない。人が都心部に集中して生活せざるをえない社会システム、つまりは国家そのもののあり方の問題だと思う。経済の問題というより国家ビジョンの問題だ。この国がどこえ向かうべきなのかという。

では、高度成長した日本を逆行すべきなのか?そうは思わない。高度成長は、技術力を中心に様々な不便を便利にしてくれた。高速道路のお陰で全国どこでも車一台あれば自由な時間に行けるようになったし、何よりもひもじい思いをすることもなった。しかし、これまで便利だけを求めてきたおかげで、既に十分便利になってしまった今、一所懸命お金を稼いで購入したいというものも無くなってきたのではないか。高画質のテレビが売れない理由は分かる。白黒がカラー化したときの感動はすごかっただろう。しかし、カラーテレビが多少高画質化しても大したインパクトはない。

では、どうすればいいのか?経済成長すれば田舎が廃れるし、そうでなければ不便な世の中に逆戻りだ。既に便利になってしまった今、何も国民全体が活動意欲を持つような向上は望めないのか?私は、これに対して一つのアイデアがある。もっと成長すべきだと思う。今足りないものは便利ではなく、豊かさである。高速道路は出来た、新幹線はできた、テレビも車も買った。でも、この汚くなった世の中に皆満足しているか?便利さに加えて我々が満足できる美しさ豊かさを求めるべきではないか。

便利さだけではなく、豊かさを

高速道路のお陰で山の風景は台無しだ。東京も、大阪も便利だがゴミゴミしてる。ヨーロッパに行って思った。なぜ街並みが綺麗なのだろうと。それははなから見た目を意識してデザインされているからだ。便利さよりも豊かさが重視されている。日本は違う。便利優先でデザインされている。全て。だから、ちぐはぐで汚らしい。田舎に帰れば、高度成長以前の姿が残っていることがある。とても綺麗だ。古い家は自然と調和しており、街並みも統一感がある。これこそ豊かさではないかと思う。

高度成長期、祖父、父親の世代が、戦争で失った便利さだけをひたすらに求めて、技術力のみを頼って、便利な世の中を作り上げてくれた。これからは、技術と芸術だと思う。同じ高速道路を作るにしても、日本固有の自然の風景にマッチしたデザインにすべきではないか?街並みも、我々の服装も、すべべの人の美的感覚が向上するくらい、国家政策として芸術に力を入れるべきではないか?技術を軽視するのではない。技術と芸術、これを両方強化するのだ。そして、我々の価値観がやがては単なる便利追求だけから、豊かさの追求が加われば、そこに向けて国民の行動力が注がれることになる。インターネットがありとあらゆるものを変えつつある。その場に居なくてもできる仕事も多い。田舎に居ながらにして、今まで行なってきた仕事が出来れば、便利さと豊かさを両方得たことにはならないか。技術を使う方向性をそういう方向に向ければいいのではないかと思う。

後で知ったのだが、驚くべきことに高度成長もとになった所得倍増計画を立案した本人、当時の役人下村治がすごいことを言っている。

「経済的に高い水準を達成したうえでのことだが、ゼロ成長の日本は江戸時代のような姿になるのがいい。経済の後には文化とか芸術とか教養に力を入れる時代になるべきじゃないのかな。」

なななんと、既にこれを予言していたのだ。便利になってしまったら、経済成長が難しくなることまで見越して、次の策を述べているのだ。こういう先を見越す考えが今の政治家に全く無い証拠に、民主党も自民党も震災をきっかけにして、国土強靱化などと称して、また50年前と同じ事をやろうとしてる。今度は安心だそうだ。安心という消極的なキーワードでどれだけの人が動くのか疑問だ。

私たちは、そろそろこの国家のありかたをもう一度一から考えなおさないといけない。

2012年8月10日金曜日

has_binary_operator.hppでwarning C4819が大量発生

boost 1.50を使うとファイルboost/type_traits/detail/has_binary_operator.hppの文字コードの問題でこの警告が大量発生する。

boost/type_traits/detail/has_binary_operator.hpp : warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。

問題は、このファイルの34行目。しかし、文字コードがダメなのかVisual Studioのバグなのか良くわからない。Visual Studio 2010で開くとこのように一見問題無さそうに見える。

//    warning C4547: '*' : operator before comma has no effect; expected operator with side-effect

Terapadで開くと「'*'」の次が文字化けしているのが分かった。

//    warning C4547: '*'?: operator before comma has no effect; expected operator with side-effect

これを別のスペースで上書きして対処した。

2012年8月7日火曜日

OpenCV(C++) 超々々入門

ヘッダファイルやライブラリの指定方法,画像のロード/セーブ,行列の操作方法のみに絞ってメモ書き.

Visual Studioのプロジェクト:
https://github.com/r168xr169/ForOpenCvSuperBeginner

インクルードファイルとライブラリ

正しくインストールされていればインクルード方法は至って簡単.

#include <opencv2/opencv.hpp>

ただし、ライブラリは使う機能に応じて指定する必要がある. 例えば下記のサンプルコードで使用されているimreadという関数は, OpenCV3.1.0ではopencv_imgcodecs310.libというファイルの中で実装されてる このライブラリを指定するためにVisual Studioではソースコード中に次のマクロを追加すればよい.

#pragma comment(lib, "opencv_imgcodecs310.lib")

しかし,単純なOpenCVのプログラムを書くだけでも複数のライブラリが必要なので上記方法で全部指定するのはかなり面倒. 今のところ このヘッダファイルを インクルードし必要なライブラリに対応する行だけコメントを外すのが今のところ一番簡単な方法.

画像のロードとセーブ

//ロード
cv::Mat_<cv::Vec3b> img_rgb = cv::imread("img000.png");

//セーブ
cv::imwrite("output1.png", img_rgb);

画素の参照、画素への代入

//画素(i, j)のrgb値を変数に代入
cv::Vec3b rgb = img_rgb(j, i);

//画素(i, j)のr値を変数に代入
uchar r = img_rgb(j, i)(0);

//画像(i, j)に色(0, 100, 200)を代入
img_rgb(j, i) = cv::Vec3b(0, 100, 200);

行列の初期化

cv::Mat_のinitializerを使う

意外と知られていないが便利な初期化機能.

//cv::Mat_の初期化機能
cv::Mat_<double> mat_ = (cv::Mat_<double>(3, 2) <<
    1.0, 2.0,
    3.0, 4.0,
    5.0, 6.0);

//cv::Matもcv::Mat_を介して初期化可能
cv::Mat mat2 = (cv::Mat_<double>(3, 2) <<
    1.2, 2.3,
    3.4, 4.5,
    5.6, 6.7);

配列のポインタに直接アクセス

配列の先頭ポインタdataを使って値をダイレクトに書き換えることも参照することも簡単.OpenGLのglTexSubImage2Dなど直接配列のポインタを渡さないと行けない場合など.
//memcpyでコピーしてしまう
cv::Mat_<cv::Vec3b> a = cv::imread("img000.png");    
a.data[0] = 0;
a.data[1] = 0;
a.data[2] = 255;

詳しくは

2012年7月22日日曜日

捨てれない人は優しい人

整理整頓のコツは要らない物を捨てることだという.全くその通りだと思う.ついついこれは捨てるの勿体無いなとか,そのうち使うかもしれないとか考えて置いておいてしまった物が整理整頓のじゃまになってしまう.

私は,どちらかというと整理整頓が苦手な方だが,全くできないという訳でもないのでいつも微妙な汚さを保っている.苦労しながら整理整頓しようと頑張っているという感じだろうか.捨てれない物がたくさんあるなかで,気づいたことは・・・捨てないということは物を大事にしているようにみえて,他の用途に使用できる可能性のある空間という目に見えないものを粗末にしているということだ.

しかし,本当にその考えでいいのだろうかと思い直した出来事がある.それは,部屋を整理整頓しようと二着の冬物コートを捨てるためにクローゼットから出して,床にとりあえず置いた後のことである.私は,少しつかれていたので床に腰を下ろして休息をとった.その時,おしりにふんわりと柔らかく暖かい感触を感じた.先ほど捨てようと床に放り投げたあまり着ていない冬用のコートである.必死で私にその着心地を訴えているように感じた.

そういえば,私は,小さい頃,自分のコップを床に落として壊してしまった時,泣いたことがある.母親に怒られるからではない.コップがかわいそうだと思ったからである.だってそのように教えられていたのである.全ての物には心があると.車や家を擬人化したアニメはいくらでもある.なんのためか.物を大事にするため,人の心を感じ取るためじゃなかろうか.そこらじゅうに見えない神様が居るとも教えられていた.2000年以上続く日本古来のからの教えである.

そんな自分が,整理整頓ために一度は自分が可愛いと思った物をポイポイと捨てられるだろうか?昔の人が物を捨てられないのは当然ではないか?着古した着物も縫い目をほどいて,その生地を再利用していたのである.ず~っとそうやって来た.なのに今,部屋が狭いという理由だけでその物を捨てられるだろうか・・・

捨てられないという性格は,単に整理整頓できないというバカにすべき性格というだけではなく,子供の頃に教えられた優しい気持ちを残しているということではなかろうかと思った.

2012年7月4日水曜日

裁判官さん、なぜこんなことが分からないの?

物事を文系と理系に分けたがる人がいる。

私は文系なので数学は分かりません。僕は理系なので経済には興味がありません。が、そこに本質的違いはない。強いて言うなら大学で勉強しなくてもよかった人が文系で、嫌々勉強させられた人が理系なのかもしれない。

Cambridge dictionaries onlineでscienceを調べるとこう書いてある。
the systematic study of the structure and behaviour of the physical world...

physicalは別に「物理学」という意味ではない。
芸術のように心にとって気持いいか気持よくないかを追求するのではなく、
この自分の外の世界をsystematicに調べるということだ。

つまり文系も理系も通常の大学で勉強していることはsystematicに事実を積み重ねた学問体系で、全部scienceなのだ。しかし、どうも裁判官という極めてscience的な頭が必要となる責任重大な職についている人ですら、まさに私は文系ですと平気で言ってしまうような人が多いような気がする。

裁判官が理解していない、もしくは理解していないふりをしている、ことが2つある。

1つ目は「逆」

AがBが同じものだと言い切るには、本来次の2つを示さないといけない。
・AはBだと考えて矛盾がないということ
・B以外にありえないということ

例えば、こういうことである。防犯カメラに犯人らしき人が写っているときに、カメラの画像に写るその人の顔の特徴が、被告人とぴったり一致したとする。このとき、カメラに写る人は、被告人と言えるだろうか?

答えはもちろんNo!

なぜなら他の人でもぴったり一致するかもしれないから。
つまり、AならばBを示した上で、BならばAという「逆」を示さないといけない!。

だが、鑑定書にこのような記述は多い。
「特徴が一致したので犯人と断定できる」と。

そして、それを証拠採用してしまっている。
裁判官でこの「逆」を理解している人がいるのだろうか・・・?

もう1つは「精度」

なんらかの数値を測ったときは必ず精度を示さないといけない。例えば、犯人の慎重は防犯カメラ画像から170cmだった。な~んて話があるだろうけど、その精度はいったいいくつ?±5cmの精度だったら証拠でもなんでもない。

落とし穴は、上の「逆」でも述べた「一致」という言葉。一致というのは、2つの数値を計測してその数値が近い値の時に一致という。全く同じになることはありえない。大抵の場合は誤差を無視しているだけで・・・

防犯画像Aの中の人の身長は170cm±3cmで
防犯画像Bの中の人の身長も170cm±3cmだった。

だからAの人とBの人の身長は一致しているので同一人物だ!!

もちろん、これも間違っている。インチキ鑑定書では±3cmという記述すらないのではないだろうか?

裁判官も分かっていないから鑑定した証人にこう尋ねる。

「つまり、一致しているのですか、していないのですか?」

誠実な証人は「○○%の確率で一致しています」と答える。
不誠実な証人は「一致しています」と答える。

そして、裁判官は、この不誠実な証人の自信満々な回答を聞いて、彼の方が信用できそうだ!と判断する。裁判員も同じだろう。

文系の裁判官はいらない

しかし、裁判とい現実では、かならずしもこうしたscienceだけで全てが分かる訳ではない。だからこそ、裁判官が曖昧な法律と曖昧な証拠から黒か白かを判断しなければならない重大な責任がある。ならばせめて、白黒がはっきりつくscienceを学んだ人が裁判官になるべきではないだろうか。

文系の裁判官はいらない。

Terminator2のここがおかしい

Terminator2は西洋文化の影響を受けた世界中の誰もが知る有名な映画で,自身も大好きな映画の1つである.中学の頃にはあまりにも好きすぎて何十回と繰り返し見ただけにとどまらず,英語で何を行っているのかディクテーションしてみたり,シュワちゃんがバイクに乗って銃をくるくると回転させる,あの動作はいったい何をしているのかと,スローモーションで解析したり,パート3が出るならば溶鉱炉に流れ込んだ液体窒素によって実は溶鉱炉の温度が低下しておりT1000は生きていた!なんて妄想してみたりした.

思えばストーリー的におかしい部分いっぱいある.撮影上のおかしい部分はここで挙げられているけれど,見え方の問題で(不自然な説明も含めて)なんとでも説明できる部分が多い.しかし,ストーリー上のおかしな部分は単に見え方の問題ではない.

なぜシュワちゃんは電話を切ったのか?

シュワちゃんがジョンを助けた後,育ての親を心配して(これもあれだけ反発してたならちょっと変なのだが)電話をかけた時,なぜか家で犬マックスが泣いているのが聞こえた。シュワちゃんは機転を利かせて,その犬の名前とは違う名前を出して「ウルフィーはどうしたの?」と親に化けたT1000に問いかける.T1000はまんまと引っかかって「ウルフィーは元気よ.どこにいるの?」と答えてしまう.これによってT1000が親に化けていることが判明する.シュワちゃんの賢さが分かるシーンである.問題はその直後.なぜかシュワちゃんは電話をガチャンと切ってしまう.「どこにいるの?」と聞かれているのだから,ロシアにいるとでも答えておけばよかったのに.シュワちゃんは方んとうにジョンを守る気があるのか?それとも機転を利かせた自分に酔ってしまってついついガチャンと切ってしまったのか?

なぜT1000は警察官の姿に戻ったのか?

サラ・コナーが収容されていた精神病院にT1000が来たとき,自販機前でおっさんを殺してそのおっさんに化ける.その後,なぜか警察官の姿に戻る.警察官の姿はすでにジョンとシュワちゃんに見られている.おっさんから警察官に戻らなければあっさり殺せたのでは?

なぜT1000はエレベータ天井を突き続けたのか?

病院でT1000に見つかりシュワちゃんとジョン,サラはエレベーターに逃げこむ.エレベーターに乗り遅れたT1000は,自動ドアをこじ開けエレベーターの天井に飛び乗り,自らの腕をナイフのような形状に変形させてエレベーターの天井を突き刺してジョンを殺そうとする.なぜ,その時どろどろに溶けてエレベーターの中に入ろうとしなかったのか?T1000はもしかしたらドSで,すぐに殺してしまうのは勿体無い的な感情に流されてしまったのか?

なぜ最初から溶鉱炉を目指さなかったのか?

パート1でターミネーターの電源は100年を超えるという説明がされていたように思う.T1000もちょっとやそっとでは電源は切れないだろう.だとすれば,T1000を殺さない限り永久に追われることになる.最初から溶鉱炉を目指してT1000を殺すつもりで罠にかけようとしないのはなぜか?

なぜシュワルツネッガーは溶鉱炉に入って自滅したのか?

T1000を溶鉱炉に突き落として平和をとりもどしたジョン達.シュワちゃんは,自らのチップが未来のターミネーターの開発につながると,自滅すべきことをジョンに伝えて,グッドサインまでして格好良く溶鉱炉の中に消える.でも,ちょっと待てよ.その後,ジョン達はどうすればいいのか?さんざんT1000が人を殺しているし,シュワちゃんも破壊しまくっている.ジョンたちが共犯だと思われたらジョン達は警察からも追われる身となる.使った武器を考えれば軍隊が出てきてもおかしくない.最悪,警察や軍隊に狙撃されるかもしれない.そんな心配も何も考えずに自分だけ格好良く消えたシュワちゃんは,本当にジョンを助ける気があるのか?ターミネーターは本当に存在した,サラ・コナーの心配は真実だと,みんなに分からせてから消えた方が良かったのでは?

リンカーンの言葉

第16第アメリカ合衆国大統領Abraham Lincolnの言葉

... government of the people by the people for the people, ...

は、「人民の人民による人民のための政治」などと訳されて日本でも有名な言葉。 中学の時に英語の授業で習った覚えがある。 その時、すぐに感じた疑問は「of the people」と「for the people」何が違うのかという事だった。 正直未だによく分からないし、よくわかっていない人(明確に答えられない人)の方が多いのではないか。 学校で教わったような「of」は所有、「for」は目的?などという説明ではちんぷんかんぷん。 これを正確に答えるためには、peopleとされる人に何ができるのかという権限と絡めて説明しないと意味が無いと気づいた。

今、これと同じように国民に与えられた権限とは何なのか考えさせられる問題として、外国人の参政権がある。 私の感覚からすれば、なぜそんな大事なものを外国人に与える必要があるのかと、むしろ疑問に思ったのだが、 これを訴える人の言い分を聞けば気持ちは全く分からないでもない。

参政権を与える理由として、まず第一に挙げられるものは納税である。 長いこと日本に住んでいて納税しているもはや日本人と同じように義務を果たしているのだから参政権を与えてもいいじゃないかと。

気持ちは分からないでもないが、納税と参政は全く独立なものである。 我々普通の日本人が海外旅行に行けば税金を払わされるけど、 だからと行って少しも海外の国の政治に携わることはできないし、 税金を払っていない日本人にも日本国の参政権はある。 納税をするのは公共サービスを受けるための利用料だと思う。 例えて言うなら、飲み屋でいくら金を払ったからと言って、 その店のお酒の値段を決めたりできる経営者やオーナーには成れないのと同じではないか? この場合、オーナーの、店員による、客のための飲み屋、かな。 この飲み屋の登場人物が全部、人民(=国民)に置き換えられたのがリンカーンの言葉だと考えれば、 「of」と「for」の違いが明確になる。 つまり、for the peopleの「for」はサービスを受ける主な対象を指しているのかなと思う。 「of」は構成員で人民から選出された人が政治・行政を行うということ。 「by」は当然、その選出は人民が行うということ。

要するに今の日本を踏まえてLincolnの言葉を意訳するとこうなるのかな。

... government of the people by the people for the people, ...

... 選出された国民で構成され、二十歳以上の国民全員の選挙による、国内に居る人全員のための、あ政府

「すべての電子機器の電源をお切り下さい」は本当か?

なぜ誰も疑わないの?

前々からおかしい、おかしいと思っていたことがある。

少なくとも日本のメディアで取り上げられているのは見たこともないし、周りの人間が同じようなことを言っているのを耳にしたことはない。些細なことかもしれないが、海外出張するたびに不思議に思うことである。

それは、飛行機のセキュリティ。電波を発する機器やその他の電子機器の使用まで制限される、あれである。タバコのように最長十数時間の我慢くらいいいではないか、と思われるかもしれないが、私が危惧することはそんな些細なことではない。

スッチーが「計器に支障をきたす恐れがあります・・・」と言う。

これ、ほんまか?

飛行機が微弱電波で誤作動するなら欠陥商品では?

なぜそれを疑わしく思うかというと電波を発する機器やその他の電子機器で、飛行機の計器などに事故につながる重大な影響を及ぼし、それが設計上避けられないことであるなら、なぜ電子機器の塊である自動車で同じことが言われないのか?電子機器が自然と発する微弱電波で誤作動するなら、それは欠陥商品ではないのか?

飛行機は自動車よりも上空を飛んでいるのだから、宇宙線(宇宙からくる放射線)の影響が地上より大きいはずである。宇宙線は半導体などの電子機器に入ると半導体内で電子をまき散らして誤作動を誘発することがあるという。宇宙船ではこれが起きないように、起きても大丈夫なように設計されている。飛行機ではフライトアテンダントは被爆するという。ということは機内まで宇宙線粒子が飛び込んできているのである。その宇宙線よりも電子機器が発する電波のエネルギーは低いはずなのに、なぜ?

パイロットの無線通信に悪影響があるのだと言う説明も聞いたことがある。そうかもしれないが、そうだとすればスッチーは嘘をついている。無線通信機器は、計器ではない。

全員が電源OFFに出来る訳がない

飛行機の詳細な構造は分からないけれど、旅客機の製造がアメリカのボーイングと欧州のエアバスの2社によってほぼ独占されているので、この2社がそうだと言えば誰も反論できないのではないか。しかも、乗客全員が全ての電子機器の電源をオフにできるとは思えない。かならず電源を切り忘れている人がいるだろう。それは、搭乗前のセキュリティゲートで金属探知機に引っかかる乗客が絶えず居ることでも分かる。金属を外せばいいだけであるのに、(自分も含めた)大衆というのはそういう簡単なことが出来ないものである。人数が多くなればなるほど少なくとも一人が電源を切り忘れる確率は高まるのである。

では、なぜこんな無理なことを要求するのか?

飛行機が落ちた時、客のせいにするためでは?

私は、飛行機が落ちたとき、大事故が起きたときにその責任を乗客になすりつけるために考えられた、「嘘」ではないかと疑っている。2社が結託して、航空会社に対し乗客が全ての電子機器を切るように指導することを契約書の保証条件の中に含めてしまえば、誰も逆らえないのではないかと。他の航空機メーカーや航空会社にとっても自分たちの責任の一部を乗客に転嫁できる旨みがあるので積極的に反発する理由もない。乗客を守るための最低限のマナーなどという偽善的匂いのプンプンする言葉でもって、嘘をついているのではないかと。

そもそも、本当に飛行機が墜落するくらい危険なものであるなら、危険物を調査するあの面倒くさいセキュリティゲートのように、本気になって全員が電源OFFにしているのか、微弱な電波が出てないかチェックするはずではないか?それを全くやらずに電源をOFFにしろという。確かにOFFにしたはずの携帯の電源が他の荷物との接触で電源ONになり、電波全開になってしまっていた事があるが、機内のスタッフからはまったく指摘されなかった。

日本が飛行機を作るべきだ

ついでに言えば、50年経っても未だに飛行機の構造やサービが根本的に変わらないのも、その独占に原因があるのではないかと思う。戦後日本の飛行機技術がアメリカによって叩き潰されていなければ、日本車のように高性能で低燃費の飛行機が世界中を飛んでいたのではないかと思う。我々は、未だに燃費効率の悪いデカイだけのアメ車に乗らされているのである。

昨年だったろうか同盟国であるはずのアメリカは、日本に最新の戦闘機を売らないと言ってきた。これは日本にとって大きな痛手あると報じられた。しかし、私はこれはチャンスだと思う。これまで戦闘機を作らせてもらえなかったが、日本が独自に作る大義名分を与えられたと考えればよい。実際、すでにステンレス性能をもつ戦闘機の試作機が完成しているらいしい。これらの技術が民間に降りてゆき、日本が旅客機製造競争に参戦できる日も来るのではないか。

sprintfの代わりにboost::format

C++を使い始めてもなかなかsprintfを手放せない・・・
#include<cstdout>
char filename[256];
sprintf(filename, "test%02d.ppm", i);
cv::imwrite(filename, image[i]);
boost::formatを使うと簡単で覚えやすくてよい。
#include<boost/format.hpp>
string filename = (format("test%02d.ppm") % i).str();
cv::imwrite(filename, image[i]);

glMultMatrixを用いたHomography変換

OpenGLのglMultMatrix関数を使えば、ピンホールカメラモデルであれば任意の投影が扱える。そんな原理は頭では理解しているけれど、いざ実際にプログラムを作ろうとすると色々分からないことがあることに気づく。殆どはOpenGLを正確に理解していないことが原因だった。いったい何を理解していなかったのか?

何がしたかったのか?

プロジェクタを用いて平面に正対した像を投影するプログラムを作ろうとした。平面からプロジェクタへの投影行列であるHomographyは、OpenCV等を用いて正しい物が簡単に求まったにも関わらず、OpenGLのglTranslateとglMultMatrixを用いて平面上の任意の位置にあるマークを表示しようとして時間がかかってしまった。

配列と行列の関係

まずは、glMultMatrixd関数を使う上で気をつけること。この関数は、glMultMatrixd(GLdouble *)となっているので、配列のポインタを渡す必要がある。画像を配列で表す場合は、通常スキャンライン(水平)方向に画素をたどって、順に配列に格納するよう実装するが、4x4の行列を配列で表す場合もこうすることが多いように思う。OpenGLは特別で、下記のように行方向(垂直)に要素をたどり配列に格納する。ただし、これは点の位置をベクトルで表す場合に、縦ベクトル(左から変換行列を掛けるとする)か横ベクトルにする(右側から変換行列を掛ける)かの違いかもしれない。いつもこれを考えるとき混乱します。頼むから統一してくれ・・・

// 行列の定義はこれ。
cv::Mat_<double> m(4, 4);
double m_tmp[16] = {
    m(0, 0),    m(1, 0),    m(2, 0),    m(3, 0),
    m(0, 1),    m(1, 1),    m(2, 1),    m(3, 1),
    m(0, 2),    m(1, 2),    m(2, 2),    m(3, 2),
    m(0, 3),    m(1, 3),    m(2, 3),    m(3, 3)
};
glMultMatrixd(m_tmp);

「行列の積の順序」と「関数の記述順序」の関係

例えば、ユークリッド変換(回転+並進)は回転行列$R$と並進ベクトル$\mathbf{t}$で次のように書ける。 \[ \mathbf{x}'=R\mathbf{x} + \mathbf{t} \] さらに並進を同次座標表現の行列で表すとこうなる。 \[ \begin{bmatrix}\mathbf{x}'\\1\end{bmatrix}^T= \begin{bmatrix} O& \mathbf{t}\\ \mathbf{0}^T& 1 \end{bmatrix} \begin{bmatrix} R& \mathbf{0}\\ \mathbf{0}^T& 1 \end{bmatrix} \begin{bmatrix} \mathbf{x}\\ 1 \end{bmatrix} \] このとき、式中では左から並進、回転、$\mathbf{x}$の順に現れるが、コードでも下記のようにこの順に書く。計算する順序は、まず回転行列を掛けて、並進行列を掛けるので、イメージ的に逆にも考えることができて、これまた混乱することが多い。

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslated(t[0], t[1], t[2]);
glRotated(theta, r[0], r[1], r[2]);
glVertex4d(x[0], x[1], x[2], 1.0);

GL_MODELVIEWとGL_PROJECTIONどっち?

OpenGLなどの3D-CG可視化ツールは、カメラの外部パラメータを表すModelView行列とカメラの内部パラメータに相当するProjection行列に分かれている。しかも両者4x4の行列でほぼ同じ。しかし、目的のHomographyはそれら両方を内包している行列なので、どちらにHomography行列をLoadすればいいのか少し迷った。単純な方法は、Projection行列を正射影(単位行列)にし、ModelView行列にHomography行列を代入する方法。mは行列の各要素が格納された配列へのポインタである。

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMultMatrixd(m);
glVertex4d(x, y, z, 1.0);

実際のコード

次のコードはC++でOpenCVを使用している。

// homography行列の定義はこれ。値は何からの方法で計算する。
cv::Mat_<double> h;
// ウィンドウ全体をビューポートにする
glViewport(0, 0, w, h);
 
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
 
glOrtho(-0.0f, w, h, 0.0f, -1.0f, 1000.0f);

//3x3のhomography行列を4x4の行列に代入する(結局$z$座標が無効化されいてるだけ)
double h_tmp[16] = {
    h(0, 0),    h(1, 0),    0,    h(2, 0),
    h(0, 1),    h(1, 1),    0,    h(2, 1),
    0,          0,          0,    0,
    h(0, 2),    h(1, 2),    0,    h(2, 2)
};
 
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMultMatrixd(h_tmp);

OpenCV2サンプルプログラムcalibration.exeの出力ファイルの読み方

このページの情報は最新ではありません。最新の情報はOpenCV 3.0.0サンプルプログラムcalibration.exeの出力ファイルの読み方を御覧ください。


たぶんどこかにに書いてあるんだろうけど、メモ。(OpenCV2.29でテスト済み)
cv::FileStorage cvfs("../data/out_camera_data.yml", CV_STORAGE_READ);
cv::FileNode node_top(cvfs.fs, NULL);
 
cv::FileNode node_intrinsic = node_top[string("camera_matrix")];
cv::Mat intrinsic;
cv::read(node_intrinsic, intrinsic);
 
cv::FileNode node_distortion = node_top[string("distortion_coefficients")];
cv::Mat distortion;
cv::read(node_distortion, distortion);
 
cv::Mat dx, dy;
 
CvMat intrinsic_ = intrinsic;
CvMat distortion_ = distortion;
CvMat dx_ = dx;
CvMat dy_ = dy;
 
cvInitUndistortMap(&intrinsic_, &distortion_, &dx_, &dy_);

VisualSVNリポジトリの移動

VisualSVN Server v1.6.2上での操作

  1. コンソールツリー(左の枠)を表示
  2. VisualSVN Serverというアイコンをクリック
  3. Start a new instance of command interpreterというボタンが現れるのでこれをクリック

コマンドプロンプトでの操作

コマンドプロンプト上で以下のようにリポジトリを1つずつダンプして,別のリポジトリに1つずつloadするだけ.

まず、ファイルに書き出す。

svnadmin dump c:\svnroot\test > c:\test.dump

そして、 ファイルから読み込む。

svnadmin create c:\svnroot2\test
svnadmin load c:\svnroot2\test < c:\test.dump

セカンダリモニタでフルスクリーン

libQGLViewerライブラリでOpenGLのフルスクリーンが簡単にできると知って飛びついてみたものの,セカンダリモニタへのフルスクリーンには対応していなかった.

Webで調べると一番多いのがGetSystemMetrics()に入れる引数を駆使して, セカンダリモニタのサイズを計算しようとするものであるが, GetSystemMetrics()で取得できるのは全てのモニタを含む大きな枠の座標のみで, 2番目以降のモニタのスクリーンサイズは取得できない. そのため,スクリーンサイズの計算は間接的で, 直ぐに破綻する何らかの仮定(例えばスクリーンのアスペクト比が固定値など)が必要. せっかく調べて実装するのだから,直接スクリーンサイズを得る方法を探した.

正解は,EnumDisplayMonitors()とGetMonitorInfo()を使ってスクリーンサイズを取得し, SetWindowLong()で枠をなくし,SetWindowPos()でスクリーンにぴったり張り付くように配置する方法である. 今回Qtを使って実装していたので下記のようにまず, ウィンドウのデバイスコンテキストを取得するコードになっています.

RECT bounds;
 
//callback function from EnumDisplayMonitors()
BOOL CALLBACK MyInfoEnumProc(
    HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
    MONITORINFOEX mInfo;
    mInfo.cbSize = sizeof(MONITORINFOEX);
    GetMonitorInfo(hMonitor,&mInfo);
    
    if(mInfo.dwFlags == 0)
    {
        //bounds is global variable available only in this file
        bounds = mInfo.rcMonitor;
    }
    return TRUE;
}
 
// set full screen in the second monitor
void ProjectorWindow::setFullScreen(bool fullScreen)
{
    int n_monitors = GetSystemMetrics(SM_CMONITORS);
    if(n_monitors < 2){
        return;
    }
 
    EnumDisplayMonitors(NULL, NULL, MyInfoEnumProc, 0);
 
    // Obtaining a handler for the window of the current context
    // (in English: getting a reference to the current OpenGL screen)
    HDC dc = GetDC(this->winId());
    HWND hwnd = WindowFromDC(dc);
 
    SetWindowLong(hwnd, GWL_STYLE, WS_POPUP|WS_VISIBLE); 
 
     // Placing the window with the bigger dimensions to cover the whole
    // multi-monitor screen
    SetWindowPos(hwnd, NULL, bounds.left, bounds.top, bounds.right - bounds.left + 1,
            bounds.bottom - bounds.top + 1, SWP_FRAMECHANGED);
}

ただ,モニタの情報が欲しいだけで何で関数ポインタなぞ使わないといけないようにな設計になっているのか理解出来ない.MSDNの説明も相変わらず分かりにくいし.早くMicrosoftから独立しなければ!!

http://msdn.microsoft.com/ja-jp/library/dd162610(v=VS.85).aspx

windows xpでsvnサーバのアカウント管理


目的
WinXP Pro(SP3)上のVisualSVN Serverで複数のアカウント管理する
各ユーザ自信がsvnのパスワード発行後変更できるようにする

問題
すでにhttpdがインストールされていてアカウントuser000をログオフするとサービスが停止する
user000以外のアカウントでリモートデスクトップ接続するとuser000が強制ログオフされる
XPはドメインに参加していなければリモートデスクトップでのログインなしに遠隔からパスワード変更ができない

方針
  • VisualSVN ServerのアカウントをWindowsのアカウントに設定する
  • Cygwinのsshdをインストールし,遠隔からリモートデスクトップなしにログインできるようにする
  • ログイン後Windowsのコマンドラインでパスワード変更
セットアップ手順
  1. アカウントuser123をAdministratorグループで追加する
    (PowerUser等ではsshからパスワード変更できなかった)
  2. VisualSVN Server (2.1.5)をインストール
  3. svnのアカウントをWindowsのアカウントで管理する設定にする
  4. svnにインポート,ユーザ設定する
  5. Cygwin+OpenSSHインストール
  6. Cygwinコンソールでsshdセットアップ
    > ssh-host-config> net start sshd
  7. WindowsユーザをCygwinに追加する
    mkpasswd -l -u user123 >> /etc/passwd
ローカルセキュリティポリシーでリモートデスクトップ接続できないアカウントを指定する
「ローカルポリティー > ユーザ権利の割り当て > ターミナルサービスを通したログオンを拒否する」をクリック
リモート接続させたくないユーザuser123を追加する
パスワード変更方法
 クライアントから以下のコマンドで変更する
> ssh user@server
> net user user123 [password]

報道を信用しなくなった出来事

報道の信頼性をどのようにお思いでしょうか?

確かにたまには誤報はあるだろうけど、大手メディあであれば基本的に正しいことを報じているんじゃないの?これが多くの人の感覚ではないだろうか?

しかし、私の感覚はある出来事を境に全く変わった。はっきり言ってそんな生ぬるいことではない。報道を信用しない人も多いだろうけど、その多くは又聞き情報に基づくのではないだろうか。私の場合は、又聞きではなく実際に自分が体験した出来事がきっかけである。

その出来事とは、読売テレビからある事件の取材を受けた時のことである。私は、デジタル画像を解析する職業だったため、当時起きた大阪でのひき逃げ事件の防犯カメラ映像を解析できないかと依頼が来た。正確には私の上司への依頼である。

1時間で鑑定しろ

依頼内容は、防犯カメラに移った犯人と思わしき車の車種を特定してくれというもの。そのために職場に持ち込んだ映像データの扱いの素人っぷりにも驚いたのだが、何よりも驚いたのはそれを2時間後に放送するから1時間くらいで結論を出してくれということだった。いい加減なことは言えないし、映像だけで車種が特定出来るわけがない。車のサイズや形などのデータが必要だし、防犯カメラの取り付け位置や画角などの情報も重要である。

それなのに2時間後に放送?我々は占い師か?

「テレビ局=映像のプロ」ではない

しかも持ってきた映像データは、DVD-Video形式といってMPEG2圧縮がかかったものだった。防犯カメラ映像は、おそらく既に映像に圧縮がかかっており劣化している上に、さらにMPEG2で圧縮されれば本来の防犯カメラの映像の品質は著しく低下する。しかも夜の映像だったから、品質劣化は極端に悪くなる。物の輪郭や色すらわからなくなってしまう。そんな素人でも知っている常識を知らずに、データを持ってきているのである。

結局、大したことは分からずに、分かる範囲内で上司がニュース番組の解説をした。言えることといえば、車の色は黒か紺、車種はワゴン、などという素人でもその映像を見れば分かることだ。しかし、それが科学的な答えだ。いい加減にとにかく答えを出すことが科学ではなく、分からないことは分からないと答えることが科学者、すなわち真実を追求する者の答えなのだ。

だが、私が経験した出来事とはそれだけではない。

2回目の取材

再び同じ取材班から取材を受けることになったのである。しかも、今度は上司ではなく私自身に。それは、そのひき逃げ犯が捕まり、ひき逃げに使われた車種が分かったとき。私の上司はその時出張中で、私自身に電話がかかって来た。取材をしたいという。コメントして欲しいという。しかもまた、2時間後くらいに放送するので1時間後には、我々の職場に向かいたいという。

その電話を受けて、私は、本当に頭の中がクエスチョンマークだらけになった。そして聞き返した。

私:「もう、犯人は捕まったんですよね。車種も分かったんですよね。私は何をコメントすればいいんでしょうか?」
取材班:「車種は分かりました。ですから、もう一度画像をテレビに写して、この車種で間違いないとコメントして頂くだけでいいんです。」
私:「はぁ・・・そんなもんですか。」

というやり取りで取材を引き受けてしまった。私は、何かおかしいと思いながら、10分ぐらい考えた。やっぱりおかしい、結論ありきでコメントしろと言ってるだけじゃないか。もし、警察がやっぱり間違えてました、犯人は別に居ますとなったらどうなるんだろう。私は、ただいい加減なことを言うだけの、人間になってしまうじゃないか。

そう思って、再び電話しやはりそういうコメントは無理だと断った。

これは取材ではない

今から冷静に思えば断って良かったと思う。何のためにそんな意味のないコメントを求めるのか、しばらく考えた。要するに、自分たちの報道の責任を学者や専門家になすりつけてるだけじゃないか。当事者になってみれば、なかなか冷静な判断ができずに、こういったインチキ取材でもテレビにでれるということで舞い上がってしまって引き受けてしまう人はいるのではないか。

でも、よくよく取材をしている側の気持ちになれば分からないでもない。おそらく、毎日何かしらのニュースを撮ってこなければならないのだろう。撮ってこれなければ上司に怒られる。なんでもいいから意味ありげなコメントが撮れればいい。最終的には、自分たちの責任でもないし。

大手メディアを特別視するな

私は、読売テレビだけを非難するつもりは全くない。おそらく他のメディアにもそういう取材をする人はいるものだろう。逆に、読売テレビにも立派な人はいるはずだ。ただ、あまりにも上記のようないい加減な取材をいとも自然に申し込んできたことを考えると、常態化してるのではないかと思う。はっきり言って、こういうやり方をしていれば、誤報など時間の問題である。大手メディアとはいえ一個人の情報発信だと思わなければならない。

確かに、そう言えば、テレビで専門家がコメントするシーンがよくある。でも、あれに引っかかってはいけない。専門家だからといって正しい訳ではないのである。 お金をもらっていい加減な分析を公表する専門家もいる。機会あれば、このこともこのブログに書きたい。

結局は自分の責任だ

そんな、いい加減な情報の中で、大事なことは自分自信で納得できるかどうかではないだろうか。自分が納得したことは自分の責任で,それを自分の行動に繋げなければならない。だからこそ、誤報であっても文句を言うべきではない。信頼できなければ見なければいいのである。誤報にクレームをするから、やがてメディアは誤報を隠すようになるのである。

選挙に行っても・・・は間違いだと気付いた

政治に全く興味がない頃、自分が選挙に行っても結局ただの一票だから意味が無い。自分が行っても何も変わらない、と思っていた。でも最近それは間違いだと気付いた。

投票率が上がっても割合は変わらないのでは?

普段政治に興味のない人間が投票して、国民全体の投票率があがったとしても、結局政党間や立候補者間への投票数の割合が変化しなければ、結果は変わらないんじゃないか?と考えることもできる。私もそう思っていた。実は、これは全くの間違い。

なぜなら組織票があるから。組織票とは、 会社や宗教団体など特定の利害を持つ人間の集団内で、どこどこに投票しなさいなどの圧力をかけて、とにかく個人の意志とはあまり関係なく投票させる票のこと。この票数は、日本全体の投票率が上がってもあまり変化しない。逆に投票率が低いと組織票の割合が増え、その組織にとってだけ有利な方向に政治が向かってしまう。一部の人間の利益だけを代表する人の意見が、増幅されてしまうことになる。結果、いわゆる一般人の意識と政治がかけ離れていく結果につながる。

私が投票してもでたらめな票を増やすだけでは?

とは言っても、私にはやはり政治は理解出来ない。私みたいな理解していない人間がいい加減に投票しても、本来政治をよく理解している人だけで投票した結果を乱すだけのようなもの。よく理解している人だけが入れればいいんじゃないか?これはある意味正しいかもしれない。よく理解せずに間違って自分の想像しているものとは違う方針の政党や立候補者に投票してしまうかもしれない。そうなれば、投票しなかったほうが良かったということになる。

それでも投票に行く意味

それでもなお、投票に行くべき理由がある。 いい加減な票を入れれば、結局その結果変な政治になって損害を受けるのは自分たちで、この国の最終責任者は自分達だから。自分が票を入れるということはそういう事だと意識して投票しに行くこと自体が、結果的に責任の意識もつことになる。

それでもなお、どこに入れれば良いのか分からなければ白票を入れればよい。白票だって立派な投票である。分からないから他の人に託す。その結果どうなっても自分が責任者である。という意思表示なのである。 加えて,選挙では他党や他候補に対する自分の票の割合だけでなく、得票数(自分に何票入ったか)自体も気にしている。なぜなら、投票しなかった人が次の選挙でどの政党に入れるかは、自分たちの政治生命に関わることだから。投票しなかった人が選挙に興味のない人なのか、興味はあるけど何処にも入れないと白票でメッセージを残した人なのかは彼らにとって大違い。やはりプレッシャーを与えることができる。

首相官邸にメールすればいい!

選挙制度に色々問題があることはご承知のとおり。現在に小選挙区制では、自分の選挙区に入れたい人が居ないということも良くある。テレビに出てしっかり発言しているあの人に入れたいと思っても入れれないのである。国政選挙なのに、なぜか地元に利益を還元する人を応援するようになるのはそのせいだとも思う。

しかし!!!私達に選択肢は残されている。路上でデモ行進などしなくてもちゃんと国民の意見を伝える場がある。選挙なんてネットの概念もない時代に作られたものだけど、今は、私達にはネットがある!そう直接、首相官邸にメールすればいいのです。試しに送ってみたら分かるけれど、ちゃんと読んでくれていて、関係大臣のところに転送してくれる。これはおかしい!!ってのがあれば迷わず首相官邸にメールしよう!強引にものを進めようとしても民意がついてきていないと分かれば怖気づくんです。

参考

ノートPCの無線LANをアクセスポイント化


ノートPCがUSB接続の3Gモデムが刺さっている状態で無線LANをアクセスポイント化する。

使用機器
  • Let's note CF-S10, Window 7 Pro (64bit)
  • Ht-03a, android 2.3.2-ez Gingerbread
  • Nokia CS-17 internet modem
操作手順
基本的には、このサイトに従ったが多少工夫したので全部含めて記録しておく。
http://www.atmarkit.co.jp/fwin2k/win7/12wlan/12wlan_02.html
  1. コントロールパネル→ネットワークとインターネット→ネットワークと共有センター→アダプター設定の変更
  2. モデムを接続している状態で、DNA internetのアイコンを右クリック
  3. コピーの作成(コピーを作成しておかないと非接続時はアイコンが消える)
  4. 管理者権限でコマンドプロンプトを開く(Microsoft Virtual Wifi Miniport)
  5. netsh wlan set hostednetwork ssid=sample key=aabbccdd keyusage=persistent
  6. アダプター設定の変更ウィンドウ内に新しいアイコンが生成される
  7. netsh wlan set hostednetwork mode=allow
  8. netsh wlan start hostednetwork
  9. DNA internetのアイコンを右クリック→プロパティ→共有タブ
  10. ネットワークの他のユーザに、このコンピュータのインターネット接続を通しての接続を許可する、にチェックを入れ、Microsoft Virtual Wifi Miniportに対応するネットワークアダプタを選択する

legacy/compat.hppでwarning C4819

問題

OpenCV2.2や2.3をSVN経由で取得してコンパイルすると、こんな警告が多発。

opencv2/legacy/compat.hpp : warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。

こちらのサイトで書かれているとおりファイルを少しいじって適当なエンコードでセーブしなおしても、この問題は治らない。

http://tessy.org/wiki/index.php?C%2FC%2B%2B%A4%CE%A5%C8%A5%E9%A5%D6%A5%EB%BD%B8#e785e7d6

(このサイトではWarningそのものを消す方法が書かれています)

原因

compat.hppの493行目の文字コードがおかしいようです。

/*  shift  - direction ( it's value must be one of the CV_SHIFT_Ö constants )  */

解決策

この行のCV_SHIFT_Oの部分がおかしいけれど、コメント部分なので適当に違うもので上書きしてやればいいのかと思います。 例えば、terapadで開くとなぜかちゃんと?認識してくれますので、そのままエンコードを指定して保存すると治りました。

/*  shift  - direction ( it's value must be one of the CV_SHIFT_O constants )  */

環境

  • VS2008 Pro
  • Windows7 Pro 64bit

王様のタイル貼り


ある王国の王様はとても権力があり,国民にも好かれていました.王様は何でも自分でやってみるのが好きで,パンを作ることや麦を植えること,ナイフを研ぐこと,草刈,掃除と色んなことに挑戦していました.それらは王様にとって,とても楽しい事だったのですが,どうも楽しさが足らないような気もしていました.

ある日,王様がふとお城の壁を見たところ,そこに不規則に貼ってある白と黒のタイルの模様がかわいいウサギに見えてきました.これはすごいと,他の部分も何かに見えないかと探してみましたが,どうも動物などに見える模様は無かったので,もう一度さっきのウサギを見てみました.すると,さっきまでウサギに見えた模様が見方を変えるとカニにも見えてきました.もっと違った見方でタイルを眺めようとすると女の人にも見えるではありませんか.王様はうれしくてうれしくたまりませんでした.そして一晩中,なぜ,あの不規則な模様がウサギに見えたり,カニに見えたり女の人にまで見えるのか考えました.眠れないので,紙とペンでウサギに見えるように黒と白の四角を描いてみましたが,どうみてもそこにはかろうじてウサギに見える模様が1つあるだけです.何度も何度も模様を描いてはその不思議な現象の理由を見つけようとしました.そして明け方,ついに王様は自分なりの結論に達しました.ウサギに見えるように模様を作るのではなく,何も考えずに不規則な模様を描けば偶然に色んな物や動物に見えてくるのだと.

王様は,自分でお城の壁のタイルを貼りたくなりました.お城を囲む高くて横に長い塀に黒と白のタイルを貼ることにしました.家来に黒のタイルも白のタイルをいっぱい用意させました.王様は,なるべく不規則な模様になるように,左上から順番に下に貼ってゆき,一番下までゆくと,またその右隣に上から順に張り続けるのです.もちろん黒と白のタイルには,選び方もなく,無作為で決めてゆきました.ある程度貼り進めたので,ちょっと離れて休んでいると,期待どうり模様は何か渦巻きのように見えてきました.もっとよく見ると風車のようにも見えなくもありません.王様は大喜びです.王様の考えは正しかったのです.その日から,毎日毎日,タイル貼りに明け暮れました.

そして,だんだん横に伸びてゆくタイルの模様に,国民も気がつくようになりました.ある人は,王様は自然の風景を描いているのだと思いました.また,ある人は,王様の貼るタイルの模様は,人々が踊る様子を表していると思いました.次々に出てくる模様に,王様と同様国民も楽しみました.そして国民は,だんだんと次に出てくる模様を早く見たいと思うようになりました.たぶん,なすびとトマトが出てきたので,次はピーマンではないか?馬とラクダだから次は牛じゃないか?国民は次の模様が知りたくて知りたくたまらないので,こんなふうに王様が何を考えて,模様を描いているのか想像するようになりました.もちろん,誰にも分かるはずはありません.王様は,そんなことを考えてタイルを貼っている訳ではないからです.

ところが,そんなタイル貼りが続いているとき,一人の占い師が国民に,「私は,王様が次に何色のタイルを貼るか当ててみせましょう」と言いました.国民は始めは信じませんでしたが,その占い師は本物でした.確かに王様はその占い師が予言したとおりにタイルを貼り付けていたのです.

王様にもその予言の話は伝わりました.当然,信じられないので,占い師を呼び出し予言させました.占い師は,10年先のタイルまで予言してそうすると自分が貼り付けたタイル,確かにその予言どおりに貼っていたのが分かりました.王様は,びっくりしましたがうれしくもありました.今まで一人でタイル貼りをしていましたが,この預言書があれば,家来達に手伝わせることができ,もっと早く色んな模様に見ることができると思ったからです.その日から王様はその預言書どおりにタイルを貼り付けるようになりました.

今までと同じようにタイルの模様は色んなものに見えましたが,国民はみんな次にどんな模様が現れるのか預言書を見て知っています.国民はだんだん次に現れる模様を想像するのが面白いと思わないようになりました.王様も同じです.そうしてタイル貼りはいつの間にか止めてしまいました.お城の塀の模様は,まだ残っていますが,その続きのタイルを貼ろうとする人は誰もいません.だって,次に何色のタイルを貼れば,どんな模様がでてくるか,みんな知っているからです.タイルを貼る必要もないのです.

牛乳の皮膜に住んでいる

電子レンジでチンした熱々の牛乳を見たことあるだろうか? 表面に皮膜ができることはよく知られているが、その皮膜が絶えずぷくぷく動いていることはあまり知られていないのではないか。

動く理由はもちろん牛乳の熱による対流である。 これを見たときふと、似たような現象を思い出す。

それは、地球の地殻変動である。 簡単にいえば地面が動いているのである。地球の内部はものすごく熱くて岩石がドロドロに溶けた状態なのであろう。 そのドロドロの岩石が地球表面で冷めていく過程で対流が生じ、その対流に沿った方向に地殻が流されいるというのである。 東北大震災もこのような地殻の動きにより生じ、多くの人々の命を奪ったのである。

しかし、よく良く考えてみるとこれは牛乳の皮膜とよく似た話なのである。 つまり地球は、宇宙空間に浮かぶ熱々の液体だ。だから、丸い。 そして我々は、その熱々の牛乳の皮膜のうえにへばりついて住んでいるのである。 地球規模の時間の流れではぷくぷく動く皮膜も、我々の時間感覚では、 年間数センチしか動かないどっしりとした地殻であると錯覚してしまう。

その皮膜にわずかなシワがあり、そこが日本列島なのか。 皮膜が多くの人が気づかないくらい小さく、 ぷくぷく動くたびに我々人間は慌てふためいているのである。

美酒鍋の悲劇

酒の町、広島西条には美酒鍋と呼ばれる鍋が存在する。酒と塩胡椒のみで味付けする名物鍋である。この鍋を作ろうとして大失敗したことがある。失敗を繰り返さないために、ここにその失敗を記しておこうと思う。

それは体育会サークルの合宿での出来事だった

美酒鍋を作ったのは鹿児島の山の中である。当時大学1年生の春休み。これが最初で最後となった。

大学のクラブ活動で鹿児島の名も無き、道も無き山々を地図のみを頼りにして約一週間歩き通すという、大学生ならではの無謀をやっていた。一緒に行動していたのは、3年生2人、2年生1人、私を含めて1年生2人の合計5名である。その年度最後のイベント、春合宿の話である。

体育会サークルのそのグループは、まるで軍隊のように3年生のリーダーが、計画から現地での采配を担当する。1年生の仕事は朝昼晩と食事を作ること。普段から週末になれば近場の山々に登りに行って、テント生活やら地図読み、天気図の読解などを訓練した。すべての装備、食料、燃料を80リットルくらいのザックに詰め込み、山中テント泊、一切のゴミや茹で汁すら捨てない、遭難対策を含めて全て自分たちの責任でやる。このような極めて高い自立精神の下に活動するクラブだったため、日々の訓練を欠かさなかった。「起床!」の掛け声で起き、鍋に水を入れてから温め始めるまでの時間は数十秒という世界である。

何日目だっただろうか。おそらく終盤あたり、もう藪の中を歩き続けて、5日くらいたったとき、ようやく町にでた。それまで、ささやぶ、茨のやぶ、倒木やぶ、ボロボロに枯れたX字に交差しまくる竹やぶなどありとあらゆるヤブを体験してきた。先頭を歩くものの体力消耗が激しいので、一定時間経てば先頭が交代させられる。雪山でのラッセル(雪を踏み固めてあるく方法)とよく似ている。このようにやぶの中を歩き続けてヘトヘトになった末に、たどり着いたのが、町につながる道路の末端、貯水施設の前である。

先輩の最後のプレゼント

いつもなら食事の準備にとりかかるはずだが、なぜか今回は1年生は休んでよしと言われた。その理由は、最後の合宿ということで特別に3年生が食事を作るという企画が用意されていたからである。何を作るかは直前まで内緒というサプライズ付きで。当然我々は、真昼にも関わらずザックをベッド替わりにして爆睡して待った。

3年生が帰ってきた。何やら楽しそうである。いつものごとくテントの中で食事の準備を始めた。食事の準備で1年生である我々がただ眺めているというのも、久しぶりで新鮮だった。ヘトヘトにつかれた時に食事の準備をしなくていいというのは、こんなにいいものかと思った。

美酒鍋、調理開始!・・・そして地獄絵図

その夜は、どうやら美酒鍋らしい。鹿児島だからか肝心の日本酒が売ってなかったらしく、パック酒の焼酎を使うことになった。

水1:酒1。

プリントアウトしてきたレシピにはそう書かれている。焼酎を鍋半分くらいまで入れた時、箱の中にはもう半分残ってしまった。我々のクラブのルールは既に書いたとおりである。残ってしまったものは持ち帰らないといけない。これ以外に宴会用のかなり高級な酒はたっぷり持たされている。残ったパック酒を飲みたいとも思わない。結局、残った焼酎の処理に困り、全部鍋に投入してしまった。

つまり水0:焼酎1。

「こんなに入れたらなかなかアルコール飛ばないじゃないかな~」と誰かが言った。だが、事態はその程度のものではなかった。テント内でグツグツ煮るうちに、みるみるアルコールは飛んだ。が、入り口を全開にしているにも関わらず、飛んだアルコールはテント内に充満した。みるみる皆の顔が赤くなってきた。肺から飲酒しているようなものだ。ダイレクトに血管に注入されるアルコールのせいで頭がふらふらした。

それでも規律正しい軍隊のようなこの集団。誰も文句いわずじっと出来上がるのを待った。やがて全てのアルコールが飛んだことを確認し、小皿に分け与えられた。めちゃくちゃ腹が減っている。これまで多少のまずい飯は食ってきた。少なくとも見た目は美味そうである。しかし、この自信は簡単に覆された。

美味い不味いの問題ではない

一口食べて誰もが「うわっ!なんじゃこれ」。既に酔っているいるのに、まだ焼酎の強烈な味がする。飲み過ぎて吐きそうな時に、「水飲みな」といって焼酎の入ったコップを渡されたときのような気分である。うまいかまずいかの問題ではなかった。吐くか吐かないか、であった。

とりあえず液体を絞って具だけでも…食べよう。そうして、なんとか具だけは食べ終えた。しかし、自分の小皿に残ったその液体と、鍋に残っている大量の液体、これだけはどうしようも無かった。

リーダーの3年生は頭を抱えて考えこんでしまった。これまで自分たちが貫き通してきたルール。一切のゴミは捨てないということ。でも、この液体だけはどうしようもない。まだ、行程はあるのに、これを背負って帰るわけにも行かないし。何分か考えた末に、よし捨てよう。リーダー最後の一大決心であった。

私は今でもこの決心は正しかったと思うし、私がリーダーでもそうしていただろう。

振り返れば

何がこの失敗の分岐点だったかと思い返せば、それは鹿児島で広島西条の美酒鍋を作ろうとしたことである。何もわざわざ鹿児島に来てまで、西条ですら滅多に食べない美酒鍋を作ることなど無いのである。

それ以来、美酒鍋と聞くと吐きそうになる。どうかこの失敗を繰り返さないでもらいたい。

留学生さん、その“復興支援活動”は偽善ですよ。

私は震災当時、訳あって外国にいた。そして未だに帰ってこれない状況にあり、どうして肝心な時に自分は何もできないんだろうと思うばかりである。気持ちだけは愛国者のつもりである。それだけに日本人が団結して復興支援に乗り出す姿は心動かされるものがある。

でも、どうしても納得いかない"復興支援”がある

それは海外で日本人が行う復興支援の呼びかけ。具体的に私が見たのは、震災後何ヶ月も経ったにもかかわらず、海外に留学中の大学生が「被災した人たちを助けるためにどうか募金お願いします」などと呼びかけて、外国人に募金をさせる、あれ。結論から言うと、それは全く悪意がないが、日本人に自立心がないために起こしてしまった勘違い行為だと思う。

なぜか? 

まともな大人なら直ぐ理由は分かると思う。分かる人はこの先を読んでもがっかりするだけで、それ以上のことは書いていない。あたりまえのことを書いているだけだから。それでも、自分の考えを整理するために書こうと思う。自分の子供が将来同じようなことをやっていたら、ちゃんと理由を説明してやるために。

その"復興支援”に納得いかない一番の理由は、自分が日本人なのに、外国人に日本人のために助けてくれと言うことは、自分を助けてくれと言っていることと同じだから。だから、日本国内で日本人に向けて別の日本人(被災者)のために、助けてくれと言うことは、何の問題もない。外国人が、他の外国人に対して、日本人(被災者)を助けましょう、と言うのも同じく問題ない。

自分を助けてくれと言うことが、なぜいけないか?この部分なかなかうまく説明できないが、自分を助けてと言ってもいいのは、自分が本当に危機的状況にある場合だけだと思う。それが自立心ということだから。

では、震災にあった日本(自分たち)は、他人にお金を援助してもらうほど危機的状況だろうか?全くそんなことはない。震災にあっても、それでもなお、GDPが世界第3位のどうどうたる経済大国であるし、日本政府は日本国民に借金していても日本全体では外国への借金なんかない。つまり、世界ではお金持ちの方なのである。むしろ、この経済危機の中、他国を支援しようとしているのが日本である。そういう事実は、知っているのであろうか?

では、震災直後、募金活動をするのはなぜか?それは、震災復興のための予算を確保するためには、時間がかかるから、直ぐにでも必要な人のために、予算や法律うんぬんの問題抜きで直ぐにお金を渡すためにやるのである。つまり一時的なお金のためで、長期的に復興のためのお金が足らない訳ではない。

それなのに、外国人に対して助けてくれと言うことは、自分の兄弟が病気をして、自分たち家族には貯金がたっぷりあるのに、他人に対して病気をした兄弟のために援助してくれ、と言う行為と同じである。要するに、ただの物乞いではないか?

 しかも、私が見たのは留学生である。留学費用を自分で稼いでいるようには見えない。募金しろと言うくらいなら、他人にお金を恵んでくれと言うくらいなら、まず自分の身を削るべきではないか?その留学費用を募金すべきでは?

納得行かないもう1つの理由は、集めた募金ができるだけ早く使ってもらえるように行動していなさそうだから。一部の人と話してみたが、どうもそういうことはしていないらしい。募金活動はしても、そのお金がどう使われようと、興味ないという団体(人)は多いのではないだろうか?

なんで?それは本当の復興支援か?

今回、政府の対応がまずくて集めた支援金がなかなか被災者に渡らないという問題があった。この時、政府に文句を言った人がどれくらいいるのだろうか。多くの人は、募金はするけど、政府に文句を言ったりしない。首相官邸にメールするだけでも構わない。誰だってできるはずの行為でもである。

それは、募金活動という、善意の活動としてみなされる、聞こえの良い、心地よい行為はするけれど、政府に文句言うという、他人に「えっそこまでするの?」と思われてしまうような行為はしたくない、ということではないか?要するに、自分のためにやっているだけではないか?そうなら、はっきり言ってただの偽善行為である。

私は、その留学生達が悪意あって、そのような私から言わせれば偽善行為をしているのだとは、全く思わない。単に、ちゃんと考えて行動していないだけだと思う。その根底にあるのは、自立心の無さではないだろうか?自分たちが主役で自分たちが頑張らなければならないという自立心である。それは他人に助けを求めることでは、決してない。

留学生ならばそういう横道にそれたような活動に力を入れるよりも、しっかりと勉強をして、将来の日本のための人材になることこそが、本当の復興支援ではないだろうか。

printf + std::cout + OutputDebugString


目的

以下の4つの特徴を合わせ持つ関数を設計する。
  • 書式指定が簡潔(printf)
  • 型セーフかつ拡張性がある(std::cout)
  • Visual StudioやDebugViewに出力できる(OutputDebugString)
  • Visual Studioの「出力」ウィンドウ内をダブルクリックするとソースコードの指定行にジャンプする
使い方
桁数など細かいしても簡単。
int w = 1024;
int h = 768;
DLOG("%04d, %04d") % w % h;
文字列だって簡単。
std::string fname = "test.txt";
DLOG("%s") % fname.c_str();
ただし、他の演算子を含む場合は、%演算子の優先順が低いため括弧()が必要になる。
DLOG("%d") % (1+5);
std::coutと同様の機能は全て備えている!cv::Mat_等の出力も簡単。
#include<opencv2/opencv.hpp>
cv::Mat mat = (cv::Mat_<double>(3, 1) <<
 1.0, 2.0, 3.0,
 1.0, 2.0, 3.0,
 1.0, 2.0, 3.0
);
DLOG("\n%s") % mat;
上記の4つの例の出力例。
.\Test.cpp                    (  10) : 12345 TestClass::testFunc 1024, 768
.\Test.cpp                    (  20) : 12345 TestClass::testFunc test.txt
.\Test.cpp                    (  30) : 12345 TestClass::testFunc 
.\Test.cpp                    (  40) : 12345 TestClass::testFunc
[1 2, 3;
 1, 2, 3;
 1, 2, 3]
フォーマットはこの通り。「:」までがVisual Studio特有のフォーマットでこのように出力すると、出力ウィンドウ内の出力行をクリックすると、ソースコードの対応する行にジャンプするようになっている。
ファイル名                    (行番) :スレッド番号 クラス名::関数名 出力

ソースコード

※以下は、このサイトに書かれているかなりの部分を流量している。
http://reindeer7125.blog129.fc2.com/blog-entry-6.html
#include <boost/format.hpp>
#include <string>
#include <iosfwd>
#include <boost/iostreams/categories.hpp>
#include <boost/iostreams/stream.hpp>
#include <windows.h>
 
#define DLOG dbgout() << (boost::format("%-20s(%4d) : %d %s ") \
 % __FILE__ % __LINE__ % GetCurrentThreadId() % __FUNCTION__) << boost::format
 
namespace detail{
inline void output_debug_string( const char *s){
    ::OutputDebugStringA( s);
}
 
inline void output_debug_string( const wchar_t *s){
    ::OutputDebugStringW( s);
}
 
template<class _Ch>
class dbgout_sink
{
    public:
    typedef _Ch char_type;
    typedef boost::iostreams::sink_tag category;

std::streamsize write( const char_type *s, std::streamsize n){
    std::basic_string<char_type> buf( s, n );
    buf += '\n';
    output_debug_string(buf.c_str());
    return n;
}
};
 
template<class _Ch>
class basic_dbgout : public boost::iostreams::stream<detail::dbgout_sink<_Ch> >
{
    public:
    basic_dbgout(): boost::iostreams::stream<detail::dbgout_sink<_Ch> >(dout_sink){}
    private:
    detail::dbgout_sink<_Ch> dout_sink;
};
}//namespace detail
 
typedef detail::basic_dbgout<char> dbgout;
typedef detail::basic_dbgout<wchar_t> wdbgout;
typedef detail::basic_dbgout<TCHAR> tdbgout;

Is this "寿司" or "酢飯の刺身のせ"?

海外では日本から来たというと寿司の話をされることが多い。寿司がこれほどにまで外国人に受け入れられるとは、なんとも不思議なものである。米にドレッシングをかけるような、ジャポニカ米の米自身の旨みも感じ取れない人が本当に寿司が美味いと思っているのだろうか?

プラシーボ効果か?

たとえ 全く効き目の無いはずの薬を飲んでも、その薬が病気に効くんだと聞かされていれば、1割くらいの人には実際に効果があるらしい。先入観とは恐ろしいものである。これはプラシーボ効果といって、実際に新薬のテストをするときは、このプラシーボ効果の分を差し引きしなければならない。寿司にもこの効果が影響していたりはしないのだろうか?つまり「寿司」=「うまいもの」という先入観ではなかろうか?

確かに、食べ物の好き嫌いは先入観の影響は極めて大きいと思う。だから子供の頃に吐いてしまったような食べ物は中々食べれない。逆に嫌いな物でも、一度食べることができれば、それ以降は本当の美味しさを感じることができるようになる。食べれないものなど、要するにただの先入観なのである。

寿司がうまいと言われるのが単なる先入観かどうか確かめるには、新薬のテストと同様の実験をすればいい。寿司が美味いと聞いてはいるが、詳しく見るのは初めてで 、初めて食べる人に、寿司とは全く違う食べ物を食べさせたらどうなるのだろう?

わさびと醤油が好きなのか?

外国人の寿司の食べ方を見ていると、醤油とわさびをたっぷりシャリに浸して、お箸でつまんで食べる。そんなにつけること無いのに・・・・と思うが、当の本人はむしろ、たっぷりつけることが日本食をたっぷり理解することにつながるかのような態度で、「日本人はもっとわさびをつけても平気なんだろう?」と聞いてきたりする。醤油はソイソースなどと訳した事がそもそもの間違いなのか?醤油はソースとは全く違う。皿についたソースは、ソース単体でも美味いからパンで拭きとって食べるけれど、米一粒も残さない日本人ですら醤油を舐め回す人はいないのがいい例だ。

そもそも、あれは寿司なのか?

NHKのためしてガッテンによると、寿司の旨みは酢飯と魚肉が十分に密着して初めて生じるらしい。確かに江戸前寿司以外の日本古来の寿司は、押し寿司で魚肉と米がしっかりと密着してるじゃないか。酢飯に刺身をのせただけの物を、寿司、寿司と言うけれど、多くは刺身の部分が旨ければ、寿司がうまいと言ってしまっていないだろうか?もっというと中途半端な鮮度の刺身をごまかすために、酢飯と一緒に食べてるだけではないか。

刺身と酢飯が十分に密着していれば、ネタのみを箸で持ち上げてもシャリが外れないらしい。しかも、うまい寿司というのは、シャリがふんわりしている。十分に密着させるために圧力をかけるにもかかわらず、シャリを潰しすぎないようにする。だからこそ、寿司を握るのは難しいのだ。そう考えると回転寿司など、酢飯のおにぎりに刺身をのせているだけである。本当に寿司がうまいと思ったのはごくわずかだが、いずれもネタとシャリの境目を感じなかった。

デフレかもしれないが、日本人は、もう少しいい物を食べるべきだと思う。ファーストフードや回転寿司でも満足できる馬鹿舌を育成してしまったら、日本の素晴らしい文化は、どんどん消えてしまうだろう。外国人に、酢飯の刺身のせが寿司だと勘違いされきってしまわないうちに、我々自身が本当の寿司の旨味を体験して、それは寿司じゃないよ教えるべきだ。

※ためしてガッテン(2007年01月03日放送
http://www9.nhk.or.jp/gatten/archives/P20070103.html

画像でステンシルバッファを操作する in OpenGL

画像を使って画素単位でステンシルバッファの値を定め、一部分が欠けたオブジェクトを描画する方法.

  • アルファチャンネルを持つテクスチャを準備する
  • GL_ALPHA_TESTを有効化
  • glDrawBuffer(GL_NONE)にしてテクスチャマッピング
  • glDrawBuffer(GL_BACK)にして描画したいものを描く

コード例

#include <gl/glut.h>
 
void reshape(int w, int h)
{
    glViewport(0, 0, w, h);
 
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
 
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
 
void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 
    glEnable( GL_STENCIL_TEST );
 
    glDrawBuffer(GL_NONE);
    glStencilFunc(GL_ALWAYS, 1, 1);
    glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
 
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_ALPHA_TEST);
    glAlphaFunc( GL_NOTEQUAL, 0.0 );
 
    glColor4d(1.0, 1.0, 1.0, 0.5);
    glBegin( GL_QUADS );
    glTexCoord2d(0, 0); glVertex2d(-0.8, -0.8);
    glTexCoord2d(1, 0); glVertex2d(+0.8, -0.8);
    glTexCoord2d(1, 1); glVertex2d(+0.8, +0.8);
    glTexCoord2d(0, 1); glVertex2d(-0.8, +0.8);
    glEnd();
 
    glStencilFunc(GL_NOTEQUAL, 1, 1);
    glDrawBuffer(GL_BACK);
 
    glDisable(GL_TEXTURE_2D);
    glColor4d(1.0, 0.0, 1.0, 1.0);
    glBegin( GL_QUADS );
    glVertex2d(+0.0, -0.4);
    glVertex2d(+0.9, -0.4);
    glVertex2d(+0.9, +0.4);
    glVertex2d(-0.0, +0.4);
    glEnd();
 
    glutSwapBuffers();
}
 
void main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
 
    //ウィンドウ(camera)の作成
    glutInitWindowPosition(256, 256);
    glutInitWindowSize(256, 256);
    glutCreateWindow("write texture in stencil buf"); 
    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    glClearColor(0.0, 0.5, 0.0, 1.0);
 
    //ATI-RADEONでは指定すると正しく動作しない
    //glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
 
    const int size = 256*256;
    unsigned char image[size];
    for(int i=0;  i<size/2; i++)
    {
        image[i] = 0;
    }
    for(int i=size/2; i<size; i++)
    {
        image[i] = 255;
    }
 
    //テクスチャが確保できているかどうかチェックする
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA, 256, 256, 0, GL_ALPHA,  GL_UNSIGNED_BYTE, image );
 
    glutMainLoop();
}

高卒就職で教育改革

「大学生のレベルが低下している」、「少子化で中堅大学の入学志望者が人気の高い大学に流れたためだ」、「ゆとり教育の弊害だ」などなど、よく聞く言葉である。大学院生を教育した経験から、これらは大方真実だろうと思う。しかし、こういった言葉は所詮無責任な批判にすぎない。私は微力ながら日本を支える一人の成人として、ここで建設的な提案をしたい。まずここで述べたい問題点を整理し、次に提案の詳細を述べる.

大学生レベル低下の問題

学部の前半2年間を無駄に過ごしてしまう

学部時代の前半2年間は、一般教養の単位取得、飲み会、麻雀、バイト、クラブ活動、これで殆どの時間を費やしてしまう。高校時代思い描いていた、大学の専門的な勉強に殆ど触れないまま、その志は腐っていく。受験勉強で疲れはててしまった反動でどうしても遊びまくってしまうのだ。その後の2年間は前半の気が抜けた生活のせいで思うように受験勉強のようにはエンジンがかからずに研究室配属に至る。

研究よりも就職活動に専念してしまう

大学生は、研究が始まる頃と同時に就職活動が始まる。不景気の就職難もあり最近は早めに活動を開始したりする。一旦就職活動を始めてしまうと、それだけに専念するようになり、研究どころではない。誰もがブランド志向で就職活動をするので希望は大手企業に殺到するため就職活動に相当な労力を割くことになる。企業側も、良い学生を取ろうと必死である。研究の軌道が乗りかけていても、研究など真面目にしない大学生と同じように扱われて、4月の採用まで宿題をだしたり、何かと研修と称して勝手に働かせたりする。学生はまだ正式に採用が決まっていない会社に言われれば、弱い立場から学業よりもそちらを優先してしまうのである。卒業論文・修士論文がいいかんげんに終わっても、大学側が卒業・修了させなければ、不良債権(実際そう呼ばれる)が残ってしまい、卒業できない学生が研究室に残っても研究室の雰囲気を乱すだけ。逆に、既に就職が決まっている学生にとってみればただの災難で、論文などどうでもいいから卒業単位をもらって就職したいだけなのである。

これらの問題は総じて言えば、大学生活を無駄に過ごしているというだけであり、その原因は、学生自身に自立心がないことである.つまり、無駄に過ごしても自動的に生活費や学費が自動的に供給される仕組みがあるから、それらが提供されることが当たり前になってしまうのである.そのような学生が千人以上の規模で存在する以上、個々の学生の問題というよりは社会の仕組みの問題である.

高卒後に働いて学費を稼ぎ、入学・就職試験で評価
私が提案するのは、政府主導のもとで高卒の学生に対して任期3年程度の就職を斡旋し、自ら稼いだお金で大学に進学することを支援する政策である。この政策により以下の効果を期待する。
  • 大学生の自立心を育成する
  • 入学試験以外の評価基準の採用を促す
  • 過度な就職活動により学業が妨げられることを防ぐ
  • 大手企業への就職希望殺到を軽減する
就職先は?

ではどこを斡旋するのか?一番良いのは、農業、漁業、林業などの第一次産業、自衛隊、伝統工芸などおよそ大学生が一生知ることのなくかつ国家の基盤となる職業である.これにより、一次産業の高年齢化を食い止める効果も期待する.また、このような国家の基盤となる職業を体験させることにより、そういった職業の重要性を認識できるようになると考えられる.学生どおしの情報交換を通じて他の職業についても偏見なく知る良い機会となる.大学生は卒業すればいずれの職についたとしても人の上に立つ人財になる可能性が高い.人の上に立つ者は,その下の者達の苦労も知る必要がある.まさにこれは,会社が行う研修を国家が教育の一環として行うことと同じである.

希望者は確保できるか?

そもそもこのような制度ができたとして、希望者は確保できるのか?徴兵制のように国民全員一定の歳で仕事させる懲役制も考えられる.しかし、懲役制となるととたんに反対者が増え実現は困難になるのではないか.しかも義務となったとたんに自主性を失い本来の目的である自立心を養うことにはならない.希望者を確保するためには、それなりのメリットが必要である.簡単な方法は,役人の採用で公務員試験だけでなくこのような経験を評価に入れることである.そうすることである程度、人数が増え、効果が確認できれば企業側もどうようの評価規準を採用し始めるのではないか.

効果は? 

効果があるのは、もしかしたら中堅クラス以下の大学入学を希望する学生だけかもしれない.そのようなメリットがなくても就職の心配をしない優秀な人もいる.本当に優秀な人は,そういった回り道をさせずに本来の目的の学業を突っ走る必要もある.ターゲットはあくまでも特別な目的意識も持たずに周囲の学生と同じ行動をとるだけの目的で大学に入学してくる学生である. 

段階的に規模を拡大でき、様々な微調整もできるこの政策はかなり現実的で強力な教育改革になると 信じている.

cv::Mat_をテキストファイルに保存・読み込み

 XML/YAML形式でのデータ保存/読み込み方法は紹介されているが,そんなややこしいフォーマット使いたくない,という人向け.

使い方
cv::Mat_<double> mat = (cv::Mat_<double>(3, 2) <<
    1.23, 1.23,
    1.23, 1.23,
    1.23, 1.23);
writeTxt("mat.txt", mat);
readTxt("mat.txt", mat);

フォーマット

行列の要素を見た目のままの配列で保存する.
  • ヘッダー無し
  • 列:スペース区切り
  • 行:改行区切り 
1.23 1.23
1.23 1.23
1.23 1.23

必要なヘッダファイル

だぶんこれで全部だと思う.
#include <opencv2/opencv.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include <fstream> 

データの保存
フォーマットがシンプルなので特別な処理は何もない.
template<typename Type>
bool writeTxt(const std::string filename, const cv::Mat_<Type> & mat)
{
    using std::string;
    string line;
    std::ofstream ofs(filename.c_str());
    if(!ofs)
    {
        std::cout << boost::format(" cannot open %s\n") % filename;
        return false;
    }
 
    for(int j=0; j<mat.rows; j++)
    {
        for(int i=0; i<mat.cols; i++)
        {
            ofs << mat(j, i);
            if(i < mat.cols - 1) ofs << " ";
            if(i == mat.cols - 1) ofs << "\n";
        }
    }
    return true;
}

データの読み込み 

読み込み時は,行列の行数に応じてcv::Mat_のサイズを変える必要があるのでpush_backを使う.
列数も同様のboost::splitとboost::lexical_castを使う.
template<typename Type>
bool readTxt(const std::string filename, cv::Mat_<Type> & mat)
{
    using std::string;
    string line;
    std::ifstream ifs(filename.c_str());
    if(!ifs)
    {
        std::cout << boost::format(" cannot open %s\n") % filename;
        return false;
    }
 
    mat = cv::Mat_<Type>();
 
    while( getline(ifs, line))
    {
        boost::trim(line);
        std::list<std::string> results;
        boost::split(results, line, boost::is_any_of(" \t"),
            boost::token_compress_on);
 
        cv::Mat_<Type> row(1, results.size());
        std::list<std::string>::iterator iter = results.begin();
        std::list<std::string>::iterator tail = results.end();
        for(int i=0; iter != tail; ++iter, i++)
        {
            row(i) =  boost::lexical_cast<Type>(*iter);
        }
 
        mat.push_back(row);
    }
    return true;
}; 

課題
  • 高速化
  • エラー処理
参考ページ