VS Opencv 字符模板匹配小实例

 初步认识

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>


using namespace cv;
using namespace std;

//声明一些全局变量,比如图像、模板和结果矩阵,以及匹配方法和窗口名称
Mat img; Mat templ; Mat result;
char* image_window = "Source Image";
char* result_window = "Result window";
int match_method;



int main(int argc, char* argv[])
{
	//图片加载
	img = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\1.png");
	templ = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\template\\1.png");

	namedWindow(image_window, WINDOW_AUTOSIZE);
	namedWindow(result_window, WINDOW_AUTOSIZE);

	//创建将存储每个模板位置匹配结果的结果矩阵。详细观察结果矩阵的大小(匹配所有可能的位置)
	int result_cols = img.cols - templ.cols + 1;
	int result_rows = img.rows - templ.rows + 1;
	result.create(result_rows, result_cols, CV_32FC1);

	//执行模板匹配
	matchTemplate(img, templ, result, match_method);

	// 归一化
	normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());

	//利用cv::minMaxLoc对结果矩阵R中的最小值和最大值进行局部定位
	double minVal; double maxVal; Point minLoc; Point maxLoc;
	Point matchLoc;
	minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

	//对于前两个方法(TM_SQDIFF和MT_SQDIFF_NORMED),最好的匹配是最小值。
	//对于其他来说,值越高代表匹配越好。因此,我们将相应的值保存在matchLoc变量中:
	if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
	{
		matchLoc = minLoc;
	}
	else
	{
		matchLoc = maxLoc;
	}
	
	//显示源图像和结果矩阵。在可能匹配的最高区域周围画一个矩形

	rectangle(img, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
	rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
	imshow( image_window, img);
	imshow( result_window, result );

	waitKey(0);
	destroyAllWindows();

}

-

 大图是匹配的效果

字符识别算法

想法:把所有的字符模板放到一张图片,根据其各自位置建一个列表,将分割好的图片一次检测匹配,根据其在模板大图上的位置,根据索引在列表找到对应的字符

 

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <imgproc.hpp>
#include <iostream>
#include <map>

using namespace cv;
using namespace std;


Mat getPos(Mat& image);//定位到字串的位置
void segChar(Mat& image);//字符分割 保存至文件夹
void charTem(Mat& image);//利用图像融合,做一个字符模板
string recog();//读取文件夹中char 图片,进行模板匹配,找到字符
//建立对应的字符字典
map<int, string> maps = { {0, "N",},
						{1, "J",},
						{2, "S",},
						{3, "G",},};

int pos[4] = {};



int main(int argc, char* argv[])
{
	//图片加载
	Mat img;
	img = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\1.png");
	//定位 并取出文字区域 
	Mat pos_img;
	pos_img = getPos(img);

	//分割每个字符图片 保存至文件夹
	segChar(pos_img);

	//实现字符的模板
	charTem(img);

	//识别
	string chars = recog();
	cout << chars << endl;

	imshow("test1", pos_img);
	waitKey(0);
	destroyAllWindows();

}

Mat getPos(Mat& image)
{
	Mat ret;
	Mat img_copy;
	image.copyTo(img_copy);
	Mat img = img_copy;
	//!!! 图像预处理 部分
	cvtColor(img_copy, img_copy, COLOR_BGR2GRAY);
	bitwise_not(img_copy, img_copy);//黑白像素取反
	GaussianBlur(img_copy, img_copy, Size(3, 3), 0, 0);//高斯模糊,消除噪点
	threshold(img_copy, img_copy, 50, 255, THRESH_BINARY | THRESH_OTSU);//阈值化
	dilate(img_copy, img_copy, Mat(), Point(-1, -1),5);//膨胀
	int morph_size = 2;//闭运算,把字符之间进一步缩短,变成一团
	Mat element = getStructuringElement(MORPH_RECT, Size(morph_size * 2, morph_size * 2), Point(morph_size, morph_size));
	morphologyEx(img_copy, img_copy,MORPH_CLOSE,element,Point(-1,1),3);
	//!!! 找轮廓部分
	
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;
	findContours(img_copy, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
	
	//获取轮廓的左上和右下两个坐标位置,左上是坐标最小的,右下是坐标最大的
	int left[2] = { contours[0][0].x, contours[0][0].y };
	int right[2] = { contours[0][0].x, contours[0][0].y };
	for (size_t i = 0; i < contours.size(); i++)
	{
		for (size_t j = 1; j < contours[i].size(); j++)
		{
			if (left[0] > contours[i][j].x) {
				left[0] = contours[i][j].x;
			}
			if (left[1] > contours[i][j].y) {
				left[1] = contours[i][j].y;
			}
			if (right[0] < contours[i][j].x) {
				right[0] = contours[i][j].x;
			}
			if (right[1] < contours[i][j].y) {
				right[1] = contours[i][j].y;
			}
		}
	}
	//在原图中框出区域
	//rectangle(ret,Point(left[0],left[1]), Point(right[0],right[1]),(255));
	//取出框中的图像
	ret = img(Rect(left[0], left[1], right[0]- left[0], right[1] - left[1]));//参数是 矩形区域的左上角坐标和对应的x,y的边长

	return ret;


}



void segChar(Mat& image)
{
	Mat img_copy;
	image.copyTo(img_copy);
	//!!! 图像预处理 部分
	cvtColor(img_copy, img_copy, COLOR_BGR2GRAY);
	bitwise_not(img_copy, img_copy);//黑白像素取反
	GaussianBlur(img_copy, img_copy, Size(3, 3), 0, 0);//高斯模糊,消除噪点
	threshold(img_copy, img_copy, 50, 255, THRESH_BINARY | THRESH_OTSU);//阈值化
	dilate(img_copy, img_copy, Mat(), Point(-1, -1), 3);//膨胀
	erode(img_copy, img_copy, Mat(), Point(-1, -1), 3);

	//!!! 投影法进行字符分割,投影到水平方向,那就是统计每个横坐标上的那一列有多少个白色的像素
	//!  画出像素投影图
	int src_width = img_copy.cols;
	int src_height = img_copy.rows;
	int* projectValArry = new int[src_width]();//创建用于储存每列白色像素个数的数组
	//memset(projectValArry, 0, src_width*4);//初始化数组
	//取列白色像素个数
	for (int i = 0; i < src_height; i++) {
		for (int j = 0; j < src_width; j++) {
			if (img_copy.at<uchar>(i, j)) {
				projectValArry[j]++;
			}
		}
	}
	//将每列白色像素数目绘制成直方图
	//定义画布 绘制垂直投影下每列白色像素的数目
	Mat verticalProjectionMat(src_height, src_width, CV_8UC1, Scalar(0));
	for (int i = 0; i < src_width; i++) {
		for (int j = 0; j < projectValArry[i]; j++) {
			verticalProjectionMat.at<uchar>(src_height - j - 1, i) = 255;
		}
	}
	imshow("verticalProjectionMat", verticalProjectionMat);//汉字的分割确实没有英文效果好

	//! 根据每列白色像素数目设置截取起始和截止列
	vector<Mat> split_src;//定义Mat vector ,存储图片组
	bool white_block = 0, black_block = 0;//定义标志,用来指示在白色像素区还是在全黑区域
	int temp_col_forword = 0, temp_col_behind = 0;//定义列temp_col_forword  temp_col_behind,记录字符截取起始列和截止列
	Mat split_temp;
	//遍历数组projectValArry,统计每列的白色像素,投影到水平区域
	for (int i = 0; i < src_width; i++) {
		if (projectValArry[i]) {//表示区域有白色像素
			white_block = 1;
			black_block = 0;
		}
		else {				//若无白色像素(进入黑色区域)
			if (white_block == 1) {//若前一列有白色像素
				temp_col_behind = i;//取当前列为截止列
				//截取下一部分
split_temp = img_copy(Rect(temp_col_forword, 0, temp_col_behind - temp_col_forword, src_height)).clone();
split_src.push_back(split_temp);//将截取玩的图片存入列表中
			}
			temp_col_forword = i;//记录最新黑色区域的列号,记为起始列
			black_block = 1;//表示进入黑色区域
			white_block = 0;
		}
	}
	//! 将截出来的图片组放入文件夹
	for (size_t i = 0; i < split_src.size(); i++)
	{
		string img_Name = "E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\" + to_string(i) + ".jpg";
		imwrite(img_Name, split_src[i]);
	}

	imshow("test2", img_copy);


}

void charTem(Mat& image)
{
	Mat img = Mat::zeros(image.size(), image.type());
	int img_col = img.cols;
	int img_row = img.rows;
	//cout << img_col << " : " << img_row << endl;//778 : 270
	//依次将几个字符按顺序图像融合,注意各自的坐标位置
	Mat img1 = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\0.jpg");
	cout << img1.size() << endl;
	Mat imageROI1 = img(Rect(100, 100, img1.cols, img1.rows));// 设置图片1要放入模板的位置
	pos[0] = 100;// 0位置 对应 字符 N 
	addWeighted(imageROI1, 0, img1, 1, 0, imageROI1);
	Mat img2 = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\1.jpg");
	Mat imageROI2 = img(Rect(200, 100, img2.cols, img2.rows));// 设置图片1要放入模板的位置
	pos[1] = 200;// 0位置 对应 字符 J 
	addWeighted(imageROI2, 0, img2, 1, 0, imageROI2);
	Mat img3 = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\2.jpg");
	Mat imageROI3 = img(Rect(300, 100, img3.cols, img3.rows));// 设置图片1要放入模板的位置
	pos[2] = 300;// 0位置 对应 字符 J 
	addWeighted(imageROI3, 0, img3, 1, 0, imageROI3);
	Mat img4 = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\3.jpg");
	Mat imageROI4 = img(Rect(400, 100, img4.cols, img4.rows));// 设置图片1要放入模板的位置
	pos[2] = 400;// 0位置 对应 字符 J 
	addWeighted(imageROI4, 0, img4, 1, 0, imageROI4);

	imshow("test3", img);
	string img_Name = "E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\template\\temp.jpg";
	imwrite(img_Name, img);

}
string recog()
{
	string ret;
	Mat temp = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\template\\temp.jpg");//模板
	String pattern = "E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr";
	vector<cv::String> fn;
	glob(pattern, fn, false);
	vector<Mat> images;
	//size_t count = fn.size(); //number of png files in images folder,我这10个字符的图片
	for (size_t i = 0; i < fn.size(); i++)
	{
		string img_Name = "E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\" + to_string(i) + ".jpg";
		Mat img = imread(img_Name);
		images.push_back(img);

	}
	//识别过程
	Mat result;
	int result_cols = temp.cols - images[0].cols + 1;
	int result_rows = temp.rows - images[0].rows + 1;
	int match_method = 0;
	result.create(result_rows, result_cols, CV_32FC1);
	vector<Point> loc;
	for (size_t i = 0; i < images.size(); i++)
	{
		matchTemplate(temp, images[i], result, match_method);
		// 归一化
		normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());

		//利用cv::minMaxLoc对结果矩阵R中的最小值和最大值进行局部定位
		double minVal; double maxVal; Point minLoc; Point maxLoc;
		Point matchLoc;
		minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

		//对于前两个方法(TM_SQDIFF和MT_SQDIFF_NORMED),最好的匹配是最小值。
		//对于其他来说,值越高代表匹配越好。因此,我们将相应的值保存在matchLoc变量中:
		if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
		{
			matchLoc = minLoc;
		}
		else
		{
			matchLoc = maxLoc;
		}
		loc.push_back(matchLoc);
	}
	//根据matchloc 的数值进行字符的匹配
	for (size_t i = 0; i < loc.size(); i++)
	{
		cout << loc[i].x << endl;
		if (loc[i].x > 95 &loc[i].x < 105){
			ret = ret + maps[0];
		}
		else if (loc[i].x > 195 & loc[i].x < 205) {
			ret = ret + maps[1];
		}
		else if (loc[i].x > 295 & loc[i].x < 305) {
			ret = ret + maps[2];
		}
		else if (loc[i].x > 395 & loc[i].x < 405) {
			ret = ret + maps[3];
		}
		
	}
	return ret;

}

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值