代码参考

#include <iostream>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <map>

#include "opencv2/imgproc/imgproc.hpp"

using namespace std;
using namespace cv;
#include<vector>
#include<string>
#include <opencv2/features2d.hpp>
#include <opencv2/photo.hpp>
#include<set>

//高斯滤波





int getThreshold(Mat grayImg)
{
	//1.求灰度直方图
	vector<int>histo(256);
	for (int i = 0; i < grayImg.rows; i++)
	{
		for (int j = 0; j < grayImg.cols; j++)
		{
			histo[grayImg.at<uchar>(i, j)]++;
		}
	}
	//2.根据上面的直方图 用迭代法求阈值
	int count0, count1;//count0,count1分别是大于t0和小于t0的像素点的个数

	int t0 = 150, t = 0; //t0是初始的阈值,t是每一次经过迭代运算后的阈值 当t=t0时认为找到

	int z0, z1;      //z0,在z1分别是大于t0和小于t0的像素值的总和
	while (1)
	{
		count0 = count1 = z0 = z1 = 1;
		for (int i = 0; i < histo.size(); i++)
		{
			if (i <= t0)
			{
				count0 += histo[i];
				z0 += i * histo[i];
			}
			else
			{
				count1 += histo[i];
				z1 += i * histo[i];
			}
		}

		t = (z0 / count0 + z1 / count1) / 2;
		if (t0 == t)
			break;
		else t0 = t;
	}
	return  t0 - 11;
}

Mat Hough(Mat srcImage)
{
	Mat grad_y;
	//调整第六个参数  有不同的效果1,3,5,7 第3个参数也可以换具体参照254页
	Sobel(srcImage, grad_y, -1, 0, 1, 5);
	vector<Vec2f>lines;
	HoughLines(grad_y, lines, 1, CV_PI / 180, 210);
	float sum = 0;
	for (int i = 0; i < lines.size(); i++)
	{
		sum += lines[i][1];
	}
	float alpha = 0;
	if (lines.size() == 0)
	{
		alpha = CV_PI / 2;
	}


	else
	{
		alpha = sum / lines.size();
	}
	alpha = alpha * 180 / CV_PI - 90;
	Mat dstImage;
	double width = srcImage.cols, height = srcImage.rows;
	//Point2f center(srcImage.cols / 2, srcImage.rows / 2);
	Mat M = getRotationMatrix2D(Point(width / 2, height / 2), alpha, 1);
	warpAffine(srcImage, dstImage, M, srcImage.size());
	//imshow("旋转", dstImage);
	return dstImage;
}


//侵蚀操作
//Mat qinshi(Mat& img)
//{
//
//	//	namedWindow("原始图", WINDOW_NORMAL);
//	//	imshow("原始图", img); 
//	Mat out;
//	//获取自定义核
//	Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的
//	//腐蚀操作
//	dilate(img, out, element);
//	//同一图片大小
//	double width = 800;
//	double height = width * img.rows / img.cols;
//	resize(out, out, Size(width, height));
//
//	//imshow("腐蚀操作", out);
//	return out;
//}


