1. 程式人生 > >使用OpenCv中Mat進行水平投影與垂直投影並實現字元切分

使用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)先水平後垂直


可以看到,有的切分出的影象,上下仍然有空白的地方。所以,先水平後垂直的切分方法不適合於本程式的要求。
以上是本人的一點見解,有什麼不正確的地方,歡迎指正。

相關推薦

使用OpenCvMat進行水平投影垂直投影實現字元切分

因為要做影象處理方面的工作,所以最近在學習OpenCv的使用,學習了OpenCv中Mat物件的相關使用之後,實現了使用Mat物件來進行影象的水平投影和垂直投影,並且在投影之後,對字元進行相對應的切分。現在將相關程式碼貼出,一來可以供大家參考並指正錯誤,而來也為的是防止忘記了

OpenCV 實現圖片的水平投影垂直投影,並進行行分割

對於印刷體圖片來說,進行水平投影和垂直投影可以很快的進行分割,本文就在OpenCV中如何進行水平投影和垂直投影通過程式碼進行說明。水平投影:二維影象在y軸上的投影垂直投影:二維影象在x軸上的投影由於投影的影象需要進行二值化,本文采用積分二值化的方式,對圖片進行處理。具體程式碼

OpencvMat結構體元素的獲取賦值

【OpenCV3影象處理】Mat中元素的獲取與賦值 ( 對比.at<>()函式 和 .ptr<>()函式) 2017年04月12日 10:08:55 閱讀數:7542 標籤: opencvopencv3 更多 個人分類:&nbs

OpenCVMatAndroidBitmap簡介

        因為在介紹這部分系列的內容時,預設是對Android開發有一點基礎的,所以這樣的話,Bitmap可能就相對很熟悉了,相較陌生的是Mat,那我們就首先來看看Mat是什麼。 1,Mat 1.1 Mat基本介紹 Mat是OpenCV中用於

opencvMat陣列之間值傳遞的方法

1.將陣列內容傳遞給Mat 示例程式碼: unsigned char cbuf[height][width]; cv::Mat img(height, width, CV_8UC1, (unsigned char*)cbuf); 1 2 2.將Mat中的內容傳遞給

水平居中垂直居中,以及對齊

代碼 布局 ott 水平居中 研究 背景圖 兼容性 vertical float 我以前一直都搞不清楚水平居中與垂直居中,更不用談什麽對齊,臨時抱佛腳,也找不到很好的答案,於是把網上的代碼研究了一番,總結一下經驗: 盒子水平居中:margin:0 auto 註意:在

水平居中垂直居中

js xml order 模型 cti 固定 屬性 元素垂直居中 nbsp attribute 一、水平居中 1)如果是行內元素,需要在它的父元素上設置text-align: center; 2)如果是塊元素,直接設置元素的css屬性text-align: center;

opencv Mat矩陣申明形式

bits 8bit 技術 發現 對象創建 符號 通道 log play 在調用opencv的時候,可能不像matlab那樣直接就可以 新建矩陣。在C++中需要嚴格的定義矩陣形式; 在opencv中一共有顯示創建Mat對象的方法; 一、使用mat()構造函數:   

OpenCV影象的淺拷貝深拷貝 = copy clone區別

下面介紹三種OpenCV複製影象的方法: 方法1、過載運算子= 使用過載運算子“=”進行的拷貝是一種淺拷貝,雖然它們有不同的矩陣頭,但是二者共享相同的記憶體空間,二者內容相互關聯,任何一個變數變化的同時另一個變數也隨之改變。 /*OpenCV v1版本*/ IplImage im

檢視opencv Mat的資料和影象

1、列印Mat中的資料 Mat element; ..... cout << "3*3矩形核:" << endl << element << endl;  列印顯示如下: 2、檢視程式中的Mat影象  

OpenCVMat和IplImage之間的相互裝換(OpenCV2.0和OpenCV3.0)

Mat是OpenCV和C++介面的矩陣類,IplImage是OpenCV和C語言介面的結構體。 Mat讀取顯示用的是imread、imshow等,IplImage讀取顯示用的是cvLoadImage()、cvShowImage()。 有時候會涉及到兩者之間的轉換,下面詳細見介紹一下兩者之

OpencvMat矩陣相乘——點乘、dot、mul運算詳解

Mat矩陣點乘——A*B Opencv過載了運算子“*”,姑且稱之為Mat矩陣“點乘”,其中一個過載宣告為: CV_EXPORTS MatExpr operator * (const Mat& a, const Mat& b); 點乘說明: 1.  

opencvMat進行排序

由於sort函式和sortId函式是對一行或一列進行排序的,所以對整個需要reshape一下,還要指定是行還是列。 Mat c1 = (Mat_<uchar>(3, 3) << 1, 5, 6, 2, 4, 3, 8, 9, 7); Mat c3 = c1.resha

OpenCV影象顯示、讀取儲存

眾所周知,opencv中的cv2.imread函式返回的影象資料,通道是BGR,而不是一般意義上的RGB;但是,這時如果用cv2.imshow進行顯示,看到的卻是正常的樣子;而如果用其他庫的顯示函式,如matplotlib的plt.imshow來顯示,則是異常的顯示,一般都是

OpenCVMat類at函式具體用法

Mat.at<儲存型別名稱>(行,列)[通道] 具體例子 假設M中儲存一張512*512的彩色圖片,那這張圖有三個通道,下面兩個at都表示的是這個矩陣的最右下的那個點。 M矩陣有512行,512*3列,不加入通道引數,也就是第一個at命令,是簡單的遍歷,可以

OpenCVMat類的影象如何設定ROI

Mat類表示的影象進行ROI操作有兩種方法 (1)使用拷貝建構函式Mat(constMat& m, const Rect& roi ),矩形roi指定了興趣區 例如: Mat src = imread(“xx.jpg”); Mat srcROI( src,

OpenCVMat的type

OpenCV中幾種不同深度的資料格式,其特性統計如下: 影象型別 資料型別 位元組 取值範圍 顯示範圍 IPL_DEPTH_8U unsigned char 1 0~255 0~255 IPL_DEPTH_8S c

關於opencvMat資料對齊的問題

opencv中IplImage的資料往往是自動對齊的,所以我們直接用IPLImage資料時通常不會出現對齊錯誤。 但是在進行Mat和IplImage資料轉換後,通常會出現資料錯位,需要補齊: 通常按照4位補齊,可以直接資料賦值: http://blog.csdn.net/s

OpenCVMat的基本用法

一、矩陣基礎操作:Mat image(240, 320, CV8UC3);第一個引數是rows,該矩陣的行數;第二個引數是cols,該矩陣的列數;第三個引數是該矩陣元素的型別。這句話表示建立一個大小為240×320的矩陣,裡面的元素為8位unsigned型,通道數(chan

C++ 動態分配陣列空間,以及opencvMat類的初始化

在C++中,如果想要申請動態陣列,必須要用動態分配的方式。 int **matrix=new int*[num_of_rows]; for(int i=0;i<num_of_rows;i++){ int *row=new int[num_of