对于印刷体图片来说,进行水平投影和垂直投影可以很快的进行分割,本文就在OpenCV中如何进行水平投影和垂直投影通过代码进行说明。
水平投影:二维图像在y轴上的投影
垂直投影:二维图像在x轴上的投影
由于投影的图像需要进行二值化,本文采用积分二值化的方式,对图片进行处理。
具体代码如下:
//积分二值化 void thresholdIntegral (Mat inputMat, Mat& outputMat) { int nRows = inputMat.rows; int nCols = inputMat.cols; // create the integral image Mat sumMat; integral (inputMat, sumMat); ; double T = 0.15; // perform thresholding ; int x1, y1, x2, y2, count, sum; int* p_y1, *p_y2; uchar* p_inputMat, *p_outputMat; ; i < nRows; ++i) { y1 = i - s2; y2 = i + s2; ) { y1 = ; } if (y2 >= nRows) { y2 = nRows - ; } p_y1 = sumMat.ptr<int> (y1); p_y2 = sumMat.ptr<int> (y2); p_inputMat = inputMat.ptr<uchar> (i); p_outputMat = outputMat.ptr<uchar> (i); ; j < nCols; ++j) { // set the SxS region x1 = j - s2; x2 = j + s2; ) { x1 = ; } if (x2 >= nCols) { x2 = nCols - ; } count = (x2 - x1)* (y2 - y1); // I(x,y)=s(x2,y2)-s(x1,y2)-s(x2,y1)+s(x1,x1) sum = p_y2[x2] - p_y1[x2] - p_y2[x1] + p_y1[x1]; if ((int) (p_inputMat[j] * count) < (int) (sum* (1.0 - T))) { p_outputMat[j] = ; } else { p_outputMat[j] = ; } } } } //垂直方向投影 void picshadowx (Mat binary) { Mat paintx (binary.size(), CV_8UC1, Scalar ()); //创建一个全白图片,用作显示 int* blackcout = new int[binary.cols]; memset (blackcout, , binary.cols * ); ; i < binary.rows; i++) { ; j < binary.cols; j++) { ) { blackcout[j]++; //垂直投影按列在x轴进行投影 } } } ; i < binary.cols; i++) { ; j < blackcout[i]; j++) { paintx.at<uchar> (binary.rows--j, i) = ; //翻转到下面,便于观看 } } delete blackcout; imshow ("paintx", paintx); } //水平方向投影并行分割 void picshadowy (Mat binary) { //是否为白色或者黑色根据二值图像的处理得来 Mat painty (binary.size(), CV_8UC1, Scalar ()); //初始化为全白 //水平投影 int* pointcount = new int[binary.rows]; //在二值图片中记录行中特征点的个数 memset (pointcount, , binary.rows * );//注意这里需要进行初始化 ; i < binary.rows; i++) { ; j < binary.cols; j++) { ) { pointcount[i]++; //记录每行中黑色点的个数 //水平投影按行在y轴上的投影 } } } ; i < binary.rows; i++) { ; j < pointcount[i]; j++) //根据每行中黑色点的个数,进行循环 { painty.at<uchar> (i, j) = ; } } imshow ("painty", painty); vector<Mat> result; ; ; bool inblock = false; //是否遍历到字符位置 ; i < painty.rows; i++) { ) //进入有字符区域 { inblock = true; startindex = i; cout << "startindex:" << startindex << endl; } ) //进入空白区 { endindex = i; inblock = false; Mat roi = binary.rowRange (startindex, endindex+); //从而记录从开始到结束行的位置,即可进行行切分 result.push_back (roi); } } ; i < result.size(); i++) { Mat tmp = result[i]; imshow ("test"+to_string (i), tmp); } delete pointcount; } int main (int argc, char* argv[]) { Mat src = cv::imread ("test.jpg"); if (src.empty()) { cerr << "Problem loading image!!!" << endl; ; } imshow("in",src); Mat gray; ) { cv::cvtColor (src, gray, CV_BGR2GRAY); } else { gray = src; } Mat bw2 = Mat::zeros (gray.size(), CV_8UC1); thresholdIntegral (gray, bw2); cv::imshow ("binary integral", bw2); //picshadowx (bw2); picshadowy (bw2); waitKey (); ; }
输入图片:
二值图片:
水平投影:
垂直投影:
行切割: