使用OpenCv中Mat进行水平投影与垂直投影并实现字符切分

来源:互联网 发布:阿里云服务器新手 编辑:程序博客网 时间:2024/06/05 23:14

因为要做图像处理方面的工作,所以最近在学习OpenCv的使用,学习了OpenCv中Mat对象的相关使用之后,实现了使用Mat对象来进行图像的水平投影和垂直投影,并且在投影之后,对字符进行相对应的切分。现在将相关代码贴出,一来可以供大家参考并指正错误,而来也为的是防止忘记了相关知识。以下就是程序的代码,欢迎大家指正错误。

推荐http://blog.csdn.net/u011688208/article/details/48031807


[cpp] view plain copy
print?
  1. #include <stdafx.h>  
  2. #include "opencv2/imgproc/imgproc.hpp"  
  3. #include "opencv2/highgui/highgui.hpp"  
  4. #include <iostream>  
  5. #include <stdio.h>  
  6. using namespace cv;  
  7. using namespace std;  
  8.   
  9. vector<Mat> horizontalProjectionMat(Mat srcImg)//水平投影  
  10. {  
  11.     Mat binImg;  
  12.     blur(srcImg, binImg, Size(3, 3));  
  13.     threshold(binImg, binImg, 0, 255, CV_THRESH_OTSU);  
  14.     int perPixelValue = 0;//每个像素的值  
  15.     int width = srcImg.cols;  
  16.     int height = srcImg.rows;  
  17.     int* projectValArry = new int[height];//创建一个储存每行白色像素个数的数组  
  18.     memset(projectValArry, 0, height * 4);//初始化数组  
  19.     for (int col = 0; col < height; col++)//遍历每个像素点  
  20.     {  
  21.         for (int row = 0; row < width; row++)  
  22.         {  
  23.             perPixelValue = binImg.at<uchar>(col, row);  
  24.             if (perPixelValue == 0)//如果是白底黑字  
  25.             {  
  26.                 projectValArry[col]++;  
  27.             }  
  28.         }  
  29.     }  
  30.     Mat horizontalProjectionMat(height, width, CV_8UC1);//创建画布  
  31.     for (int i = 0; i < height; i++)  
  32.     {  
  33.         for (int j = 0; j < width; j++)  
  34.         {  
  35.             perPixelValue = 255;  
  36.             horizontalProjectionMat.at<uchar>(i, j) = perPixelValue;//设置背景为白色  
  37.         }  
  38.     }  
  39.     for (int i = 0; i < height; i++)//水平直方图  
  40.     {  
  41.         for (int j = 0; j < projectValArry[i]; j++)  
  42.         {  
  43.             perPixelValue = 0;  
  44.             horizontalProjectionMat.at<uchar>(i, width - 1 - j) = perPixelValue;//设置直方图为黑色  
  45.         }  
  46.     }  
  47.     vector<Mat> roiList;//用于储存分割出来的每个字符  
  48.     int startIndex = 0;//记录进入字符区的索引  
  49.     int endIndex = 0;//记录进入空白区域的索引  
  50.     bool inBlock = false;//是否遍历到了字符区内  
  51.     for (int i = 0; i <srcImg.rows; i++)  
  52.     {  
  53.         if (!inBlock && projectValArry[i] != 0)//进入字符区  
  54.         {  
  55.             inBlock = true;  
  56.             startIndex = i;  
  57.         }  
  58.         else if (inBlock && projectValArry[i] == 0)//进入空白区  
  59.         {  
  60.             endIndex = i;  
  61.             inBlock = false;  
  62.             Mat roiImg = srcImg(Range(startIndex, endIndex + 1), Range(0, srcImg.cols));//从原图中截取有图像的区域  
  63.             roiList.push_back(roiImg);  
  64.         }  
  65.     }  
  66.     delete[] projectValArry;  
  67.     return roiList;  
  68. }  
  69. vector<Mat> verticalProjectionMat(Mat srcImg)//垂直投影  
  70. {  
  71.     Mat binImg;  
  72.     blur(srcImg, binImg, Size(3, 3));  
  73.     threshold(binImg, binImg, 0, 255, CV_THRESH_OTSU);  
  74.     int perPixelValue;//每个像素的值  
  75.     int width = srcImg.cols;  
  76.     int height = srcImg.rows;  
  77.     int* projectValArry = new int[width];//创建用于储存每列白色像素个数的数组  
  78.     memset(projectValArry, 0, width * 4);//初始化数组  
  79.     for (int col = 0; col < width; col++)  
  80.     {  
  81.         for (int row = 0; row < height;row++)  
  82.         {  
  83.             perPixelValue = binImg.at<uchar>(row, col);  
  84.             if (perPixelValue == 0)//如果是白底黑字  
  85.             {  
  86.                 projectValArry[col]++;  
  87.             }  
  88.         }  
  89.     }  
  90.     Mat verticalProjectionMat(height, width, CV_8UC1);//垂直投影的画布  
  91.     for (int i = 0; i < height; i++)  
  92.     {  
  93.         for (int j = 0; j < width; j++)  
  94.         {  
  95.             perPixelValue = 255;  //背景设置为白色  
  96.             verticalProjectionMat.at<uchar>(i, j) = perPixelValue;  
  97.         }  
  98.     }  
  99.     for (int i = 0; i < width; i++)//垂直投影直方图  
  100.     {  
  101.         for (int j = 0; j < projectValArry[i]; j++)  
  102.         {  
  103.             perPixelValue = 0;  //直方图设置为黑色    
  104.             verticalProjectionMat.at<uchar>(height - 1 - j, i) = perPixelValue;  
  105.         }  
  106.     }  
  107.     imshow("垂直投影",verticalProjectionMat);  
  108.     cvWaitKey(0);  
  109.     vector<Mat> roiList;//用于储存分割出来的每个字符  
  110.     int startIndex = 0;//记录进入字符区的索引  
  111.     int endIndex = 0;//记录进入空白区域的索引  
  112.     bool inBlock = false;//是否遍历到了字符区内  
  113.     for (int i = 0; i < srcImg.cols; i++)//cols=width  
  114.     {  
  115.         if (!inBlock && projectValArry[i] != 0)//进入字符区  
  116.         {  
  117.             inBlock = true;  
  118.             startIndex = i;  
  119.         }  
  120.         else if (projectValArry[i] == 0 && inBlock)//进入空白区  
  121.         {  
  122.             endIndex = i;  
  123.             inBlock = false;  
  124.             Mat roiImg = srcImg(Range(0, srcImg.rows), Range(startIndex, endIndex + 1));  
  125.             roiList.push_back(roiImg);  
  126.         }  
  127.     }  
  128.     delete[] projectValArry;  
  129.     return roiList;  
  130. }  
  131. int main(int argc, char* argv[])  
  132. {  
  133.         Mat srcImg = imread("E:\\b.png", 0);//读入原图像  
  134.        char szName[30] = { 0 };   
  135.         vector<Mat> b = verticalProjectionMat(srcImg);//先进行垂直投影     
  136.         for (int i = 0; i < b.size(); i++)  
  137.         {  
  138.             vector<Mat> a = horizontalProjectionMat(b[i]);//水平投影  
  139.             sprintf(szName,"E:\\picture\\%d.jpg",i);  
  140.             for (int j = 0; j < a.size(); j++)  
  141.             {  
  142.                 imshow(szName,a[j]);  
  143.                 IplImage img = IplImage(a[j]);  
  144.                 cvSaveImage(szName, &img);//保存切分的结果  
  145.             }  
  146.         }  
  147.      /* 
  148.         vector<Mat> a = horizontalProjectionMat(srcImg); 
  149.         char szName[30] = { 0 }; 
  150.         for (int i = 0; i < a.size(); i++) 
  151.         { 
  152.             vector<Mat> b = verticalProjectionMat(a[i]); 
  153.             for (int j = 0; j<b.size();j++) 
  154.             { 
  155.                 sprintf(szName, "E:\\%d.jpg", j); 
  156.                 imshow(szName, b[j]); 
  157.             } 
  158.         } 
  159.          */  
  160.         cvWaitKey(0);  
  161.         getchar();  
  162.         return 0;  
  163. }  

以下是程序的结果截图:

(1)原始图像


(2)垂直投影


(3)水平投影


(4)字符切分


由于水平切分的结果只有一个,而垂直切分的结果有多个,所以应该要先进行垂直切分,然后再进行水平切分,这样得到的结果才是切分出字符的大小。若是先进行水平切分,而后进行垂直切分的话,得到的结果就并非要切割出的字符的大小,改代码已经给出,即注释掉的代码,而先进行水平切分,再进行垂直切分的结果如下图所示:

(5)先水平后垂直


可以看到,有的切分出的图像,上下仍然有空白的地方。所以,先水平后垂直的切分方法不适合于本程序的要求。
以上是本人的一点见解,有什么不正确的地方,欢迎指正。

原创粉丝点击