Mat splitbian(Mat& src_img)
{
	Mat mattemp = src_img.clone();//深度拷贝
	int height = src_img.rows, width = src_img.cols;//赋值
	vector<int>hang(src_img.rows);
	vector<int>lie(src_img.cols);
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			if (src_img.at<uchar>(i, j) != 0)
			{
				hang[i]++;
				lie[j]++;
			}
		}
	}
	/*cout << "每列的和:" << endl;
	for (int i = 0; i < width; i++)
		cout << lie[i] << "  " << i << endl;

	cout << "每行的和:" << endl;
	for (int i = 0; i < height; i++)
		cout << hang[i] << "   ";*/
	int shangqie = 0;  //表示左上的y
	for (int i = 0; i < height; i++)
	{
		if (hang[i] > 400)
			shangqie = i;
		if (hang[i] < 400)
		{
			//shangqie = i;
			break;
		}
	}
	int xiaqie = (height / 20) * 19;//表示右下的y
	for (int i = height - 1; i > (height / 20) * 19; i--)
	{
		if (hang[i] > 400)
			xiaqie = i;
		if (hang[i] < 400)
		{
			//	xiaqie = i;
			break;
		}

	}
	int zuoqie = (width / 20);
	for (int i = 0; i < width; i++)
	{
		if (lie[i] > 400)
			zuoqie = i;
		if (lie[i] < 400)
		{
			//zuoqie = i;
			break;
		}

	}
	int youqie = (width / 20) * 19;
	for (int i = width - 1; i > (width / 20) * 19; i--)
	{
		if (lie[i] > 300)
			youqie = i;
		if (lie[i] < 200)
			break;
	}
	if (zuoqie < (width / 10))
	{
		zuoqie = zuoqie;
	}
	else
		zuoqie = (width / 10);
	const Rect img1 = Rect(zuoqie, 0, youqie - zuoqie - 5, height);
	Mat temp1;
	temp1 = src_img(img1);
	//	imshow("裁剪图", temp1);

	waitKey();
	return temp1;
}

int th(Mat src_img)
{
	Mat mattemp = src_img.clone();//深度拷贝
	int height = src_img.rows, width = src_img.cols;//赋值
	int sum = 0;
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			if (src_img.at<uchar>(i, j) != 0)
			{
				sum++;
			}
		}
	}

	return sum;
}


//水平投影
Mat splitrow(Mat mat)
{

	Mat mattemp = mat.clone();//深度拷贝
	//	imshow("原图", mattemp);
		//waitKey();
	int height = mattemp.rows, width = mattemp.cols;
	int temp = 0;//存储255灰度级的个数?
	int* heightnum = new int[height];//行号//保存每一行255数目的数组
	int y1 = height;
	int y2 = 0;
	//开始遍历二维数组
	for (int i = 0; i < height; i++)
	{
		temp = 0;
		for (int j = 0; j < width; j++)
		{
			if (mattemp.at<uchar>(i, j) != 0)
			{
				temp++;//统计白色

			}
		}
		//一行完了
		heightnum[i] = temp;
		//cout << heightnum[i] << "   " << i << endl;

	}




	for (int i = 0; i < height - 1; i++)
	{


		if (heightnum[i] < 10 && heightnum[i + 10]>10)
		{
			y1 = i;

			cout << "y1是:" << y1 << endl;
			break;
		}

	}
	for (int i = y1 + 10; i < height - 1; i++)
	{


		if (heightnum[i] < 10)
		{
			y2 = i;
			cout << "y2是:" << y2 << endl;
			break;

		}
	}
	cout << "执行完毕" << endl;
	Mat temp1;
	if (y1 > height / 2 || y2 > height / 3 * 2)
	{
		temp1 = splitbian(mat);
		temp1 = splitrow(temp1);

	}
	else
	{
		if (y2 - y1 <= 20)
			y2 = y1 + 60;


		//开始切割图片

		//设横轴是x轴,竖轴是y轴
		//这个先输入x ,后输入y
		Rect img1 = Rect(0, y1, width, y2 - y1 + 5);
		temp1 = mat(img1);
		//imshow("裁剪图", temp1);
		//waitKey();
	}
	delete[] heightnum;
	return temp1;


}






