C++利用opencv进行OCR Win10-x64+VS2015

本文介绍了如何利用OpenCV库在C++环境下进行OCR(光学字符识别)的实现。首先,通过采集图像并进行二值化处理,然后寻找图像中的轮廓并提取ROI。接着,对提取的ROI进行缩放并构建训练集,最后使用KNN算法进行模型训练并保存。在预测阶段,加载模型并应用到新的图像上进行字符识别。
摘要由CSDN通过智能技术生成

C++利用opencv进行OCR Win10-x64+VS2015

原理

OCR的应用非常广泛,在工业上,常常用于印刷体的识别,例如印刷数字的识别。对于这种问题,我们的做法是先去采集工业场景下大批量的图像数据,分割为单个字体区域,绑定对应的标签,制作印刷字体的训练集。再使用机器学习或者神经网络对数据集进行训练,在得到一个准确率高的模型后,再使用模型去分类待测的字体区域图片。

步骤

制作数据集

简单演示,这里我直接用word文档输入数字截图作为我采集的图像

如下为train.png
在这里插入图片描述
test.png
在这里插入图片描述
第一步:二值化

cv::Mat gray = cv::imread("E:\\DataSet\\OCR\\train\\1.png", 0);
cv::Mat thresh;
cv::threshold(gray, thresh, 100, 255, cv::THRESH_BINARY_INV);

在这里插入图片描述
第二步:寻找轮廓

cv::Mat con;
thresh.copyTo(con);
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierachy;
cv::findContours(con, contours, hierachy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);

第三步:创建轮廓的最小外包直立矩形

第四步:从二值图中取每一个ROI,并进行Resize

第五步:将图像及其标签添加进数据及标签集中

// 第三步、第四步、第五步
cv::Mat sample, response;  // 初始化数据、标签
for (int i = 0; i < contours.size(); i = hierachy[i][0]) {
  cv::Rect r = cv::boundingRect(contours[i]);
  cv::rectangle(gray, cv::Point(r.x, r.y), cv::Point(r.x + r.width, r.y + r.height), cv::Scalar(128), 2, 8, 0);
  cv::Mat ROI = thresh(r);  // 取最小直立外包矩形的ROI
  cv::Mat tmp1, tmp2;
  cv::resize(ROI, tmp1, cv::Size(10, 10), 0, 0, cv::INTER_LINEAR);  // 缩放
  tmp1.convertTo(tmp2, CV_32FC1);
  // 将每个ROI reshape成size为100*1的Mat,即1行100列;并push进sample的底部
  sample.push_back(tmp2.reshape(1, 1));
  // 利用键盘输入标签,并push进response底部
  cv::imshow("ROI", ROI);
  int label = cv::waitKey(0);
  label -= 0x30;
  response.push_back(label);
}
response = response.reshape(1, 1);
response.convertTo(response, CV_32FC1);

在这里插入图片描述
注意:for循环结构中i并不是用++,而是用的hierachy[i][0],这是因为我们findContours取到的是所有轮廓,且从最外层开始寻找,即0号轮廓一定是外层轮廓。但是当一个轮廓同时存在内嵌轮廓和同级轮廓时,内嵌轮廓的优先级更高,序号更小。例如,假如在没有内嵌轮廓的情况下8轮廓序号为8,9轮廓序号为9,但由于8中有两个内嵌轮廓,这时这两个内嵌轮廓的序号就是9,10,而9轮廓序号就变成11了,这样寻找ROI就不可控了,而hierachy[i][0]就解决了这个困局,hierachy[i]对应的是contours[i],hierachy[i][0]~hierachy[i][3]中0代表平级轮廓的后一个,1代表平级轮廓的前一个,2代表子级,3代表父级,没有的话就返回-1,所以hierachy[i][0]可以遍历所有同级轮廓,并在遍历后跳出循环。其实opencv的findContours可以只检测最外层轮廓,只需要将mode参数从cv::RETR_CCOMP修改为cv::RETR_EXTERNAL即可。

第六步:保存训练集

cv::FileStorage Data("TrainingData.yml", cv::FileStorage::WRITE);
Data << "data" << sample;
Data.release();
cv::FileStorage Label("Label.yml", cv::FileStorage::WRITE);
Label << "label" << response;
Label.release();
std::cout << "Success to Create TrainSet...!" << std::endl;

在这里插入图片描述

训练模型

第一步:读取训练集

cv::Mat sample, response;
cv::FileStorage Data("TrainingData.yml", cv::FileStorage::READ);
Data["data"] >> sample;
Data.release();
cv::FileStorage Label("Label.yml", cv::FileStorage::READ);
Label["label"] >> response;
Label.release();
std::cout << "Success to Read TrainSet...!" << std::endl;

第二步:创建初始化模型,以KNN为例

cv::Ptr<cv::ml::KNearest> knn(cv::ml::KNearest::create());

第三步:训练

knn->train(sample, cv::ml::ROW_SAMPLE, response);
std::cout << "Train Finished...!" << std::endl;

第四步:保存模型

knn->save("knn.xml");

在这里插入图片描述

使用模型预测

第一步:加载模型

cv::Ptr<cv::ml::KNearest> knn = cv::ml::StatModel::load<cv::ml::KNearest>("knn.xml");

第二步:测试图像预处理,测试

参考制作训练集的步骤提取ROI,并测试

...
...
for (int i = 0; i < contours.size(); i = hierachy[i][0]) {
  ...
  float p = knn->findNearest(tmp2.reshape(1, 1), 1, response); // 测试
  cv::imshow("ROI", ROI);
  std::cout << int(p) << std::endl;
  cv::waitKey(0);
}
  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值