• OpenCV 实现图片的水平投影与垂直投影,并进行行分割


    前言:对于印刷体图片来说,进行水平投影和垂直投影可以很快的进行分割,本文在OpenCV中如何进行水平投影和垂直投影通过代码进行说明。

    水平投影:二维图像在y轴上的投影

    垂直投影:二维图像在x轴上的投影

    由于投影的图像需要进行二值化,本文采用积分二值化的方式,对图片进行处理。


    具体代码如下:

      1 //积分二值化
      2 void thresholdIntegral (Mat inputMat, Mat& outputMat)
      3 {
      4 
      5     int nRows = inputMat.rows;
      6     int nCols = inputMat.cols;
      7 
      8     // create the integral image
      9     Mat sumMat;
     10     integral (inputMat, sumMat);
     11 
     12     int S = MAX (nRows, nCols) / 8;
     13     double T = 0.15;
     14 
     15     // perform thresholding
     16     int s2 = S / 2;
     17     int x1, y1, x2, y2, count, sum;
     18 
     19     int* p_y1, *p_y2;
     20     uchar* p_inputMat, *p_outputMat;
     21 
     22     for (int i = 0; i < nRows; ++i)
     23     {
     24         y1 = i - s2;
     25         y2 = i + s2;
     26 
     27         if (y1 < 0)
     28         {
     29             y1 = 0;
     30         }
     31         if (y2 >= nRows)
     32         {
     33             y2 = nRows - 1;
     34         }
     35 
     36         p_y1 = sumMat.ptr<int> (y1);
     37         p_y2 = sumMat.ptr<int> (y2);
     38         p_inputMat = inputMat.ptr<uchar> (i);
     39         p_outputMat = outputMat.ptr<uchar> (i);
     40 
     41         for (int j = 0; j < nCols; ++j)
     42         {
     43             // set the SxS region
     44             x1 = j - s2;
     45             x2 = j + s2;
     46 
     47             if (x1 < 0)
     48             {
     49                 x1 = 0;
     50             }
     51             if (x2 >= nCols)
     52             {
     53                 x2 = nCols - 1;
     54             }
     55 
     56             count = (x2 - x1)* (y2 - y1);
     57 
     58             // I(x,y)=s(x2,y2)-s(x1,y2)-s(x2,y1)+s(x1,x1)
     59             sum = p_y2[x2] - p_y1[x2] - p_y2[x1] + p_y1[x1];
     60 
     61             if ((int) (p_inputMat[j] * count) < (int) (sum* (1.0 - T)))
     62             {
     63                 p_outputMat[j] = 0;
     64             }
     65             else
     66             {
     67                 p_outputMat[j] = 255;
     68             }
     69         }
     70     }
     71 }
     72 //垂直方向投影
     73 void picshadowx (Mat binary)
     74 {
     75     Mat paintx (binary.size(), CV_8UC1, Scalar (255)); //创建一个全白图片,用作显示
     76 
     77     int* blackcout = new int[binary.cols];
     78     memset (blackcout, 0, binary.cols * 4);
     79 
     80     for (int i = 0; i < binary.rows; i++)
     81     {
     82         for (int j = 0; j < binary.cols; j++)
     83         {
     84             if (binary.at<uchar> (i, j) == 0)
     85             {
     86                 blackcout[j]++; //垂直投影按列在x轴进行投影
     87             }
     88         }
     89     }
     90     for (int i = 0; i < binary.cols; i++)
     91     {
     92         for (int j = 0; j < blackcout[i]; j++)
     93         {
     94             paintx.at<uchar> (binary.rows-1-j, i) = 0; //翻转到下面,便于观看
     95         }
     96     }
     97     delete blackcout;
     98     imshow ("paintx", paintx);
     99 
    100 }
    101 //水平方向投影并行分割
    102 void picshadowy (Mat binary)
    103 {  
    104    //是否为白色或者黑色根据二值图像的处理得来
    105     Mat painty (binary.size(), CV_8UC1, Scalar (255)); //初始化为全白
    106    
    107    //水平投影
    108    int* pointcount = new int[binary.rows]; //在二值图片中记录行中特征点的个数
    109     memset (pointcount, 0, binary.rows * 4);//注意这里需要进行初始化
    110 
    111     for (int i = 0; i < binary.rows; i++)
    112     {
    113         for (int j = 0; j < binary.cols; j++)
    114         {
    115             if (binary.at<uchar> (i, j) == 0)
    116             {
    117                 pointcount[i]++; //记录每行中黑色点的个数 //水平投影按行在y轴上的投影
    118             }
    119         }
    120     }
    121 
    122     for (int i = 0; i < binary.rows; i++)
    123     {
    124         for (int j = 0; j < pointcount[i]; j++) //根据每行中黑色点的个数,进行循环
    125         {
    126             
    127             painty.at<uchar> (i, j) = 0; 
    128         }
    129 
    130     }
    131 
    132     imshow ("painty", painty);
    133 
    134     vector<Mat> result;
    135     int startindex = 0;
    136     int endindex = 0;
    137     bool inblock = false; //是否遍历到字符位置
    138 
    139     for (int i = 0; i < painty.rows; i++)
    140     {
    141       
    142         if (!inblock&&pointcount[i] != 0) //进入有字符区域
    143         {
    144             inblock = true;
    145             startindex = i;
    146             cout << "startindex:" << startindex << endl;
    147         }
    148         if (inblock&&pointcount[i] == 0) //进入空白区
    149         {
    150             endindex = i;
    151             inblock = false;
    152             Mat roi = binary.rowRange (startindex, endindex+1); //从而记录从开始到结束行的位置,即可进行行切分
    153             result.push_back (roi);
    154         }
    155     }
    156 
    157     for (int i = 0; i < result.size(); i++)
    158     {
    159         Mat tmp = result[i];
    160         imshow ("test"+to_string (i), tmp);
    161      }
    162     delete pointcount;
    163 
    164 }
    165 int main (int argc, char* argv[])
    166 {
    167    
    168       Mat src = cv::imread ("test.jpg");
    169 
    170         if (src.empty())
    171         {
    172             cerr << "Problem loading image!!!" << endl;
    173             return -1;
    174         }
    175 
    176         imshow("in",src);
    177       
    178         Mat gray;
    179 
    180         if (src.channels() == 3)
    181         {
    182             cv::cvtColor (src, gray, CV_BGR2GRAY);       
    183         }
    184         else
    185         {
    186             gray = src;
    187         }
    188      
    189 
    190         Mat bw2 = Mat::zeros (gray.size(), CV_8UC1);
    191         thresholdIntegral (gray, bw2);
    192 
    193         cv::imshow ("binary integral", bw2);
    194   
    195         //picshadowx (bw2);
    196         picshadowy (bw2);
    197         waitKey (0);
    198     
    199     return 0;
    200 }

     输入图片:

    二值图片:

     水平投影:

     

    垂直投影:

     行切割:

    该处理方法,对印刷体有较好的效果,因为印刷体的行列区分明显,因此可以很快的进行行与列的分割。


    by Shawn Chen,2017.12.13日,晚。

  • 相关阅读:
    linux进程调度(zz)
    为什么vfork的子进程里用return,整个程序会挂掉,而且exit不会(zz)
    ubuntu安装samba
    【前端知乎系列】ArrayBuffer 和 Blob 对象
    【Web技术】442- 关于图片懒加载的几种方案
    【Web技术】441- 蚂蚁前端研发最佳实践
    【面试题】440- 10 道 Nodejs EventLoop 和事件相关面试题
    【面试题】439- 这是一道网红面试题
    【Web技术】438- 移动端体验优化经验总结与实践
    记 · 寒风依旧 · 虎跑路和人生路
  • 原文地址:https://www.cnblogs.com/developer_chan/p/8026707.html
Copyright © 2020-2023  润新知