//垂直投影截取列
//竖直投影分割列
void splitCol(Mat& mat, int* position1, int* position2, int* stringwidth, int& idx)
{
	cout << "开始垂直投影" << endl;

	Mat mattemp = mat.clone();//深度拷贝
	//imshow("原图", mattemp);
	int height = mattemp.rows, width = mattemp.cols;//赋值
	int* proj = new int[width];



	for (int col = 0; col < width; col++)
	{
		int temp = 0;
		for (int row = 0; row < height; row++)
		{
			if (mattemp.at<uchar>(row, col) != 0)
			{
				temp++;
			}
		}
		proj[col] = temp;  //统计白的个数

	}
	//    //黑底白字 遇到黑是字
		  //for (int i = 0; i < height; i++)
		  //	cout << proj[i] << endl;
		  //确认字符位置

	int count = 1;
	int pix;//字符的分割位置
	//int position1[100000];//第一次遇到白色的位置
	//int position2[100000];//第二次遇到白色的位置
	for (int i = 0; i < width; i++)
	{
		pix = proj[i];
		if (pix >= 2 && (count % 2 != 0))  //遇到黑色了要开始剪了
		{
			if (i - 1 > 0)
				position1[idx] = i - 1;
			else
				position1[idx] = i;
			count++;
			continue;
		}
		else if (pix < 2 && (count % 2 == 0))//遇到 白色剪的停止位置
		{
			if (i + 1 < width)
				position2[idx] = i + 1;
			else
				position2[idx] = i;
			count++;
			idx++;
		}

		//if (i > (width - 2) && (count % 2 != 0))  //前边有一半了,但是这后一半没找到
		//{
		//	
		//	position2[idx] = i;
		//	count++;
		//	idx++;
		//}

	}


	//记录所有字符的宽度
	for (int i = 0; i < idx; i++)
	{
		stringwidth[i] = position2[i] - position1[i];
		//	cout << "宽为:"<< stringwidth[i] << endl;
	}




	//Mat number_img = Mat(Scalar(0));
	//for (int i = 0; i < idx; i++) 
	//{
	//	Rect choose_rect(position1[i], 0, swidth[i], height);
	//	number_img = mat(choose_rect);
	//	imshow("number" + to_string(i), number_img);
	//
	//	// imwrite("number" + to_string(i) + ".jpg", number_img);
	//}

}



//找最小矩阵
void findminjuzhen(Mat src, pair<int, int>& num1, pair<int, int>& num2)
{


	int height = src.rows, width = src.cols;//赋值
	vector<int>hang(src.rows);
	vector<int>lie(src.cols);
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			if (src.at<uchar>(i, j) != 0)
			{
				hang[i]++;
				lie[j]++;
			}
		}
	}
	for (int i = 0; i < height; i++)
		if (hang[i] != 0)
			num1.second = i;    //最下界
	for (int i = 0; i < width; i++)
		if (lie[i] != 0)
			num2.second = i;
	for (int i = height - 1; i >= 0; i--)
		if (hang[i] != 0)
			num1.first = i; //最上界
	for (int i = width - 1; i >= 0; i--)
		if (lie[i] != 0)
			num2.first = i;




	cout << "(" << num1.first << " " << num1.second << ")";  //上下边界
	cout << " ";
	cout << "(" << num2.first << " " << num2.second << ")";//左右边界

}

//字符匹配
//首先是两张图作差
double imgcha(Mat inimg1, Mat mubanimg)
{
	//记录两张图不同的像素点的个数
	double difnum = 0;
	//cvtColor(mubanimg, mubanimg, COLOR_BGR2GRAY);
	//threshold(mubanimg, mubanimg, getThreshold( mubanimg), 255, THRESH_OTSU);  //模板二值化
	//同样大小的处理
	resize(inimg1, inimg1, Size(30, 55));
	resize(mubanimg, mubanimg, Size(30, 55));

	Mat temp0;
	absdiff(inimg1, mubanimg, temp0);
	for (int i = 0; i < temp0.rows; i++)
	{
		for (int j = 0; j < temp0.cols; j++)
		{
			difnum += temp0.at<uchar>(i, j);
		}
	}

	return difnum;  //返回不一样的值
}
typedef pair<double, int>PDL;
int pathnum = 0;


