opencv数字识别

这是基于opencv写的数字识别程序 对于记事本里的宋体常规数字能够完美的识别

思路:   放大图片→灰度化→二值化→开运算→寻找外轮廓→轮廓排序→遍历像素与模板匹配→得到数字

最后为了验证识别率,读入txt文件与识别图片后的输出结果对比

总结:

1.图片放大可以把各个数字分开一点,防止数字黏在一起导致找轮廓时出错

2.开运算,进一步把各个数字分开  开运算(膨胀->腐蚀)//去除图像中较小区域

3.寻找外轮廓,必须是外轮廓,不可以找所有轮廓,这样就可以等到各个数字

4.根据轮廓找出各个轮廓的最小矩形

5.根据最小矩形左上角的坐标为各个矩形排序,这样就可以知道各个字符输出顺序了

6,.把事先分割好的模板图片进行匹配(把这个程序改改就可以成一个分割数字的程序了)

7.最后读入txt文件与图片输出结果对比

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

#include <fstream>
#include <cassert>
#include <string>
using namespace std;
using namespace cv;

const int NUM = 10000;
#define WINDOW_NAME "二值图"
int g_nThreshold = 200;
static void on_Threshold(int, void*);
void getNumber();//分割字符
char getNumChar(Mat&);//识别单个字符图片

Mat g_srcImage, gray, image_threshold;
Mat open;

char result_num[NUM];//txt文件中的数字
char result_true[NUM];//识别后的数字

/*读取txt文件*/
void readTxt(string file)
{
	ifstream infile;
	infile.open(file.data());   //将文件流对象与文件连接起来 
	assert(infile.is_open());   //若失败,则输出错误消息,并终止程序运行 
	int i = 0;
	char c;
	while (!infile.eof())
	{
		infile >> c;
		result_true[i] = c;
		result_true[i + 1] = '\0';
		i++;
	}
	infile.close();
}
int main()
{
	readTxt("123.txt");
	cout << "文件值为:";
	for (unsigned int i = 0; i < strlen(result_true)-1; i++)//读取txt输出数字
	{
		cout << result_true[i];
	}
	cout << endl;
	g_srcImage = imread("1.png");
	//放大原图,图片太小数字可能会连在一起
	resize(g_srcImage, g_srcImage, Size(g_srcImage.cols * 2, g_srcImage.rows * 2), 0, 0, INTER_LINEAR);
	cvtColor(g_srcImage, gray, COLOR_BGR2GRAY);
	namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
	createTrackbar("阈值", WINDOW_NAME, &g_nThreshold, 255, on_Threshold);
	on_Threshold(g_nThreshold, 0);
	waitKey(0);
	return 0;
}

/*阈值操作*/
static void on_Threshold(int, void*)
{
	threshold(gray, image_threshold, g_nThreshold, 255, 1);
	imshow(WINDOW_NAME, image_threshold);
	Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
	morphologyEx(image_threshold, open, MORPH_OPEN, element);//开运算,防止图片连在一起
	getNumber();
}

/*单字符识别*/
char getNumChar(Mat &temp)
{
	int min = 10000;
	int min_i = 0;
	int total = 0;
	for (int n = 0; n < 10; n++)//n对应图片n.jpg
	{
		total = 0;
		char a[100];
		sprintf(a, "number/%d.jpg", n);//读取number模板文件夹图片,名字对应数字
		string name = a;
		Mat match = imread(name); 
		cvtColor(match, match, COLOR_BGR2GRAY);
		threshold(match, match, 254, 255, 1);
		resize(temp, temp, match.size(), 0, 0, INTER_LINEAR);//转换图片大小和模板图片一样
		for (int i = 0; i < match.rows; i++)//遍历所有像素与n.jpg对比
		{
			uchar *data_match = match.ptr<uchar>(i);
			uchar *data_temp = temp.ptr<uchar>(i);
			for (int j = 0; j < match.cols; j++)
			{
				if (*data_match == *data_temp)
				{
					total++;
				}
				*data_match++;
				*data_temp++;
			}
		}
		//读入的图片二值化后与原图二值化相同像素为0
		//读入的图片二值化与程序运行过程中二值化的产生图片刚好相反
		//所以相同的像素越少图片相似度越高
		//在这里match二值化后与temp二值化图相反即黑白正好颠倒了
		if (total < min)
		{
			min = total;
			min_i = n;	
		}
	}
	char a = min_i+'0';
	return a;
}

/*字符分割*/
void getNumber()
{
	Mat image_threshold_clone = image_threshold.clone();
	vector<vector<Point>>contours;
	vector<Vec4i>hierarchy;
	findContours(image_threshold_clone, contours, hierarchy,
		CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);//找出图片外轮廓,不要连内轮廓也找出来
	int contour_size = contours.size();
	int average_height = 0;
	int count = 0;
	Rect rc[NUM];
	int min_y =100000;
	int max_y = 0;
	//找出字符平均高度,最上方的数字的y,最下方的数字的y
	for (int i = 0; i < contour_size; i++)
	{
		rc[i] = boundingRect(contours.at(i));
		
		if (rc[i].y<min_y)
		{
			min_y = rc[i].y;
		}
		if (rc[i].y>max_y)
		{
			max_y = rc[i].y;
		}
		average_height += rc[i].height;
		
	}
	average_height /= contour_size;
	Rect sort_rc[NUM];
	Rect rc_y[NUM];
	int _t = 0;
	//由于轮廓顺序不是字符顺序所以要排序,主要根据矩形左上方坐标排序
	//排序从上到下,从左到右,每隔一个字符高度获取一次,当超出图片边界时停止
	for (int n = 0;; n++)
	{
		int t = 0;
		for (int i = 0; i < contour_size; i++)
		{
			if (min_y - average_height*0.5 < rc[i].y&& rc[i].y < min_y + average_height*0.5)
			{
				rc_y[t] = rc[i];
				t++;
			}
		}
		if (min_y>max_y)
		{
			break;
		}
		if (t == 0)
		{
			_t += t;
			min_y += average_height;
			continue;
		}
		for (int i = 0; i < t; i++)
		{
			for (int j = i+1; j < t; j++)
			{
				if (rc_y[i].x>rc_y[j].x)
				{
					Rect temp = rc_y[i];
					rc_y[i] = rc_y[j];
					rc_y[j] = temp;
				}
			}
		}
		for (int i = 0; i < t; i++)
		{
			sort_rc[i + _t] = rc_y[i];
		}
		_t += t;
		min_y += average_height;
	}
	//把排序后的图片一一识别
	for (int i = 0; i < contour_size; i++)
	{
		Mat ROI = image_threshold(sort_rc[i]);
		resize(ROI, ROI, Size(40, 56), 0, 0, INTER_LINEAR);
		result_num[i] = getNumChar(ROI);
		result_num[i + 1] = '\0';
	}
	//与txt文件的数字进行比较
	int equal_num = 0;
	cout << "结果值为:";
	for (unsigned int i = 0; i < strlen(result_num); i++)
	{
		cout << result_num[i];
		if (result_num[i] == result_true[i])
			equal_num++;
	}
	cout << "\n字符数为:" << contour_size << endl;
	cout << "正确率为:" << (double)equal_num / contour_size *100<<"%"<< endl;
}




以下为输出结果:

 

当然识别数字后你想干嘛 就是你自己的事了

 

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序资源库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值