c/c++语言开发共享C++编程模板匹配超详细的识别手写数字实现示例

首先,本篇文章用到的方法是模板匹配,而不是基于神经网络的,还请各位注意了!(模板匹配还请自行了解,站上有很多介绍)我刚开始做实验的时候只有一点c++基础,对于文件和opencv我一点都不了解,所以导致

首先,本篇文章用到的方法是模板匹配,而不是基于神经网络的,还请各位注意了!(模板匹配还请自行了解,站上有很多介绍)我刚开始做实验的时候只有一点c++基础,对于文件和opencv我一点都不了解,所以导致了我刚开始迷茫了很久,直到后来才渐渐做起来。废话不多说,让我们开始吧!

过程很简单,如下:

C++编程模板匹配超详细的识别手写数字实现示例

匹配成功:存在一个最小距离(这些距离相等),且为一个数字;存在多个最小距离,且为同一个数字。

拒绝识别:存在多个最小距离,且为不同数字。

识别错误:存在一个最小距离,但与被测数字不是相同的数字。

也许乍一看看不明白,我在这里解释一下,明白的可以绕过。我们这里假设1,2,3(注意,他们的样本都有多个)为训练集,d为测试样本。匹配时匹配到d与1距离最小且只与1距离最小,(可能与多个1的样本距离最小或者只有一个)那么匹配成功;匹配时匹配到d与1和2的某个样本都有最小距离,那么拒绝匹配;匹配时匹配到d(假如d是1的样本)与2有最小距离,那么识别错误。

因为图片处理不是c/c++开发分享C++编程模板匹配超详细的识别手写数字实现示例章的主要内容,我们跳过图像处理步骤(有兴趣的可以去看图像处理这门课),直接给处理好的图片。那么我们该如何构建训练库,又该如何让计算机能够识别我们的图片呢?接下来我们来看看如何实现构建训练库。

我的实验中有1000张训练样本(200张测试样本),既然要让计算机能够识别,那当然是把图片数字化。在图像处理的步骤里,我们得到的训练样本都是28*28像素点的图片,可以想到28*28是一个不小的数量,为了提高处理速度,我们把图片压缩成7*7大小的,这样即提高了处理速度,也方便我们写代码,因为7*7和4*4都是正方形。如下图:

C++编程模板匹配超详细的识别手写数字实现示例

压缩图片:我们纵向遍历7*7的方格,将里面像素大于127的小格子进行计数,当其数量超过6(有的同学会觉得应该是8,因为8是一半,但是8最终得到的正确率太低了,所以我找了一个合适的参数)我们就把大格子对应的7*7的二维数组的相应位置设置为1,反之为0;然后再将数组转换成字符串,这样下来我们就会得到一个长度为49的字符串,这个字符串就是我们计算机匹配的核心。

另外,我是先把训练集和测试集分别数据化,再依次取出来作比较。也可以采用一边遍历测试集和处理,一遍作比较,我没有输出文件名,因为我采用的方式比较笨,代码量也很多,主要是因为我之前写完之后有很多bug,导致我不能成功运行,所以我采用这种简单代码来避免错误,小伙伴们大可不必用这种方式!

值得注意的是,文件流的打开和关闭的时机也会很大程度上影响代码运行,这个问题困扰了我很久,希望大家引以为戒,代码中具体位置我已经标出来了。(标***的位置)另外,大家对于读文件写文件的文件流自己去了解,读文件是ofstream,写文件是ifstream,每次访问文件都要打开文件和关闭文件。getline函数每次依次取一行数据,所以我们在遍历完一个文档之前不会关闭文档,也就不会再打开文档。

最后我对我字符串比较做一个解释,我是采用了一个标志refused来标志当前字符串有没有被拒绝识别,当发现相似度(代码中用total表示的)小于49的就把它赋值给相似度,并且把拒绝识别设置为假,直到找到最小的,当找到最小的之后又找到了另一个相同相似度的,则判断两个样本数字是不是相同的,不是的话就把refused设置为真,即在后面直接输出拒绝识别。

我判断两个样本是否为同一个数字是通过范围比对,简单地来说就是训练样本的第0——99个对应测试样本的第0——19个,这是一个偷懒的办法,我没时间改代码了所以就这样代替了别人那种文本带文件名的。(带文件名比对时还需要去文件名)

其他的解释我放在代码里,有助于大家更直接的理解!

  #include<iostream>  #include<fstream>  #include<opencv2/opencv.hpp>  #include<opencv2/highgui.hpp>  #include<opencv2/core.hpp>  #include<io.h>                          //api和结构体  #include<string.h>  #include<string>  using namespace std;  using namespace cv;  void ergodictest(string filename, string name);    //遍历函数  string image_compression(string imgpath);          //压缩图片并返回字符串  int distance(string str1, string str2);            //对比函数不一样的位数  void compare();  int turn(char a);  void main()  {  	const char* filepath = "e:\learn\vsfile\c++project\picturedata\train-images";       	ergodictest(filepath,"train_num.txt");         //处理训练集  	const char* test_path= "e:\learn\vsfile\c++project\picturedata\test-images";  	ergodictest(test_path, "test_num.txt");  	compare();     }     void ergodictest(string filename,string name)       //遍历并把路径存到files  {  	string firstfilename = filename + "\*.bmp";  	struct _finddata_t fileinfo;  	intptr_t handle;            //不能用long,因为精度问题会导致访问冲突,longlong也可  	string rout = "e:\learn\vsfile\c++project\picturedata\" + name;  	ofstream file;  	file.open(rout, ios::out);  	handle = _findfirst(firstfilename.c_str(), &fileinfo);  	if ( _findfirst(firstfilename.c_str(), &fileinfo) != -1)  	{  		do  		{  			file << fileinfo.name << ":" << image_compression(filename + "\" + fileinfo.name) << endl;  		} while (!_findnext(handle, &fileinfo));  		file.close();  		_findclose(handle);  	}  }  string image_compression(string imgpath)   //输入图片地址返回图片二值像素字符  {  	mat image = imread(imgpath);               //输入的图片  	cvtcolor(image, image, color_bgr2gray);  	int matrix[28][28];                        //将digitization转化为字符串类型  	for (int row = 0; row < image.rows; row++)  //把图片的像素点传给数组  		for (int col = 0; col < image.cols; col++)  		{  			matrix[row][col] = image.at<uchar>(row, col);  		}  	string img_str = "";                   //用来存储结果字符串  	int x = 0, y = 0;  	for (int k = 1; k < 50; k++)  	{  		int total = 0;  		for (int q = 0; q < 4; q++)  			for (int p = 0; p < 4; p++)  				if (matrix[x + q][y + p] > 127) total += 1;  		y = (y + 4) % 28;  		if (total >= 6) img_str += '1';    //将28*28的图片转化为7*7即压缩  		else img_str += '0';  		if (k % 7 == 0)  		{  			x += 4;  			y = 0;  		}  	}  	return img_str;  }     int distance(string str1, string str2)  //比对两个字符串有多少个不一样  {  	int counts=0;  	for (int i = 0; i < str1.length(); i++)  	{  		if (str1[i] == str2[i]) continue;  		else counts++;   	}  	return counts;  }  int turn(char a)  {  	stringstream str;  	int f = 1;  	str << a;  	str >> f;  	str.clear();  	return f;  }     void compare()  {  	ifstream train_data;//建立读文件流  	ifstream test_data;  	string tmp1 = "";         //从train中取数据存在tmp1  	string tmp11 = "";  	string tmp2 = "";         //从test中取  	string tmp22 = "";  	bool refused = false; //拒绝识别标志  	int tr_num = 0;       //用来存储最小值的文件名(训练集)  	int num_refused = 0;   //拒绝识别个数  	int num_false = 0;     //识别错误个数  	int num_true = 0;      //正确识别个数  	test_data.open("e:\learn\vsfile\c++project\picturedata\test_num.txt");  	for (int p = 0; p < 200; p++)  	{  		int total = 49;      //方便比大小,设置初值为49  		getline(test_data, tmp2);  		tmp22 = tmp2;    //在切割字符串之前保留,以便后面知晓该字符串是哪个数字的  		if(tmp2.length()==57) tmp2.erase(0,8); //erase函数是用来切割字符串的,这里是切割第0位的后面8位,存剩余的其他位  		else tmp2.erase(0,9);  		train_data.open("e:\learn\vsfile\c++project\picturedata\train_num.txt");  		for (int j = 0; j < 1000; j++)         //一个测试样本和所有训练样本对比  		{  			getline(train_data, tmp1);  			tmp11 = tmp1;  			if (tmp1.length() == 57) tmp1.erase(0, 8);  			else tmp1.erase(0, 9);  			if (distance(tmp1, tmp2) < total)  //取最相近的  			{  				refused = false;   //拒绝识别被设置为否,即识别没有被拒绝  				total = distance(tmp1, tmp2);  				tr_num = turn(tmp11[0]);          //记录数字  			}  			else if(distance(tmp1, tmp2) == total && tr_num!= turn(tmp11[0]))  //发现相同相似度,且两者归属的数字不同  			{  				refused = true;   //拒绝识别  				continue;          //循环继续  			}	  		}  		train_data.close();     		if (!refused)  		{  			if (tr_num == turn(tmp22[0]))  			{  				//cout << tmp2[0] << endl;  				num_true++;  				cout << "识别为:" << tr_num << endl;  			}  			else  			{  				num_false++;  				cout << "识别错误!" << endl;  			}  		}  		else  		{  			num_refused++;  			cout << "拒绝识别!" << endl;  		}  	}  	test_data.close();  	double t = num_true / 200.0, f = num_false / 200.0, r = num_refused / 200.0;  	cout << "正确率为:" << t << endl;  	cout << "错误率为:" << f << endl;  	cout << "拒绝识别率为:" << r << endl;  }

代码中有很多//注释,都是我调试代码用的,不用管。

我把遍历文件夹的参考链接放在这里:

另外,如有错误欢迎大家指正!

以上就是c++编程模板匹配超详细的识别手写数字实现示例的详细内容,更多关于c++编程模板匹配识别手写数字的资料请关注<计算机技术网(www.ctvol.com)!!>其它相关文章!

需要了解更多c/c++开发分享C++编程模板匹配超详细的识别手写数字实现示例,都可以关注C/C++技术分享栏目—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/841564.html

(0)
上一篇 2021年10月16日
下一篇 2021年10月16日

精彩推荐