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

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

过程很简单,如下:

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

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

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

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

因为图片处理不是本文章的主要内容,我们跳过图像处理步骤(有兴趣的可以去看图像处理这门课),直接给处理好的图片。那么我们该如何构建训练库,又该如何让计算机能够识别我们的图片呢?接下来我们来看看如何实现构建训练库。

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

压缩图片:我们纵向遍历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;
}

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

我把遍历文件夹的参考链接放在这里://www.jb51.net/article/225454.htm

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

以上就是C++编程模板匹配超详细的识别手写数字实现示例的详细内容,更多关于C++编程模板匹配识别手写数字的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解PyTorch手写数字识别(MNIST数据集)

    MNIST 手写数字识别是一个比较简单的入门项目,相当于深度学习中的 Hello World,可以让我们快速了解构建神经网络的大致过程.虽然网上的案例比较多,但还是要自己实现一遍.代码采用 PyTorch 1.0 编写并运行. 导入相关库 import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torchvision import datasets, t

  • Python开发之基于模板匹配的信用卡数字识别功能

    环境介绍 Python 3.6 + OpenCV 3.4.1.15 原理介绍 首先,提取出模板中每一个数字的轮廓,再对信用卡图像进行处理,提取其中的数字部分,将该部分数字与模板进行匹配,即可得到结果. 模板展示 完整代码 # !/usr/bin/env python # -*- coding: utf-8 -*- # @Time: 2020/1/11 14:57 # @Author: Martin # @File: utils.py # @Software:PyCharm import cv2

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

    首先,本篇文章用到的方法是模板匹配,而不是基于神经网络的,还请各位注意了!(模板匹配还请自行了解,站上有很多介绍)我刚开始做实验的时候只有一点c++基础,对于文件和opencv我一点都不了解,所以导致了我刚开始迷茫了很久,直到后来才渐渐做起来.废话不多说,让我们开始吧! 过程很简单,如下: 匹配成功:存在一个最小距离(这些距离相等),且为一个数字:存在多个最小距离,且为同一个数字. 拒绝识别:存在多个最小距离,且为不同数字. 识别错误:存在一个最小距离,但与被测数字不是相同的数字. 也许乍一看看

  • 超详细PyTorch实现手写数字识别器的示例代码

    前言 深度学习中有很多玩具数据,mnist就是其中一个,一个人能否入门深度学习往往就是以能否玩转mnist数据来判断的,在前面很多基础介绍后我们就可以来实现一个简单的手写数字识别的网络了 数据的处理 我们使用pytorch自带的包进行数据的预处理 import torch import torchvision import torchvision.transforms as transforms import numpy as np import matplotlib.pyplot as plt

  • TensorFlow教程Softmax逻辑回归识别手写数字MNIST数据集

    基于MNIST数据集的逻辑回归模型做十分类任务 没有隐含层的Softmax Regression只能直接从图像的像素点推断是哪个数字,而没有特征抽象的过程.多层神经网络依靠隐含层,则可以组合出高阶特征,比如横线.竖线.圆圈等,之后可以将这些高阶特征或者说组件再组合成数字,就能实现精准的匹配和分类. import tensorflow as tf import numpy as np import input_data print('Download and Extract MNIST datas

  • Python-OpenCV实战:利用 KNN 算法识别手写数字

    目录 前言 手写数字数据集 MNIST 介绍 基准模型--利用 KNN 算法识别手写数字 改进模型1--参数 K 对识别手写数字精确度的影响 改进模型2--训练数据量对识别手写数字精确度的影响 改进模型3--预处理对识别手写数字精确度的影响 改进模型4--使用高级描述符作为图像特征提高 KNN 算法准确率 完整代码 相关链接 前言 K-最近邻 (k-nearest neighbours, KNN) 是监督学习中最简单的算法之一,KNN 可用于分类和回归问题,在博文<Python OpenCV实战

  • Python实现识别手写数字 Python图片读入与处理

    写在前面 在上一篇文章Python徒手实现手写数字识别-大纲中,我们已经讲过了我们想要写的全部思路,所以我们不再说全部的思路. 我这一次将图片的读入与处理的代码写了一下,和大纲写的过程一样,这一段代码分为以下几个部分: 读入图片: 将图片读取为灰度值矩阵: 图片背景去噪: 切割图片,得到手写数字的最小矩阵: 拉伸/压缩图片,得到标准大小为100x100大小矩阵: 将图片拉为1x10000大小向量,存入训练矩阵中. 所以下面将会对这几个函数进行详解. 代码分析 基础内容 首先我们现在最前面定义基础

  • python实现识别手写数字 python图像识别算法

    写在前面 这一段的内容可以说是最难的一部分之一了,因为是识别图像,所以涉及到的算法会相比之前的来说比较困难,所以我尽量会讲得清楚一点. 而且因为在编写的过程中,把前面的一些逻辑也修改了一些,将其变得更完善了,所以一切以本篇的为准.当然,如果想要直接看代码,代码全部放在我的GitHub中,所以这篇文章主要负责讲解,如需代码请自行前往GitHub. 本次大纲 上一次写到了数据库的建立,我们能够实时的将更新的训练图片存入CSV文件中.所以这次继续往下走,该轮到识别图片的内容了. 首先我们需要从文件夹中

  • Python实现识别手写数字 简易图片存储管理系统

    写在前面 上一篇文章Python实现识别手写数字-图像的处理中我们讲了图片的处理,将图片经过剪裁,拉伸等操作以后将每一个图片变成了1x10000大小的向量.但是如果只是这样的话,我们每一次运行的时候都需要将他们计算一遍,当图片特别多的时候会消耗大量的时间. 所以我们需要将这些向量存入一个文件当中,每次先看看图库中有没有新增的图片,如果有新增的图片,那么就将新增的图片变成1x10000向量再存入文件之中,然后从文件中读取全部图片向量即可.当图库中没有新增图片的时候,那么就直接调用文件中的图片向量进

  • python使用KNN算法识别手写数字

    本文实例为大家分享了python使用KNN算法识别手写数字的具体代码,供大家参考,具体内容如下 # -*- coding: utf-8 -*- #pip install numpy import os import os.path from numpy import * import operator import time from os import listdir """ 描述: KNN算法实现分类器 参数: inputPoint:测试集 dataSet:训练集 lab

  • Python神经网络TensorFlow基于CNN卷积识别手写数字

    目录 基础理论 一.训练CNN卷积神经网络 1.载入数据 2.改变数据维度 3.归一化 4.独热编码 5.搭建CNN卷积神经网络 5-1.第一层:第一个卷积层 5-2.第二层:第二个卷积层 5-3.扁平化 5-4.第三层:第一个全连接层 5-5.第四层:第二个全连接层(输出层) 6.编译 7.训练 8.保存模型 代码 二.识别自己的手写数字(图像) 1.载入数据 2.载入训练好的模型 3.载入自己写的数字图片并设置大小 4.转灰度图 5.转黑底白字.数据归一化 6.转四维数据 7.预测 8.显示

  • Python利用 SVM 算法实现识别手写数字

    目录 前言 使用 SVM 进行手写数字识别 参数 C 和 γ 对识别手写数字精确度的影响 完整代码 前言 支持向量机 (Support Vector Machine, SVM) 是一种监督学习技术,它通过根据指定的类对训练数据进行最佳分离,从而在高维空间中构建一个或一组超平面.在博文<OpenCV-Python实战(13)--OpenCV与机器学习的碰撞>中,我们已经学习了如何在 OpenCV 中实现和训练 SVM 算法,同时通过简单的示例了解了如何使用 SVM 算法.在本文中,我们将学习如何

随机推荐