国产性生交xxxxx免费-国产中文字幕-啊灬啊灬啊灬快灬高潮了,亚洲国产午夜精品理论片在线播放 ,亚洲欧洲日本无在线码,色爽交视频免费观看

鍋爐信息網 > 鍋爐知識 > 鍋爐百科

BEV-LSS

發布時間:

前言Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3Dcode:https://github.co

前言

Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D

code:https://github.com/nv-tlabs/lift-splat-shoot

LSS是ECCV 2020年的一篇從BEV角度做3D檢測及規劃的文章,是圈內公認的基于BEV做3D感知的開山之作。要搞明白基于BEV的3D目標檢測方法,LSS是基礎,有助于后續文章的理解,目前很多文章講解LSS的流程和框架,我從自己理解的過程結合代碼對這篇經典的文章做個解析,主要分析理解前兩步,即Lift和Splat,廢話少說,直接進入主題。

主要涉及幾個問題

1、每個攝像頭如何構建3D特征;

2、如何將構建的特征投影到BEV空間;

3、如何編碼投影到BEV空間的特征。

以上的3個問題就涉及到Lift, Splat的操作,弄清了上述三個問題,就弄清了全文的操作。

從理論上來看,整個文章中解釋Lift和Splat的部分是比較少的,大概也就一頁半的篇幅。

下圖就是Lift 和 Splat的流程。

前置說明

輸入:每個時刻下,環視攝像頭的圖像,攝像頭的內外參,訓練和測試中不需要任何深度傳感器

輸出:對于3D目標檢測來說,BEV視角下的3D坐標和目標類別。

構建3D特征

n個攝像頭同一時刻的表示: ,每個攝像頭的內外參為: ,構建了一個BEV的坐標系, ,目的是最終將2D特征都投影到構建的y坐標上,即投影到BEV空間上。

每個攝像頭相互是獨立的,單目攝像頭獲得深度信息是個比較難的事情,那在Lift中的解決方法中,生成了每個像素所有深度的可能性特征。

首先,深度做了離散化的處理,每個像素的深度劃分了D份,那這個像素的所有深度值為 , 就是每個深度的最小單位,比如 。

比如,一個攝像頭拍攝到的圖像的像素坐標為(h,w),這個像素點所對應的深度 ,在這一步的轉換中是沒有參數,每個像素在所有的深度值都是有可能存在的。通過這種方式,就創建了大批量的密集的點云數據,點云的數量為 。每個點就是2D圖像的特征。

點云特征是如何構建,可以從代碼上來看更清晰:

#這里是如何從2D圖像特征轉到3D相關的特征上ndef get_cam_feats(self, x):n """Return B x N x D x H/downsample x W/downsample x Cn """n B, N, C, imH, imW = x.shapenn # 首先將環視圖像和batch拍在同一個維度上,因為每個攝像頭的特征是相互獨立的n x = x.view(B*N, C, imH, imW)n n # 這個函數是提取2D特征,并把特征與3D位置做關聯,也就是Lift操作,下面的代碼段解釋是如何操作的。n x = self.camencode(x)n # camencode函數輸出的特征是 (B*N) * CamC * D * feaH * feaW,將第一維B*N拆開n x = x.view(B, N, self.camC, self.D, imH//self.downsample, imW//self.downsample)n # 將channle維度放到最后一維上n x = x.permute(0, 1, 3, 4, 5, 2)n return x


class CamEncode(nn.Module):n def __init__(self, D, C, downsample):n super(CamEncode, self).__init__()n self.D = Dn self.C = Cn n # 構建的網絡結構n self.trunk = EfficientNet.from_pretrained("efficientnet-b0")nn self.up1 = Up(320+112, 512)n # depthnet的channle是深度+特征維度n self.depthnet = nn.Conv2d(512, self.D + self.C, kernel_size=1, padding=0)nn def get_depth_dist(self, x, eps=1e-20):n # 得到深度的預測值,采用softmax預測哪個深度的概率大n return x.softmax(dim=1)nn def get_depth_feat(self, x):n x = self.get_eff_depth(x)n # Depthn x = self.depthnet(x)nn depth = self.get_depth_dist(x[:, :self.D])n # 這里是重點,將深度和特征做乘法,變為(B*N) * CamC * D * feaH * feaWn new_x = depth.unsqueeze(1) * x[:, self.D:(self.D + self.C)].unsqueeze(2)nn return depth, new_xnn def get_eff_depth(self, x):n ...n return xnn def forward(self, x):n depth, x = self.get_depth_feat(x)n # 返回深度和特征整合的特征,(B*N) * CamC * D * feaH * feaWn return x

構建的特征投影到BEV空間

從文章上講的,主要是分2步,1)將構建的點云特征通過pillar的方式投影到預先構建的BEV的空間;2)對于在同一個pillar中重復的點云做融合。

還是從代碼上來做解釋說明:

xbound=[-50.0, 50.0, 0.5],n ybound=[-50.0, 50.0, 0.5],n zbound=[-10.0, 10.0, 20.0],n dbound=[4.0, 45.0, 1.0],n def create_frustum(self):n # make grid in image planen ## 輸入網絡的圖片大小n ogfH, ogfW = self.data_aug_conf['final_dim']n fH, fW = ogfH // self.downsample, ogfW // self.downsamplen # 在 fH和fW上構建[4.0, 45.0, 1.0]的深度值n ds = torch.arange(*self.grid_conf['dbound'], dtype=torch.float).view(-1, 1, 1).expand(-1, fH, fW)n D, _, _ = ds.shapen #寬和高 按照輸入網絡的圖片尺寸來構建n xs = torch.linspace(0, ogfW - 1, fW, dtype=torch.float).view(1, 1, fW).expand(D, fH, fW)n ys = torch.linspace(0, ogfH - 1, fH, dtype=torch.float).view(1, fH, 1).expand(D, fH, fW)nn # D x H x W x 3n # 構建成視錐,寬高的尺寸與輸出特征一致n frustum = torch.stack((xs, ys, ds), -1)n return nn.Parameter(frustum, requires_grad=False)

接下來就是真正構建bev特征的過程:

## 這個函數構建BEV特征的入口,其中包含了2D 3D的投影關系 -> 2D特征提取,n ## 以及從2D特征構建3D特征 -> 特征投影到BEV空間的過程n def get_voxels(self, x, rots, trans, intrins, post_rots, post_trans):n ##參數說明: x是環視圖片 B x N x 3 x orgH x orgWn ##rots、trans 每張圖片對應的相機外參,旋轉和平移n ##intrins 每張圖片對應的相機內參n ##post_rots, post_trans 每張圖片在訓練過程中,圖片數據增強中用到的旋轉和平移nn ## 這個函數的目的,是為了找到每個相機中,視錐中的每個點對應在BEV空間中的位置關系。 n geom = self.get_geometry(rots, trans, intrins, post_rots, post_trans)n ## Lift的部分已經說明了n x = self.get_cam_feats(x)n ## 跟蹤geom 和 x 構建bev特征n x = self.voxel_pooling(geom, x)n return xnn def get_geometry(self, rots, trans, intrins, post_rots, post_trans):n """Determine the (x,y,z) locations (in the ego frame)n of the points in the point cloud.n Returns B x N x D x H/downsample x W/downsample x 3n """n B, N, _ = trans.shapenn # undo post-transformation ## 視錐上加上數據增強的旋轉和平移n # B x N x D x H x W x 3n ## 由于每個相機的視錐都一樣的,所以這里的frustum所有圖片都可以共用。n ## frustum矩陣也可以根據旋轉平移矩陣的大小做broadcasen points = self.frustum - post_trans.view(B, N, 1, 1, 1, 3)n points = torch.inverse(post_rots).view(B, N, 1, 1, 1, 3, 3).matmul(points.unsqueeze(-1))nn # cam_to_egon ## 這里就是2D 3D轉換中用的公式,在uv的坐標上乘上scale尺度的操作n points = torch.cat((points[:, :, :, :, :, :2] * points[:, :, :, :, :, 2:3],n points[:, :, :, :, :, 2:3]n ), 5)n ## 內參矩陣 * 外參的旋轉矩陣n combine = rots.matmul(torch.inverse(intrins))n ## 相乘再加上平移舉證,從而得到每個攝像頭下視錐所對應的世界坐標系位置。n points = combine.view(B, N, 1, 1, 1, 3, 3).matmul(points).squeeze(-1)n points += trans.view(B, N, 1, 1, 1, 3)n n #輸出的point維度:B x N x D x H/downsample x W/downsample x 3n return pointsnn def voxel_pooling(self, geom_feats, x):n B, N, D, H, W, C = x.shapen Nprime = B*N*D*H*Wnn # flatten xn 將所有的特征拉平n x = x.reshape(Nprime, C)nn # flatten indicesn # 構建的世界坐標下的對應關系,做了平移沒有負半軸的數,并限制了dx只有一個像素,同時long()是將數值變成整型。n # 將數值變為整型的目的是,知道了那些點是在BEV的同一個格子里,在同一個格子里的特征需要做特征融合n geom_feats = ((geom_feats - (self.bx - self.dx/2.)) / self.dx).long()n ## 和特征一樣,將前面的維度都拉平,這里geom的位置關系和x的位置關系是一一對應的。n geom_feats = geom_feats.view(Nprime, 3)n #由于拉平后丟失了batch的信息,將batch id放在geom_feats的最后一維上。n batch_ix = torch.cat([torch.full([Nprime//B, 1], ix,n device=x.device, dtype=torch.long) for ix in range(B)])n geom_feats = torch.cat((geom_feats, batch_ix), 1)nn # filter out points that are outside boxn # 過濾掉超過bev空間的點n kept = (geom_feats[:, 0] >= 0) & (geom_feats[:, 0] < self.nx[0])n & (geom_feats[:, 1] >= 0) & (geom_feats[:, 1] < self.nx[1])n & (geom_feats[:, 2] >= 0) & (geom_feats[:, 2] < self.nx[2])n x = x[kept]n geom_feats = geom_feats[kept]nn # get tensors from the same voxel next to each othern # 判斷哪些點是處于相同的格子里,ranks相同的值表示在同一個BEV的格子里n ranks = geom_feats[:, 0] * (self.nx[1] * self.nx[2] * B)n + geom_feats[:, 1] * (self.nx[2] * B)n + geom_feats[:, 2] * Bn + geom_feats[:, 3]n # 對得到的ranks排序的索引n sorts = ranks.argsort()n # 根據選擇的點和排序后的索引選擇x 和 geom_feats, 也對ranks排序n x, geom_feats, ranks = x[sorts], geom_feats[sorts], ranks[sorts]nn # cumsum trickn ## 這里就是跟蹤ranks來做特征融合,如果有相同的rank,表示在同一個格子里需要做特征融合。n ## 融合后的特征與攝像頭就沒沒關系了,所以這里geom_feats就沒有N這個維度了n if not self.use_quickcumsum:n x, geom_feats = cumsum_trick(x, geom_feats, ranks)n else:n x, geom_feats = QuickCumsum.apply(x, geom_feats, ranks)nn # griddify (B x C x Z x X x Y)n # 構建BEV特征,此時Z還沒有被壓縮。將geom特征索引對應填充融合后的x特征n final = torch.zeros((B, C, self.nx[2], self.nx[0], self.nx[1]), device=x.device)n final[geom_feats[:, 3], :, geom_feats[:, 2], geom_feats[:, 0], geom_feats[:, 1]] = xnn # collapse Z 壓縮Z維度,變成BEV特征n final = torch.cat(final.unbind(dim=2), 1)nn return final

