OpenCVから見るフォトグラメトリ。（なぜ柱は消失しがちなのか）

※　過去に[xRTechNagoya](https://vrm-nagoya.connpass.com/event/162233/)、[XRMTG](https://togetter.com/li/1470739)でフォトグラメトリとOpenCVについてLTしたものとほぼ同様の内容です。

##この文書について
複数の写真から３Ｄデータを作成できるフォトグラメトリ。特別な機材もいらずにデータを生成できて素敵なのですが・・・
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/d68147a6-f9ef-ad5b-33db-ada43bcb55c1.jpeg" height="300px" width="250px"/><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/5c73ea0c-bdee-7a53-50ca-6768801bd2ed.gif" height="350px" width="480px"/>

3DF Zephyrでフォトグラメトリしていると、木の幹のように綺麗に取れる場合と、柱のようになんだか酷い事になる場合があると思います。
その原因についてフォトグラメトリの処理内容をOpenCVの本を読んでなんとなくまとめてみました。
（画像が多めなので縮小表示しています。クリックで原寸表示）

##まずは、上手くいく例　いかない例
下図は、4枚の写真を3DZFでテクスチャ付きメッシュを作成したものになります。（デフォルト設定で作成）

↓綺麗に立体化できるケース↓
![木の幹２.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/3dc66e2a-d86b-2f5e-ffd9-51f0c6281bff.jpeg)

↓柱が残念ことになってるケース↓
![ルーセントの柱.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/b2dd5ff3-eaea-7b02-b829-5dacbd16b66e.jpeg)


##フォトグラメトリの処理概要
まず、フォトグラメトリが何をやっているかをなんとなく理解しておきます。
立体データを作成するには、下記の処理を行っているはず。（多分。）
**１．写真の特徴を検出する。**
**２．複数の写真の特徴点の紐づけを行う。**
**３．カメラの位置を推定する。**
**４．各特徴点に対して測量する。**
**５．測量して得た点群をグループ化して、３Ｄデータを生成する。**


###１．特徴検出処理
画像の特徴となる点を求めます。
Python-OpenCVだと、Akaze.detectAndCompute関数に画像を食わせるとやってくれます。下図がAkaze.detectAndComputeで写真を処理した結果をを検出になります。
![Image1b.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/2be56d0e-efe6-f5ef-92ad-69ed3cc0ecc4.jpeg)

###２．特徴点の紐づけ
複数画像から、特徴が一致する点の紐づけを行います。
Python-OpenCVだと、matchar関数に画像を食わせるとやってくれます。下図は2枚の横に移動して撮影した写真を、matchar関数で紐づけした結果になります。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/ccede592-c1df-b44e-1df8-a7fbfe6747ba.png">

###３、４．カメラの位置の推定、特徴点に対しての測量
上図のように、横移動しながら撮影した２枚の写真では手前の特徴点は大きく動き、遠くの特徴点はあまり動いてません。
中学の三角関数で暗記させられた角度で例えると、下図のような感じ。
![sankakusokuryou.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/b58b5c8d-7586-0093-9b10-e01714d4b133.jpeg)
特徴点の移動量から、まずはカメラの位置（辻褄の合う位置）を推定し、次にカメラの位置から各特徴点の位置を推定します。

↓3DF Zephyrで特徴点の座標から点群を求めたところ↓
![3dz1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/53c582d5-2910-de7c-47ce-af58684bebe6.jpeg)

###５．特徴点のグループ化、ポリゴン生成
あとは、特徴点をグループ化して、ポリゴン化します。
↓点群をグループ化して、ポリゴンを生成↓
![3dz2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/dfe13e98-1e35-e3fc-d056-db9f63a8889c.jpeg)

↓テクスチャ塗り塗り↓
![3dz3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/067ce44d-c5f8-0bb5-08c6-fbdb72e1be51.jpeg)

O'REILLYの詳細OpenCV３にはグループ化の説明に下のような説明。機械学習のラベリングと同じような感じ？
（自分がへっぽこなのでよく判ってない＞＜）
![opencva.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/f618ed21-04a3-6336-d6c7-84cc23ae3a6d.jpeg)

##んで、上手くいく例と行かない例で何が違うか
OpenCVの特徴点を抽出する処理を、冒頭の木の幹と柱の画像に掛けてみます。
###木の幹の画像に特徴点抽出をした結果
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/76bbc9a5-894c-c1cc-4182-ba283eac9d3a.jpeg" width="640px" >
↓拡大したもの。幹の表面の凹凸をこれでもかってくらい拾っています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/762f92ee-08ff-2267-f7c0-95bb0d625220.jpeg" width="600px" >

###柱の画像に特徴点抽出をした結果
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/ead3948c-70c9-9e41-f37d-0da1e861d718.jpeg" width="640px" >
上手くいったケースに比べて、特徴点の検出に偏りが出ています。
特に柱なんかは、大きい割に特徴点が少ないです。

##特徴点になるのはどこ？
###図形を特徴点抽出してみる
直線と円形の図形をAkaze.detectAndComputeで処理してみます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/079fd5bd-aa31-3758-3edf-a83410b12f7e.jpeg" width="640px" >
↓特徴点抽出結果↓
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/2d82cb65-1835-06c4-53c3-d5dea27ac50f.jpeg" width="640px" >
曲線と角に特徴点が発生していますが、直線部分がスルーされています。なんということだ。

O'REILLYの詳細OpenCV３には特徴点の説明に下のような説明。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/cafb6d4c-f8d0-0d6f-b72c-e8fa5ca4726b.jpeg" width="640px" >
グレースケールで、周辺のドットとの差異が大きいドットを特徴点としている模様。
###直線は特徴点にならない
↓先ほどの画像を拡大したもの。角の内側が特徴点になっています。↓
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/7a3feb2b-8464-c4fe-0ba5-37a529fbef61.jpeg" width="800px" >
Ａ点（黒）を基準に周囲の８近傍のドットと比較した場合、黒３：白５（38％一致　特徴にする）
Ｂ点（白）を基準に周囲の８近傍のドットと比較した場合、黒５：白３（38％一致　特徴にする）
と、中心と異なる値のドットが多いです。

逆に特徴点となっていない直線は
Ｃ点（白）を基準に周囲の８近傍のドットと比較した場合、黒３：白５（62％一致）
Ｄ点（黒）を基準に周囲の８近傍のドットと比較した場合、黒５：白３（62％一致）
と、中心と同じ値のドットが多いです。
角の外側（Ａ点の左上など）なんかは、白が起点になって、黒１：白７ですね。（88％一致）

周辺のドットとの差異が大きいものを特徴点とする場合は、角の内側が特徴として強く、直線は特徴としては弱いことになります。
これが柱や平面に特徴点が発生しない理由になる。




例えば冒頭のマフィンの画像の場合も、
![マフィン.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/02e3a8c9-fcea-b3cd-5f65-a2d9b24b7582.gif)

特徴点を抽出するとマフィン周辺の立体データはマフィンの粉が特徴点になり、いい感じに立体化されていていますが。（粉スゲー）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/eb9e4ccf-dd47-512e-6cb6-4f71cb65ddd7.jpeg" width="640px" >


一方で、紙コップは崩れていました。特徴点で見ると見事に特徴点が取れません。背景のトレイと色相も明度も近いせいでしょうか。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/6fafbb36-d8f5-9bc1-1249-645d7c408a29.jpeg" width="640px" >

3DFZephyrの内部処理がどうなっているかは分かりませんが、Akazeの特徴点検出と傾向が似ているので参考にはなるかと思います。

##得られた知見
直線、平面は特徴点の判定条件のため無視されがち。そういう処理なのだから飲むしかない。
ボケ、ブレを減らし、小さな傷や粉などを特徴点として捉えることでフォロー。

それで駄目らなマフィンの粉をかけろ！


って書いたけど、美味い人がやると弁当箱の直線とか取れてるんですよね。教えて偉い人。