OpenCV-Python Tutorialsの「カメラ校正」への補足

[OpenCV-Python Tutorialsの「カメラ校正」](http://qiita.com/nonbiri15/items/e36c12d444d7cc4a00b7)への補足が記述が増えたので別記事として書き直した。


###**カメラ行列の意味**
[fx, 0, cx;
 0, fy, cy;
 0, 0, 1]
の焦点距離 fx, fyは単位がpixelであることが見落としがちです。
カメラ中心の(cx, cy) [pixel]は、必ずしも、画像サイズの半分に一致していません。
一致するのは、カメラが適切に正面をむいている場合だけです。
ステレオカメラの２つのカメラの結ぶ軸に対して、カメラの正面の軸が必ずしも直交していません。
そのため、カメラから被写体が遠ざかったときの消失点の位置が、画像の中央と違ったものになっています。
カメラ中心の(cx, cy)は、その消失点の座標です。

ステレオカメラでキャリブレーションしたときには、
左カメラ、右カメラのカメラ中心の位置の違いは、２つのカメラの向きがわずかに平行ではないことを示しています。

(cx - 画像の幅の半分)/fx は
カメラの向きのずれ角θx の　tan(θx) になっているようです。

(専門家ではないので、間違えていたらごめんなさい。)



ステレオマッチングをする際には、ステレオカメラモジュールを作った後に
ステレオペアに対して、校正＋歪み補正をするようにして確かめることです。
そうすることで、どの範囲まで歪み補正がうまくいっているかを視覚的に確かめることができます。
歪み補正がうまくいっていない領域では、視差の値が系統的にずれてしまって、正しい値を返しません。

##(opencv_3のディレクトリ)\sources\samples\pythonにあるスクリプト
###calibrate.py
校正＋歪み補正のスクリプトです。
cv2.calibrateCamera()
cv2.getOptimalNewCameraMatrix()
cv2.undistort()

(opencv_2のディレクトリ)\sources\samples\python2にある
calibrate.py
校正だけで歪み補正がないのスクリプトです。

 cv2.calibrateCamera()


Stereo Rectification の例
[tom5760/stereo_vision](https://github.com/tom5760/stereo_vision/blob/master/stereo/vision.py)


###stereo_match.py
はステレオ平行化後の画像からステレオマッチングを計算し、
視差を出し、距離変換するものです。
その中で、視差は次のようにして3Dの座標に変換されます。

```.py:
Q = np.float32([[1, 0, 0, -0.5*w],
                    [0,-1, 0,  0.5*h], # turn points 180 deg around x-axis,
                    [0, 0, 0,     -f], # so that y-axis looks up
                    [0, 0, 1,      0]])
points = cv2.reprojectImageTo3D(disp, Q)
```

その空間座標は.plyの拡張子のファイルに容易に変換されており、
Resulting .ply file cam be easily viewed using MeshLab ( http://meshlab.sourceforge.net/ )
と書かれていますので、次のようにMeshLabで3D表示することができます。
![meshlab_half.png](https://qiita-image-store.s3.amazonaws.com/0/83761/7bd8d5c7-3669-10c4-56e9-65ff09dddb8d.png)

図は、stereo_match.pyで生じた3D座標をMeshLabで表示させたものです。

![aloe画像](https://siddhantahuja.files.wordpress.com/2009/05/iml2.png)
の背後の布が、この3D表示のような奥行きの変化をもっていたことはなかなか気づかずにいました。
![aloe画像とdepth画像](http://www.cvlibs.net/software/libelas/aloe.jpg)


このようにMeshLabなどで3次元表示をすることは発見があります。

そのためのplyファイル形式への変換は上記のpythonスクリプトの中に含まれています。
以下のコードはそのための3Dデータ(各点の3D座標と色との両方)を出力させている部分のコードです。
numpyのsavetxt()を使っているので記述がとても簡単になっています。


```.py:
import numpy as np
import cv2

ply_header = '''ply
format ascii 1.0
element vertex %(vert_num)d
property float x
property float y
property float z
property uchar red
property uchar green
property uchar blue
end_header
'''

def write_ply(fn, verts, colors):
    verts = verts.reshape(-1, 3)
    colors = colors.reshape(-1, 3)
    verts = np.hstack([verts, colors])
    with open(fn, 'w') as f:
        f.write(ply_header % dict(vert_num=len(verts)))
        np.savetxt(f, verts, '%f %f %f %d %d %d')

```


なお、視差を計算させる際には、その配置と対象物のある範囲でだけ視差を探索するように、最小の視差の値と、探索の数とを指定します。そのことで、
・間違った視差を与えることが減る。
・計算時間が減る。
ことにつながります。


###**OpenCV-C++での関連するサンプルプログラム（ステレオ系 / カメラ校正系）**
OpenCV 2.4.0 のサンプルソースの一覧(samples/cpp)
https://github.com/YusukeSuzuki/opencv_sample_list_jp/blob/master/samples_cpp.rst
からC++でのステレオに関係するサンプルプログラムを読むのも有益です。
3calibration.cpp
calibration.cpp
calibration_artificial.cpp
stereo_calib.cpp
stereo_match.cpp



このようにして検出した視差の値は、ノイズの影響を受けたものになりがちです。
OpenCV3.1 では、次の関数を使って、そのノイズの影響を低減できるとのことです。
[cv::ximgproc::DisparityWLSFilter Class Reference](http://docs.opencv.org/master/d9/d51/classcv_1_1ximgproc_1_1DisparityWLSFilter.html#gsc.tab=0)
OpenCV3.1ではOpenCV2.4系統とは関数のインタフェースが変わっているので注意がいりますが、試してみる価値があります。



###**魚眼カメラの場合**
[OpenCVで魚眼カメラのキャリブレーション](http://d.hatena.ne.jp/takmin/20150910/1441881437)

> 魚眼カメラ用のキャリブレーション関数cv::fisheye::calibrate()を使ってカメラパラメータを取得します。関数の説明は以下のページにあります。
http://docs.opencv.org/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#fisheye-calibrate


魚眼カメラのキャリブレーションは、cv2の中には含まれていないことが[python documentation server]でわかりました。
そのような場合には、Pythonで処理することはあきらめ、C++でコーディングすることとしましょう。

cv::fisheyeのnamespace の機能を必要とするのは、ステレオ平行化までです。
ステレオ平行化が済んだ後は、従来のように視差画像の導出すればよいだけです。

次の記事は魚眼の場合のコードを含んでいます。
[OpenCVのカメラキャリブレーションのサンプルコード](http://news.mynavi.jp/series/cv_future/005/)


### **Omnidirectional Camera Calibration**
魚眼カメラよりもさらに広角になっている全方位カメラでは、ステレオ平行化の難易度が高くなってきます。

YouTube [OpenCV GSoC 2015 Omnidirectional Camera Calibration](https://www.youtube.com/watch?v=E7YvGX6_RHE)

“Omnidirectional Cameras Calibration and Stereo 3D Reconstruction” – opencv_contrib/ccalib module (Baisheng Lai, Bo Li)


[Davide Scaramuzza Omnidirectional Camera pdf](http://rpg.ifi.uzh.ch/docs/omnidirectional_camera.pdf)


魚眼カメラの方式はRとθの関係式が異なるもので様々な方式があります。
以下のURLを参照ください。
opencv_contrib　にあるソースコードで対応している方式は、次に示す複数の方式のどれなのか確認しなくては。

[Fisheye Projection](http://hugin.sourceforge.net/docs/manual/Fisheye_Projection.html)

等距離射影方式（Equidistance Projection ）
等立体角射影方式（Equisolid Angle Projection ）
正射影方式（Orthographic Projection ）


[射影方式のちがい](http://tin0.blog.fc2.com/blog-entry-49.html)  


[ 従来の魚眼レンズ(H180)                   新規検討の魚眼レンズ(TY180)]
(http://www.mvision.co.jp/mcm320fish.html)


[Design Wave MAGAZINE (デザイン ウェーブ マガジン) 2008年 09月号 [雑誌] ](http://books.rakuten.co.jp/rb/7848928/) の［重点企画］クルマに搭載され始めた魚眼レンズの研究-各種射影方式の比較と相互変換方法　は15ページのわたる記事です。方式についての記載が詳しく、方式間の画像の見え方を変換する方法なども書かれています。



###[【Windows】【Python】 OpenCVで魚眼レンズのカメラキャリブレーション](http://qiita.com/Kazuhito/items/cccdeb55d101dcc69203)


-------------------------------

###**[スクエアグリッドとサークルグリッドどっちを使うべき?](http://news.mynavi.jp/series/cv_future/005/)**

>結論から言うとサークルグリッドを使った方がよいです。理由は単純で、ステップ2の特徴点の座標の算出精度がサークルグリッドの方が高いからです。楕円(円を含む)は、斜めから見ても楕円という特性があるため、重心を特徴点とすることで特徴点の座標を精度良く求めることができます。一方、スクエアグリッドの場合は、直線と直線の交点を特徴点とします。斜めから見ると正方形の形状が歪んでしまい、安定して座標を求めることができません。



[cv2.findCirclesGridDefault(image, patternSize[, centers[, flags]]) ]
(http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#findcirclesgrid)





### **garbage in garbage out**
ステレオマッチングは、計測です。計測に適した状態にして画像を撮影しないと、良好な結果は得られません
 左右のカメラの明るさをそろえる(オートゲインはきっておく).
画像を適切な輝度値の範囲にする(極端に明るすぎる、黒つぶれする)。
テクスチャーの変化がある画像にする(平坦すぎる画像は避ける)。
それらのことは、プログラミングではありませんが、それらのことを無視して良好な結果を得ることはできません。
重ねて言います。ステレオマッチングは計測です。撮影するときの被写体の条件やカメラの撮影条件によって、ステレオマッチングへの入力データのよしあしが変わります。プログラム自体がどんなによくても、入力データの品質が悪ければ、悪い結果しか出てきません。"garbage in garbage out" です。


###　**ステレオカメラを原理から理解する**
ステレオカメラの理解は、3次元計測という実験の色彩が強く、他の画像処理や画像認識の部分と少しばかり勝手が違ってきます。
通常の画像処理は得意だという人でも、ステレオマッチングの関数の多数の引数を使いこなすのが難しくなりがちです。

- [【コンピュータビジョン】ネコと学ぶエピポーラ幾何](http://qiita.com/ykoga/items/14300e8cdf5aa7bd8d31)
は分かりやすくてお勧めです。


- python+opencvでカメラキャリブレーション→ステレオマッチング→奥行き計測をする方法
http://russeng.hatenablog.jp/entry/2015/07/02/080515

- ３次元画像の多視点復元とステレオビジョン辺りのまとめ
http://qiita.com/sobeit@github/items/d419ea39b06e43e6200f

もわかりやすく書かれています。


slideshare　
Stefano Mattoccia[「 Stereo Vision: Algorithms and Applications」
 Stefano Mattoccia DEIS University of Bologna] (http://www.slideshare.net/DngNguyn43/stereo-vision-42147593)
214 pages
はかなり詳細なslideです。
これだけ詳細な説明は、画像認識の本でもなかなかあるものではありません。

[ステレオカメラ作成の道](http://www.slideshare.net/ytanno/ss-21302350?next_slideshow=2)

[Jetson TK1でSemi-Global Matching](http://www.slideshare.net/RyoSakamoto/jetson-tk1semiglobal-matching)



帯状パタンによる魚眼レンズカメラの高精度な校正
http://www.iim.cs.tut.ac.jp/~kanatani/papers/fisheyecalib.pdf


###**左右のカメラでの輝度の差があると、SADアルゴリズムでは、視差の誤差を生じやすいことへの対策**

輝度値の差がステレオマッチングの結果を悪化させることは、よく知られた事実です。
それに対する対策としてヒストグラムを変更することが考えられています。
[Suppression for Luminance Difference of Stereo
Image-Pair Based on Improved Histogram Equalization ]
http://onlinepresent.org/proceedings/vol6_2012/17.pdf


>CENSUS変換の利点• 輝度値を直接比較しないので照明変動につ よい – 中央との相対関係をもとに比較• ノイズに強い – バイナリの値を比較するので，微小なノイズの影 響が少ない• 模様が少ない領域での対応付けが安定する – 中心との差分→微分のような効果があるためエッ ジ（模様）を強調する効果
http://www.slideshare.net/FukushimaNorishige/popcnt

---
追記

SlideShare [SSII2019TS: 実践カメラキャリブレーション ～カメラを用いた実世界計測の基礎と応用～ ](https://www.slideshare.net/SSII_Slides/ssii2019ts34)