#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);
}