源代码
这个源代码仅供参考,给大家一个流程,不能运行就不能运行,理解思路就好,自己改进,我要是传一份正确率90多的,大家还比什么呢?
不要因为不能运行再找我了,也不要跟我要源代码了,问题可以告诉你,要源码没有
#include<opencv.hpp> #include<opencv2/opencv.hpp> #include<iostream> #include<vector> #include<string> using namespace cv; using namespace std; void splitSample(char ch,int k,Mat inputImg) { string st = to_string(k) + ".jpg"; imwrite("D:\\二级项目图片\\"+to_string(ch) +"."+ st, inputImg); } //计算修正角度 double GetTurnTheta(Mat inputImg) { //计算垂直方向导数 Mat yImg; Sobel(inputImg, yImg, -1, 0, 1, 5); //直线检测 vector<Vec2f>lines; HoughLines(yImg, lines, 1, CV_PI / 180, 180); //计算旋转角度 float thetas = 0; for (int i = 0; i < lines.size(); i++) { float theta = lines[i][1]; thetas += theta; } if (lines.size() == 0) {//未检测到直线 thetas = CV_PI / 2; } else {//检测到直线,取平均值 thetas /= lines.size(); } return thetas; } /*-------------------------------------------------------------------------------------------------------------------------- Function: originalImgToGrayImg Description:将原图转化成灰度图 --------------------------------------------------------------------------------------------------------------------------- Calls:NONE Called By: main Table Accessed: NONE Table Updated: NONE --------------------------------------------------------------------------------------------------------------------------- Input: 第一个参数:原图,Mat 第二个参数:转出的灰度图,Mat Output : 灰度图 Return : None Others: NONE --------------------------------------------------------------------------------------------------------------------------*/ void originalImgToGrayImg(Mat inputImg, Mat& outputImg) { int row = inputImg.rows; int col = inputImg.cols; outputImg.create(row, col, CV_8UC1); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { double sum = 0; //得到三个通道的像素值 int b = inputImg.at<Vec3b>(i, j)[0]; int g = inputImg.at<Vec3b>(i, j)[1]; int r = inputImg.at<Vec3b>(i, j)[2]; sum = b * 0.114 + g * 0.587 + r * 0.299; outputImg.at<uchar>(i, j) = static_cast<uchar>(sum); } } } //冒泡排序对非边界值与其八邻域的进行排序,找到中值 int BubbletoMedian(vector<int>& a) { int median; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9 - i - 1; j++) { if (a[j] > a[j + 1]) { int temp; temp = a[j + 1]; a[j + 1] = a[j]; a[j] = temp; } } } median = a[4]; return median; } //去噪处理 void denoising(Mat gray1, Mat& grayImg) { grayImg = Mat(gray1.rows, gray1.cols, CV_8UC1); vector<int>temp(9); //定义九个方向 int dx[9] = { 1,-1,1,-1,-1,0,1,0,0 }; int dy[9] = { 1,-1,-1,1,0,1,0,-1,0 }; for (int i = 0;i < gray1.rows;i++) { for (int j = 0;j < gray1.cols;j++) { //边缘部分不做处理 if (i == 0 || i == gray1.rows - 1 || j == 0 || j == gray1.cols - 1) { grayImg.at<uchar>(i, j) = gray1.at<uchar>(i, j); } else { for (int k = 0;k < 9;k++) { temp[k] = gray1.at<uchar>(i + dx[k], j + dy[k]); } grayImg.at<uchar>(i, j) = BubbletoMedian(temp); } } } } //迭代法求阈值 void getThreshold(Mat grayImg, int &theThreshold) { //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 = 127,t=0; //t0是初始的阈值,t是每一次经过迭代运算后的阈值 当t=t0时认为找到 int z0, z1; //z0,在z1分别是大于t0和小于t0的像素值的总和 while (1) { count0=count1=z0 = z1= 0; 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; } theThreshold = t0-10; } // 根据灰度直方图得到二值图 Mat toBinaryGraph( Mat grayImg) { int theThreshold; //求阈值 getThreshold(grayImg, theThreshold); Mat binG=Mat(grayImg.rows,grayImg.cols, CV_8UC1); for (int i = 0;i < grayImg.rows;i++) { for (int j = 0;j < grayImg.cols;j++) { if (grayImg.at<uchar>(i, j) < theThreshold)binG.at<uchar>(i, j) = 255; else binG.at<uchar>(i, j) = 0; } } return binG; } //水平投影找到行 pair<int, int> SelectRow2(Mat inputImg) { pair<int, int>p; //记录ISBN所在的上界(p.first)和下届(p.second) vector<int>arr(inputImg.rows); //存储每行的水平投影的结果 for (int i = 0;i < inputImg.rows;i++) { //水平投影 for (int j = 0;j < inputImg.cols;j++) if (inputImg.at<uchar>(i, j) != 0)arr[i]++; //当此行的水平投影值大于指定阈值表示找到上届 if (arr[i] > 10) { p.first = i;break; } } for (int i = p.first;i < inputImg.rows;i++) { //水平投影 for (int j = 0;j < inputImg.cols;j++) if (inputImg.at<uchar>(i, j) != 0)arr[i]++; //当此行的水平投影值小于指定阈值表示找到下届 if (arr[i] < 10) { p.second = i;break; } } //有些图片不规则,图中没有全零行单独处理 if (p.second-p.first<=10) p.second = p.first+32; return p; } pair<int, int> SelectRow(Mat inputImg) { pair<int, int>a;vector<int>arr(inputImg.rows); bool find = false; for (int i = 0; i < inputImg.rows; i++) { for (int j = 0; j < inputImg.cols; j++) { //0表示黑 255表示白 if (inputImg.at<uchar>(i, j) == 255) { a.first = i; find = true; break; } if (find)break; } } for (int i = a.first + 1; i < inputImg.rows; i++) { for (int j = 0; j < inputImg.cols; j++) { //0表示黑 255表示白 if (inputImg.at<uchar>(i, j) == 255) arr[i]++;//这里是在水平投影 } if (arr[i] == 0) { a.second = i;break; } } //有些图片不规则,图中没有全零行模糊处理 if (a.second - a.first <= 10) a.second = a.first + 32; return a; } //竖直投影分割列 void sliptCol(Mat inputImg, vector<pair<int, int> >& a) { vector<int>theCol(inputImg.cols); //做竖直投影 for (int i=0;i<inputImg.rows;i++) { for (int j=0;j<inputImg.cols;j++) { if (inputImg.at<uchar>(i, j) != 0) theCol[j]++; } } //用nums区分走右边界,num为偶数则代表左边界 num 为奇数是右边界 int num = 0;pair<int, int>p; for (int j = 0;j < inputImg.cols;j++) { //用theCol[j] >= 3判断 适当把截取范围取大一点 if (theCol[j] >= 3 && num % 2 == 0) { num++; p.first = j;j += 2; } else if (theCol[j] == 0 && num % 2 != 0) { num++; p.second = j; a.push_back(p);j += 2; } } } //找最小矩形框 void findMinRectangle(Mat inputImg, int& st, int& ed, int& le, int& ri) { vector<int>hProjectoin(inputImg.rows); vector<int>vProjectoin(inputImg.cols); for (int i=0;i<inputImg.rows;i++) { for (int j=0;j<inputImg.cols;j++) { if (inputImg.at<uchar>(i, j) != 0) { hProjectoin[i]++;vProjectoin[j]++; } } } st = le = 0;ed = inputImg.rows;ri = inputImg.cols; for (int i = 0;i < inputImg.rows;i++) if (hProjectoin[i] != 0) ed = i; for (int i = 0;i < inputImg.cols;i++) if (vProjectoin[i] != 0)ri = i; for (int i = inputImg.rows - 1;i >= 0;i--) if (hProjectoin[i] != 0) st = i; for (int i = inputImg.cols - 1;i >= 0;i--) if (vProjectoin[i] != 0)le = i; } //两张图做差(统计两张图片不同点像素点的个数) double absDi(Mat inputImg, Mat sampleImg) { //记录两张图不同的像素点的个数 double diffNums = 0;double sameNums = 0; for (int i=0;i<inputImg.rows;i++) { for (int j=0;j<inputImg.cols;j++) { //对应位置的像素值不同 则diffnums++ if (inputImg.at<uchar>(i, j) != sampleImg.at<uchar>(i, j)) diffNums++; else sameNums++; } } return diffNums; } //字符识别 char recognition(Mat inputImg,int k) { string sampleImgPath = "样例3"; vector<String> sampleImgFN; glob(sampleImgPath, sampleImgFN, false); int sampleImgNums = sampleImgFN.size(); vector<pair< double,int> >nums(sampleImgNums+1); for (int i = 0; i < sampleImgNums; i++) { nums[i].second= i; Mat numImg = imread(sampleImgFN[i], 0); //大小同一 resize(numImg, numImg, Size(40, 60)); resize(inputImg,inputImg,Size(40,60)); nums[i].first = absDi(inputImg, numImg); } imshow("图片", inputImg); waitKey(); //排序 越小说明匹配度越高 sort(&nums[0], &nums[sampleImgNums]); int index= nums[0].second; //截取模板的函数 //splitSample(sampleImgFN[index][sampleImgPath.size() + 1],k,inputImg); return sampleImgFN[index][sampleImgPath.size() + 1]; /* if (index >= 0 && index <= 9) return index+'0'; else if (index == 10) return 'B'; else if (index == 11) return 'I'; else if (index == 12) return 'N'; else if (index == 13) return 'S'; else if (index == 14) return 'X'; else return ' ';*/ } int main() { int rtNums = 0, accNums = 0, sunNums = 0;//分别代表:正确的数量,被准确识别的字符的数量,要识别的字符的总和 //读取 ISBN 图片 string testImgPath = "数据集B/*"; vector<String> testImgFN;//必须cv的String glob(testImgPath, testImgFN, false); int testImgNums = testImgFN.size(); for (int index =12; index < testImgNums; index++) { Mat src = imread(testImgFN[index]); //图片大小统一 double width = 700; double height = width * src.rows / src.cols; resize(src, src, Size(width, height)); imshow("原图",src); Mat gray1; //原图转灰度图 originalImgToGrayImg(src, gray1); imshow("灰度图", gray1); //去噪处理 denoising(gray1,gray1); imshow("去噪后的图片", gray1); //二值化 Mat binImg = toBinaryGraph(gray1); imshow("二值图", binImg); //计算调整角度 double thetas = GetTurnTheta(binImg); thetas = 180 * thetas / CV_PI - 90; //旋转二值图像 Mat bin; Mat M = getRotationMatrix2D(Point(width / 2, height / 2), thetas, 1); warpAffine(binImg, bin, M, binImg.size()); imshow("旋转后的二值图", bin); //waitKey(); //旋转原图 Mat bin2; warpAffine(src, bin2, M, src.size()); imshow("旋转的原图", bin2); //从原图上截取ISBN所在行 Mat temp = Mat(bin2, Range(SelectRow2(bin).first, SelectRow2(bin).second+1), Range(0, bin.cols)); imshow("原图上的所在行",temp); //大小统一一 resize(temp,temp, Size(60 * (temp.cols / temp.rows), 60)); //转灰度图 Mat gray2; originalImgToGrayImg(temp, gray2); //去噪处理 denoising(gray2,gray2); //二值化 Mat binImg2 = toBinaryGraph(gray2); imshow("原图所在行二值化 ", binImg2); //waitKey(); //分割ISBN所在行 vector<pair<int, int> >a;//{(110,125),(135,150)。。。。} sliptCol(binImg2,a); int st, ed, le, ri; string result = ""; for (int i = 0;i < a.size();i++) { //做列分割 Mat subImg = Mat(binImg2, Range(0, binImg2.rows), Range(a[i].first, a[i].second+1)); findMinRectangle(subImg, st, ed, le, ri); //ed - st > subImg.rows / 2如果最小矩形的高小于原来的一半 判断为‘-’宽过小判断为杂纹 if (ed - st > subImg.rows / 2&&ri-le>3) { //切出最小矩形的 Mat minImg = Mat(subImg, Range(st, ed+1), Range(le, ri+1)); imshow("最小矩形", minImg); //模板匹配得到字符 char ch = recognition(minImg,index); if (ch >= '0' && ch <= '9' || ch == 'X') { result += ch; cout << ch << " "; } } } cout << endl; //确定正确的 ISBN 号,来跟识别出来的 ISBN 做对比 string cmpData = ""; for (int i = 0; i < testImgFN[index].length(); i++) { if (testImgFN[index][i] >= '0' && testImgFN[index][i] <= '9' || testImgFN[index][i] == 'X' ){//|| testImgFN[index][i] == 'I' || testImgFN[index][i] == 'S' || testImgFN[index][i] == 'B' || testImgFN[index][i] == 'N') { cmpData += testImgFN[index][i]; } } cout << cmpData << endl; sunNums += cmpData.length(); cout << result <<endl<<index << endl; int times = min(cmpData.length(), result.length()); bool flag = true; for (int i=0;i<times;i++) { if (cmpData[i] != result[i]) flag = false; else accNums++; } if (flag&&result.length()==cmpData.length()) { rtNums++; cout << "Yes" << endl; } else cout << "No" << endl; } printf("正确个数:%4.d 正确率:%f\n", rtNums, rtNums * 1.0 / testImgNums); printf("准确个数:%4.d 准确率:%f\n", accNums, accNums * 1.0 / sunNums); waitKey(0); }