void splitSample(char ch, int k, Mat inputImg)
{
	string st = to_string(k) + ".jpg";
	//cout << st << endl;
	imwrite("F:\\erjixiangmu\\erjixiangmu\\muban2\\" + to_string(ch - 48) + "." + st, inputImg);

}
char recognition(Mat inputImg, int k)
{
	string sampleimgpathall = "muban";
	vector<String> sampleimgpath;
	glob(sampleimgpathall, sampleimgpath, false);//读取模板所有的路径,存到FN里面
	int sampleimgnum = sampleimgpath.size();
	vector<pair< double, int> >nums(sampleimgnum + 1);//i表示图片在路径vector里的下标 并存储该图片和模板里的图片的差
	for (int i = 0; i < sampleimgnum; i++) {

		Mat numImg = imread(sampleimgpath[i], 0);
		//大小同一
		resize(numImg, numImg, Size(40, 60));
		resize(inputImg, inputImg, Size(40, 60));
		nums[i].first = imgcha(inputImg, numImg);//不同像素值的差
		nums[i].second = i;//在迭代器中的下标
	}
	// imshow("图片", inputImg);
	waitKey();
	//排序 越小说明匹配度越高
	sort(&nums[0], &nums[sampleimgnum]);
	int index = nums[0].second; //读取模板里的该图片
	//截取模板的函数
	splitSample(sampleimgpath[index][sampleimgpathall.size() + 1], k, inputImg);
	return sampleimgpath[index][sampleimgpathall.size() + 1];

}


//图像锐化
Mat ToSharoen(Mat inputImage)
{
	Mat outputImage(inputImage.size(), inputImage.type());
	int cnel = inputImage.channels();
	int rows = inputImage.rows;
	int cols = (inputImage.cols - 1) * cnel;

	for (int row = 1; row < rows - 1; row++)
	{
		const uchar* previous = inputImage.ptr<uchar>(row - 1);
		const uchar* current = inputImage.ptr<uchar>(row);
		const uchar* next = inputImage.ptr<uchar>(row + 1);
		uchar* output = outputImage.ptr(row);
		for (int col = cnel; col < cols; col++)
		{
			output[col] = saturate_cast<uchar>(5 * current[col] - (previous[col] + current[col + 1] + current[col - 1] + next[col]));
		}
	}
	//namedWindow("锐化图", WINDOW_AUTOSIZE);
	//imshow("锐化图", outputImage);
	return outputImage;
}

// 图像锐化





