本記事はアドベントカレンダー ほぼ厚木の民 の記事です。
はじめまして、11日目担当の新人の稲生(いのう、と読みます。)です。技術系の記事を書くのが初めてなので拙いところが多いかもしれませんが、温かい目で見ていただけると幸いです。
この1年カメラキャリブレーションについて論文やツールをひたすら漁っていました。その中でOpenCVのモジュールについて備忘録がてら、まとめていこうと思います。
##そもそもカメラキャリブレーションとは
MATLABのチュートリアルが一番わかりやすいのでそちらを読んでもらった方が早いと思いますが、端的に言うとカメラの焦点距離や歪み具合、位置などの情報をパラメータとして推定することです。チェッカーボードをカメラの前でいろんな姿勢で撮影した画像を使って行うのが一般的です。
パラメータを使って写真の歪みを修正したり、物体の3次元位置の推定、などなどいろんなことができるようになります。
##OpenCVでのカメラキャリブレーション
OpenCVでは3つのモジュールが実装されています。
- cv::calibrateCamera()
- cv::fisheye::calibrate()
文字通りこれは魚眼レンズ向けのモジュールになります。なのですが、例えばこんなレンズやこんなレンズのような画角180度を超える(=レンズ後方の景色が少し映る)ものでキャリブレーションすると大体うまくいきません。ソースコードをのぞいてみるとこんなことが書いてあります。
// for larger FOV the loop below does not converge
// clip values so we still get plausible results for super fisheye images > 180 grad```
**このカメラモデルは画角180度までしか対応していない**、と明記されています。でも最近の魚眼レンズは180度以上のものがむしろメジャーなのではないかと思います。360度カメラとして有名なRicoh ThetaやInsta360などは前後に2つレンズがついていて、それぞれ画角180度以上あります。
ちなみにMATLABの[魚眼レンズ向けモジュール](https://sites.google.com/site/scarabotix/ocamcalib-toolbox)では画角195度まで対応、としています。では画角200度超のレンズはどうすればいいか。
- cv::omnidir::calibrate()
こちらはomnidirectional(無指向性、全方位)なレンズ向けのモジュールです。Omnidirectionalなレンズって何って話ですが下のようなカメラです。
<center><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214360/c9290e74-8fd0-12f2-f2ca-4ad7e235d76d.png" title="全方位" width=50%>
見た目が魚眼レンズと全然違いますが立派なレンズの一種です。
モジュールの名前もレンズの形も全然違うし、一見、魚眼レンズには使えなさそうな印象もあります。しかし、結論から言うと画角に関わらず魚眼レンズをキャリブレーションする場合も**fisheye::calibrate()よりこちらを使った方が良い**です。
ただomnidir自体は拡張モジュールなので使う際は自分でOpenCVをビルドする必要がある、使ってる人が少なすぎて情報が殆どないのが欠点です。執筆し始めたのが遅すぎたのでこのあたりの話は時間があるときにまた書こうと思います。実装と比較の参考にソースコードを置いておきます。
[calibrate.cpp](https://github.com/urotanke95/opencv_calib/blob/main/calibrate.cpp)
##まとめ
OpenCVには3つのキャリブレーション機能が実装されていて、良い推定結果を得るためにはレンズの特性に合わせて使うモジュールを選ぶ必要があります。
- cv::calibrateCamera()
中心射影レンズ向け
- cv::fisheye::calibrate()
魚眼レンズ(~画角180度)向け
- cv::omnidir::calibrate()
魚眼レンズ、全方位レンズ向け