用OpenCv中Mat進行水平投影與垂直投影並實現字符切分

目前即將開始的一個新項目是進行字符識別,下面就使用水平投影機垂直投影的測試代碼貼出,供大家參考。

#include<iostream>
#include<opencv2\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<stdio.h>

using namespace cv;
using namespace std;

vector<Mat> horizontalProjectionMat(Mat srcImg)//水平投影
{
	Mat binImg;
	blur(srcImg, binImg, Size(3, 3));//blur均值濾波函數,濾波核大小爲3*3
	threshold(binImg, binImg, 0, 255, CV_THRESH_OTSU);//threshold函數是進行圖像二值化處理,使用OTSU算法確定分割閾值
	int perPixelValue = 0;
	int width = srcImg.cols;
	int height = srcImg.rows;
	int* projectValArry = new int[height];//創建一個存儲每行白色像素個數的數組
	memset(projectValArry, 0, height * 4);//初始化數組: 比如memset(ch,0,8),就是把數組ch前八項置爲零,後面的不一定爲零。
	for (int col = 0; col < height; col++)//遍歷每個像素點
	{
		for (int row = 0; row < width; row++)
		{
			perPixelValue = binImg.at<uchar>(col, row);//img.at<uchar>(i, j)=128這是指將灰度圖的第x行,第y列的置爲灰度爲128的灰色(0爲黑色,255爲白色)
			//如果是彩圖,則寫爲ima.at<cv::Vec3b>(x, y)[0],ima.at<cv::Vec3b>(x, y)[1],ima.at<cv::Vec3b>(x, y)[2],分別表示紅綠藍(順序不確定)三種顏色的色度
			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;//設置直方圖爲黑色
		}
	}
	imshow("水平投影", horizontalProjectionMat);
	cvWaitKey(0);
	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);//push_back() 在Vector最後添加一個元素(參數爲要插入的值)
		}
	}
	delete[] projectValArry;
	return roilist;
}
int main(int argc, char* argv[])
{
	Mat srcImg = imread("E:/OpenCV/OpenCVTest/4.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_s(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);//保存切分圖像
		}
	}
	cvWaitKey(0);
	getchar();
	return 0;
}

結果:

(1)原始圖像:

(2)垂直投影:

(3)水平投影

(4)字符切分結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章