int main()
{
	int imgsum = 0, rightimg = 0;
	int stringnum = 0, rightstringnum = 0;
	int errornum = 0;
	//读取ISBN图片
	string testimgpathall = "ISBN";
	vector<string>testimgpath;//存储单个图片的路径

	glob(testimgpathall, testimgpath, false);//读取函数
	int testimgsums = testimgpath.size();
	vector<string>errorpath(testimgsums);
	vector<int>yuzhi(testimgsums);
	//开始对每一个ISBN进行操作
	for (int testindex = 0; testindex < testimgsums; testindex++)
	{
		cout << "开始处理第" << testindex << "张照片" << endl;
		Mat src = imread(testimgpath[testindex]);

		//同一图片大小
		double width = 600;
		double height = width * src.rows / src.cols;
		resize(src, src, Size(width, height));
		//imshow("原图", src);

		Mat gary_src;
		cvtColor(src, gary_src, COLOR_BGR2GRAY);
		//imshow("灰度图", gary_src);

		Mat gary_sharoen = ToSharoen(gary_src);
		//imshow("锐化后的灰度图", gary_sharoen);
		// 
			//高斯去噪处理
		Mat gas;
		GaussianBlur(gary_sharoen, gas, Size(7, 7), 1, 1);
		//	imshow("高斯去噪后的灰度图", gas);
		waitKey();




		//二值化
		Mat binImg;
		int thethreshold = getThreshold(gas);
		cout << "阈值为:" << thethreshold << endl;
		//	THRESH_BINARY (白底黑字)、THRESH_BINARY_INV(黑底白字)
			//THRESH_TRUNC(很暗很暗)THRESH_OTSU
		if (thethreshold < 123)
			thethreshold = thethreshold;
		else
			thethreshold += 31;

		threshold(gas, binImg, thethreshold, 255, THRESH_BINARY_INV);

		//判断是白底还是黑底图
		int thethreshold2 = th(binImg);
		cout << "白点数:" << thethreshold2;
		if (thethreshold2 > 190000)
		{
			cout << "白底图";
			threshold(gas, binImg, thethreshold, 255, THRESH_BINARY);
		}
		else
			threshold(gas, binImg, thethreshold, 255, THRESH_BINARY_INV);


		//	imshow("二值化图", binImg);
		waitKey();
		//旋转
		Mat temp1 = Hough(binImg);



		//	imshow("二值化图片进行霍夫旋转", temp1);
		waitKey();




		//水平裁剪

		Mat shui_img;
		shui_img = splitrow(temp1);
		//	imshow("水平剪完的二值图", shui_img);
		waitKey();
		//侵蚀一下
			//shui_img = qinshi(shui_img);
			//imshow("水平寝室", shui_img);
		waitKey();


		//做列的分割
		int position1[100000];//第一次遇到白色的位置
		int position2[100000];//第二次遇到白色的位置
		int  swidth[1000];//记录字符宽
		int posidx = 0;

		//垂直裁剪
		splitCol(shui_img, position1, position2, swidth, posidx);
		//开始对每个数字进行处理

		string ans = "";
		Mat number_img = Mat(Scalar(0));
		for (int i = 4; i < posidx; i++)
		{
			//Mat subImg = Mat(shuiimg, Range(0, shuiimg.rows), Range(position1[i], position2[i]));
			//做列分割
			Rect choose_rect(position1[i], 0, swidth[i], shui_img.rows);
			number_img = shui_img(choose_rect);
			//	imshow("垂直投影的分割图" + to_string(i), number_img);
			waitKey();
			//找该数字的最小矩阵
			pair<int, int> num1;
			pair<int, int> num2;
			findminjuzhen(number_img, num1, num2);
			//imshow("最小矩阵", number_img);
			//waitKey();
			int numimgheight = num1.second - num1.first;
			int numimgwidth = num2.second - num2.first;
			if (numimgheight > numimgwidth / 2 && numimgwidth > 3 && numimgheight > 5)
			{
				Mat minImg = Mat(number_img, Range(num1.first, num1.second + 1), Range(num2.first, num2.second + 1));
				// imshow("最小矩形", minImg);
				//waitKey();
				//是数字或者字母
				char ch = recognition(minImg, testindex);
				//char ch = pipei(minImg);
				if (ch >= '0' && ch <= '9')
				{
					ans += ch;
					cout << ch << " ";
				}
			}



		}
		cout << endl;

		//现在开始对比
		cout << "现在的路径是:" << testimgpath[testindex] << endl;
		string rightans = "";
		for (int i = 0; i < testimgpath[testindex].length(); i++)
		{
			char temp = testimgpath[testindex][i];
			if (temp >= '0' && temp <= '9')
			{
				rightans += temp;
			}
		}

		cout << "正确的数据是:" << rightans << endl;
		cout << "读出来的答案是:" << ans << endl;
		stringnum += rightans.length();//总的字符数
		int ansidx = min(ans.length(), rightans.length());
		bool right = true;
		for (int i = 0; i < ansidx; i++)
		{
			if (ans[i] != rightans[i])
				right = false;
			else
				rightstringnum++;//答对的字符数				
		}
		if (ans.length() == rightans.length() && right)//识别正确
		{
			rightimg++;
			cout << "YES" << endl;
		}
		else
		{
			cout << "NO" << endl;
			cout << "-------------------------------------" << endl;

			errorpath[errornum] = testimgpath[testindex];
			errornum++;
		}

	}
	printf("正确个数:%4.d 正确率:%f\n", rightimg, rightimg * 1.0 / testimgsums);
	printf("准确个数:%4.d 准确率:%f\n", rightstringnum, rightstringnum * 1.0 / stringnum);



	for (int i = 0; i < errornum; i++)
		cout << "错误的路径是:" << errorpath[i] << endl;

	waitKey(0);

}