作者|張強來源|相信許多讀者體驗過b站上的全景視頻,如果還沒有,快來體驗一下吧[1]!只需鼠標點擊并移動,便可360度無死角的瀏覽全景視頻,讓人如同身臨其境。全景圖像用ai切割圖片,又稱360°全景圖,其數據分布在球面空間上。但是,當我們將全景圖像展開時,會造成畸變。
怎么處理?直接將傳統二維平面圖像處理方法應用到球面數據上,其效果則會大大降低。而要解決分布在球面空間上的數據,需要特定的方法,比如球面卷積網絡。本文手把手帶你實踐一個有趣的應用——全景圖像語義分割,使用多種傳統CNN方法和球面CNN方法進行對比。如下圖所示,全景圖分割實例像素級別分類,每種實例對應一個標簽。完成本教程后,你將能夠做一個圖中所示的全景圖小應用。
文章數據集獲取與代碼地址見文末1. 環境構建基于深度學習的編程環境往往有各種復雜的環境依賴,而各種安裝報錯總是消磨我們的時間,其實之一過程可以大大縮短。我們所需要的也就是通過一個命令安裝所有的依賴并打開環境
make?up?#等價于?docker-compose?up?-d
再通過一個命令
make?in
來進入我們需要的環境,然后運行程序。為實現構建這一過程,基于 –- – make來搭建我們的環境,其原理如下圖所示:
–- – make三個工具對應三個配置文件,都在項目根目錄進行了聲明:
Dockerfile
docker-compose.yml
Makefile
其中本文實驗環境:.04,.0,.安裝
#?1.安裝docker
sudo?apt?install?-y?docker?docker.io
#?2.安裝英偉達docker
distribution=$(.?/etc/os-release;echo?$ID$VERSION_ID)?\
&&?curl?-s?-L?https://nvidia.github.io/nvidia-docker/gpgkey?|?sudo?apt-key?add?-?\
&&?curl?-s?-L?https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list?|?sudo?tee?/etc/apt/sources.list.d/nvidia-docker.list
sudo?apt-get?update
sudo?apt-get?install?-y?nvidia-docker2
#?3.安裝docker-compose(apt常常不能安裝最新版本的docker-compose)
pip?install?docker-compose
#?4.解決linux下docker的權限問題,將用戶放在docker組里
GROUPNAME=docker
getent?group?$GROUPNAME?2>&1?>/dev/null?||?groupadd?$GROUPNAME
sudo?usermod?-aG?docker?$(whoami)
#?5.重啟
sudo?systemctl?daemon-reload
sudo?systemctl?restart?docker
使用鏡像鏡像構建好之后,可以直接運行命令啟動鏡像,但是這樣不是最方便的。使用-搭配,具體操作如下:首先寫好-.yml啟動文件,可參考本項目對應的-.yml,接著,在里寫常見相關命令,我們將應用分為啟動(up)、關閉(down)、進入容器環境(in)三個需求,如下:
up:
?docker-compose?up?-d
down:
?docker-compose?down
in:
?docker-compose?exec?spherical-env?bash
本項目鏡像已上傳,可以直接使用下列命令下載
docker?pull?qiangzibro/spherical_image_segmentation
#?或者使用下面命令自己編譯
make?build
接著,一鍵完成編譯、啟動
make?up?#等價于?docker-compose?up?-d
再通過下列命令便可以進入終端
make?in
使用- logs可以看到對應的網址2. 數據獲取使用2D-3D-S 數據集進行本實驗,該數據集提供了來自 2D、2.5D 和 3D 域的各種相互注冊的數據,以及實例級語義和幾何注釋。它收集在來自 3 座不同建筑的 6 個大型室內區域。它包含超過 70,000 張 RGB 圖像,以及相應的深度、表面法線、語義注釋、全局 XYZ 圖像(均以常規和 360° 等距柱狀圖圖像的形式)以及相機信息。它還包括注冊的原始和語義注釋 3D 網格和點云。數據集從獲取,這個網站匯總了AI開發者常見的公開數據集,用戶可以對數據集進行可視化預覽、在線使用和下載等操作。這里我們不用下載,可以直接通過SDK讀取數據集,操作步驟如下:a. 打開本文對應數據集鏈接 b. 右上角注冊登錄c. fork數據集
d. 點擊網頁上方開發者工具,獲取使用SDK所需的,獲取到 后用ai切割圖片,將其存在項目根目錄的.py里。
KEY = " "
然后即可以通過可以上傳數據、讀取數據、使用數據,靈活對接模型開發和訓練,與數據快速集成。
e. 寫入后就可以寫代碼讀取數據了。
from?PIL?import?Image
dataset?=?Dataset("DatasetName",?gas)
segment?=?dataset[0]
for?data?in?segment:
???with?data.open()?as?fp:
???????image?=?Image.open(fp)
???????width,?height?=?image.size
???????image.show()
3. 方法使用多種二維CNN方法和球面卷積方法。其中,二維CNN有三種:UNet、和FCN;[3]參考自論文《 CNNs on Grids》,下面著重看一下的方法。對卷積算子進行定義:
class?MeshConv(_MeshConv):
????def?__init__(self,?in_channels,?out_channels,?mesh_file,?stride=1,?bias=True):
????????super(MeshConv,?self).__init__(in_channels,?out_channels,?mesh_file,?stride,?bias)
????????pkl?=?self.pkl
????????if?stride?==?2:
????????????self.nv_prev?=?pkl['nv_prev']
????????????L?=?sparse2tensor(pkl['L'].tocsr()[:self.nv_prev].tocoo())?#?laplacian?matrix?V->V
????????????F2V?=?sparse2tensor(pkl['F2V'].tocsr()[:self.nv_prev].tocoo())??#?F->V,?#V?x?#F
????????else:?#?stride?==?1
????????????self.nv_prev?=?pkl['V'].shape[0]
????????????L?=?sparse2tensor(pkl['L'].tocoo())
????????????F2V?=?sparse2tensor(pkl['F2V'].tocoo())
????????self.register_buffer("L",?L)
????????self.register_buffer("F2V",?F2V)
????????
????def?forward(self,?input):
????????#?compute?gradient
????????grad_face?=?spmatmul(input,?self.G)
????????grad_face?=?grad_face.view(*(input.size()[:2]),?3,?-1).permute(0,?1,?3,?2)?#?gradient,?3?component?per?face
????????laplacian?=?spmatmul(input,?self.L)
????????identity?=?input[...,?:self.nv_prev]
????????grad_face_ew?=?torch.sum(torch.mul(grad_face,?self.EW),?keepdim=False,?dim=-1)
????????grad_face_ns?=?torch.sum(torch.mul(grad_face,?self.NS),?keepdim=False,?dim=-1)
????????grad_vert_ew?=?spmatmul(grad_face_ew,?self.F2V)
????????grad_vert_ns?=?spmatmul(grad_face_ns,?self.F2V)
????????feat?=?[identity,?laplacian,?grad_vert_ew,?grad_vert_ns]
????????out?=?torch.stack(feat,?dim=-1)
????????out?=?torch.sum(torch.sum(torch.mul(out.unsqueeze(1),?self.coeffs.unsqueeze(2)),?dim=2),?dim=-1)
????????out?+=?self.bias.unsqueeze(-1)
????????return?out
分割網絡基于算子構建了一個Unet網絡:
4. 訓練環境構建好后只需簡單的幾個命令便可以運行起來
再使用make in成功進入到容器終端
cd?cnns
#?基于
./run.sh?UNet
#?基于FCN
./run.sh?FCN8s
#?基于ResNetDUCHDC
./run.sh?ResNetDUCHDC
腳本run.sh解釋
#?Model?choice
#?ResNetDUCHDC,FCN8s,UNet
#?Run?example
#?1)?./run.sh
#?2)?./run.sh?FCN8s
#?3)?./run.sh?ResNetDUCHDC
model="${1:-UNet}"
MESHFILES=../data/mesh_files
DATADIR=../data/2d3ds_pano_small/
#?create?log?directory
mkdir?-p?logs
python?train.py?\
--batch-size?16?\?#?訓練批量大小
--test-batch-size?16?\?#測試批量大小
--epochs?200?\?#?訓練epoch數量
--data_folder?$DATADIR?\
--mesh_folder?$MESHFILES?\?#?正二十面體網格文件位置
--fold?3?\?# K-fold交叉驗證,k=3。將原始數據分成K組(K-Fold),將每個子集數據分別做一次驗證集,其余的K-1組子集數據作為訓練集,這樣會得到K個模型。這K個模型分別在驗證集中評估結果,最后的誤差MSE(Mean Squared Error)加和平均就得到交叉驗證誤差。交叉驗證有效利用了有限的數據,并且評估結果能夠盡可能接近模型在測試集上的表現,可以做為模型優化的指標使用。
--log_dir?logs/log_${model}_f16_cv3_rgbd?\?#?日志目錄
--decay?\?#?學習率衰減
--train_stats_freq?5?\
--model?${model}?\?#模型選擇
--in_ch?rgbd?\?#?輸入數據通道
--lr?1e-3?\?#?學習路
--feat?16?#特征層的數量
基于對球面數據進行分割
cd?ugscnn
./run.sh
訓練200個epoch后,可得如下結果:
5. 測試
使用提供的測試腳本test.sh即可進行測試
#?基于UNet
./test.sh?UNet
#?基于FCN
./test.sh?FCN8s
#?基于ResNetDUCHDC
./test.sh?ResNetDUCHDC
測試結果保存在當前目錄下,命名格式為模型名+.npz,將其打開進行結果分析,如下所示。全景圖實例:
結果:
總結本文介紹了作為環境構建的知識,介紹幾種基于傳統CNN方法和一種基于球面CNN的方法,并將上述方法在全景數據集上完成了分割任務。數據集地址(代碼上傳在數據集討論區):
參考資料[ 1 ] : [ 2 ] : [ 3 ] : [ 4 ] :
往期回顧資訊技術資訊
資訊