写真から3Dモデルを作る方法を思いついた その03３次元空間への投影

どーもKsukeです。　
写真から3Dモデルを作る方法を思いついたその03で、３次元空間への投影をやっていきます。
その2はこちらhttps://qiita.com/Ksuke/items/8a3a2faa90263b439f8b

<b>※注意※</b>
この記事は思いついて試した事の末路を載せているだけなので、唐突なネタやBad Endで終わる可能性があります。
##やってみる
<b>手順</b>
1.画像(2次元データ)を3次元データへ拡張
2.向きの調整
3.重ね合わせ

ところどころにあるコードは、最後にまとめたものを載せてあります。

###1.画像(2次元データ)を3次元データへ拡張
　画像は2次元のデータのまま3次元空間に投影します。投影という言葉を使っているのは、画像のオブジェクトに光を当て、光が当たらない影の部分を取り出すように値を置いていくからです。イメージとしては、100枚の画像を重ね合わせて空間にしているようなものです。
　こうすることで、
・前方から見るとオブジェクトを正面から見たときのシルエットと一致する空間
・前方から見るとオブジェクトを側面から見たときのシルエットと一致する空間
・前方から見るとオブジェクトを上面から見たときのシルエットと一致する空間
が出来上がります。

```python:3次元データへ拡張

#2次元の画像を、3次元方向に引き延ばして投影する。
def imgProject(img,imgSize):
    
    #画像をz軸方向に繰り返し配置することで、画像に厚みを持たせ引き延ばして投影する
    projectSpace = np.tile(img[:,:,None],(1,1,imgSize))
    
    #投影した空間を返す
    return projectSpace


#背景分離した画像を3次元空間に点群のオブジェクトとして投影する(トレース・オン！)
imgProjectSpaces = [imgProject(sepBackImg,imgSize) for sepBackImg in sepBackImgs]
```

###2.向きの調整

　画像を3次元空間に投影しましたが、各空間のオブジェクトの向きはバラバラです。オブジェクトを正面から撮った画像から作った空間の前方にはオブジェクトの正面が来ているでしょうし、オブジェクトを側面から撮った画像から作った空間の前方にはオブジェクトの側面が来ているはずです。
　ここで向きをそろえるために、軸の入れ替えを行います。全ての空間で、空間の前方にオブジェクトの正面が来るようにします。
　こうすることで
・前方から見るとオブジェクトを正面から見たときのシルエットと一致する空間
・側方から見るとオブジェクトを側面から見たときのシルエットと一致する空間
・上方から見るとオブジェクトを上面から見たときのシルエットと一致する空間
が出来上がります。

```python:向きの調整

#元にした画像によって空間の前方に来ている面が、コップの正面、側面、上面とバラバラなので、
#すべての空間において前方にコップの正面が来るように、軸を入れ替え向きをそろえる
transposeValues = [(0,1,2),(0,2,1),(2,1,0)]
transposedSpaces = [imgProjectSpace.transpose(*transposeValue) for imgProjectSpace,transposeValue in zip(imgProjectSpaces,transposeValues)]
```

###3.重ね合わせ
　各空間の向きが合わさったら、すべての空間を重ねていきます。重ねることで、前方、側方、上方のいずれから見てもオブジェクトのシルエットと一致する空間が出来上がります。

```python:重ね合わせ

#各空間の点群を重ね合わせて、正面、側面、上面のいづれから見ても写真と同じシルエットに見える点群を作成する
imgProjectSpace = transposedSpaces[0]
for transposedSpace in transposedSpaces[1:]:
    imgProjectSpace = imgProjectSpace*transposedSpace
```

##動作確認
最後にコードが問題なく動くか確認。

###1.3次元データへ拡張の確認
下のコードをblenderで実行して

```python:動確用1

#投影したばかりの空間から、点群の位置の座標のlistを作る
imgCoords = [binary2coords(imgProjectSpace) for imgProjectSpace in imgProjectSpaces]

#各画像の頂点のずらし幅
offsets = [[-50,-150,-50],[-50,-50,-50],[-50,50,-50]]

#各画像の頂点をオブジェクトとして登録するときの名前
names = ['frontSpace','sideSpace','topSpace']

#頂点を描画
[addObj(coords=imgCoord,name = name,offset=offset) for imgCoord,name,offset in zip(imgCoords,names,offsets)]
    
```
こんな感じのオブジェクト(という名の点群)が表示されれば成功。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699258/80544aec-3510-0f94-169a-0ee82c0c98a8.png)

###2.向きの調整の確認
下のコードをblenderで実行して

```python:動確用2

#軸を調整した空間から、点群の位置の座標のlistを作る
imgCoords = [binary2coords(transposedSpace) for transposedSpace in transposedSpaces]

#各画像の頂点のずらし幅
offsets = [[-150,-150,-50],[-150,-50,-50],[-150,50,-50]]

#各画像の頂点をオブジェクトとして登録するときの名前
names = ['frontTransposedSpace','sideTransposedSpace','topTransposedSpace']

#頂点を描画
[addObj(coords=imgCoord,name = name,offset=offset) for imgCoord,name,offset in zip(imgCoords,names,offsets)]
```
こんな感じのオブジェクト(という名の点群)が表示されれば成功。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699258/fcb3e357-6906-61bf-50f7-6dfc89537e8c.png)

###3.重ね合わせの確認
下のコードをblenderで実行して

```python:動確用3
addObj(coords=binary2coords(imgProjectSpace),name = "objectSpace",offset=[-250,-50,-50])
```
こんな感じのオブジェクト(という名の点群)が表示されれば成功。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699258/49d2fb7e-301a-1aa0-474a-019f1d607b22.png)


ちなみに上の確認用3つを同時に実行するとこんな感じ
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699258/97f4b0f4-c6ca-8d1d-85d7-039e79729219.png)

##次は？
やっと点群でのオブジェクトの3次元表示ができたので、次は点群からポリゴンやその頂点を生成していきたいと思います。

2020/9/18追記
その4を公開しました。
https://qiita.com/Ksuke/items/144c06f128b015b001dd

##コードまとめ
前回のコードの後ろに追加すれば動くはずです。

###関数編
```python:コードまとめ(関数編)

#2次元の画像を、3次元方向に引き延ばして投影する。
def imgProject(img,imgSize):
    
    #画像をz軸方向に繰り返し配置することで、画像に厚みを持たせ引き延ばして投影する
    projectSpace = np.tile(img[:,:,None],(1,1,imgSize))
    
    #投影した空間を返す
    return projectSpace
```

###実行コード編
```python:コードまとめ(実行コード編)

#背景分離した画像を3次元空間に点群のオブジェクトとして投影する(トレース・オン！)
imgProjectSpaces = [imgProject(sepBackImg,imgSize) for sepBackImg in sepBackImgs]

#元にした画像によって空間の前面に来ている面が、コップの正面、側面、上面とバラバラなので、
#すべての空間において前面にコップの正面が来るように、軸を入れ替え向きをそろえる
transposeValues = [(0,1,2),(0,2,1),(2,1,0)]
transposedSpaces = [imgProjectSpace.transpose(*transposeValue) for imgProjectSpace,transposeValue in zip(imgProjectSpaces,transposeValues)]

#各空間の点群を重ね合わせて、正面、側面、上面のいづれから見ても写真と同じシルエットに見える点群を作成する
imgProjectSpace = transposedSpaces[0]
for transposedSpace in transposedSpaces[1:]:
    imgProjectSpace = imgProjectSpace*transposedSpace

print("step03:projection of image in 3D space is success\n")


#以下確認表示用(メインの流れと関係ないので、次の回では多分消えてる)

#投影したばかりの空間から、点群の位置の座標のlistを作る
imgCoords = [binary2coords(imgProjectSpace) for imgProjectSpace in imgProjectSpaces]

#各画像の頂点のずらし幅
offsets = [[-50,-150,-50],[-50,-50,-50],[-50,50,-50]]

#各画像の頂点をオブジェクトとして登録するときの名前
names = ['frontSpace','sideSpace','topSpace']

#頂点を描画
[addObj(coords=imgCoord,name = name,offset=offset) for imgCoord,name,offset in zip(imgCoords,names,offsets)]
    

#軸を調整した空間から、点群の位置の座標のlistを作る
imgCoords = [binary2coords(transposedSpace) for transposedSpace in transposedSpaces]

#各画像の頂点のずらし幅
offsets = [[-150,-150,-50],[-150,-50,-50],[-150,50,-50]]

#各画像の頂点をオブジェクトとして登録するときの名前
names = ['frontTransposedSpace','sideTransposedSpace','topTransposedSpace']

#頂点を描画
[addObj(coords=imgCoord,name = name,offset=offset) for imgCoord,name,offset in zip(imgCoords,names,offsets)]
    

addObj(coords=binary2coords(imgProjectSpace),name = "objectSpace",offset=[-250,-50,-50])
```