使用OpenCv中Mat進行水平投影與垂直投影並實現字元切分
因為要做影象處理方面的工作,所以最近在學習OpenCv的使用,學習了OpenCv中Mat物件的相關使用之後,實現了使用Mat物件來進行影象的水平投影和垂直投影,並且在投影之後,對字元進行相對應的切分。現在將相關程式碼貼出,一來可以供大家參考並指正錯誤,而來也為的是防止忘記了相關知識。以下就是程式的程式碼,歡迎大家指正錯誤。
#include <stdafx.h> #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> #include <stdio.h> using namespace cv; using namespace std; vector<Mat> horizontalProjectionMat(Mat srcImg)//水平投影 { Mat binImg; blur(srcImg, binImg, Size(3, 3)); threshold(binImg, binImg, 0, 255, CV_THRESH_OTSU); int perPixelValue = 0;//每個畫素的值 int width = srcImg.cols; int height = srcImg.rows; int* projectValArry = new int[height];//建立一個儲存每行白色畫素個數的陣列 memset(projectValArry, 0, height * 4);//初始化陣列 for (int col = 0; col < height; col++)//遍歷每個畫素點 { for (int row = 0; row < width; row++) { perPixelValue = binImg.at<uchar>(col, row); if (perPixelValue == 0)//如果是白底黑字 { projectValArry[col]++; } } } Mat horizontalProjectionMat(height, width, CV_8UC1);//建立畫布 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { perPixelValue = 255; horizontalProjectionMat.at<uchar>(i, j) = perPixelValue;//設定背景為白色 } } for (int i = 0; i < height; i++)//水平直方圖 { for (int j = 0; j < projectValArry[i]; j++) { perPixelValue = 0; horizontalProjectionMat.at<uchar>(i, width - 1 - j) = perPixelValue;//設定直方圖為黑色 } } vector<Mat> roiList;//用於儲存分割出來的每個字元 int startIndex = 0;//記錄進入字元區的索引 int endIndex = 0;//記錄進入空白區域的索引 bool inBlock = false;//是否遍歷到了字元區內 for (int i = 0; i <srcImg.rows; i++) { if (!inBlock && projectValArry[i] != 0)//進入字元區 { inBlock = true; startIndex = i; } else if (inBlock && projectValArry[i] == 0)//進入空白區 { endIndex = i; inBlock = false; Mat roiImg = srcImg(Range(startIndex, endIndex + 1), Range(0, srcImg.cols));//從原圖中擷取有影象的區域 roiList.push_back(roiImg); } } delete[] projectValArry; return roiList; } vector<Mat> verticalProjectionMat(Mat srcImg)//垂直投影 { Mat binImg; blur(srcImg, binImg, Size(3, 3)); threshold(binImg, binImg, 0, 255, CV_THRESH_OTSU); int perPixelValue;//每個畫素的值 int width = srcImg.cols; int height = srcImg.rows; int* projectValArry = new int[width];//建立用於儲存每列白色畫素個數的陣列 memset(projectValArry, 0, width * 4);//初始化陣列 for (int col = 0; col < width; col++) { for (int row = 0; row < height;row++) { perPixelValue = binImg.at<uchar>(row, col); if (perPixelValue == 0)//如果是白底黑字 { projectValArry[col]++; } } } Mat verticalProjectionMat(height, width, CV_8UC1);//垂直投影的畫布 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { perPixelValue = 255; //背景設定為白色 verticalProjectionMat.at<uchar>(i, j) = perPixelValue; } } for (int i = 0; i < width; i++)//垂直投影直方圖 { for (int j = 0; j < projectValArry[i]; j++) { perPixelValue = 0; //直方圖設定為黑色 verticalProjectionMat.at<uchar>(height - 1 - j, i) = perPixelValue; } } imshow("垂直投影",verticalProjectionMat); cvWaitKey(0); vector<Mat> roiList;//用於儲存分割出來的每個字元 int startIndex = 0;//記錄進入字元區的索引 int endIndex = 0;//記錄進入空白區域的索引 bool inBlock = false;//是否遍歷到了字元區內 for (int i = 0; i < srcImg.cols; i++)//cols=width { if (!inBlock && projectValArry[i] != 0)//進入字元區 { inBlock = true; startIndex = i; } else if (projectValArry[i] == 0 && inBlock)//進入空白區 { endIndex = i; inBlock = false; Mat roiImg = srcImg(Range(0, srcImg.rows), Range(startIndex, endIndex + 1)); roiList.push_back(roiImg); } } delete[] projectValArry; return roiList; } int main(int argc, char* argv[]) { Mat srcImg = imread("E:\\b.png", 0);//讀入原影象 char szName[30] = { 0 }; vector<Mat> b = verticalProjectionMat(srcImg);//先進行垂直投影 for (int i = 0; i < b.size(); i++) { vector<Mat> a = horizontalProjectionMat(b[i]);//水平投影 sprintf(szName,"E:\\picture\\%d.jpg",i); for (int j = 0; j < a.size(); j++) { imshow(szName,a[j]); IplImage img = IplImage(a[j]); cvSaveImage(szName, &img);//儲存切分的結果 } } /* vector<Mat> a = horizontalProjectionMat(srcImg); char szName[30] = { 0 }; for (int i = 0; i < a.size(); i++) { vector<Mat> b = verticalProjectionMat(a[i]); for (int j = 0; j<b.size();j++) { sprintf(szName, "E:\\%d.jpg", j); imshow(szName, b[j]); } } */ cvWaitKey(0); getchar(); return 0; }
以下是程式的結果截圖:
(1)原始影象
(2)垂直投影
(3)水平投影
(4)字元切分
由於水平切分的結果只有一個,而垂直切分的結果有多個,所以應該要先進行垂直切分,然後再進行水平切分,這樣得到的結果才是切分出字元的大小。若是先進行水平切分,而後進行垂直切分的話,得到的結果就並非要切割出的字元的大小,改程式碼已經給出,即註釋掉的程式碼,而先進行水平切分,再進行垂直切分的結果如下圖所示:
(5)先水平後垂直
可以看到,有的切分出的影象,上下仍然有空白的地方。所以,先水平後垂直的切分方法不適合於本程式的要求。
以上是本人的一點見解,有什麼不正確的地方,歡迎指正。