LPRnet輕量級實時車牌識別,主網絡代碼以及論文思路簡要介紹


 

LPRnet輕量級實時車牌識別

簡述LPRnet特點

LPRNet由輕量級的卷積神經網絡組成,所以它可以采用端到端的方法來進行訓練。據我們所知,LPRNet是第一個沒有采用RNNs實時車牌識別系統。因此,LPRNet算法可以為LPR創建嵌入式部署的解決方案,即便是在具有較高挑戰性的中文車牌識別上。

骨干網絡的結構在表[3]中進行了描述。骨干網絡獲取原始的RGB圖片作為輸入,並且計算出大量特征的空間分布。寬卷積(1*13的卷積核)利用本地字符的上下文從而取代了基於LSTM的RNN網絡。骨干子網絡的輸出可以被認為是一個代表對應字符可能性的序列,它的長度剛到等於輸入圖像的寬度。由於解碼器的輸出與目標字符序列的長度是不一致的,因此采用了CTC損失函數,無需分割的端到端訓練。CTC 損失函數是一種廣泛地用於處理輸入和輸出序列不對齊的方法。

LPRnet網絡結構如下表:

 

 small basic block主要是Inception結構

 借鑒parsenet,嵌入全局上下文特征

為了進一步地提升模型的表現,增強解碼器所得的中間特征圖,采用用全局上下文關系進行嵌入[12]。它是通過全連接層對骨干網絡的輸出層進行計算,隨后將其平鋪到所需的大小最后再與骨干網絡的輸出進行拼接 ,  加入GAP思想源於Parsenet,parsenet主要圖:,右側部分為加入GAP拼接到feature map上進行識別的表示。

 

 結果--速度:

LPRNet簡化模型被移植到各種硬件平台,包括CPU,GPU和FPGA。 結果如表6所示

 

主網絡代碼:

import torch.nn as nn
import torch
#定義samll_basic_block模塊,借鑒Inception模塊,通過1*3和3*1的卷積核來提取長寬比異常的圖像特征,同時減少參數量。
class small_basic_block(nn.Module):
    def __init__(self, ch_in, ch_out):
        super(small_basic_block, self).__init__()
        self.block = nn.Sequential(
            nn.Conv2d(ch_in, ch_out // 4, kernel_size=1),
            nn.ReLU(),
            nn.Conv2d(ch_out // 4, ch_out // 4, kernel_size=(3, 1), padding=(1, 0)),
            nn.ReLU(),
            nn.Conv2d(ch_out // 4, ch_out // 4, kernel_size=(1, 3), padding=(0, 1)),
            nn.ReLU(),
            nn.Conv2d(ch_out // 4, ch_out, kernel_size=1),
        )
    def forward(self, x):
        return self.block(x)

class LPRNet(nn.Module):
    def __init__(self, lpr_max_len, phase, class_num, dropout_rate):
        super(LPRNet, self).__init__()
        self.phase = phase
        self.lpr_max_len = lpr_max_len
        self.class_num = class_num
        self.backbone = nn.Sequential(
            nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1), # 0
            nn.BatchNorm2d(num_features=64),
            nn.ReLU(),  # 2
            nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(1, 1, 1)),
            small_basic_block(ch_in=64, ch_out=128),    # *** 4 ***
            nn.BatchNorm2d(num_features=128),
            nn.ReLU(),  # 6
            nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(2, 1, 2)),
            small_basic_block(ch_in=64, ch_out=256),   # 8
            nn.BatchNorm2d(num_features=256),
            nn.ReLU(),  # 10
            small_basic_block(ch_in=256, ch_out=256),   # *** 11 ***
            nn.BatchNorm2d(num_features=256),   # 12
            nn.ReLU(),
            nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(4, 1, 2)),  # 14
            nn.Dropout(dropout_rate),
            nn.Conv2d(in_channels=64, out_channels=256, kernel_size=(1, 4), stride=1),  # 16
            nn.BatchNorm2d(num_features=256),
            nn.ReLU(),  # 18
            nn.Dropout(dropout_rate),
            nn.Conv2d(in_channels=256, out_channels=class_num, kernel_size=(13, 1), stride=1), # 20
            nn.BatchNorm2d(num_features=class_num),
            nn.ReLU(),  # *** 22 ***
        )
    #通過1*13的異形卷積核提取特征,寬卷積(1*13的卷積核)利用本地字符的上下文從而取代了基於LSTM的RNN網絡
    #為了調整映射到每一個字符類的特征的深度,采用了1×1的卷積,作用等同論文中提到的(為了進一步地提升模型的表現,預增強解碼器所得的中間特征圖,
    #采用用全局上下文關系進行嵌入。它是通過全連接層對骨干網絡的輸出層進行計算,隨后將其平鋪到所需的大小最后再與骨干網絡的輸出並拼接起來)
        self.container = nn.Sequential(
            nn.Conv2d(in_channels=448+self.class_num, out_channels=self.class_num, kernel_size=(1, 1), stride=(1, 1)),
            # nn.BatchNorm2d(num_features=self.class_num),
            # nn.ReLU(),
            # nn.Conv2d(in_channels=self.class_num, out_channels=self.lpr_max_len+1, kernel_size=3, stride=2),
            # nn.ReLU(),
        )

    def forward(self, x):
#保存不同層的特征,目的用於下邊拼接global_context特征
        keep_features = list()
        for i, layer in enumerate(self.backbone.children()):
            x = layer(x)
            if i in [2, 6, 13, 22]: # [2, 4, 8, 11, 22]
                keep_features.append(x)
#GAP提取全局平均池化特征,拼接起來送入識別container(1*1的卷積)
        global_context = list()
        for i, f in enumerate(keep_features):
            if i in [0, 1]:
                f = nn.AvgPool2d(kernel_size=5, stride=5)(f)
            if i in [2]:
                f = nn.AvgPool2d(kernel_size=(4, 10), stride=(4, 2))(f)
            f_pow = torch.pow(f, 2)
            f_mean = torch.mean(f_pow)
            f = torch.div(f, f_mean)
            global_context.append(f)

        x = torch.cat(global_context, 1)
        x = self.container(x)
        logits = torch.mean(x, dim=2)

        return logits

def build_lprnet(lpr_max_len=8, phase=False, class_num=66, dropout_rate=0.5):

    Net = LPRNet(lpr_max_len, phase, class_num, dropout_rate)

    if phase == "train":
        return Net.train()
    else:
        return Net.eval()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM