OpenCV C++案例实战三十二《字符识别》

  • 前言
  • 一、结果演示
  • 二、制作数据集
  • 三、字符识别
  • 四、源码
  • 总结

前言

本案例将使用OpenCV C++ 进行字符识别。主要包括制作数据集、以及模型预测两部分。先看看效果如何吧。

一、结果演示

二、制作数据集

首先第一步,我们需要制作数据集。这里我的方法是,读取一张字符图像,然后通过提取字符轮廓找到字符ROI图像,利用键盘输入给字符打上相应的标签,即完成数据集制作。由于我这里的数据字符图像只包含数字以及大写英文字符,故只识别数字字符以及大写英文字符。如图所示,这是我使用的字符图像,下面需要进行图像预处理提取到字符轮廓。

//进行图像预处理,提取字符轮廓
Mat grayImg;
cvtColor(Train_Chars, grayImg, COLOR_BGR2GRAY);Mat blurImg;
GaussianBlur(grayImg, blurImg, Size(3, 3), 0);Mat binImg;
threshold(blurImg, binImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);


接下来需要提取字符轮廓,使用findContours提取最外轮廓。然后提取字符ROI图像,并通过键盘输入对应的ASCII码给其打上标签(注:区分大小写)。

Rect rect = boundingRect(contours[cnt]);
rectangle(Train_Chars, rect, Scalar(0, 255, 0), 2);Mat ROI = binImg(rect);
imshow("ROI", ROI);
imshow("Training_Chars", Train_Chars);
int charVal = waitKey(0); //将字符通过键盘输入给予标签

进行KNN训练,具体看源码相应注释。

 //由于我这里的数据字符图像只包含数字以及大写英文字符,只识别数字字符以及大写英文字符vector<int>ValidChars ={'0', '1', '2', '3', '4', '5', '6', '7', '8', '9','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T','U', 'V', 'W', 'X', 'Y', 'Z' };//进行图像预处理,提取字符轮廓Mat grayImg;cvtColor(Train_Chars, grayImg, COLOR_BGR2GRAY);Mat blurImg;GaussianBlur(grayImg, blurImg, Size(3, 3), 0);Mat binImg;threshold(blurImg, binImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);imwrite("result.jpg", binImg);vector<vector<Point>>contours;findContours(binImg, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//准备训练所用数据集,将字符通过键盘输入给予标签Mat Train_Data, Train_Label;for (int cnt = 0; cnt < contours.size(); cnt++){if (contourArea(contours[cnt]) > 10){Rect rect = boundingRect(contours[cnt]);rectangle(Train_Chars, rect, Scalar(0, 255, 0), 2);Mat ROI = binImg(rect);imshow("ROI", ROI);imshow("Training_Chars", Train_Chars);int charVal = waitKey(0); //将字符通过键盘输入给予标签if (find(ValidChars.begin(), ValidChars.end(), charVal) != ValidChars.end()){//如果输入的字符在字符匹配表中,则进行存储//由于我们在识别字符时,会遇到各种尺寸的字符,故将所有的字符固定同一尺寸Mat resizeRoi;resize(ROI, resizeRoi, Size(ImgWidth, ImgHeight));//将图像转成浮点型,因为KNN训练数据集读取的是浮点型数据Mat RoiFloat;resizeRoi.convertTo(RoiFloat, CV_32FC1);Train_Data.push_back(RoiFloat.reshape(0,1));Train_Label.push_back(charVal);cout << charVal << endl;}}}//进行KNN训练Train_Data.convertTo(Train_Data, CV_32FC1);Train_Label.convertTo(Train_Label, CV_32FC1);const int k = 3;//k取值,基数Ptr<ml::KNearest>knn = ml::KNearest::create();//构造KNN模型knn->setDefaultK(k);//设定k值knn->setIsClassifier(true);//KNN算法可用于分类,回归knn->setAlgorithmType(ml::KNearest::BRUTE_FORCE);//字符匹配算法knn->train(Train_Data, ml::ROW_SAMPLE, Train_Label);//模型训练knn->save(filename);//模型保存cout << "Model training is complete!" << endl;

三、字符识别

当我们完成训练后,会保存相应的模型。这时我们仅需直接读取模型数据就可以了。

 Ptr<ml::KNearest>knn = cv::Algorithm::load<cv::ml::KNearest>(filename);//加载KNN模型//图像预处理Mat grayImg;cvtColor(Test_Chars, grayImg, COLOR_BGR2GRAY);Mat blurImg;GaussianBlur(grayImg, blurImg, Size(3, 3), 0);Mat binImg;threshold(blurImg, binImg, 200, 255, THRESH_BINARY_INV);vector<vector<Point>>contours;findContours(binImg, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//查找最外轮廓for (int cnt = 0; cnt < contours.size(); cnt++){if (contourArea(contours[cnt]) > 10){Rect rect = boundingRect(contours[cnt]);//轮廓外接矩形rectangle(Test_Chars, rect, Scalar(0, 0, 255), 2);Mat ROI = binImg(rect);//字符ROI图像Mat resizeRoi;//将字符resize成固定大小resize(ROI, resizeRoi, Size(ImgWidth, ImgHeight));Mat RoiFloat;//将图像转化成CV_32FC1resizeRoi.convertTo(RoiFloat, CV_32FC1);RoiFloat = RoiFloat.reshape(0, 1);float f = knn->predict(RoiFloat);//进行字符识别预测//结果显示char text[50];sprintf_s(text, "%c",char(int(f)));cout << char(int(f)) << endl;//将字符结果float转成chardouble scale = rect.width * 0.02;putText(Test_Chars, text, rect.br(), FONT_HERSHEY_SIMPLEX, scale, Scalar(0, 255, 0), 2);imshow("Test_Chars", Test_Chars);waitKey(0); }}

四、源码

#include<iostream>
#include<opencv2/opencv.hpp>
#include<opencv2/ml.hpp>
#include<fstream>
using namespace std;
using namespace cv;const int ImgWidth = 20;//图片宽
const int ImgHeight = 30;//图片高int main()
{//准备数据集Mat Train_Chars = imread("training_chars.png");//数据集图像Mat Test_Chars = imread("test.jpg");//测试图像if (Train_Chars.empty()|| Test_Chars.empty()){cout << "can not read the image..." << endl;system("pause");return -1;}string filename = "model.xml";//模型文件fstream fin;fin.open(filename, ios::in);if (fin.is_open()){Ptr<ml::KNearest>knn = cv::Algorithm::load<cv::ml::KNearest>(filename);//加载KNN模型//图像预处理Mat grayImg;cvtColor(Test_Chars, grayImg, COLOR_BGR2GRAY);Mat blurImg;GaussianBlur(grayImg, blurImg, Size(3, 3), 0);Mat binImg;threshold(blurImg, binImg, 200, 255, THRESH_BINARY_INV);vector<vector<Point>>contours;findContours(binImg, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//查找最外轮廓for (int cnt = 0; cnt < contours.size(); cnt++){if (contourArea(contours[cnt]) > 10){Rect rect = boundingRect(contours[cnt]);//轮廓外接矩形rectangle(Test_Chars, rect, Scalar(0, 0, 255), 2);Mat ROI = binImg(rect);//字符ROI图像Mat resizeRoi;//将字符resize成固定大小resize(ROI, resizeRoi, Size(ImgWidth, ImgHeight));Mat RoiFloat;//将图像转化成CV_32FC1resizeRoi.convertTo(RoiFloat, CV_32FC1);RoiFloat = RoiFloat.reshape(0, 1);float f = knn->predict(RoiFloat);//进行字符识别预测//结果显示char text[50];sprintf_s(text, "%c",char(int(f)));cout << char(int(f)) << " ";//将字符结果float转成chardouble scale = rect.width * 0.02;putText(Test_Chars, text, rect.br(), FONT_HERSHEY_SIMPLEX, scale, Scalar(0, 255, 0), 2);imshow("Test_Chars", Test_Chars);waitKey(0); }}cout << endl;}else{//由于我这里的数据字符图像只包含数字以及大写英文字符,只识别数字字符以及大写英文字符vector<int>ValidChars ={'0', '1', '2', '3', '4', '5', '6', '7', '8', '9','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T','U', 'V', 'W', 'X', 'Y', 'Z' };//进行图像预处理,提取字符轮廓Mat grayImg;cvtColor(Train_Chars, grayImg, COLOR_BGR2GRAY);Mat blurImg;GaussianBlur(grayImg, blurImg, Size(3, 3), 0);Mat binImg;threshold(blurImg, binImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);vector<vector<Point>>contours;findContours(binImg, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//准备训练所用数据集,将字符通过键盘输入给予标签Mat Train_Data, Train_Label;for (int cnt = 0; cnt < contours.size(); cnt++){if (contourArea(contours[cnt]) > 10){Rect rect = boundingRect(contours[cnt]);rectangle(Train_Chars, rect, Scalar(0, 255, 0), 2);Mat ROI = binImg(rect);imshow("ROI", ROI);imshow("Training_Chars", Train_Chars);int charVal = waitKey(0); //将字符通过键盘输入给予标签if (find(ValidChars.begin(), ValidChars.end(), charVal) != ValidChars.end()){//如果输入的字符在字符匹配表中,则进行存储//由于我们在识别字符时,会遇到各种尺寸的字符,故将所有的字符固定同一尺寸Mat resizeRoi;resize(ROI, resizeRoi, Size(ImgWidth, ImgHeight));//将图像转成浮点型,因为KNN训练数据集读取的是浮点型数据Mat RoiFloat;resizeRoi.convertTo(RoiFloat, CV_32FC1);Train_Data.push_back(RoiFloat.reshape(0,1));Train_Label.push_back(charVal);cout << charVal << endl;}}}//进行KNN训练Train_Data.convertTo(Train_Data, CV_32FC1);Train_Label.convertTo(Train_Label, CV_32FC1);const int k = 3;//k取值,基数Ptr<ml::KNearest>knn = ml::KNearest::create();//构造KNN模型knn->setDefaultK(k);//设定k值knn->setIsClassifier(true);//KNN算法可用于分类,回归knn->setAlgorithmType(ml::KNearest::BRUTE_FORCE);//字符匹配算法knn->train(Train_Data, ml::ROW_SAMPLE, Train_Label);//模型训练knn->save(filename);//模型保存cout << "Model training is complete!" << endl;}destroyAllWindows();system("pause");return 0;
}

总结

本文使用OpenCV C++ 进行字符识别,主要操作有以下几点。
1、制作数据集,利用键盘输入字符相应的ASCII码
2、进行KNN训练,得到训练模型
3、将预测结果转回相应的字符char类型

