1. 背景介紹
文本檢測分為基于回歸和基于分割兩種方法,DBNet 的原理是基于分割算法杠娱。對于一般分割算法流程:先通過網(wǎng)絡(luò)輸出文本分割的概率圖澡绩,然后使用設(shè)定閾值將概率圖轉(zhuǎn)化為二值圖,然后通過后處理得到檢測結(jié)果(文本框坐標)挽拔。但是缺點在于閾值的選取非常關(guān)鍵酌毡。
DBNet 針對這個問題克握,提出可微分二值化的概念:即對每一個像素點進行自適應(yīng)二值化,二值化閾值由網(wǎng)絡(luò)學(xué)習得到枷踏,徹底將二值化這一步驟加入到網(wǎng)絡(luò)里一起訓(xùn)練菩暗,這樣最終的輸出圖對于閾值就會非常魯棒。
自適應(yīng)閾值
上圖中藍線部分就是傳統(tǒng)的文本檢測算法流程:
- 首先旭蠕,通過設(shè)置一個固定閾值將分割網(wǎng)絡(luò)訓(xùn)練得到的概率圖(segmentation map)轉(zhuǎn)化為二值圖(binarization map)停团。
- 然后,使用一些啟發(fā)式技術(shù)(例如像素聚類)將像素分組為文本實例掏熬。
上圖紅色部分就是 DBNet 的算法流程:
通過分割網(wǎng)絡(luò)獲取概率圖和閾值圖佑稠,閾值圖是網(wǎng)絡(luò)預(yù)測得出,并不是固定的值旗芬,這樣就可以很好將背景與前景分離出來舌胶,但是這樣的操作會給訓(xùn)練帶來梯度不可微的情況,對此對于二值化提出了一個叫做 Differentiable Binarization來解決不可微的問題疮丛。
2. DBNet 算法整體架構(gòu)
上圖是 DBNet 模型網(wǎng)絡(luò)結(jié)構(gòu)示意圖幔嫂,主要分為 3 個模塊:
-
第一模塊(1):左邊的紅框使用的是一個 FPN 結(jié)構(gòu),分為自底向上的卷積操作與自頂向下的上采樣誊薄,以此來獲取多尺度的特征婉烟。1 圖下面部分是 3x3 的卷積操作,按照卷積公式分別獲取原圖大小比例的
1/2暇屋、1/4、1/8洞辣、1/16咐刨、1/32
的特征圖;然后自頂向下進行上采樣 x2扬霜,然后與自底向上生成的相同大小的特征圖融合定鸟;融合之后再采用 3x3 的卷積消除上采樣的混疊效應(yīng);最后對每層輸出結(jié)果進行上采樣著瓶,統(tǒng)一為 1/4 大小的特征圖联予。 - 第二模塊(2):將 1/4 大小的特征圖經(jīng)過一系列卷積和轉(zhuǎn)置卷積的機構(gòu)獲取概率圖 P 和閾值圖 T,可參考 FCN 網(wǎng)絡(luò)結(jié)構(gòu),目的是生成與原圖一樣大小的特征圖 P 和 T沸久。
- 第三模塊(3):將特征圖 P 和 T 經(jīng)過 DB 方法(后續(xù)介紹)得到近似二值圖季眷。
經(jīng)過上面三個模塊,可以得到概率圖卷胯、閾值圖和近似二值圖子刮。在訓(xùn)練過程中,對這三個圖進行監(jiān)督學(xué)習窑睁,更新各個模塊的參數(shù)挺峡。在推理過程中,直接使用概率圖担钮,然后使用固定閾值獲取結(jié)果橱赠。
下面介紹可微二值化的發(fā)展流程:
標準二值化
在傳統(tǒng)的圖像分割算法中,我們獲取概率圖后箫津,會使用標準二值化(Standard Binarize)方法進行處理狭姨,將低于閾值的像素點置0,高于閾值的像素點置1鲤嫡,公式如下:
是預(yù)先設(shè)置的閾值送挑,代表的是像素點坐標位置∨郏可見標準的二值化是不可微的惕耕,所以也就無法放入到網(wǎng)絡(luò)中進行優(yōu)化學(xué)習。
可微二值化
可微二值化就是將標準二值化中的階躍函數(shù)進行了近似诫肠,公式如下所示:
可微二值化本質(zhì)上是一個 帶系數(shù) k 的 sigmoid 函數(shù)司澎,取值范圍為,k 是膨脹因子(經(jīng)驗型設(shè)置為50)栋豫。 是指概率圖像素點挤安, 是指閾值圖像素點。
標準二值化和可微二值化的對比如下圖 (a) 所示丧鸯,SB 曲線代表標準二值化蛤铜,DB 代表可微二值化,可以看到曲線變得更為平滑丛肢,也就是可微:
除了可微之外围肥,DB 方法也會改善算法的性能,在反向傳播是梯度的計算上進行觀察蜂怎。當使用交叉熵損失(代表文字區(qū)域)時穆刻,正負樣本的 loss 分別為 和 ,公式如下:
對輸入 求偏導(dǎo)杠步,則會得到:
可以看到 增強因子k 對于錯誤預(yù)測對梯度的影響變大了氢伟,從而可以促進模型的優(yōu)化過程產(chǎn)生更為清晰的預(yù)測結(jié)果榜轿。對于時,按照(a)圖屬于正樣本(文字區(qū)域)朵锣,時屬于負樣本(非文字區(qū)域)谬盐。
上圖(b)是指 的導(dǎo)數(shù)曲線,如果發(fā)生漏報(正樣本被預(yù)測為負樣本)猪勇,圖(b)小于 0 的部分導(dǎo)數(shù)非常大设褐,證明損失也是非常大的,則更能清晰的進行梯度回傳泣刹。同理助析,圖(c)代表 的導(dǎo)數(shù)曲線,當發(fā)生誤報(負樣本被預(yù)測為正樣本)椅您,導(dǎo)數(shù)也是非常大的外冀,損失也比較大。
3. 真實標簽生成
DB 網(wǎng)絡(luò)中掀泳,訓(xùn)練過程中網(wǎng)絡(luò)有 3 個輸出:概率圖雪隧、閾值圖和近似二值圖:
- 概率圖:圖中每個像素點的值為該位置屬于文本區(qū)域的概率。
- 閾值圖:圖中每個像素點的值為該位置的二值化閾值员舵。
- 近似二值圖:由概率圖和閾值圖通過 DB 算法計算得到脑沿,圖中像素的值為 0 或 1。
概率圖的標簽 和閾值圖標簽马僻,DB 網(wǎng)絡(luò)參考 PSENet 中的方法庄拇,使用擴張和收縮的方式構(gòu)建閾值圖和概率圖。在該方法中韭邓,對于一幅文字圖像措近,文本區(qū)域的每個多邊形使用一組線段 來進行描述, n 為線段個數(shù)女淑。
概率圖標簽
對于概率圖和近似二值圖來說瞭郑,使用收縮的方式構(gòu)建標簽(Vatti clipping算法),收縮的偏移量D由多邊形的周長 L 和面積 A 計算得到鸭你,公式如下:其中屈张, r 是收縮因子,實驗中經(jīng)驗設(shè)置為 0.4 袱巨。
閾值圖標簽
- 首先使用計算過程中的偏移量 D 進行多邊形的擴充阁谆。得到和之間的區(qū)域。
- 計算之間區(qū)域到原始框的距離瓣窄,并得到最近邊(長方形就是 4 條邊)的距離。最外面的大框線上區(qū)域和最里面的小框線上區(qū)域計算為 D 纳鼎,原始框位置的距離為 0俺夕。
- 進行第一次的歸一化(除以D)裳凸,這樣距離控制到 [0,1] 之間,并且最中間的區(qū)域越接近0劝贸,越里面和越外面的區(qū)域越接近1姨谷。然后使用 1-X 操作,讓越中心的距離為 1映九,越邊緣的距離為 0梦湘。(這樣圖片顯示就是中間亮兩頭暗)。
- 最終再進行縮放件甥,比如歸一化到 [0.3捌议,0.7] 的值。
4. 損失函數(shù)
由于在訓(xùn)練階段輸出 3 個預(yù)測圖(大小與原圖一致)引有,所以在損失函數(shù)中瓣颅,也需要有對應(yīng)的真實標簽去構(gòu)建 3 部分損失函數(shù)∑┱總的損失函數(shù)的公式定義如下:
其中,為總的損失曾我,為近似二值圖的損失,使用 Dice 損失抒巢;為概率圖損失贫贝,使用帶 OHEM 的 Dice 損失;為閾值圖損失虐秦,使用預(yù)測值和標簽間的 ??1 距離平酿。其中,?? 和 ?? 為權(quán)重系數(shù)悦陋。
接下來分析這 3 個loss:
1)首先是Dice Loss蜈彼,Dice Loss是比較預(yù)測結(jié)果跟標簽之間的相似度,常用于二值圖像分割俺驶。
2)其次是MaskL1 Loss幸逆,是計算預(yù)測值和標簽間的??1距離
3)最后是Balance Loss,是帶OHEM的Dice Loss暮现,目的是為了改善正負樣本不均衡的問題。OHEM 為一種特殊的自動采樣方式栖袋,可以自動的選擇難樣本進行l(wèi)oss的計算拍顷,從而提升模型的訓(xùn)練效果。Dice Loss (遷移)
其中 |X∩Y| 是X和Y之間的交集塘幅,|X|和|Y|分表表示X和Y的元素的個數(shù)昔案,其中尿贫,分子的系數(shù)為2,是因為分母存在重復(fù)計算X和Y之間的共同元素的原因踏揣。
同時庆亡,一般會加入平滑因子,防止分子分母全為0捞稿。對于分割任務(wù)而言又谋,|X| 和 |Y| 代表分割的 ground truth 和 predict_mask。計算步驟:
首先娱局,使用預(yù)測圖 predict_mask 和 ground truth 之間的點乘彰亥。
逐元素相乘的結(jié)果元素的相加和。
計算|X|和|Y|铃辖,這里可以采用直接元素相加剩愧,也可以采用元素平方求和的方法:
5. 模型訓(xùn)練流程
以 PaddleOCR (https://github.com/PaddlePaddle/PaddleOCR/blob/0791714b91/deploy/lite/readme.md) 為例,模型的訓(xùn)練流程分為以下部分:
- 數(shù)據(jù)預(yù)處理:主要包括解析圖片娇斩、真實標簽處理仁卷、隨機裁剪和圖片增強。
- 模型結(jié)構(gòu)部分:分為 Backbone犬第、Neck锦积、Head 部分。
- loss 部分:分為 鉴分。
- metric 部分:經(jīng)過后處理之后進行評價指標的計算哮幢。
5.1 數(shù)據(jù)預(yù)處理
數(shù)據(jù)預(yù)處理包含以下部分:
DecodeImage 解析圖片、DetLabelEncode 解析 label 文件志珍、IaaAugment 進行數(shù)據(jù)增強橙垢、EastRandomCropData 隨機裁剪(裁剪到指定 size (960, 960, 3))、MakeBorderMap 閾值圖真實標簽生成(batch, 960, 960)伦糯、MakeShrinkMap 概率圖標簽生成(batch, 960, 960)柜某、NormalizeImage 歸一化、ToCHWImage 緯度變化為(3, 960, 960)敛纲、KeepKeys 指定格式喂击。
經(jīng)過數(shù)據(jù)預(yù)處理 image 字段的大小變?yōu)椋╞atch, 3, 960, 960)。
5.2 模型前向傳播
模型依次經(jīng)過 backbone(MobileNetV3)--> neck(DBFPN) --> head(DBHead) 步驟淤翔,緯度變化如下所示:
輸入的 images size: [2, 3, 960, 960]翰绊,batch 為 2
backbone 輸出1:backbone behind [2, 16, 240, 240],尺寸對應(yīng) 1/4
backbone 輸出2:backbone behind [2, 24, 120, 120],尺寸對應(yīng) 1/8
backbone 輸出3:backbone behind [2, 56, 60, 60]监嗜,尺寸對應(yīng) 1/16
backbone 輸出4:backbone behind [2, 480, 30, 30]琳要,尺寸對應(yīng) 1/32
neck 輸出:neck behind [2, 96, 240, 240],輸出 1/4
head 輸出:head behind [2, 3, 960, 960]秤茅,與原圖大小一致,分別代表 shrink_maps, threshold_maps, binary_maps童叠,大小都為 [960, 960]
5.3 后處理
后處理的邏輯流程如下:
- 首先框喳,對概率圖進行固定閾值處理,得到分割圖厦坛。
- 對分割圖計算輪廓五垮,遍歷每個輪廓,去除太小的預(yù)測杜秸;對每個輪廓計算包圍矩形放仗,然后計算該矩形的預(yù)測score。
- 對矩形進行反向shrink操作撬碟,得到真實矩形大械ぁ;最后還原到原圖 size 就可以了呢蛤。
下面對 python 代碼進行解析:
pred 是模型的輸出惶傻,shape 為 (1,height, weight)其障,訓(xùn)練階段都為(1银室,960,960)励翼,推理階段則不一定蜈敢。
查看 boxes_from_bitmap 的代碼:
def boxes_from_bitmap(self, pred, _bitmap, dest_width, dest_height):
'''
_bitmap: single map with shape (1, H, W),
whose values are binarized as {0, 1}
'''
bitmap = _bitmap
height, width = bitmap.shape
# findContours 獲取輪廓,如長方形獲取四點頂點坐標
outs = cv2.findContours((bitmap * 255).astype(np.uint8), cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
# py2汽抚、py3 不同版本的情況
if len(outs) == 3:
img, contours, _ = outs[0], outs[1], outs[2]
elif len(outs) == 2:
contours, _ = outs[0], outs[1]
# 文本框最大數(shù)量
num_contours = min(len(contours), self.max_candidates)
boxes = []
scores = []
for index in range(num_contours):
contour = contours[index]
# 計算最小包圍矩抓狭,獲取四個坐標點,左上為起點(順時針)
points, sside = self.get_mini_boxes(contour)
# 長方形中寬高最小值過濾
if sside < self.min_size:
continue
points = np.array(points)
# 利用 points 內(nèi)部預(yù)測概率值殊橙,計算出一個score,作為實例的預(yù)測概率
score = self.box_score_fast(pred, points.reshape(-1, 2))
# score 得分的過濾
if self.box_thresh > score:
continue
# shrink反向還原辐宾,之前概率圖進行了縮放,故還原
box = self.unclip(points).reshape(-1, 1, 2)
box, sside = self.get_mini_boxes(box)
if sside < self.min_size + 2:
continue
box = np.array(box)
# 還原到原始坐標膨蛮,反向還原之后叠纹,還需要還原到原始圖片(原始圖片在預(yù)處理時被縮放處理)
box[:, 0] = np.clip(
np.round(box[:, 0] / width * dest_width), 0, dest_width)
box[:, 1] = np.clip(
np.round(box[:, 1] / height * dest_height), 0, dest_height)
boxes.append(box.astype(np.int16))
scores.append(score)
return np.array(boxes, dtype=np.int16), scores
def get_mini_boxes(self, contour):
# 返回點集 cnt 的最小外接矩形:# 得到最小外接矩形的(中心(x,y), (寬,高), 旋轉(zhuǎn)角度)
bounding_box = cv2.minAreaRect(contour)
# 排序,最終以左上的坐標為起點敞葛,順時針排列四個坐標點
points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0])
index_1, index_2, index_3, index_4 = 0, 1, 2, 3
if points[1][1] > points[0][1]:
index_1 = 0
index_4 = 1
else:
index_1 = 1
index_4 = 0
if points[3][1] > points[2][1]:
index_2 = 2
index_3 = 3
else:
index_2 = 3
index_3 = 2
box = [
points[index_1], points[index_2], points[index_3], points[index_4]
]
return box, min(bounding_box[1])
6. 模型推理流程
如果不考慮 label誉察,則其處理邏輯和訓(xùn)練邏輯有一點不一樣,其把圖片統(tǒng)一 resize 到指定的長度進行預(yù)測惹谐。
數(shù)據(jù)預(yù)處理
數(shù)據(jù)預(yù)處理沒有訓(xùn)練階段的數(shù)據(jù)增強持偏、隨機裁剪和生成標簽部分驼卖,但是會存在一個 resize 的操作,將寬高設(shè)置為 32 的倍數(shù)鸿秆。
DetResizeForTest 步驟如下:
1)對圖片進行等比例的放縮酌畜,設(shè)置最大的尺寸為 960。如 3 張圖片分別為 (720卿叽,1280)桥胞、(230,230)考婴、(1150贩虾,720)。
2)對圖片進行放縮沥阱,以最大邊為準(操作960的直接放縮到 960)缎罢,縮小至能被 32 整除的最大尺寸。則輸出:(512考杉,960策精,3)、(224崇棠,224蛮寂,3)、(960易茬,576酬蹋,3)。由于寬高都為 32 的倍數(shù)抽莱,則放縮的比例不統(tǒng)一范抓,但盡可能相差不大。
后續(xù)的操作就與訓(xùn)練階段一致食铐,通過網(wǎng)絡(luò)結(jié)構(gòu)匕垫,然后進行后處理獲得結(jié)果。