BEV特征編碼

這一部分就比較簡單了,BEV特征其實就變成了一個2維特征,按照通常的2D卷積編碼即可。

class BevEncode(nn.Module):n def __init__(self, inC, outC):n super(BevEncode, self).__init__()nn trunk = resnet18(pretrained=False, zero_init_residual=True)n self.conv1 = nn.Conv2d(inC, 64, kernel_size=7, stride=2, padding=3,n bias=False)n self.bn1 = trunk.bn1n self.relu = trunk.relunn self.layer1 = trunk.layer1n self.layer2 = trunk.layer2n self.layer3 = trunk.layer3nn self.up1 = Up(64+256, 256, scale_factor=4)n self.up2 = nn.Sequential(n nn.Upsample(scale_factor=2, mode='bilinear',n align_corners=True),n nn.Conv2d(256, 128, kernel_size=3, padding=1, bias=False),n nn.BatchNorm2d(128),n nn.ReLU(inplace=True),n nn.Conv2d(128, outC, kernel_size=1, padding=0),n )nn def forward(self, x):n x = self.conv1(x)n x = self.bn1(x)n x = self.relu(x)nn x1 = self.layer1(x)n x = self.layer2(x1)n x = self.layer3(x)nn x = self.up1(x, x1)n x = self.up2(x)nn return x

得到BEV特征后,后面的過程如果需要做3D目標檢測,則跟蹤生成的BEV空間的gt監督BEV空間預測的目標。

小結

總的來說這篇paper開創了,在不采用直接深度估計的方式,利用環視輸入來構建BEV特征,從而在BEV空間做目標檢測和運動規劃。是個非常不錯的創新點,后續很多研究也是圍繞該paper做改進和創新的。

當然,也有文章論證這種直接將深度和特征融合的方式并不能很好構建深度信息,Simple-BEV- What Really Matters for Multi-Sensor BEV Perception?文章有做對比實驗說明這種隱式的深度構建方式和隨機深度沒有太大區別。

以上是個人對LSS讀后筆記,有不對的地方歡迎指正。

參考文獻

[1] Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D

[2] Simple-BEV- What Really Matters for Multi-Sensor BEV Perception

精選推薦

  • 711關東煮供應商
    711關東煮供應商

    今天給大家介紹三位,奶粉,全家、羅森這些便利店里關東煮的供應商。店里賣三四塊錢一串的關東煮,在網上買不到,一塊錢就搞定。首先關東

  • 健康日歷|高壓鍋容易爆炸的4個原因
    健康日歷|高壓鍋容易爆炸的4個原因

    來源:醫藥養生保健報設計:李雅琴醫學審核:姜峰出品人:胡麗麗

  • 高爐
    高爐

    今天這活卻是個白事,等到了時辰,那家人便準備火化,本來準備送普爐,我卻心中一動,便對那家人說道:“這老人走也不要省,還是送高爐吧?!?/p>

  • 高壓鍋和電壓力鍋的區別,推薦幾款點壓力鍋
    高壓鍋和電壓力鍋的區別,推薦幾款點壓

    記得之前有一次去朋友家玩,他正在用高壓鍋煮小米粥,是的,高壓鍋壓小米粥,大概煮了半小時,高壓鍋突然爆炸了,現場慘不忍睹啊,幸好廚房里沒

0