EAST結(jié)構(gòu)分析+pytorch源碼實(shí)現(xiàn)

EAST結(jié)構(gòu)分析+pytorch源碼實(shí)現(xiàn)

@[toc]

一. U-Net的前車之鑒

在介紹EAST網(wǎng)絡(luò)之前我們先介紹一下前面的幾個(gè)網(wǎng)絡(luò),看看這個(gè)EAST網(wǎng)絡(luò)怎么來的做入?為什么來的氓拼?

當(dāng)然這里的介紹僅僅是引出EAST而不是詳細(xì)的講解其他網(wǎng)絡(luò)职辨,有需要的讀者可以去看看這三個(gè)優(yōu)秀網(wǎng)絡(luò)铁孵。

1.1 FCN網(wǎng)絡(luò)結(jié)構(gòu)

? FCN網(wǎng)絡(luò),在之前FCN從原理到代碼的理解已經(jīng)詳細(xì)分析了旧巾,有需要的可以去看看耸序,順便跑一跑代碼。

圖1-1
  • 網(wǎng)絡(luò)的由來

不管是識(shí)別(傳統(tǒng)機(jī)器學(xué)習(xí)鲁猩、CNN)還是檢測(SSD坎怪、YOLO等),都只是基于大塊的特征進(jìn)行的廓握,檢測之后都是以長方形去表示檢測結(jié)果搅窿,由于這是其算法內(nèi)部回歸的結(jié)果導(dǎo)致,而且feature map經(jīng)過卷積一直減小隙券,如果強(qiáng)行進(jìn)行256X256512X512的插值男应,那么結(jié)果可以想象,邊界非常不好娱仔。

那么如何實(shí)現(xiàn)圖1-1所示的結(jié)果呢沐飘?把每個(gè)像素都進(jìn)行分割?

  • 網(wǎng)絡(luò)的成果

FCN給出的方法是使用反卷積進(jìn)行上采樣操作牲迫,使得經(jīng)過CNN之后減小的圖能夠恢復(fù)大小耐朴。

當(dāng)然作者還提出一個(gè)好方法,不同的feature map進(jìn)行組合盹憎,使得感受野進(jìn)行擴(kuò)充筛峭。

注釋:筆者認(rèn)為使用反卷積有兩個(gè)作用,其一是使得計(jì)算LOSS比較方便陪每,標(biāo)簽和結(jié)果可以直接進(jìn)行計(jì)算影晓。其二是可以進(jìn)行參數(shù)的學(xué)習(xí),更為智能化奶稠。

1.2 U-NET網(wǎng)絡(luò)

U-net網(wǎng)絡(luò)之前沒怎么看過俯艰,現(xiàn)在也僅僅是大概看了論文和相關(guān)資料捡遍,內(nèi)部實(shí)現(xiàn)不是很了解锌订。

圖1-2
  • 網(wǎng)絡(luò)的由來

FCN完全可以做到基于像素點(diǎn)的分割,為什么還要這個(gè)U-net網(wǎng)絡(luò)盎辍辆飘?

FCN網(wǎng)絡(luò)檢測的效果還可以啦辐,但是其邊緣的處理就特別的差。雖然說多個(gè)層進(jìn)行合并蜈项,但是合并的內(nèi)容雜亂無章芹关,導(dǎo)致最后的信息沒有完全得到。

總的來說FCN分割的效果不夠紧卒,精度也不夠侥衬。

  • 網(wǎng)絡(luò)的成果

U-net提出了對稱的網(wǎng)絡(luò)結(jié)構(gòu),使得網(wǎng)絡(luò)參數(shù)的學(xué)習(xí)效果更好(為什么對稱網(wǎng)絡(luò)學(xué)習(xí)更好跑芳,這個(gè)理解不透轴总,如果是結(jié)果再放大一倍使得不對稱不也一樣嗎?感覺還是網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計(jì)的好博个,而不是對稱)

不同feature map合并的方式更加優(yōu)化怀樟,使得在邊緣分割(細(xì)節(jié))上更加優(yōu)秀。

網(wǎng)絡(luò)架構(gòu)清晰明了盆佣,分割效果也很好往堡,現(xiàn)在醫(yī)學(xué)圖像分割領(lǐng)域還能看見身影。

1.3 CTPN網(wǎng)絡(luò)

剛開始準(zhǔn)備使用CTPN進(jìn)行文本的檢測共耍,所以看了一些相關(guān)資料虑灰,致命缺點(diǎn)是不能檢測帶角度文字和網(wǎng)絡(luò)比較復(fù)雜。

圖1-3
  • 網(wǎng)絡(luò)的由來

文本檢測和其他檢測卻別很大征堪,比如用SSD檢測文本就比較困難(邊緣檢測不好)瘩缆,如何針對文本進(jìn)行檢測?

  • 網(wǎng)絡(luò)的成果

CTPN網(wǎng)絡(luò)有很多創(chuàng)造的想法-->>

目標(biāo)分割小塊佃蚜,然后一一進(jìn)行檢測庸娱,針對文本分割成height>width的方式,使得檢測的邊緣更為精確谐算。

使用BiLSTM對小塊進(jìn)行連接熟尉,針對文本之間的相關(guān)性。

CTPN想法具有創(chuàng)造性洲脂,但是太過復(fù)雜斤儿。

  1. 首先樣本的制作麻煩
  2. 每個(gè)小框進(jìn)行回歸,框的大小自己定義
  3. 邊緣特意進(jìn)行偏移處理
  4. 使用RNN進(jìn)行連接

檢測水平效果還是不錯(cuò)的恐锦,但是對于傾斜的文本就不行了往果。

為什么不加一個(gè)angle進(jìn)行回歸?

本就很復(fù)雜的網(wǎng)絡(luò)一铅,如果再給每個(gè)小box加一個(gè)angle參數(shù)會(huì)更復(fù)雜陕贮,當(dāng)然是可以實(shí)施的。

二. EAST結(jié)構(gòu)分析

2.1 結(jié)構(gòu)簡述

EAST原名為: An Efficient and Accurate Scene Text Detector

結(jié)構(gòu):檢測層(PVANet) + 合并層 + 輸出層

圖2-1

下圖圖2-2是檢測效果潘飘,任意角度的文本都可以檢測到肮之。

注意:EAST只是一個(gè)檢測網(wǎng)絡(luò)掉缺,如需識(shí)別害的使用CRNN等識(shí)別網(wǎng)絡(luò)進(jìn)行后續(xù)操作。

圖2-2

具體網(wǎng)絡(luò)在2-2節(jié)進(jìn)行詳細(xì)介紹=====>>>

2.2 結(jié)構(gòu)詳解

  • 整體結(jié)構(gòu)

EAST根據(jù)他的名字戈擒,我們知道就是高效的文本檢測方法眶明。

上面我們介紹了CTPN網(wǎng)絡(luò),其標(biāo)簽制作很麻煩筐高,結(jié)構(gòu)很復(fù)雜(分割成小方框然后回歸還要RNN進(jìn)行合并)

看下圖圖2-3搜囱,只要進(jìn)行類似FCN的結(jié)構(gòu),計(jì)算LOSS就可以進(jìn)行訓(xùn)練柑土。測試的時(shí)候走過網(wǎng)絡(luò)犬辰,運(yùn)行NMS就可以得出結(jié)果。太簡單了是不是冰单?

圖2-3
  • 特征提取層

特征的提取可以任意網(wǎng)絡(luò)(VGG幌缝、RES-NET等檢測網(wǎng)絡(luò)),本文以VGG為基礎(chǔ)進(jìn)行特征提取诫欠。這個(gè)比較簡單涵卵,看一下源碼就可以清楚,見第四章源碼分析

  • 特征合并層

在合并層中荒叼,首先在定義特征提取層的時(shí)候把需要的輸出給保留下來轿偎,通過forward函數(shù)把結(jié)構(gòu)進(jìn)行輸出。之后再合并層調(diào)用即可

如下代碼定義被廓,其中合并的過程再下面介紹

#提取VGG模型訓(xùn)練參數(shù)
class extractor(nn.Module):
    def __init__(self, pretrained):
        super(extractor, self).__init__()
        vgg16_bn = VGG(make_layers(cfg, batch_norm=True))
        if pretrained:
            vgg16_bn.load_state_dict(torch.load('./pths/vgg16_bn-6c64b313.pth'))
        self.features = vgg16_bn.features
    
    def forward(self, x):
        out = []
        for m in self.features:
            x = m(x)
            #提取maxpool層為后續(xù)合并
            if isinstance(m, nn.MaxPool2d):
                out.append(x)
        return out[1:]
  • 特征合并層

合并特征提取層的輸出坏晦,具體的定義如下代碼所示,代碼部分已經(jīng)注釋.

其中x中存放的是特征提取層的四個(gè)輸出

    def forward(self, x):

        y = F.interpolate(x[3], scale_factor=2, mode='bilinear', align_corners=True)
        y = torch.cat((y, x[2]), 1)
        y = self.relu1(self.bn1(self.conv1(y)))     
        y = self.relu2(self.bn2(self.conv2(y)))
        
        y = F.interpolate(y, scale_factor=2, mode='bilinear', align_corners=True)
        y = torch.cat((y, x[1]), 1)
        y = self.relu3(self.bn3(self.conv3(y)))     
        y = self.relu4(self.bn4(self.conv4(y)))
        
        y = F.interpolate(y, scale_factor=2, mode='bilinear', align_corners=True)
        y = torch.cat((y, x[0]), 1)
        y = self.relu5(self.bn5(self.conv5(y)))     
        y = self.relu6(self.bn6(self.conv6(y)))
        
        y = self.relu7(self.bn7(self.conv7(y)))
        return y
  • 輸出層

輸出層包括三個(gè)部分嫁乘,這里以RBOX為例子昆婿,發(fā)現(xiàn)網(wǎng)上都沒有QUAN為例子的?

首先QUAN的計(jì)算是為了防止透視變換的存在蜓斧,正常情況下不存在這些問題仓蛆,正常的斜框可以解決。

因?yàn)?code>QUAN的計(jì)算沒啥好處挎春,前者已經(jīng)完全可以解決正常的檢測問題看疙,后者回歸四個(gè)點(diǎn)相對來說較為困難(如果文本變化較大就更困難,所以SSD和YOLO無法檢測文本的原因)直奋。

如果想得到特殊的文本能庆,基本考慮別的網(wǎng)絡(luò)了(比如彎曲文字的檢測)

    def forward(self, x):
        score = self.sigmoid1(self.conv1(x))
        loc   = self.sigmoid2(self.conv2(x)) * self.scope
        angle = (self.sigmoid3(self.conv3(x)) - 0.5) * math.pi
        geo   = torch.cat((loc, angle), 1) 
        return score, geo

三. EAST細(xì)節(jié)分析

3.1 標(biāo)簽制作

注意:這里是重點(diǎn)和難點(diǎn)!=畔摺搁胆!

文章說要把標(biāo)簽向里縮進(jìn)0.3

筆者認(rèn)為這樣做的目的是提取到更為準(zhǔn)確的信息,不論是人工標(biāo)注的好與不好,我們按照0.3縮小之后提取的特征都是全部的文本信息丰涉。

但是這樣做也會(huì)丟失一些邊緣信息,如果按照上述的推斷斯碌,那么SSD或YOLO都可以這樣設(shè)計(jì)標(biāo)簽了一死。

作者肯定是經(jīng)過測試的,有好處有壞處吧傻唾!

圖3-1

標(biāo)簽格式為:5個(gè)geometry(4個(gè)location+1個(gè)angle) + 1個(gè)score ==6 × N × M

其中(b)為score圖 投慈,(d)為四個(gè)location圖, (e)為angle圖

上圖可能看的不清楚冠骄,下面以手繪圖進(jìn)行說明:

圖3-2

上圖可能看不清楚伪煤,下面再用文字大概說一下吧!

  1. 先進(jìn)行0.3縮放凛辣,這個(gè)時(shí)候的圖就是score
  2. 沒縮放的圖像為基準(zhǔn)抱既,畫最小外接矩形,這個(gè)外接矩形的角度就是angle扁誓。這個(gè)大小是縮放的的圖大小防泵。感覺直接以score圖做角度也一樣的。
  3. score圖的每個(gè)像素點(diǎn)到最小外接矩形的距離為四個(gè)location圖蝗敢。

3.2 LOSS計(jì)算

LOSS計(jì)算就比較簡單的捷泞,直接回歸location、angle寿谴、score即可锁右。

    def forward(self, gt_score, pred_score, gt_geo, pred_geo, ignored_map):
        #圖像中不存在目標(biāo)直接返回0
        if torch.sum(gt_score) < 1:
            return torch.sum(pred_score + pred_geo) * 0
        #score loss 采用Dice方式計(jì)算,沒有采用log熵計(jì)算讶泰,為了防止樣本不均衡問題
        classify_loss = get_dice_loss(gt_score, pred_score*(1-ignored_map))
        #geo loss采用Iou方式計(jì)算(計(jì)算每個(gè)像素點(diǎn)的loss)
        iou_loss_map, angle_loss_map = get_geo_loss(gt_geo, pred_geo)
        #計(jì)算一整張圖的loss咏瑟,angle_loss_map*gt_score去除不是目標(biāo)點(diǎn)的像素(感覺這句話應(yīng)該放在前面減少計(jì)算量,放在這里沒有減少計(jì)算loss的計(jì)算量)
        angle_loss = torch.sum(angle_loss_map*gt_score) / torch.sum(gt_score)
        iou_loss = torch.sum(iou_loss_map*gt_score) / torch.sum(gt_score)
        geo_loss = self.weight_angle * angle_loss + iou_loss#這里的權(quán)重設(shè)置為1
        print('classify loss is {:.8f}, angle loss is {:.8f}, iou loss is {:.8f}'.format(classify_loss, angle_loss, iou_loss))
        return geo_loss + classify_loss

注意:這里score的LOSS使用Dice方式痪署,因?yàn)槠胀ǖ慕徊骒責(zé)o法解決樣本不均衡問題O煸獭!惠桃!

圖3-3

3.3 NMS計(jì)算

NMS使用的是locality NMS浦夷,也就是為了針對EAST而提出來的。

首先我們先來看看這個(gè)LANMS的原理和過程:

import numpy as np
from shapely.geometry import Polygon

def intersection(g, p):
    #取g,p中的幾何體信息組成多邊形
    g = Polygon(g[:8].reshape((4, 2)))
    p = Polygon(p[:8].reshape((4, 2)))

    # 判斷g,p是否為有效的多邊形幾何體
    if not g.is_valid or not p.is_valid:
        return 0

    # 取兩個(gè)幾何體的交集和并集
    inter = Polygon(g).intersection(Polygon(p)).area
    union = g.area + p.area - inter
    if union == 0:
        return 0
    else:
        return inter/union

def weighted_merge(g, p):
    # 取g,p兩個(gè)幾何體的加權(quán)(權(quán)重根據(jù)對應(yīng)的檢測得分計(jì)算得到)
    g[:8] = (g[8] * g[:8] + p[8] * p[:8])/(g[8] + p[8])
    
    #合并后的幾何體的得分為兩個(gè)幾何體得分的總和
    g[8] = (g[8] + p[8])
    return g

def standard_nms(S, thres):
    #標(biāo)準(zhǔn)NMS
    order = np.argsort(S[:, 8])[::-1]
    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)
        ovr = np.array([intersection(S[i], S[t]) for t in order[1:]])
        inds = np.where(ovr <= thres)[0]
        order = order[inds+1]
        
    return S[keep]

def nms_locality(polys, thres=0.3):
    '''
    locality aware nms of EAST
    :param polys: a N*9 numpy array. first 8 coordinates, then prob
    :return: boxes after nms
    '''
    S = []    #合并后的幾何體集合
    p = None   #合并后的幾何體
    for g in polys:
        if p is not None and intersection(g, p) > thres:    #若兩個(gè)幾何體的相交面積大于指定的閾值辜王,則進(jìn)行合并
            p = weighted_merge(g, p)
        else:    #反之劈狐,則保留當(dāng)前的幾何體
            if p is not None:
                S.append(p)
            p = g
    if p is not None:
        S.append(p)
    if len(S) == 0:
        return np.array([])
    return standard_nms(np.array(S), thres)

if __name__ == '__main__':
    # 343,350,448,135,474,143,369,359
    print(Polygon(np.array([[343, 350], [448, 135],
                            [474, 143], [369, 359]])).area)

別看那么多代碼,講的很玄乎呐馆,其實(shí)很簡單:

  1. 遍歷每個(gè)預(yù)測的框肥缔,然后按照交集大于某個(gè)值K就合并相鄰的兩個(gè)框。
  2. 合并完之后就按照正常NMS消除不合理的框就行了汹来。

注意: 為什么相鄰的框合并续膳?

  1. 因?yàn)槊總€(gè)像素預(yù)測一個(gè)框(不明白就自己去看上面LOSS計(jì)算)改艇,一個(gè)目標(biāo)的幾百上千個(gè)框基本都是重合的(如果預(yù)測的準(zhǔn)的話),所以說相鄰的框直接進(jìn)行合并就行了坟岔。
  2. 其實(shí)豎直和橫向都合并一次最好谒兄,反正原理一樣的。

四. Pytorch源碼分析

源碼就不進(jìn)行分析了社付,上面已經(jīng)說得非常明白了承疲,基本每個(gè)難點(diǎn)和重點(diǎn)都說到了。

有一點(diǎn)小bug鸥咖,現(xiàn)進(jìn)行說明:

  1. 訓(xùn)練的時(shí)候出現(xiàn)孔樣本跑死
SampleNum = 3400 #定義樣本數(shù)量燕鸽,應(yīng)對空標(biāo)簽的文本bug,臨時(shí)處理方案
class custom_dataset(data.Dataset):
    def __init__(self, img_path, gt_path, scale=0.25, length=512):
        super(custom_dataset, self).__init__()
        self.img_files = [os.path.join(img_path, img_file) for img_file in sorted(os.listdir(img_path))]
        self.gt_files  = [os.path.join(gt_path, gt_file) for gt_file in sorted(os.listdir(gt_path))]
        self.scale = scale
        self.length = length

    def __len__(self):
        return len(self.img_files)

    def __getitem__(self, index):
        with open(self.gt_files[index], 'r') as f:
            lines = f.readlines()
        while(len(lines)<1):
            index = int(SampleNum*np.random.rand())
            with open(self.gt_files[index], 'r') as f:
                lines = f.readlines()
        vertices, labels = extract_vertices(lines)
        
        img = Image.open(self.img_files[index])
        img, vertices = adjust_height(img, vertices) 
        img, vertices = rotate_img(img, vertices)
        img, vertices = crop_img(img, vertices, labels, self.length,index)
        transform = transforms.Compose([transforms.ColorJitter(0.5, 0.5, 0.5, 0.25), \
                                        transforms.ToTensor(), \
                                        transforms.Normalize(mean=(0.5,0.5,0.5),std=(0.5,0.5,0.5))])
        
        score_map, geo_map, ignored_map = get_score_geo(img, vertices, labels, self.scale, self.length)
        return transform(img), score_map, geo_map, ignored_map
  1. 測試的時(shí)候讀取PIL會(huì)出現(xiàn)RGBA情況
    img_path    = './013.jpg'
    model_path  = './pths/model_epoch_225.pth'
    res_img     = './res.bmp'
    img = Image.open(img_path)
    img = np.array(img)[:,:,:3]
    img = Image.fromarray(img)
  • 后續(xù)工作
  1. 這個(gè)代碼感覺有點(diǎn)問題啼辣,訓(xùn)練速度很慢啊研,猜測是數(shù)據(jù)處理部分。
  2. 原版EAST每個(gè)點(diǎn)都進(jìn)行回歸鸥拧,太浪費(fèi)時(shí)間了悲伶,后續(xù)參考AdvanceEAST進(jìn)行修改,同時(shí)加個(gè)人理解優(yōu)化
  3. 網(wǎng)絡(luò)太大了住涉,只適合服務(wù)器或者PC上跑麸锉,當(dāng)前網(wǎng)絡(luò)已經(jīng)修改到15MB,感覺還是有點(diǎn)大舆声。
  4. 后續(xù)還要加識(shí)別部分花沉,困難重重。媳握。碱屁。。蛾找。娩脾。

這里的代碼都是github上的,筆者只是搬運(yùn)工而已4蛎J辽蕖!

原作者下載地址

五. 第一次更新內(nèi)容

  • 2019-6-30更新

之前提到這個(gè)工程的代碼有幾個(gè)缺陷幻枉,在這里進(jìn)行詳細(xì)的解決

  1. 訓(xùn)練速度很慢

這是由于源代碼的數(shù)據(jù)處理部分編寫有問題導(dǎo)致碰声,隨機(jī)crop中對于邊界問題處理
以下給出解決方案,具體修改請讀者對比源代碼即可:

def crop_img(img, vertices, labels, length, index):
    '''crop img patches to obtain batch and augment
    Input:
        img         : PIL Image
        vertices    : vertices of text regions <numpy.ndarray, (n,8)>
        labels      : 1->valid, 0->ignore, <numpy.ndarray, (n,)>
        length      : length of cropped image region
    Output:
        region      : cropped image region
        new_vertices: new vertices in cropped region
    '''
    try:
        h, w = img.height, img.width
        # confirm the shortest side of image >= length
        if h >= w and w < length:
            img = img.resize((length, int(h * length / w)), Image.BILINEAR)
        elif h < w and h < length:
            img = img.resize((int(w * length / h), length), Image.BILINEAR)
        ratio_w = img.width / w
        ratio_h = img.height / h
        assert(ratio_w >= 1 and ratio_h >= 1)

        new_vertices = np.zeros(vertices.shape)
        if vertices.size > 0:
            new_vertices[:,[0,2,4,6]] = vertices[:,[0,2,4,6]] * ratio_w
            new_vertices[:,[1,3,5,7]] = vertices[:,[1,3,5,7]] * ratio_h
        #find four limitate point by vertices
        vertice_x = [np.min(new_vertices[:, [0, 2, 4, 6]]), np.max(new_vertices[:, [0, 2, 4, 6]])]
        vertice_y = [np.min(new_vertices[:, [1, 3, 5, 7]]), np.max(new_vertices[:, [1, 3, 5, 7]])]
        # find random position
        remain_w = [0,img.width - length]
        remain_h = [0,img.height - length]
        if vertice_x[1]>length:
            remain_w[0] = vertice_x[1] - length
        if vertice_x[0]<remain_w[1]:
            remain_w[1] = vertice_x[0]
        if vertice_y[1]>length:
            remain_h[0] = vertice_y[1] - length
        if vertice_y[0]<remain_h[1]:
            remain_h[1] = vertice_y[0]

        start_w = int(np.random.rand() * (remain_w[1]-remain_w[0]))+remain_w[0]
        start_h = int(np.random.rand() * (remain_h[1]-remain_h[0]))+remain_h[0]
        box = (start_w, start_h, start_w + length, start_h + length)
        region = img.crop(box)
        if new_vertices.size == 0:
            return region, new_vertices

        new_vertices[:,[0,2,4,6]] -= start_w
        new_vertices[:,[1,3,5,7]] -= start_h
    except IndexError:
        print("\n crop_img function index error!!!\n,imge is %d"%(index))
    else:
        pass
    return region, new_vertices
  1. LOSS剛開始收斂下降熬甫,到后面就呈現(xiàn)抖動(dòng)(像過擬合現(xiàn)象)胰挑,檢測效果角度很差

由于Angle Loss角度計(jì)算錯(cuò)誤導(dǎo)致,請讀者閱讀作者原文進(jìn)行對比

def find_min_rect_angle(vertices):
    '''find the best angle to rotate poly and obtain min rectangle
    Input:
        vertices: vertices of text region <numpy.ndarray, (8,)>
    Output:
        the best angle <radian measure>
    '''
    angle_interval = 1
    angle_list = list(range(-90, 90, angle_interval))
    area_list = []
    for theta in angle_list: 
        rotated = rotate_vertices(vertices, theta / 180 * math.pi)
        x1, y1, x2, y2, x3, y3, x4, y4 = rotated
        temp_area = (max(x1, x2, x3, x4) - min(x1, x2, x3, x4)) * \
                    (max(y1, y2, y3, y4) - min(y1, y2, y3, y4))
        area_list.append(temp_area)
    
    sorted_area_index = sorted(list(range(len(area_list))), key=lambda k : area_list[k])
    min_error = float('inf')
    best_index = -1
    rank_num = 10
    # find the best angle with correct orientation
    for index in sorted_area_index[:rank_num]:
        rotated = rotate_vertices(vertices, angle_list[index] / 180 * math.pi)
        temp_error = cal_error(rotated)
        if temp_error < min_error:
            min_error = temp_error
            best_index = index

    if angle_list[best_index]>0:
        return (angle_list[best_index] - 90) / 180 * math.pi

    return (angle_list[best_index]+90) / 180 * math.pi
  1. 修改網(wǎng)絡(luò)從50MB到15MB,對于小樣本訓(xùn)練效果很好

這里比較簡單瞻颂,直接修改VGG和U-NET網(wǎng)絡(luò)feature map即可

