PSENet网络梳理

一、背景

场景文本检测一般采用两种方法:

1、基于锚框的回归,缺点是不能定位任意形状的文本,特别是形状弯曲的文本;

2、基于分割,缺点是距离较近的文本不能单独分割开来。

二、网络结构

       如图所示,网络包括两种步骤:1、文本实例分割  ---->  2、后处理算法PSE

2.1  文本实例分割

       基网络采用FPN结构的ResNet,选取feature map {P2、P3、P4、P5},4个不同尺寸的feature map通道数都为256,P3、P4、P5分别上采样2、4、8倍(与P2尺寸一致),P2、P3、P4、P5拼接后接conv_3*3卷积将维为256通道,得到融合后特征F,将F经过n个 conv_1*1卷积 + upsampling + sigmoid 操作得到对应n个分割mask,计为S1、S2、...、Sn。需注意的是S1、S2、...、Sn表示文本实例的不同尺寸分割mask,在论文中简称为“kernel”。同一个文本实例,网络会输出n个"kernel"大小不同的分割mask,即S1、S2、...、Sn(尺寸由小到大)。每个内核与原始的整个文本实例共享相似的形状,并且它们都位于相同的中心点但在比例上不同。

2.2  后处理算法 PSE

      为解决相邻文本实例不能单独分割开的问题,引入渐进式尺寸扩张算法用于后处理网络输出的分割mask。

     对于预测的n个分割实例S1......Sn ,为了得到最终的检测结果,我们采用了渐进的尺度扩展算法。 它基于广度优先搜索(BFS), 由三个步骤组成:

1、从具有最小尺度的核S1开始(在此步骤中可以区分实例,不同实例有不同的连通域);
2、通过逐步在较大的核中加入更多的像素来扩展它们的区域;
3、完成直到发现最大的核。
 

     

 

三、训练标签生成方法及损失函数

3.1  标签生成

由于PSENet对某一文本实例输出n个不同尺寸的分割mask,训练时需要生成对应的n个不同尺寸的ground truth label map。对应于Sn,ground truth label map为文本实例的原始外轮廓多边形(如下图中pn所示),而S1、S2、...、Sn-1对应的ground truth label map(如下图中pi所示)是在pn上做shrink处理(Vatti clipping algorithm)。ground truth label map都是0/1二值化的分割图。

 

3.2  损失函数

用到了多任务损失函数:

由于正负样本比例失衡(一般而言文本区域在图片中只占较小的区域),采用交叉熵损失会导致由负样本主导,训练难以收敛,因此训练采用dice coefficient。

 

四、PSE代码实现

void growing_text_line(vector<Mat> &kernals, vector<vector<int>> &text_line, float min_area) {
        // step 1: 先处理最小尺寸的分割图
        Mat label_mat;
        int label_num = connectedComponents(kernals[kernals.size() - 1], label_mat, 4);// 寻找连通区域
      
        int area[label_num + 1];//统计每个文字块像素的个数即面积
        memset(area, 0, sizeof(area));
        for (int x = 0; x < label_mat.rows; ++x) {
            for (int y = 0; y < label_mat.cols; ++y) {
                int label = label_mat.at<int>(x, y);
                if (label == 0) continue;
                area[label] += 1;
            }
        }

        queue<Point> queue, next_queue;//重要:队列,先进先出
        for (int x = 0; x < label_mat.rows; ++x) {
            vector<int> row(label_mat.cols);
            for (int y = 0; y < label_mat.cols; ++y) {
                int label = label_mat.at<int>(x, y);
                
                if (label == 0) continue;
                if (area[label] < min_area) continue;
                
                Point point(x, y);
                queue.push(point);//重要:队列保存非0位置
                row[y] = label;//非0的label保存
            }
            text_line.emplace_back(row); // text_line: 传出去的text_line先保存了最瘦的那个分割图各个像素label
        }
        
        // step1 end:处理完最小尺寸分割图,每个像素标签保存在text_line中,label非0的像素保存在queue队列中
    
        // step2: 开始遍历其他尺寸分割图(kernel),将queue中像素作上下左右的扩展,并判断被扩展出来的像素label
        //4邻域
        int dx[] = {-1, 1, 0, 0};
        int dy[] = {0, 0, -1, 1};
       // 从倒数第二个开始,因为是以倒数第一个最瘦的为基础的
        for (int kernal_id = kernals.size() - 2; kernal_id >= 0; --kernal_id) {
            while (!queue.empty()) {
                Point point = queue.front(); queue.pop();
                int x = point.x;
                int y = point.y;
                int label = text_line[x][y];

                bool is_edge = true;
                for (int d = 0; d < 4; ++d) {
                    int tmp_x = x + dx[d];
                    int tmp_y = y + dy[d];

                    if (tmp_x < 0 || tmp_x >= (int)text_line.size()) continue;
                    if (tmp_y < 0 || tmp_y >= (int)text_line[1].size()) continue;
                    if (kernals[kernal_id].at<char>(tmp_x, tmp_y) == 0) continue;
                    if (text_line[tmp_x][tmp_y] > 0) continue;
                   // 能够下来的需要满足两个条件: 1. (kernals[kernal_id].at<char>(tmp_x, tmp_y) != 0)  2. (text_line[tmp_x][tmp_y] == 0)
                   // 即:1. 上一个较大尺寸分割图对应位置上有东西   2. 本位置无东西                                               
                   // 满足这两个条件就放到队列最后(queue.push(point));,同时把该位置归化为自己的label( text_line[tmp_x][tmp_y] = label;)

                    Point point(tmp_x, tmp_y);
                    queue.push(point);
                    text_line[tmp_x][tmp_y] = label;
                    is_edge = false;
                }

                if (is_edge) {//注:当前点都是有东西的     如果当前点任一邻域有东西(文字块内)或者当前点任一邻域对应的上一个分割图位置上没有东西(文字块边界)
                    next_queue.push(point);
                }
            }
            swap(queue, next_queue);
        }
    } 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值