zoukankan      html  css  js  c++  java
  • psenet的后处理

    深度学习ocr交流qq群:1020395892

    这是作者提供的图,对应的代码如下:

    void growing_text_line(vector<Mat> &kernals, vector<vector<int>> &text_line, float min_area) {
            Mat label_mat;
            int label_num = connectedComponents(kernals[kernals.size() - 1], label_mat, 4);
    
            // cout << "label num: " << label_num << endl;
            
            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
    
            // cout << "ok" << endl;
    
            //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];
                    // cout << text_line.size() << ' ' << text_line[0].size() << ' ' << x << ' ' << y << endl;
    
                    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);
            }
        } 
    

    其中参数,vector &kernals 是分割出来的核,一般取前三个,就是说vector的size=3
    vector<vector> &text_line是传出去的,和图片大小一样,比如一个图有10个文本,那个这个传出去的像素范围就是0-10,0代表背景,1代表第一个文本轮廓,第一个文本轮廓内像素值都为1,类推。其中,vector &kernals中的每个图片也是这样
    min_area 是阈值(300),过滤小的干扰点。

      int label_num = connectedComponents(kernals[kernals.size() - 1], label_mat, 4);
    

    connectedComponents是opencv函数,传入的kernals[kernals.size() - 1]是最小的核,就是最瘦的那个,label_mat是传出,传出的是标签图,比如kernals[kernals.size() - 1]有6个文字块,那个label_mat就把每个文字块里面编号,比如第一个文字块里面像素全为1,第二个文字块里面像素全为2,类推。其余非文字块区域为0.同时,返回值label_num为文字块个数6. 还有一个参数4是4邻域

    改注释的都在代码里面了,其实一开始理解这个循环也理解了好久,有的地方怎么想都没有想明白,比如,这个代码是如何处理两个边界融合在一起的。下面分简单和容易的来:第一种简单的情况:

    比如这两张图,左边是瘦的那个,右边是轮廓稍微扩张了一点,黄色区域就是比左边的稍微外扩一点的。这是没有交叠的情况,
    if (kernals[kernal_id].at(tmp_x, tmp_y) == 0) continue;
    if (text_line[tmp_x][tmp_y] > 0) continue;
    text_line是左边这张图,queue从上到下从左到右记录了左边这张图非0区域, (kernals[kernal_id]是右边这张图,比如左边上面第一个轮廓内,都会满足 if (text_line[tmp_x][tmp_y] > 0) continue;这个条件,(即轮廓内的任一邻域都有东西),从而is_edge=true;就会把当前点 next_queue.push(point);给到下一个队列。这里似乎没有啥难理解的。
    第二种情况,有交叠了:

    按照上面的:
    if (kernals[kernal_id].at(tmp_x, tmp_y) == 0) continue;
    if (text_line[tmp_x][tmp_y] > 0) continue;
    // 能够下来的需要满足两个条件: 1. (kernals[kernal_id].at(tmp_x, tmp_y) != 0) 2. (text_line[tmp_x][tmp_y] == 0)
    // 即: 1. 上个分割图对应位置上有东西 2. 本位置无东西
    就是看本图位置邻域没有东西,同时上一个分割图对应位置有东西,我们就把该邻域归一化为自己,按照我一开始这样的逻辑,想,那么第二个轮廓扩展的都要被第一个轮廓吞并了?成下面这样:
    ,再来一个图:
    ,最左边图,然后下边有多出1,那么本来最左边图下面没有1,第二个图下面有东西,那个我就要把你归并,按照我想的,最后归并后应该是最右边那个图了,困扰了我好久好久。。。(其实这个图还有一个错误的就是归一化后第一张图也要改变,需要把归一化的也改为自己的)后来在同事的提醒下,队列,先进先出!!!其实并不是我想的这样子,它是有顺序的,比如刚刚最简单的情况:

    这个其实一开始queue压入的顺序是从上到下,从左到右,完成一次迭代是最右边那个图的样子,并不是一个轮廓扩张完了再扩另外一个的。同样的,交叠的情况:

    如图最右边是假设已经扩到交叠处了,那么上面的最右边要处理交叠的情况。

    比如,中间图扩张,会把之前的text_line归并,成第三个图,上面,比如最下面的1,1,下面的邻域,发现text_line没有东西,而kernals[kernal_id]有东西,就把2归并为自己的1,然后第一个轮廓归并结束,同时,第二个下面的轮廓,准备归并同样位置的时候,发现text_line有东西,就不能再扩了,这样就是先到先得,1归并2的时候先到的就先得到,这样虽然归并错了,但是就是一两行的问题,影响不大,结果使得交叠的能够得到分割。!!!!完毕。

  • 相关阅读:
    Java 链表
    知识点归列
    HTML和CSS必须知道的重点难点问题
    函数表达式
    javascript原型链
    canvas成长树
    checkbox选中问题
    使用vue-cli脚手架自定义iview主题
    AI学习吧-Redis操作-事务、订阅
    AI学习吧-REDIS-常识
  • 原文地址:https://www.cnblogs.com/yanghailin/p/11576504.html
Copyright © 2011-2022 走看看