OpenCV C++案例实战三十二《字符识别》相关推荐

  1. OpenCV C++案例实战三《二维码检测》

    OpenCV C++案例实战三<二维码检测> 前言 一.二维码检测 二.二维码识别 1.通过findContours找到轮廓层级关系 三.二维码绘制 四.源码 总结 前言 本文将使用Ope ...

  2. OpenCV学习笔记(三十一)——让demo在他人电脑跑起来 OpenCV学习笔记(三十二)——制作静态库的demo,没有dll也能hold住 OpenCV学习笔记(三十三)——用haar特征训练自己

    OpenCV学习笔记(三十一)--让demo在他人电脑跑起来 这一节的内容感觉比较土鳖.这从来就是一个老生常谈的问题.学MFC的时候就知道这个事情了,那时候记得老师强调多次,如果写的demo想在人家那 ...

  3. android圆角对话框,Android项目实战(三十二):圆角对话框Dialog

    原文: Android项目实战(三十二):圆角对话框Dialog 前言:html 项目中多处用到对话框,用系统对话框太难看,就本身写一个自定义对话框.android 对话框包括:一.圆角程序员 二.a ...

  4. OpenCV C++案例实战二十九《遥感图像分割》

    OpenCV C++案例实战二十九<遥感图像分割> 前言 一.准备数据 二.K-Means分类 三.效果显示 四.源码 总结 前言 本案例基于k-means机器学习算法进行遥感图像分割.主 ...

  5. OpenCV C++案例实战十二《图像全景拼接》

    OpenCV C++案例实战十二<图像全景拼接> 前言 一.OpenCV Stitcher 1.功能源码 2.效果 二.图像全景拼接 1.特征检测 2.计算单应性矩阵 3.透视变换 4.图 ...

  6. OpenCV C++案例实战二十二《手势识别》

    OpenCV C++案例实战二十二<手势识别> 前言 一.手部关键点检测 1.1 功能源码 1.2 功能效果 二.手势识别 2.1算法原理 2.2功能源码 三.结果显示 3.1功能源码 3 ...

  7. OpenCV C++案例实战十八《抖音特效——“蓝线挑战”》

    OpenCV C++案例实战十八<抖音特效--"蓝线挑战"> 前言 一.图像扫描 二.生成定格图像 三.图像混合 四.效果显示 五.源码 总结 前言 本文将使用Open ...

  8. OpenCV C++案例实战十九《制作电子相册查看器》

    OpenCV C++案例实战十九<制作电子相册查看器> 前言 一.图片读取 二.图片展示 三.键盘控制 四.效果显示 五.源码 总结 前言 本文将使用OpenCV C++ 制作电子相册查看 ...

  9. OpenCV C++案例实战十六《制作哈哈镜图像》

    OpenCV C++案例实战十六<制作哈哈镜图像> 前言 一.凸透镜 1.功能源码 2.效果显示 二.凹透镜 1.功能源码 2.效果显示 三.源码 总结 前言 本文将使用OpenCV C+ ...

最新文章

  1. 不要依赖代码中的异常
  2. 5G:这次中国说了算!
  3. 可以比较两个指针是否相等_算法一招鲜——双指针问题
  4. html上传文件与后台处理,关于前端html图片和文件上传和后台接收方法
  5. Shell 正则表达式总结及其含义举例
  6. 单击修复计算机英语怎么说,电脑选择语言方式,单击修复计算机
  7. APP不显示GPU过度渲染的问题
  8. ruby array_Ruby中带有示例的Array.select方法
  9. 杂谈---2013年,总结?吐槽?灌水?
  10. BoxFilter包滤波器
  11. 万物皆可python_Python知识点合集,学完万物皆可爬
  12. Docker+K8s视频教程下载、学习笔记
  13. linux如何查看内存命令
  14. 未来教育MySQL题库下载_未来教育考试系统(通用下载管理版)可在线下载最新试题,支持章节练习...
  15. 运营的新手先简单认识一下ASO
  16. 神武临时服回服务器在那个位置,2021年6月25日游戏更新公告
  17. Lazy evaluation
  18. Ramnit 蠕虫分析
  19. 解决网易服务器延迟大,网易WOW服务器延迟
  20. Android手把手教你使用阿里云接口实现人脸定位、人脸检测、人脸对比功能。

热门文章

  1. linux查看文件版本,Linux下查看版本号的命令
  2. Python 三角形第三边
  3. Spring 有几种事务隔离级别?
  4. 使用QLExpress动态制定计算公式
  5. 《趋势的力量》读书笔记
  6. [2021.11.20]使用ContentProvider实现数据共享
  7. php imagick 缩略图,PHP Imagick完美实现图片裁切、生成缩略图、添加水印,
  8. 把Word/Pages/keynote..文件的图片抓取出来的方法
  9. 商业隔断装修中的材料选择有何注意事项?
  10. PaperWeekly 第35期 | 如何让聊天机器人懂情感