首页 > Web开发 > 详细

psenet的后处理

时间:2019-09-24 11:13:28      阅读:405      评论:0      收藏:0      [点我收藏+]

技术分享图片

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

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,第二个图下面有东西,那个我就要把你归并,按照我想的,最后归并后应该是最右边那个图了,困扰了我好久好久。。。

psenet的后处理

原文:https://www.cnblogs.com/yanghailin/p/11576504.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!