cfg = [32, 32, 'M', 64, 64, 'M', 128, 128, 128, 'M', 256, 256, 256, 'M', 256, 256, 256, 'M']
#合并不同的feature map
class merge(nn.Module):
    def __init__(self):
        super(merge, self).__init__()

        self.conv1 = nn.Conv2d(512, 128, 1)
        self.bn1 = nn.BatchNorm2d(128)
        self.relu1 = nn.ReLU()
        self.conv2 = nn.Conv2d(128, 128, 3, padding=1)
        self.bn2 = nn.BatchNorm2d(128)
        self.relu2 = nn.ReLU()

        self.conv3 = nn.Conv2d(256, 64, 1)
        self.bn3 = nn.BatchNorm2d(64)
        self.relu3 = nn.ReLU()
        self.conv4 = nn.Conv2d(64, 64, 3, padding=1)
        self.bn4 = nn.BatchNorm2d(64)
        self.relu4 = nn.ReLU()

        self.conv5 = nn.Conv2d(128, 32, 1)
        self.bn5 = nn.BatchNorm2d(32)
        self.relu5 = nn.ReLU()
        self.conv6 = nn.Conv2d(32, 32, 3, padding=1)
        self.bn6 = nn.BatchNorm2d(32)
        self.relu6 = nn.ReLU()

        self.conv7 = nn.Conv2d(32, 32, 3, padding=1)
        self.bn7 = nn.BatchNorm2d(32)
        self.relu7 = nn.ReLU()
        #初始化網(wǎng)絡(luò)參數(shù)
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)
  1. 小的字體檢測很好豺谈,大的字體檢測不到(部分檢測不到)情況

這里是模仿AdvanceEAST的方法進(jìn)行訓(xùn)練,先在小圖像進(jìn)行訓(xùn)練贡这,然后遷移到大圖像即可茬末。

意思就是先將圖像縮小到254254訓(xùn)練得到modeul_254.pth
然后在將圖像resize到384
384,網(wǎng)絡(luò)參數(shù)使用modeul_254.pth藕坯,訓(xùn)練得到modeul_384.pth
。噪沙。炼彪。一次進(jìn)行512或者更大的圖像即可

  1. 針對圖像訓(xùn)練和檢測的慢(相對于其他檢測網(wǎng)絡(luò))

這里需要根據(jù)原理來說了,是因?yàn)槿康南袼囟夹枰A(yù)測和計(jì)算loss正歼,可以看看AdvanceEAST的網(wǎng)絡(luò)進(jìn)行處理即可

  1. 修改網(wǎng)絡(luò)說明

訓(xùn)練樣本3000
測試樣本100
檢測精度85%辐马,IOU準(zhǔn)確度80%
5個(gè)epoch收斂結(jié)束(這些都是這里測試的)
兩塊1080TI,訓(xùn)練時(shí)間10分鐘左右

這里是我完整的工程


五. 參考文獻(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市另萤,隨后出現(xiàn)的幾起案子湃密,更是在濱河造成了極大的恐慌,老刑警劉巖四敞,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泛源,死亡現(xiàn)場離奇詭異,居然都是意外死亡忿危,警方通過查閱死者的電腦和手機(jī)达箍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铺厨,“玉大人缎玫,你說我怎么就攤上這事〗庾遥” “怎么了碘梢?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長伐蒂。 經(jīng)常有香客問我煞躬,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任恩沛,我火速辦了婚禮在扰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雷客。我一直安慰自己芒珠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布搅裙。 她就那樣靜靜地躺著皱卓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪部逮。 梳的紋絲不亂的頭發(fā)上娜汁,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音兄朋,去河邊找鬼掐禁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛颅和,可吹牛的內(nèi)容都是我干的傅事。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼峡扩,長吁一口氣:“原來是場噩夢啊……” “哼蹭越!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起教届,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤般又,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后巍佑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茴迁,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年萤衰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了堕义。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脆栋,死狀恐怖倦卖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情椿争,我是刑警寧澤怕膛,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站秦踪,受9級(jí)特大地震影響褐捻,放射性物質(zhì)發(fā)生泄漏掸茅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一柠逞、第九天 我趴在偏房一處隱蔽的房頂上張望昧狮。 院中可真熱鬧,春花似錦板壮、人聲如沸逗鸣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撒璧。三九已至,卻和暖如春笨使,著一層夾襖步出監(jiān)牢的瞬間卿樱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工阱表, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留殿如,地道東北人贡珊。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓最爬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親门岔。 傳聞我的和親對象是個(gè)殘疾皇子爱致,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容